I have created an android app with CRNA, I can get the current location of the user by Expo's location API. But I can't figure out how to get the city name of a pinned location? This is my code so far:
class Map extends Component {
constructor(props) {
super(props);
this.state = {
region: {
latitude: 41.0141977,
longitude: 28.9638121,
latitudeDelta: 0.1,
longitudeDelta: 0.05,
},
x: {
latitude: 41.0238343,
longitude: 29.0335236,
},
regionName: "",
}
}
onDragEnd(e) {
this.setState({x: e.nativeEvent.coordinate});
}
onRegionChange(region) {
this.setState({region});
}
render() {
return (
<Modal
animationType={"slide"}
transparent={true}
visible={this.props.visible}
onRequestClose={this.props.changeState}
style={styles.modal}
>
<MapView
region={this.state.region}
onRegionChange={this.onRegionChange.bind(this)}
style={styles.map}
><MapView.Marker draggable
coordinate={this.state.x}
onDragEnd={this.onDragEnd.bind(this)}
/></MapView>
</Modal>
);
}
}
For that you need to use some for of geocoding.
In general look at the google maps page about geocoding.
And here is an npm package for react native.
As I googled more and more I learnt it's called reverse geocoding. So I created an redux action like this and it solved my problem very well:
export function fetchCityName(lat, lng) {
const link = `http://maps.googleapis.com/maps/api/geocode/json?latlng=${lat},${lng}`;
return dispatch => {
dispatch(startFetch());
return fetch(link)
.then(response => response.json())
.then(responseJson => {
const addressComponent = _.get(responseJson, 'results[0].address_components', []);
const getAreaName = zone => _.get(
addressComponent,
`[${_.findIndex(addressComponent, obj => _.includes(obj.types, zone))}].long_name`
);
const region = ' ' + getAreaName('administrative_area_level_1');
const country = ' ' + getAreaName('country');
const region2 = getAreaName('administrative_area_level_2');
const location = region2 ?
[region2, region, country].toString() :
region.concat(',' + country);
dispatch(getCityName(location));
})
.catch(console.error)
Related
I have a MapView that I want to add Markers to. I can successfully add Markers individually but I want to loop through an array of coordinates and add a marker for each location in the array. Whenever trying to run this code, I get an "Unexpected Token" error where the for loop begins. I have read suggestions about using the .map function on the array but that does not seem to work for me either. Any help would be highly appreciated.
const ViewNearbyScreen = ({
navigation, route
}) => {
const points = [
{
name: "Wendys",
lat: 37.4319983,
lng: -122.094,
},
{
name: "Taco Bell",
lat: 37.4419983,
lng: -122.104,
},
{
name: "Whataburger",
lat: 37.4519983,
lng: -122.114,
},
];
return (
<MapView
provider={PROVIDER_GOOGLE}
style={{flex: 1}}
region={{
latitude: 37.4219983,
longitude: -122.084,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421
}}
ref={mapRef}
showsUserLocation>
{for(let i = 0; i < 3; i++) {
<Marker
coordinate={{
latitude: points[{i}].lat,
longitude: points[{i}].lng,
}}
title={points[{i}].name}
/>
}}
</MapView>
)
};
You can use the Array Map method to loop through the array elements.
points.map(item => { // each point in points array
return (
<Marker
coordinate={{
latitude: item.lat,
longitude: item.lng,
}}
title={item.name}
/>
)
})
The reason the map method wasn't working for you might be that you forgot to return your jsx.
I am trying to get navigation directions using mapbox-sdk for react-native from npm package:
"#mapbox/mapbox-sdk": "^0.11.0"
And for rendering the directions returned by mapbox-sdk I am using the below npm package:
"#react-native-mapbox-gl/maps": "^8.1.0-rc.8",
Code I am using for retrieving directions:
import MapboxGL from '#react-native-mapbox-gl/maps'
// Mapbox SDK related package
import MapboxDirectionsFactory from '#mapbox/mapbox-sdk/services/directions'
import { lineString as makeLineString } from '#turf/helpers'
import GeoLocationService from '../../services/geolocation/GeoLocationService';
import GeoLocationCore from '#react-native-community/geolocation'
const accessToken = "ACESS_TOKEN_FROM_MAPBOX_API_DASHBOARD"
const directionsClient = MapboxDirectionsFactory({accessToken})
constructor(props) {
super(props);
this.state = {
longitude: 0,
latitude: 0,
orderLongitude: 0,
orderLatitude: 0,
route: null,
};
}
async componentDidMount() {
const {route} = this.props
// Lets say route.params contains the below object:
// { "longitude": "33.981982", "latitude": "-6.851599"}
console.log("Params from other screen: ", route.params)
MapboxGL.setAccessToken(accessToken)
MapboxGL.setConnected(true);
MapboxGL.setTelemetryEnabled(true);
const permission = await MapboxGL.requestAndroidLocationPermissions();
let latitude, longitude;
if(Platform.OS == "android") {
GeoLocationService.requestLocationPermission().then(() => {
GeoLocationCore.getCurrentPosition(
info => {
const { coords } = info
latitude = coords.latitude
longitude = coords.longitude
//this.setState({longitude: coords.longitude, latitude: coords.latitude})
this.setState({longitude: -6.873795, latitude: 33.990777, orderLongitude: route.params.longitude, orderLatitude: route.params.latitude})
console.log("your lon: ", longitude)
console.log("your lat", latitude)
this.getDirections([-6.873795, 33.990777], [route.params.longitude, route.params.latitude])
},
error => console.log(error),
{
enableHighAccuracy: false,
//timeout: 2000,
maximumAge: 3600000
}
)
})
}
}
getDirections = async (startLoc, destLoc) => {
const reqOptions = {
waypoints: [
{coordinates: startLoc},
{coordinates: destLoc},
],
profile: 'driving',
geometries: 'geojson',
};
const res = await directionsClient.getDirections(reqOptions).send()
//const route = makeLineString(res.body.routes[0].geometry.coordinates)
const route = makeLineString(res.body.routes[0].geometry.coordinates)
console.log("Route: ", JSON.stringify(route))
this.setState({route: route})
}
Code I am using for rendering road directions fetched by mapbox-sdk:
renderRoadDirections = () => {
const { route } = this.state
return route ? (
<MapboxGL.ShapeSource id="routeSource" shape={route.geometry}>
<MapboxGL.LineLayer id="routeFill" aboveLayerID="customerAnnotation" style={{lineColor: "#ff8109", lineWidth: 3.2, lineCap: MapboxGL.LineJoin.Round, lineOpacity: 1.84}} />
</MapboxGL.ShapeSource>
) : null;
};
Code I am using for rendering map and directions:
render() {
return (
<View style={{ flex: 1 }}>
<MapboxGL.MapView
ref={(c) => this._map = c}
style={{flex: 1, zIndex: -10}}
styleURL={MapboxGL.StyleURL.Street}
zoomLevel={10}
showUserLocation={true}
userTrackingMode={1}
centerCoordinate={[this.state.longitude, this.state.latitude]}
logoEnabled={true}
>
{this.renderRoadDirections()}
<MapboxGL.Camera
zoomLevel={10}
centerCoordinate={[this.state.longitude, this.state.latitude]}
animationMode="flyTo"
animationDuration={1200}
/>
</MapboxGL.MapView>
</View>
)
}
Now when I try to render the GeoJson retreived the road directions line not showing on map, so I thought maybe something wrong with my GeoJson and tested it from here but it looks fine:
https://geojsonlint.com/
GeoJson that I tested and looks okay:
{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-6.880611,33.9916],[-6.882194,33.990166],[-6.882439,33.99015],[-6.882492,33.990028],[-6.882405,33.98991],[-6.878006,33.990299],[-6.87153,33.990978],[-6.871386,33.990925],[-6.871235,33.991016],[-6.869793,33.991165],[-6.870523,33.990292]]}}
Example of what I am trying to achieve:
What could be wrong in my code that makes road directions line not showing on map?
Found what was causing the <LineLayer/> not showing on map, removing the the attribute aboveLayerID from the following line:
<MapboxGL.LineLayer id="routeFill" aboveLayerID="customerAnnotation" style={{lineColor: "#ff8109", lineWidth: 3.2, lineCap: MapboxGL.LineJoin.Round, lineOpacity: 1.84}} />
So it becomes:
<MapboxGL.LineLayer id="routeFill" style={{lineColor: "#ff8109", lineWidth: 3.2, lineCap: MapboxGL.LineJoin.Round, lineOpacity: 1.84}} />
Result:
I've added MapView into my app but i want to initialize the region with the current user's live location, The code on the emulator changes the initial region to the location of google's HQ in CA (I know that's normal and i can change the emulator's location in settings) but on my own phone, the initial region doesn't change at all.
using the GEO location api preferred by react native:
const [region, setRegion] = useState({
latitude: -1.951670,
longitude: 30.157518,
latitudeDelta: 0.015,
longitudeDelta: 0.015,
})
useEffect(() => {
let currentLocation = true;
let newRegion = { latitude: null, longitude: null, latitudeDelta: null, longitudeDelta: null };
newRegion.longitudeDelta = region.longitudeDelta;
newRegion.latitudeDelta = region.latitudeDelta;
Geolocation.getCurrentPosition((info) => {
newRegion.latitude = info.coords.latitude
newRegion.longitude = info.coords.longitude;
},
(error) => {
console.log(error.code, error.message);
},
{ enableHighAccuracy: true, timeout: 15000, maximumAge: 10000 }
)
//To make sure no null is still present in the newReion obj
for (var key in newRegion) {
if (!newRegion[key]) {
currentLocation = false;
break;
}
}
if (currentLocation) {
setRegion(newRegion);
}
}, [])
//Map view implementation:
<MapView
provider={PROVIDER_GOOGLE}
ref={map => _map = map}
showsUserLocation={true}
style={styles.map}
initialRegion={initialPosition}
onRegionChangeComplete={(region) => setInitialPosition(region)}
/>
In our app, we are obtaining the user location when he logs in (click login -> get location -> save location in redux -> navigate to home screen). When the Home component mounts, it should use the location to get data from the area around the user.
In iOS, all markers render correctly but in Android devices, not all markers are rendered, only some of them (and oddly enough all of them are image1 or image2).
It is only when we call the getDataFromRegion function again that all markers render correctly. Any idea of what we are doing wrong?
class Login extends Component {
handleLogin = (u, p) => {
getLocation(location => {
let region = {
latitude: location.coords.latitude,
longitude: location.coords.longitude,
latitudeDelta: 0.06,
longitudeDelta: 0.06
}
/* save location in redux */
this.props.setLocation(region)
login(u, p).then(() => _gotoHome())
})
}
}
class Home extends Component {
componentWillMount() {
if(this.props.location !== undefined) {
initialRegion = {
latitude: this.props.location.latitude,
longitude: this.props.location.longitude,
latitudeDelta: 0.06,
longitudeDelta: 0.06
}
}
}
componentDidMount() {
/* set data in redux */
this.getDataFromRegion(initialRegion)
}
render() {
this.props.data.map((data, index) => data.visible ? <CustomMarker key={index} data={data}/> : null)
}
}
class CustomMarker extends Component {
render() {
const {data} = this.props
const coords = { latitude: data.lat, longitude: data.lng }
return (
<MapView.Marker
coordinate={coords}
onLoad={() => this.forceUpdate()}
tracksViewChanges={Platform.OS === 'android' ? false : true}
>
<Image
onLoad={() => this.forceUpdate()}
source={data.a === '0' ? image1 : image2}
style={{width: 60, height: 60}}
/>
</MapView.Marker>
)
}
}
It seems like removing the Image component and use the MapView.Marker prop image got it working. Also, rendering a dummy MapView.Marker with opacity: 0 inside the MapView solved the problem of appearing the default markers at the first render call.
Can anyone confirm if React Native Geolocation is actually working on Android?
I am stuck with location request timed out while using getCurrentPosition() method and no error message while using watchPosition() method.
I have added the required permission (FINE LOCATION) on AndroidManifest.xml and location permission is allowed for the app.
Here is my implementation:
componentWillMount() {
navigator.geolocation.getCurrentPosition((position) => {
console.log('+++++++');
console.log(position.coords.longitude);
this.setState({
longitude: position.coords.longitude,
error: null,
});
},
(error) => {
console.log(error);
},
);
}
React Native Version : 0.42
you need to change enableHighAccuracy to false
const LATITUDE_DELTA = 0.0922;
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;
this.state = {
region:{}}
navigator.geolocation.getCurrentPosition(
(position) => {
console.log(position);
this.setState({
region: {
longitude: position.coords.longitude,
latitude: position.coords.latitude,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA
}
});
},
(error) => console.log(new Date(), error),
{enableHighAccuracy: false, timeout: 10000, maximumAge: 3000}
);
After implementing all above answers
i had to restart my new phone then it started working
Yes, location works perfectly in our React Native apps for Android.
First of all, getCurrentPosition(..) does not return a watchId. You should be using watchPosition(..) for that. Check that your device has location services enabled and if you're testing it in the emulator, that you've set a location in emulator settings. Another note: on Android M and higher, you also need to request permissions using the PermissionsAndroid.check(...) and PermissionsAndroid.request(...) API calls.
Instead of referring to the position object, just deal with the coords object.
navigator.geolocation.getCurrentPosition(
({coords}) => {
const {latitude, longitude} = coords
this.setState({
position: {
latitude,
longitude,
},
region: {
latitude,
longitude,
latitudeDelta: 0.001,
longitudeDelta: 0.001,
}
})
},
(error) => alert(JSON.stringify(error)),
// 'enableHighAccuracy' sometimes causes problems
// If it does, just remove it.
{enableHighAccuracy: true}
)
I had a similar issue to this because I was in a building which resulted to my device not being able to properly capture the gps signals. If you are in a closed building, try moving outside.
My advise to get perfect location data is to create 2 location handlers.
I use react-native-location as a first handler,
if location cant be fetched, we use the second handler using native geolocation.
Never fails, Compability:
IOS - all versions
ANDROID - all above SDK 20
Check the example below:
Configuration
RNLocation.configure({
distanceFilter: 0.5,
desiredAccuracy: {
ios: "best",
android: "highAccuracy"
},
headingOrientation: "portrait",
// Android ONLY
androidProvider: "auto",
interval: 5000, // Milliseconds
fastestInterval: 10000, // Milliseconds
maxWaitTime: 5000, // Milliseconds
// IOS ONLY
allowsBackgroundLocationUpdates: false,
headingFilter: 1, // Degrees
pausesLocationUpdatesAutomatically: false,
showsBackgroundLocationIndicator: false,
})
Function getUserLocation
static getUserLocation = async () => {
let locationSubscription: any = null
let locationTimeout: any = null
const options = { enableHighAccuracy: true, timeout: 25000 }
return new Promise<any>((resolve, reject) => {
// request permissions using RN Location
RNLocation.requestPermission({
ios: "whenInUse",
android: {
detail: "fine"
}
}).then(granted => {
console.log("Location Permissions: ", granted)
// if has permissions try to obtain location with RN location
if (granted) {
locationSubscription && locationSubscription()
locationSubscription = RNLocation.subscribeToLocationUpdates(([locations]: any) => {
locationSubscription()
locationTimeout && clearTimeout(locationTimeout)
console.log("location fetched with RN Geolocation")
resolve({ coords: { latitude: locations.latitude, longitude: locations.longitude }})
})
} else {
locationSubscription && locationSubscription()
locationTimeout && clearTimeout(locationTimeout)
console.log("no permissions to obtain location")
resolve(null)
}
// if RN Location cant resolve the request use Native Location instead
locationTimeout = setTimeout(() => {
navigator.geolocation.getCurrentPosition((locations) => {
locationSubscription()
locationTimeout && clearTimeout(locationTimeout)
console.log("location fetched with Native Geolocation")
resolve(locations)
}, error => {
locationSubscription && locationSubscription()
locationTimeout && clearTimeout(locationTimeout)
console.log("location error", error)
resolve(null)
}, options);
}, 15000)
})
})
}
cd android
gradlew clean
dependencies
"react-native": "0.62.0",
"#react-native-community/geolocation": "^2.0.2",
Better if you try this code, I have tested in Android OS 8, RN 0.49.5
React Native current location
the trick is to set
{ enableHighAccuracy: true, timeout: 25000, maximumAge: 3600000 }