How to do Reverse Geocoding in react native - react-native

I am trying to get my current location in react native, using react-native-geolocation I get latitude and longitude of my location. Now I want to convert them into the location's address without using the Google API key.
Is there any way to convert latitude longitude into an address without using the Google API key?

There are many ways to convert lon/lat to address without using Google Maps API. Search reverse geocoding api and you'll find a bunch of alternatives.
A few months ago I was being overcharged by Google for reverse geocoding API requests. So I switched to Here. They have a free tier that offers 250k requests/months, which works for my app. See the docs here: https://developer.here.com/documentation/examples/rest/geocoder/reverse-geocode
This will give you highly detailed address data (unlike ip-api.com suggested by Muhammad).
Here is the wrapper function I use to call the API:
function getAddressFromCoordinates({ latitude, longitude }) {
return new Promise((resolve) => {
const url = `https://reverse.geocoder.ls.hereapi.com/6.2/reversegeocode.json?apiKey=${HERE_API_KEY}&mode=retrieveAddresses&prox=${latitude},${longitude}`
fetch(url)
.then(res => res.json())
.then((resJson) => {
// the response had a deeply nested structure :/
if (resJson
&& resJson.Response
&& resJson.Response.View
&& resJson.Response.View[0]
&& resJson.Response.View[0].Result
&& resJson.Response.View[0].Result[0]) {
resolve(resJson.Response.View[0].Result[0].Location.Address.Label)
} else {
resolve()
}
})
.catch((e) => {
console.log('Error in getAddressFromCoordinates', e)
resolve()
})
})
}

there are many alternatives you can search reverse geocoding API
Solution 1:
By using Google map key
const myApiKey="Key Received from Google map"
function getAddressFromCoordinates({latitude, longitude}) {
return new Promise((resolve, reject) => {
fetch(
'https://maps.googleapis.com/maps/api/geocode/json?address=' +
latitude +
',' +
longitude +
'&key=' +
myApiKey,
)
.then(response => response.json())
.then(responseJson => {
if (responseJson.status === 'OK') {
resolve(responseJson?.results?.[0]?.formatted_address);
} else {
reject('not found');
}
})
.catch(error => {
reject(error);
});
});
}
Solution 2:
By using Here Plateform key
https://developer.here.com/documentation/geocoder/dev_guide/topics/example-reverse-geocoding.html
They have a free tier which gives us 250k requests/months free quota
const HERE_API_KEY="Key Received from Here Plateform"
function getAddressFromCoordinates({latitude, longitude}) {
return new Promise((resolve, reject) => {
const url = `https://reverse.geocoder.ls.hereapi.com/6.2/reversegeocode.json?apiKey=${HERE_API_KEY}&mode=retrieveAddresses&prox=${latitude},${longitude}`
fetch(url)
.then(res => res.json())
.then((resJson) => {
if (resJson
&& resJson.Response
&& resJson.Response.View
&& resJson.Response.View[0]
&& resJson.Response.View[0].Result
&& resJson.Response.View[0].Result[0]) {
resolve(resJson.Response.View[0].Result[0].Location.Address.Label)
} else {
reject('not found')
}
})
.catch((e) => {
reject(e);
})
})
}

Solution :
const getAddressFromCoordinates = async(latitude, longitude) => {
try {
const response = await fetch(`https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&key=key`);
const json = await response.json();
setRealAddress(json.results[0]?.formatted_address);
return json?.results[0]
// return json.movies;
} catch (error) {
console.error(error);
}
}

I just ran into the same issue with my react native project and then came here for answers, the first upvoted answer really helped me but I couldn't get it to work on my end so I went to their website and read their docs and it seems they updated the API's response, I was able to get it to work from the updated response and the code is below :
function getAddressFromCoordinates({ latitude, longitude }) {
return new Promise((resolve, reject) => {
const url = `https://revgeocode.search.hereapi.com/v1/revgeocodeat=${latitude}%2C${longitude}&lang=en-US&apiKey=${HERE_API_KEY}`;
fetch(url)
.then(res => res.json())
.then(resJson => {
if (resJson.items[0].title) {
resolve(resJson.items[0].address.label);
} else {
reject('not found');
}
})
.catch(e => {
reject(e);
});
});
}

Related

Can't perform a React state update on an unmounted component. useEffect Hook

I seem to be missing something subtle about avoiding memory leaks. I have read a few posts on how to avoid this with async functions and have tried a few things. All seem to fail. Could someone point out what I'm doing wrong.
useEffect(() => {
let ignore = false;
if (Platform.OS === "android" && !Constants.isDevice) {
errorMessage("Oops, this will not work on Sketch in an Android emulator. Try it on your device!");
} else {
// function to get location, weather and aurora info
const getDataAsync = async () => {
let { status } = await Permissions.askAsync(Permissions.LOCATION);
if (status !== "granted") {
setErrorMessage("Permission to access location was denied");
}
if (!ignore) {
let location = await Location.getCurrentPositionAsync({});
// do stuff with the location data, putting it into states
fetch(`http://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${long}&APPID=${API_KEY}&units=metric`)
.then(res => res.json())
.then(json => {
// do all sorts of stuff with the weather json, putting it into states
});
// Fetch the aurora data
const myUTC = new Date().getTimezoneOffset();
fetch(`http://api.auroras.live/v1/?type=ace&data=bz&tz=${myUTC}&colour=hex`)
.then(res => res.json())
.then(json => {
// do stuff with the aurora json, put it into states
});
setIsLoaded(true); // this is for the activity indicator
}
}
getDataAsync();
return () => { ignore = true; }
}
}, []);
I'm getting the error when deliberately quickly switching out of the screen and back again while the activity indicator is spinning.
Return the cleanup outside of everything! let me know if it works
useEffect(() => {
let ignore = false;
if (Platform.OS === 'android' && !Constants.isDevice) {
errorMessage(
'Oops, this will not work on Sketch in an Android emulator. Try it on your device!',
);
} else {
// function to get location, weather and aurora info
const getDataAsync = async () => {
let {status} = await Permissions.askAsync(Permissions.LOCATION);
if (status !== 'granted') {
setErrorMessage('Permission to access location was denied');
}
if (!ignore) {
let location = await Location.getCurrentPositionAsync({});
// do stuff with the location data, putting it into states
fetch(
`http://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${long}&APPID=${API_KEY}&units=metric`,
)
.then((res) => res.json())
.then((json) => {
// do all sorts of stuff with the weather json, putting it into states
});
// Fetch the aurora data
const myUTC = new Date().getTimezoneOffset();
fetch(
`http://api.auroras.live/v1/?type=ace&data=bz&tz=${myUTC}&colour=hex`,
)
.then((res) => res.json())
.then((json) => {
// do stuff with the aurora json, put it into states
});
setIsLoaded(true); // this is for the activity indicator
}
};
getDataAsync();
}
return () => {
ignore = true;
};
}, []);
That was promising, but no, it didn't work. It may have to do with the fact that there are 2 async fetch requests and one "await" location request with each taking a different amount of time.
I am trying with abortController but that isn't working either:
useEffect(() => {
const abortController = new AbortController();
if (Platform.OS === 'android' && !Constants.isDevice) {
errorMessage(
'Oops, this will not work on Sketch in an Android emulator. Try it on your device!',
);
} else {
// function to get location, weather and aurora info
const getDataAsync = async () => {
let {status} = await Permissions.askAsync(Permissions.LOCATION);
if (status !== 'granted') {
setErrorMessage('Permission to access location was denied');
}
let location = await Location.getCurrentPositionAsync({signal: abortController.signal});
// do stuff with the location data, putting it into states
fetch(
`http://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${long}&APPID=${API_KEY}&units=metric`, { signal: abortController.signal })
.then((res) => res.json())
.then((json) => {
// do all sorts of stuff with the weather json, putting it into states
});
// Fetch the aurora data
const myUTC = new Date().getTimezoneOffset();
fetch(
`http://api.auroras.live/v1/?type=ace&data=bz&tz=${myUTC}&colour=hex`, { signal: abortController.signal })
.then((res) => res.json())
.then((json) => {
// do stuff with the aurora json, put it into states
});
setIsLoaded(true); // this is for the activity indicator
};
getDataAsync();
}
return () => {
abortController.abort();
}
}, []);
In addition to the memory leak error in the console, I am also getting:
Possible Unhandled Promise Rejection (id: 0):
[AbortError: Aborted]
Possible Unhandled Promise Rejection (id: 1):
[AbortError: Aborted]

Wordpress Rest API: Return more than 100 results

I'm currently building a Vue webapp to display all custom post types, which recently exceeded 100 results. The Wordpress REST API limits the amount of posts to 100, and I'm unable to figure out how to paginate the requests so obtain all the posts on initial load.
My current code is as follows:
getPosts: function(context) {
return new Promise((resolve, reject) => {
if (context.state.posts) {
resolve();
} else {
axios
.get(
"https://localhost:81/admin/wp-json/wp/v2/cap?per_page=100"
)
.then(response => {
this.posts = response.data;
context.commit("storePosts", response.data);
console.log("Cap retrieved from Vuex!");
//console.log(this.posts);
resolve();
})
.catch(error => {
console.log(error);
reject(error);
});
}
});
}
I have the following computed code to display the results:
computed: {
caps() {
const caps = new Map();
if (this.$store.state.loading === false) {
sortPosts(this.$store.state.posts).forEach(post => {
const c = post.acf.address.country;
const s = post.acf.address.state;
if (!resorts.has(c)) resorts.set(c, new Map());
const stateMap = resorts.get(c);
if (!stateMap.has(s)) stateMap.set(s, []);
stateMap.get(s).push(post);
});
}
return caps;
}
}
How can I initiate loading all posts without user interaction?
Place this in API REST website to functions.php
add_filter( 'rest_{your_CPT}_collection_params', function ( $params, WP_Post_Type
$post_type ) {
if ( '{your_CPT}' === $post_type->name && isset( $params['per_page'] ) ) {
$params['per_page']['maximum'] = PHP_INT_MAX;
}
return $params;
}, 10, 2 );

Calling Api in side map function

I have this code
export const callingEveSkill = () => (dispatch, getState) => {
fetch('https://esi.tech.ccp.is/latest/characters/' + getState().ViewChr.Cid + '/skills/?datasource=tranquility&token=' + getState().ViewChr.At)
.then(response => response.json())
.then(json => {
var SkillList = ( json.skills.map((item, i) => {
var skill = TypeIdToName(item.skill_id)
return {
skill_id: (skill) ,
current_skill_level: item.current_skill_level,
skillpoints_in_skill: item.skillpoints_in_skill
}
}))
return SkillList
})
.then( SkillList => {
dispatch(updateSk( SkillList))
dispatch(updateSkL('true'))
})
.catch(err => {
console.log("skill error:" + err)
});
}
In side the code i call TypeIdToName to call a 3rd party api to change the skill id to readable text. I see the calls going out and that it returns the readable name but the SkillList show as undefined.
The problem here is that .map() won't wait for Promises to fulfil. here are few thoughts on how to get around that using Promises composing:
1) !IMPORTANT Refactor your API call TypeIdToName() so it returns a Promise
See this for more info: How do I convert an existing callback API to promises?
2) Install Q or any other library that allows Promises combination. (Promise.all might also work for you depending on your env)
https://github.com/kriskowal/q
OR
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
3) When mapping API calls - collect the promises they return.
4) Use Q.all() to return a Promise that will only be fulfilled when all the API calls are done. You might want to limit your pull of simultaneus connections at some point.
So your code would look like this:
import Q from 'q';
export const callingEveSkill = () => (dispatch, getState) => {
fetch('https://esi.tech.ccp.is/latest/characters/' +
getState().ViewChr.Cid +
'/skills/?datasource=tranquility&token=' +
getState().ViewChr.At
)
.then(response => response.json())
.then(json => {
//We build the array of promises here
let promises = json.skills.map((item, i) => {
//If you did the Step 1 - this should return a Promise object
//So our .map() has something to work with
return TypeIdToName(item.skill_id).then(
(skill) => {
//As promises fulfil - the array of promises
//turns into array of objects like this one
return {
skill_id: (skill),
current_skill_level: item.current_skill_level,
skillpoints_in_skill: item.skillpoints_in_skill
}
}
);
})
//And this promises fulfils when all the others do
return Q.all(promises);
})
//So here we get a SkillList
.then(SkillList => {
dispatch(updateSk(SkillList))
dispatch(updateSkL('true'))
})
.catch(err => {
console.log("skill error:" + err)
});
}

how to implement cache in react-native-video

How do we implement caching in react-native-video? Basically, when a video is currently streaming from a network resource, how do we save the video somewhere, and then retrieve it when the same resource is access. What is the best approach for this?
The best approach that i would refer you is using react-native-fetch-blob, you can implement it like this:
const RNFetchBlob = require('react-native-fetch-blob').default;
const {
fs
} = RNFetchBlob;
const baseCacheDir = fs.dirs.CacheDir + '/videocache';
//call the downloadVideo function
downloadVideo('http://....',baseCacheDir)
//Function to download a file..
const activeDownloads = {};
function downloadVideo(fromUrl, toFile) {
// use toFile as the key
activeDownloads[toFile] = new Promise((resolve, reject) => {
RNFetchBlob
.config({path: toFile})
.fetch('GET', fromUrl)
.then(res => {
if (Math.floor(res.respInfo.status / 100) !== 2) {
throw new Error('Failed to successfully download video');
}
resolve(toFile);
})
.catch(err => {
return deleteFile(toFile)
.then(() => reject(err));
})
.finally(() => {
// cleanup
delete activeDownloads[toFile];
});
});
return activeDownloads[toFile];
}
//To delete a file..
function deleteFile(filePath) {
return fs.stat(filePath)
.then(res => res && res.type === 'file')
.then(exists => exists && fs.unlink(filePath)) //if file exist
.catch((err) => {
// swallow error to always resolve
});
}
Cheers:)

nested fetch api react native giving unpredictable response for custom operation

hi i am trying to fill array with custom data using multiple apis in react native but unable to do so as nested fetch api giving unpredicatable result my final array is always empty .
Any help would be appreciated
Thanks in advance
below is my code please tell what i am doing wrong here..
fetchSubcategories(category_id){
var url=GLOBAL.BASE_URL+'/categories?filter[where][categoryId]='+GLOBAL.CATEGORY_ID+'&filter[where][category_type]=subcategory&filter[order]=name ASC';
const set = [];
const subcategoriesArray=[];
fetch(url)
.then((response) => response.json())
.then((responseData) => {
for (var i = 0; i < responseData.length; i++) {
set.push(responseData[i]);
}
console.log('response subcategories : '+JSON.stringify(set,null,2));
})
.then(()=>{
for(var j=0;j<set.length;j++){
var name=set[j].name;
var id=set[j].id;
fetch(url)
.then((response) => response.json())
.then((responseData) => {
var subcategory={
name:name,
id:id,
products:[]
}
subcategoriesArray.push(subcategory);
}).done()
}
})
.done()
console.log('final custom josn array : '+JSON.stringify(subcategoriesArray,null,2));
}
Note : urls used above giving the proper result on my end the only thing is final subcategoriesArray should be filled.
fetch is "asynchronous", so your console.log is probably running before the request has actually finished, but the array is actually being populated.
To test: try moving your console.log to just after subcategoriesArray.push(subcategory);
You should see it log out the array for each set, but at least you can see it is actually being populated.
You could wrap the function in a Promise so you can get the array after it has been async populated:
fetchSubcategories(category_id){
return new Promise((resolve, reject) => { // <- add this
var url=GLOBAL.BASE_URL+'/categories?filter[where][categoryId]='+GLOBAL.CATEGORY_ID+'&filter[where][category_type]=subcategory&filter[order]=name ASC';
const set = [];
const subcategoriesArray=[];
fetch(url)
.then((response) => response.json())
.then((responseData) => {
for (var i = 0; i < responseData.length; i++) {
set.push(responseData[i]);
}
console.log('response subcategories : '+JSON.stringify(set,null,2));
})
.then(()=>{
for(var j=0;j<set.length;j++){
var name=set[j].name;
var id=set[j].id;
fetch(url)
.then((response) => response.json())
.then((responseData) => {
var subcategory={
name:name,
id:id,
products:[]
}
subcategoriesArray.push(subcategory);
if(j == set.length-1) { // <- add this block
resolve(subcategoriesArray);
}
}).done();
}
}).done()
}); // <- completes promise block
}
You can then use it like this:
fetchSubcategories(category_id).then((subCatArray) => {
console.log('final custom join array', subCatArray);
});
Hope this helps! :)