On iOS when using map kit we are able to get the current annotations inside of the visible map rect. Here is a small snippet using swift
Array(myMap.annotations(in: myMap.visibleMapRect))
Is there any comparable method or callback in this library?
My basic use is to know when a user changed region (pinch to zoom etc) and to get an array of the current markers still visible.
There is a getMarkersFrames function to do that, but it is iOS only, and that's why I never used one in my apps.
I solved the same problem by using the RBush library to store markers: https://github.com/mourner/rbush. It allows to easily select points in the selected region. And it does it fast by using an R-tree. Example:
getInitialState() {
return {
region: {
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
},
};
}
componentWillMount() {
this.tree = new RBush();
fetch().then((points) => {
// RBush expects the point to have minX, maxX, minY, maxY
// but it could be configured.
this.tree.load(points);
});
}
onRegionChangeComplete(region) {
// Get points that are inside the region.
visibleItems = this.tree.search({
// Provide the coordinates of the south-west, north-east corners of the region.
minX: region.longitude - region.longitudeDelta,
minY: region.latitude - region.latitudeDelta,
maxX: region.longitude + region.longitudeDelta,
maxY: region.latitude + region.latitudeDelta
});
this.setState({ region, visibleItems });
}
render() {
return (
<MapView
region={this.state.region}
onRegionChangeComplete={this.onRegionChangeComplete}
/>
);
}
My app uses the same approach except I do initial markers loading and search in redux and redux-saga.
If you doesn't need to show that many markers, you can use simpler approach by storing points in the array. Then on each region change loop through all of the places and select those that are inside the region.
Related
When i call getCurrentPosition from same place ,different times it gives different coordinates as shown below
24.4715665,54.348927
24.4715665,54.348927
24.4715665,54.348927
24.4718683,54.3496188
24.4715526,54.3489885
24.4715526 54.3489885
24.4715347 54.3490144
After decimal points,from third position onwards, it changes as shown above.So i can not show current position in my web interface with these coordinate.It jumps to another position
return navigator.geolocation.default.getCurrentPosition(
position => {
console.log("Current Position",position)
updateState({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
});
},
error => console.log(error.message),
{enableHighAccuracy: true, timeout: 15000, maximumAge: 10000 },
);
the above lines makes the call for coordinates from my react native app
In this case I needs to show at-least one marker and user location on map load. Is there any way to achieve that?
My approach is to get the nearest marker from user location and add it and current user location to fitToSuppliedMarkers so it'll show user location and nearest marker and chnage zoom level according to that. any ideas?
Try using animateToRegion(), as said here.
Example:
zoomToLocation() {
let region = {
latitude: parseFloat(latitudeOfTheMarkerHere),
longitude: parseFloat(longitudeOfTheMarkerHere),
latitudeDelta: 5,
longitudeDelta: 5
};
let initialRegion = Object.assign({}, region);
initialRegion["latitudeDelta"] = 0.005;
initialRegion["longitudeDelta"] = 0.005;
this.mapView.animateToRegion(initialRegion, 2000);
}
What is the best way to simulate movement inside react-native-maps using an Android emulator? I know that you can load a gpx file in the location settings menu. However that only works with google maps inside the emulator, not a custom app that utilizes react-native-maps.
I'm also aware that it is possible to simulate user movement using the Expo-Location library.
import * as Location from 'expo-location';
const tenMetersWithDegrees = 0.0001;
const getLocation = increment => {
return {
timestamp: 100000,
coords: {
speed: 0,
heading: 0,
accuracy: 5,
altitudeAccuracy: 5,
altitude: 5,
longitude: -122.9779983 + increment * tenMetersWithDegrees,
latitude: 37.5665 + increment * tenMetersWithDegrees,
}
}
}
let counter = 0;
setInterval(() => {
Location.EventEmitter.emit('Expo.locationChanged', {
watchId: Location._getCurrentWatchI(),
location: getLocation(count)
})
counter++
}, 1000);
Would it be possible to build this same type script using just react native geolocation service?
With android emulator, you can simulate route. Check below screenshot
I am trying to implement heremap api in react-native project.While searching got https://www.npmjs.com/package/react-native-heremaps .But there is no proper documents to use this library. I am new to react-native. Please give me some suggestions to implement this.
! if you don't use expo please comment and I'll change the answer a bit !
First of all, as you probably know you need to make an account at HERE Developer webiste.
After you have made an account, you have to create a project(you can get a Freemium plan for free and it has plenty of requests available for free, upgrade if you need more). After that you need to "Generate App" for REST & XYZ HUB API/CLI at your project page. With that, you will recieve APP ID and APP CODE. With all this, HERE Developer Account setup is complete.
Lets jump to React Native now.
First of all you need to install a npm package called react-native-maps which we will use to display data that HERE provides. You can see installation instructions here.
After this, lets assume you have already created a component that will show the map. You need to import this:
import { Marker, Polyline } from 'react-native-maps'
import { MapView } from 'expo'
With that we have our map almost ready.
I will use axios in this example but you can use fetch to make requests to HERE if you want.
So we import axios(if you never worked with it you can learn more about it here):
import axios from 'axios'
Now, you should have coordinates of those two locations ready in a state or somewhere, and it should look something like this:
constructor(props){
super(props)
this.state = {
startingLocation: {
latitude: "xx.x",
longitude: "yy.y",
},
finishLocation: {
latitude: "xx.x",
longitude: "yy.y",
}
}
}
With "xx.x" and "yy.y" being actual coordinates you want.
So now when you have coordinates of start and finish location you can make a request to you HERE API project. It's as easy as this(I got this api from here):
// I will create a function which will call this, you can call it whenever you want
_getRoute = () => {
// we are using parseFloat() because HERE API expects a float
let from_lat = parseFloat(this.state.startingLocation.latitude)
let from_long = parseFloat(this.state.startingLocation.longitude)
let to_lat = parseFloat(this.state.finishLocation.latitude)
let to_long = parseFloat(this.state.finishLocation.longitude)
// we will save all Polyline coordinates in this array
let route_coordinates = []
axios.get(`https://route.api.here.com/routing/7.2/calculateroute.json?app_id=PUT_YOUR_APP_ID_HERE&app_code=PUT_YOUR_APP_CODE_HERE&waypoint0=geo!${from_lat},${from_long}&waypoint1=geo!${to_lat},${to_long}&mode=fastest;bicycle;traffic:disabled&legAttributes=shape`).then(res => {
// here we are getting all route coordinates from API response
res.data.response.route[0].leg[0].shape.map(m => {
// here we are getting latitude and longitude in seperate variables because HERE sends it together, but we
// need it seperate for <Polyline/>
let latlong = m.split(',');
let latitude = parseFloat(latlong[0]);
let longitude = parseFloat(latlong[1]);
routeCoordinates.push({latitude: latitude, longitude: longitude});
}
this.setState({
routeForMap: routeCoordinates,
// here we can access route summary which will show us how long does it take to pass the route, distance etc.
summary: res.data.response.route[0].summary,
// NOTE just add this 'isLoading' field now, I'll explain it later
isLoading: false,
})
}).catch(err => {
console.log(err)
})
}
NOTE There are few things to note here. First is that you have to replace APP ID and APP CODE with acutal APP ID and APP CODE from your HERE project.
Second note that I added &legAttributes=shape at the end of the request URL but it is not in the documentation. I put it there so Polyline coordinates acutally have a correct shape, if you don't put it, it will just respond with coordinates of road turns and that polyline will go over buildings and stuff, it will just look bad.
OK. So now we have coordinates to make a Polyline, let's do that.
<MapView>
<Polyline coordinates={this.state.routeForMap} strokeWidth={7} strokeColor="red" geodesic={true}/>
<Marker coordinate={{latitude: this.state.startingLocation.latitude, longitude: this.state.startingLocation.longitude}} title="Starting location"/>
<Marker coordinate={{latitude: this.state.finishLocation.latitude, longitude: this.state.finishLocation.longitude}} title="Finishlocation"/>
</MapView>
Explanation:
Polyline.coordinates will map through all of the coordinates that we have provided and draw a Polyline. strokeWidth is just how thick you want your line to be, and strokeColor is obviously color of a line.
Now, you should add a region to your MapView component to let it know what is the initial region you want to show on the map. So I suggest you to do something like this:
In state, define a region field and make it the same coordinates as starting location, and then set delta to make a bit larger view.
// so in state just add this
region: {
latitude: parseFloat("xx.x"),
longitude: parseFloat("yy.y"),
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}
And now, add region={this.state.region} to MapView.
You would be done now, but let's make sure this works every time. You need to make sure that HERE API request is complete before the map renders. I would do it like this:
// in your state define field to control if loading is finished
isLoading: true,
Now, you would call the function _getRoute() we made before in componendDidMount() lifecycle function provided by React Native. Like this:
componentDidMount() {
// when this function is finished, we will set isLoading state to false to let program know that API request has finished and now we can render the map
this._getRoute()
}
So finally a final step is to control isLoading in your render() function:
render() {
if(this.state.isLoading) {
return (
<Text>Loading...(you could also use <ActivityIndicator/> or what ever you want to show while loading the request)</Text>
)
} else {
// just put everything we already did here + stuff you already have
}
}
So here it is. I tried to make it as detailed as possible to make it easy for you and other people that will need help with this.
Don't ever hesitate to ask me anything if something is unclear or it's not working or you need more help! I'm always happy to help :D
I use the great react-native-maps from Airbnb on a react-native app.
I got a list of markers on a JSON file where each of these markers have a property zoom which is a integer of an approximate zoom level where the marker should display / hide on the map.
Is there a way based on the latitudeDelta and longitudeDelta of a Region to get an approximate double/integer of the current zoom level as we have on Google Maps (1 to 20) ?
Thanks
Ok I come with an handy solution, I don't know if we can do better.
I added the onRegionChange event to retrieve the region, then I use some math :
<MapView
style={styles.map}
initialRegion={this.state.region}
onRegionChange={region => {
clearTimeout(this.timerForMap)
this.timerForMap = setTimeout(() => {
this.showMarkers(region)
}, 100)
}}>
...
Then :
showMarkers(region) {
let zoom = Math.round(Math.log(360 / region.longitudeDelta) / Math.LN2)
...
}
If someone have a better way to do it, feel free to comment !
Thanks.
you can get the zoom level from getCamera() using onRegionChange in MapView
const [zoom, setZoom] = useState(''); //initiates variable zoom
const getZoom = async () => {
const coords = await mapRef.getCamera();
setZoom(coords.center.zoom); // sets variable zoom the value under coords.center.zoom
}
<MapView>
ref = {(ref) => mapRef = ref}
onRegionChange = {() => {getZoom();}}
</MapView>