Expo - React Native GeoFence with API Data - api

I am trying to work on a simple proof of concept for geofencing around certain latitude and longitudes
Structure: - location service is turned on (no need background updates for now just initial location) - user location is determined - looks at data of locations in json file and finds the ones in close proximity #10km (refer to URL for API)
EXPO has recently updated the SDK to manage this, but with looking through multiple searches nothing really comes up with managing a simple task of placing static location to a database and then runs the proximity radius.
Using a basic setup for the concept but not sure next steps... Below is the setup that I have for now and would like some direction on where the GeoFence calls begins
import React, {Component} from 'react';
import { AppRegistry, Text, View, StyleSheet, FlatList, Image } from 'react-native';
import { Constants, MapView, Location, Permissions } from 'expo';
export default class App extends Component {
constructor() {
super ()
this.state = {
dataSource: []
}
}
state = {
mapRegion: null,
hasLocationPermissions: false,
locationResult: null
};
_handleMapRegionChange = mapRegion => {
console.log(mapRegion);
this.setState({ mapRegion });
};
_getLocationAsync = async () => {
let { status } = await Permissions.askAsync(Permissions.LOCATION);
if (status !== 'granted') {
this.setState({
locationResult: 'Permission to access location was denied',
});
} else {
this.setState({ hasLocationPermissions: true });
}
let location = await Location.getCurrentPositionAsync({});
this.setState({ locationResult: JSON.stringify(location) });
// Center the map on the location we just fetched.
this.setState({mapRegion: { latitude: location.coords.latitude, longitude: location.coords.longitude, latitudeDelta: 0.0922, longitudeDelta: 0.0421 }});
};
renderItem = ({item}) => {
return (
<View>
<View>
<Text>{item.code}
<Text>{item.name}
<Text>{item.lat}
<Text>{item.lon}
</Text>
</Text>
</Text>
</Text>
</View>
</View>
)}
componentDidMount() {
this._getLocationAsync();
//const url = 'https://www.json-generator.com/api/json/get/ccLAsEcOSq?indent=1'
const url = 'https://gist.githubusercontent.com/tdreyno/4278655/raw/7b0762c09b519f40397e4c3e100b097d861f5588/airports.json'
fetch(url)
.then((repsonse) => repsonse.json())
.then((responseJson) => {
this.setState({
dataSource: responseJson
})
})
.catch((error) => {
console.log(error)
})
}
render() {
return (
<View style={styles.container}>
<Text style={styles.paragraph}>
Pan, zoom, and tap on the map!
</Text>
{
this.state.locationResult === null ?
<Text>Finding your current location...</Text> :
this.state.hasLocationPermissions === false ?
<Text>Location permissions are not granted.</Text> :
this.state.mapRegion === null ?
<Text>Map region doesn't exist.</Text> :
<MapView
style={{ alignSelf: 'stretch', height: 200 }}
region={this.state.mapRegion}
onRegionChange={this._handleMapRegionChange}
/>
}
<Text>
Location: {this.state.locationResult}
</Text>
<View style={styles.container}>
<FlatList
//={[{ key: 'a'}, { key: 'b'}]}
//renderItem={({item }) => <Text>{item.key}</Text>}
data={this.state.dataSource}
renderItem={this.renderItem}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
This is only proof of concept and just need some directions which will help me move on. Links and Tutorials anything will help
Thanks (still learning)

Related

Undefined is not an object in React native when rendering

I'm new to React and React native and am trying to retrieve data from an API and then render it but I seem to be running into problems.
I'm getting errors like:
undefined is not an object (evaluating 'attractions.map') in RenderPhotos_render
I may have jumped into React Native too early...So excuse my lack of knowledge!
import React, {Component} from 'react';
import {StyleSheet, View, ActivityIndicator} from 'react-native';
import MapView, {Marker} from 'react-native-maps';
import {connect} from 'react-redux';
const mapStateToProps = state => {
return {
attractions: state.attractions.attractions,
};
};
const mapDispatchToProps = dispatch => {
return {
GET_Attractions(callback) {
dispatch({type: 'attractions/GET_Attractions', callback});
},
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(
class Home extends Component {
state = {
loading: true,
};
componentDidMount = () => {
const callback = () => {
this.setState({
loading: false,
});
};
this.props.GET_Attractions(callback);
};
renderMapMarker = () => {
const {attractions} = this.props;
return attractions.map(marker => {
return (
<Marker
key={marker.caseId}
title={marker.caseName}
description="點擊查看詳細資料"
coordinate={{
latitude: marker.latitude,
longitude: marker.longitude,
}}
/>
);
});
};
render() {
if (this.state.loading) {
return (
<View style={styles.container}>
<ActivityIndicator />
</View>
);
} else {
return (
<View style={styles.container}>
<MapView
style={styles.mapView}
initialRegion={{
latitude: 24.149706,
longitude: 120.683813,
latitudeDelta: 8,
longitudeDelta: 8,
}}>
{this.renderMapMarker()}
</MapView>
</View>
);
}
}
},
);
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
mapView: {
width: '100%',
height: '100%',
},
});
By default attractions might be undefined so you can check to validate first then render if it has data to render like
render() {
if (this.state.loading) {
return (
...
);
} else {
return (
<View style={styles.container}>
<MapView
...
>
{this.props.attractions && this.renderMapMarker()} // and this
</MapView>
</View>
);
}
}
You are trying to use a prop "attractions" that is being populated from the reducer, so initially when your screen is being rendered the prop "attractions" will be undefined so you need to achieve this with the condition to make the error go away.
{attractions && this.renderMapMarker()}
Just add ? after attractions and it should work.
renderMapMarker = () => {
const {attractions} = this.props;
return attractions?.map(marker => {
return (
<Marker
key={marker.caseId}
title={marker.caseName}
description="點擊查看詳細資料"
coordinate={{
latitude: marker.latitude,
longitude: marker.longitude,
}}
/>
);
});
};

React Native - Get Location latitude and longitude using react-native-get-location

I am creating an app which uses the phone's location. I would like to be able to get the latitude and longitude and using it as part of an api address.
I have been following this sample code using react-native-get-location and have been able to print the information in json formate but can't pull the latitude and longitude and use them.
react-native-get-location
Here is my code.
import GetLocation from 'react-native-get-location'
export default class App extends React.Component {
constructor (props) {
super(props);
this.state = {
isLoading: true,
latitude: null,
longitude: null,
location: null
};
}
_requestLocation = () => {
GetLocation.getCurrentPosition({
enableHighAccuracy: true,
timeout: 150000,
})
.then(location => {
this.setState ({
location,
isLoading: false,
});
})
.catch(error => {
const { code, message} = error;
if (code === 'CANCELLED') {
Alert.alert('location cancelled by user or by another request');
}
if (code === 'UNAVAILABLE') {
Alert.alert('Location service is disabled or unavailable');
}
if (code === 'TIMEOUT') {
Alert.alert('Location request timed out');
}
if (code === 'UNAUTHORIZED') {
Alert.alert('Authorization denied')
}
this.setState({
location: null,
isLoading: false,
});
});
}
componentDidMount() {
GetLocation.getCurrentPosition(async (info) => {
const location = await GetLocation(
info.coords.latitude,
info.coords.longitude
);
})
const fetch = require('node-fetch');
fetch('https://api.weatherapi.com/v1/forecast.json?&q=London', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
}).then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
this.setState({
isLoading: false,
dataSource: responseJson,
})
}).catch((error) => {
console.error(error);
});
}
render() {
const {location, isLoading} = this.state;
if (this.state.isLoading) {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{flex:1, paddingTop: 20}}>
<Text>{JSON.stringify(location, 0, 2)}</Text>
<View style={{flex:1, flexDirection: 'row', textAlign: 'center', paddingLeft: 90}}>
<Button
disabled={isLoading}
title="Get Location"
onPress={this._requestLocation}
/>
</View>
</View>
)
}
}
Use expo-location instead of react-native-get-location as it is very easy to implement.
Here is the working app: Expo Snack
Screenshot:
import React, { useEffect, useState } from 'react';
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
import Constants from 'expo-constants';
// You can import from local files
let apiKey = 'YOUR_API_KEY';
import * as Location from 'expo-location';
export default function App() {
const [location, setLocation] = useState(null);
const [errorMsg, setErrorMsg] = useState(null);
const [address, setAddress] = useState(null);
// const [getLocation, setGetLocation] = useState(false);
const getLocation = () => {
(async () => {
let { status } = await Location.requestPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
}
Location.setGoogleApiKey(apiKey);
console.log(status);
let { coords } = await Location.getCurrentPositionAsync();
setLocation(coords);
console.log(coords);
if (coords) {
let { longitude, latitude } = coords;
let regionName = await Location.reverseGeocodeAsync({
longitude,
latitude,
});
setAddress(regionName[0]);
console.log(regionName, 'nothing');
}
// console.log();
})();
};
return (
<View style={styles.container}>
<Text style={styles.big}>
{!location
? 'Waiting'
: `Lat: ${location.latitude} \nLong: ${
location.longitude
} \n${JSON.stringify(address?.['subregion'])}`}
</Text>
<TouchableOpacity onPress={getLocation}>
<View
style={{
height: 100,
backgroundColor: 'teal',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 10,
marginTop: 20,
}}>
<Text style={styles.btnText}> GET LOCATION </Text>
</View>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center',
},
big: {
fontSize: 18,
color: 'black',
fontWeight: 'bold',
},
btnText: {
fontWeight: 'bold',
fontSize: 25,
color: 'white',
},
});
react-native-geolocation-service is a good alternative too for fetching latitude & longitude values.
Example usage:
import GeoLocation from 'react-native-geolocation-service';
const getDeviceCurrentLocation = async () => {
return new Promise((resolve, reject) =>
GeoLocation.getCurrentPosition(
(position) => {
resolve(position);
},
(error) => {
reject(error);
},
{
enableHighAccuracy: true, // Whether to use high accuracy mode or not
timeout: 15000, // Request timeout
maximumAge: 10000 // How long previous location will be cached
}
)
);
};

Redirecting user based on his authentication

In my react native application, I want to check if a user has already logged in before, then he must be redirected to 'Home' directly without asking him for credentials again. The 'Home' component consists of logout button in a sidebar.
My current code works fine for new user logging, but I am stuck on how to check if again the user open the application, his login token should persist and he must be redirected to 'Home' directly.
Here is my code:
import React, { Component } from 'react'
import { Text, View, Image, TextInput, TouchableOpacity, ScrollView, AsyncStorage, ToastAndroid } from 'react-native'
import axios from 'axios';
export default class Login extends Component {
constructor(){
super();
this.state = {
username: '',
password: '',
isLoggedIn: false,
loginChecked: false,
}
}
componentDidMount(){
this.getItem('userID');
}
//function to extract storage token. Any name can be given ot it.
async getItem(item){
console.log('method ran login screen');
console.log(this.state.isLoggedIn)
try{
const value = await AsyncStorage.getItem(item);
if(value !== null){
this.setState({
isLoggedIn: true,
loginChecked: true
})
}
else{
this.setState({
isLoggedIn: false,
loginChecked: true
})
}
}
catch(error){
console.log(error)
}
console.log(this.state.isLoggedIn)
}
//function to remove storage token
async removeItem(item){
try{
const value = await AsyncStorage.removeItem(item);
if(value == null){
this.setState({
isLoggedIn: false
})
}
}
catch(error){
//handle errors here
}
}
userLogin = () => {
if(this.state.username != '' && this.state.password != ''){
axios.post('http://bi.servassure.net/api/login', {
username: this.state.username,
password: this.state.password
})
.then(res => {
let userToken = res.data.token;
console.log(res.data);
if(res.data.success){
AsyncStorage.setItem('userID', userToken);
this.setState({
isLoggedIn: true
})
this.props.navigation.navigate('Home');
}
else{
ToastAndroid.showWithGravity(
'Invalid login' ,
ToastAndroid.SHORT,
ToastAndroid.CENTER
);
}
})
.catch(err => {
console.log(err);
});
}
else{
ToastAndroid.showWithGravity(
'Please Enter Credentials' ,
ToastAndroid.SHORT,
ToastAndroid.CENTER
);
}
}
logOut(){
this.removeItem('userID');
}
render() {
return (
<ScrollView>
<View style={{justifyContent:'center', alignItems:'center'}}>
<View style={{marginVertical:20}}>
<Text>
Login to your account
</Text>
</View>
<View>
<TextInput
style={{width: 300, height: 50, borderColor: 'gray', borderWidth: 1, borderRadius: 10, marginVertical: 10}}
onChangeText={(username) => this.setState({username})}
placeholder='username'
autoCapitalize = 'none'
/>
<TextInput
style={{width: 300, height: 50, borderColor: 'gray', borderWidth: 1, borderRadius: 10}}
onChangeText={(password) => this.setState({password})}
placeholder='password'
secureTextEntry={true}
/>
</View>
<View style={{justifyContent: 'center', alignItems: 'center'}}>
<TouchableOpacity
style={{width: 300, height: 50, borderWidth:1, borderRadius: 50, borderColor: 'gray', justifyContent: 'center', alignItems: 'center', marginVertical: 10}}
onPress={this.userLogin}>
<Text>
LOGIN
</Text>
</TouchableOpacity>
<Text>Forget Password</Text>
</View>
</View>
</ScrollView>
)
}
}
Also, I have a SplashScreen before login:
import React, { Component } from 'react'
import { Text, View } from 'react-native'
export default class SplashScreen extends Component {
componentDidMount(){
setTimeout( () => {
this.props.navigation.navigate('Login')
}, 2000)
}
render() {
return (
<View style={styles.viewStyles}>
<Text style={styles.textStyles}> My App </Text>
</View>
)
}
}
const styles = {
viewStyles: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'orange'
},
textStyles: {
color: 'white',
fontSize: 40,
fontWeight: 'bold'
}
}
I am bit new to react native, please help to figure this out.
Do something like this in your Login.js file:
import {AsyncStorage} from react-native;
After getting the response success of login API you can do this:
AsyncStorage.setItem('userID', responsejson.user_detail.userID);
in the same way, you can store the token also
AsyncStorage.setItem('token', responsejson.user_detail.token);
Then in your splashscreen.js, import AsyncStorage the same way as above and then place this code in componentWillMount() or componentDidMount()
of your splashscreen.js
var value = AsyncStorage.getItem('token');
value.then((e)=>{
if (e == '' || e == null ){
this.props.navigation.replace('Login')
}else {
this.props.navigation.replace('Home')
}
})
Explanation: When splashscreen.js is loaded then it checks for the token in asyncstorage and if it gets the token navigate to Home screen else navigate to Login screen.
Import the React Navigation library and use the Switch Navigator. It was designed to handle app navigation based on the login status of the user.
This article explains everything with examples

How to open the camera and taking the picture in react native?

I want to open the device camera from my app when user click on the button and when user click on back button it should react to my application from device camera. I am able to open camera and take photo by running react native project. But I want to do it how camera works in what's app. That is clicking on button -> opening camera -> send button .
I am an beginner in react native .I tried many ways but I am not getting how it can be done.
Can anybody assist me to do this.
My App.js code is,
'use strict';
import React, { Component } from 'react';
import {
AppRegistry,
Dimensions,
StyleSheet,
Text,
TouchableHighlight,
View
} from 'react-native';
import Camera from 'react-native-camera';
class BadInstagramCloneApp extends Component {
render() {
return (
<View style={styles.container}>
<Camera
ref={(cam) => {
this.camera = cam;
}}
style={styles.preview}
aspect={Camera.constants.Aspect.fill}>
<Text style={styles.capture} onPress={this.takePicture.bind(this)}>[CAPTURE]</Text>
</Camera>
</View>
);
}
takePicture() {
const options = {};
//options.location = ...
this.camera.capture({metadata: options})
.then((data) => console.log(data))
.catch(err => console.error(err));
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
},
preview: {
flex: 1,
justifyContent: 'flex-end',
alignItems: 'center'
},
capture: {
flex: 0,
backgroundColor: '#fff',
borderRadius: 5,
color: '#000',
padding: 10,
margin: 40
}
});
AppRegistry.registerComponent('BadInstagramCloneApp', () => BadInstagramCloneApp);
You can use the state to show/hide the camera view/component.
Please check the following code:
...
class BadInstagramCloneApp extends Component {
constructor(props) {
super(props);
this.state = {
isCameraVisiable: false
}
}
showCameraView = () => {
this.setState({ isCameraVisible: true });
}
render() {
const { isCameraVisible } = this.state;
return (
<View style={styles.container}>
{!isCameraVisible &&<Button title="Show me Camera" onPress={this.showCameraView} />}
{isCameraVisible &&
<Camera
ref={(cam) => {
this.camera = cam;
}}
style={styles.preview}
aspect={Camera.constants.Aspect.fill}>
<Text style={styles.capture} onPress={this.takePicture.bind(this)}>[CAPTURE]</Text>
</Camera>}
</View>
);
}
takePicture() {
const options = {};
//options.location = ...
this.camera.capture({metadata: options})
.then((data) => {
console.log(data);
this.setState({ isCameraVisible: false });
}
.catch(err => console.error(err));
}
}
...
You can use https://github.com/ivpusic/react-native-image-crop-picker for this. This component helps you to take photo and also the photo if required. Follow the documentation correctly and here is the code for camera selection option
ImagePicker.openCamera({
cropping: true,
width: 500,
height: 500,
cropperCircleOverlay: true,
compressImageMaxWidth: 640,
compressImageMaxHeight: 480,
freeStyleCropEnabled: true,
}).then(image => {
this.setState({imageModalVisible: false})
})
.catch(e => {
console.log(e), this.setState({imageModalVisible: false})
});
Correction of best answer because of deprecation of Camera to RNCamera plus missing closing bracket ")" right before the .catch and like a spelling mistake with the declaration of state:
But basically there's 2 routes, whether you're using expo or react native. You gotta have Pods/Ruby/Cocoapods or manually link and all that if you're using traditional React Native, but just go with expo-camera if you got an expo set up and don't listen to this.
This is a React-Native with Pods/Ruby/CocoaPods solution, whereas going with expo-camera might be much faster and better if you're not set up like this.
import React, { Component } from 'react';
import {
Text,
View,
StyleSheet,
Button,
TouchableOpacity
} from 'react-native';
import { RNCamera } from 'react-native-camera';
export default class Camera2 extends Component {
constructor(props) {
super(props);
this.state = {
isCameraVisible: false
}
}
showCameraView = () => {
this.setState({ isCameraVisible: true });
}
takePicture = async () => {
try {
const data = await this.camera.takePictureAsync();
console.log('Path to image: ' + data.uri);
} catch (err) {
// console.log('err: ', err);
}
};
render() {
const { isCameraVisible } = this.state;
return (
<View style={styles.container}>
{!isCameraVisible &&<Button title="Show me Camera" onPress={this.showCameraView} />}
{isCameraVisible &&
<RNCamera
ref={cam => {
this.camera = cam;
}}
style={styles.preview}
>
<View style={styles.captureContainer}>
<TouchableOpacity style={styles.capture} onPress={this.takePicture}>
<Text style={styles.capture} onPress={this.takePicture.bind(this)}>[CAPTURE]</Text>
<Text>Take Photo</Text>
</TouchableOpacity>
</View>
</RNCamera>}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
},
preview: {
flex: 1,
justifyContent: 'flex-end',
alignItems: 'center'
},
capture: {
flex: 0,
backgroundColor: '#fff',
borderRadius: 5,
color: '#000',
padding: 10,
margin: 40
}
});

In react-native there is TypeError

'use strict';
import React, {Component} from 'react';
import {
StyleSheet,
Text,
View,
TextInput,
Image
} from 'react-native';
var Forecast=require('./Forecast');
const APIKEY = "API KEY";
var WeatherProject = React.createClass({
//if you want to have a default zip code, 넌 여기에 넣을 수 있다.
getInitialState(){
return {
zip:'', //우편 번호
forecast: null
};
},
_handleTextChange(event){
var zip= event.nativeEvent.text;
this.setState({zip:zip});
fetch('http://api.openweathermap.org/data/2.5/weather?zip='
+zip+'.KR&units=metric&APPID='+APIKEY)
.then((response) => response.json())
.then((responseJSON) => {
this.setState({
forecast: {
main: responseJSON.weather[0].main,
description: responseJSON.weather[0].description,
temp: responseJSON.main.temp
}
});
})
.catch((error) => {
console.warn(error);
});
},
render(){
var content = null;
if( this.state.forecast!==null){
content = <Forecast
main={this.state.forecast.main}
description={this.state.forecast.description}
temp={this.state.forecast.temp}/>;
}
return(
<View style={styles.container}>
<Image
source = {require('./img/flower.jpeg')}
resizeMode='cover'
style={styles.backdrop}>
<View style={styles.overlay}>
<View style={styles.row}>
<Text style={styles.mainText}>
Current weather for
</Text>
<View>
<TextInput
style={styles.zipCode}
returnKeyType='go'
onSubmitEditing={this._handleTextChange}/>
</View>
</View>
{content}
</View>
</Image>
</View>
);
}
});
var baseFontSize = 16;
var styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
paddingTop:5
},
backdrop: {
flex:1,
flexDirection:'column'
},
overlay:{
paddingTop:5,
backgroundColor:'#000000',
opacity:0.5,
flexDirection:'column',
alignItems:'center'
},
row:{
flex:1,
flexDirection:'row',
flexWrap:'nowrap',
alignItems:'flex-start',
padding:30
},
zipCode:{
width:70,
height:30,
marginLeft:5,
backgroundColor:'#FFFFFF',
fontSize:20,
padding:0,
color: '#000000'
},
mainText:{
flex:1,
fontSize:baseFontSize,
color:'#FFFFFF'
}
});
module.exports=WeatherProject;
In _handleTextChange(event) function
.then((response) => response.json())
.then((responseJSON) => {
this.setState({
forecast: {
main: responseJSON.weather[0].main,
description: responseJSON.weather[0].description,
temp: responseJSON.main.temp
}
});
})
in this code, TypeError: undefined is not an object(evaluating 'responseJSON.weather[0]') There is an error.. why this error occuered?
How can I exchange this code to execute normally?
Your error is occurring because you have left out a valid API key to query against the OpenWeatherMap API. The response that comes back for what you have written is:
{"cod":401, "message": "Invalid API key. Please see http://openweathermap.org/faq#error401 for more info."}
Following this link will explain the requirement to set up an account and create an API key to use this service.
I created an account and API key with OpenWeatherMap, used your code sample, popping in a simple stateless component to dump out the props passed in to the Forecast component to verify this is all you need to correct your error.