I am using MapView of react-native map clustering and Marker and callout of react-native-maps. I am unable to use animateToRegion.
It shows me this.mapView.animateToRegion is not a function
<MapView
ref={map=>{mapView = map}}
provider='google'
clustering={true}
onClusterPress={this.onPressCluster}
region={this.state.region}
onRegionChange={this.onRegionChange}
onRegionChangeComplete={this.onRegionChangeComplete}
style={styles.map}
showsUserLocation={true}
followUserLocation={true}
zoomEnabled={true}
ScrollEnabled={true}
showsBuildings={true}
showsMyLocationButton={false}/>
animate(){
let r = {
latitude: 42.5,
longitude: 15.2,
latitudeDelta: 7.5,
longitudeDelta: 7.5,
};
this.mapView.root.animateToRegion(r, 2000);
}
render(){
return(
<MapView
ref = {(ref)=>this.mapView=ref}
region={{
latitude: 35.688442,
longitude: 51.403753,
latitudeDelta: 0.5,
longitudeDelta: 0.5,
}}
onPress={()=>this.animate()}
>
...markers...
</MapView>
);
}
clickOnSearchedAddress = (placeId, index, dscrpt) => {
getLatLongFromAddress(placeId).then((response) => {
let r = {
[Constants.KEY_LATITUDE]: response.geometry.location.lat,
[Constants.KEY_LONGITUDE]: response.geometry.location.lng,
latitudeDelta: latitudeDelta,
longitudeDelta: longitudeDelta
}
this.mapView.animateToRegion(r, 1000)
Keyboard.dismiss()
isSetTextProgramatically = true;
this.setState({
search: dscrpt,
searchData: [],
})
}).then((error) => { })
}
<MapView
ref={ref => this.mapView = ref}
provider={PROVIDER_GOOGLE}
style={[styles.map, , { width: this.state.width }]}
initialRegion={region}
onRegionChangeComplete={this.onRegionChange}
showsMyLocationButton={true}
showsCompass={true}
showsUserLocation={true}
onMapReady={() => this.setState({ width: width - 1 })}}
/>
I believe the issue is that the prop is not called ref, but instead mapRef. Pass the ref in there, and it should work.
You would then also have to call it with something like this this.mapView.[current/map]animateToRegion. Just check the mapView object you get.
Related
I have an API to get cars's position in react-native-maps.
[
{
"Message": "OK",
"NumberPlate": "*****",
"DeviceID": "60000660",
"DriverName": "",
"DriverLicense": "",
"Date": "08:10:00 - 10/01/2022",
"Lt": "latitude",
"Ln": "longitude",
"Address": "***, ***, ***",
"Angle": 285,
"CarStatus": "parking",
"Speed": 0,
"Acc": "off",
"Oil": 323
},
.....
]
To prevent the server from crashing, I only get data every 15 seconds. And as we all know, my cars will "jump" in maps.
So is there a way to make them move smoothly?
For example they will move straight between 2 points in 15s.
This is my <MapView />
<MapView
ref={mapRef}
style={{width: '100%', height: '100%'}}
initialRegion={{
latitude: mapState.lat,
longitude: mapState.long,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
}}
onPress={() => {
menuRef.current.snapTo(1);
vehiclesRef.current.snapTo(2);
}}
showsUserLocation={true}
showsTraffic={false}
showsMyLocationButton={true}
zoomEnabled={true}>
{CarInfo.map(car => {
return (
<Marker
key={car.DeviceID}
coordinate={{
latitude: car.Lt,
longitude: car.Ln,
}}
onPress={() => vehiclesRef.current.snapTo(1)}>
<Animated.View style={[styles.markerWrap]}>
<Text style={styles.numberPlate} numberOfLines={1}>
{car.NumberPlate}
</Text>
<Animated.Image
source={Car}
style={styles.marker}
resizeMode="cover"
/>
</Animated.View>
</Marker>
);
})}
</MapView>
If you use react-native-maps Marker, you can see this and follow him. It works. If you want show multiple marker and want them move smoothly, you should custom a child component and use I in return(). this is my code:
export default function CarMarker({car, onOpen}) {
const [marker, setMarker] = useState(null);
const [coordinate, setCoordinate] = useState(
new AnimatedRegion({
latitude: car.Lt || INITIAL_LAT,
longitude: car.Ln || INITIAL_LONG,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}),
);
useEffect(() => {
animateMarker();
}, [car]);
const animateMarker = () => {
const newCoordinate = {
latitude: car.Lt,
longitude: car.Ln,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
};
if (Platform.OS === 'android') {
if (marker) {
marker.animateMarkerToCoordinate(newCoordinate, 15000);
}
} else {
coordinate.timing(newCoordinate).start();
}
};
return (
<Marker.Animated
key={car.DeviceID}
ref={marker => {
setMarker(marker);
}}
coordinate={coordinate}
anchor={{x: 0.5, y: 0.5}}
onPress={onOpen}>
<Animated.View style={styles.markerWrap}>
<Text style={styles.numberPlate} numberOfLines={1}>
{car.NumberPlate}
</Text>
<Animated.Image source={Car} style={styles.marker} resizeMode="cover" />
</Animated.View>
</Marker.Animated>
);
}
and this is my <MapView/>
<MapView
ref={mapRef}
style={{width: '100%', height: '100%'}}
initialRegion={{
latitude: mapState.lat,
longitude: mapState.long,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}}
onPress={() => {
menuRef.current.snapTo(1);
vehiclesRef.current.snapTo(2);
}}
showsUserLocation={true}
showsTraffic={false}
showsMyLocationButton={true}
zoomEnabled={true}>
{CarInfo.map(car => {
return (
<CarMarker
key={car.DeviceID}
car={car}
onOpen={() => {
vehiclesRef.current.snapTo(1);
setSelectedVehicle(car);
}}
/>
);
})}
</MapView>
I have two components Place and Map. Every time when user in component Place clicks button "Show on Map" I open Map and get route params latitude/longitude from Place and I want to render area near marker. I try to set current latitude/longitude to the state to rerender component near marker but it doesn't work and my map opens at the same place where I closed it. The only option I found is to make component unmount when close Map but I don't think it is the best solution.
Place:
const onNavigationTap = () => {
navigation.navigate('Map', {
destination: data["data"].coordinates
});
}
return(
<TouchableOpacity onPress={() => onNavigationTap()}>
<View style={{flexDirection: "row", alignItems: "center"}}>
<Ionicons size={hp('5%')} name={'navigate-circle-outline'} color='white'/>
</View>
</TouchableOpacity>
)
Map:
const [currentDestination, setCurrentDestination] = useState(undefined);
if (route.params) {
var {destination} = route.params;
}
useEffect(() => {
setCurrentDestination(destination);
}, [destination]);
return (
<MapView
mapType={satellite ? "hybrid" : "standard"}
style={{flex: 1}}
showsUserLocation={true}
followsUserLocation={true}
provider={PROVIDER_GOOGLE}
initialRegion={
currentDestination ?
{
longitude: parseFloat(currentDestination.longitude),
latitude: parseFloat(currentDestination.latitude),
longitudeDelta: 0.0043,
latitudeDelta: 0.0034,
} : {
latitude: 53.227200,
longitude: 50.243698,
latitudeDelta: 3,
longitudeDelta: 3,
}
}
>
</MapView>
)
why not use directly routes.params like this :
<MapView
mapType={satellite ? 'hybrid' : 'standard'}
style={{ flex: 1 }}
showsUserLocation={true}
followsUserLocation={true}
provider={PROVIDER_GOOGLE}
initialRegion={
route.params.destination
? {
longitude: parseFloat(route.params.destination.longitude),
latitude: parseFloat(route.params.destination.latitude),
longitudeDelta: 0.0043,
latitudeDelta: 0.0034,
}
: {
latitude: 53.2272,
longitude: 50.243698,
latitudeDelta: 3,
longitudeDelta: 3,
}
}></MapView>
like this whenever you call place , routes.params will be new value
I have implemented this example where I can zoom in to the given coordinates by clicking on the button.
Below you can read what I aiming to implement and I couldn't:
First, I want to be able to read coordinates out of a dynamic array, I tried by putting the array in the state but it fails.
const ASPECT_RATIO = width / height;
const LATITUDE = 37.78825;
const LONGITUDE = -122.4324;
const LATITUDE_DELTA = 0.0922;
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;
const MARKERS = [
{
latitude: 42.637368,
longitude: 21.148682,
},
{
latitude: 42.604021,
longitude: 21.261292,
},
{
latitude: 42.500833,
longitude: 21.181641,
}
];
const DEFAULT_PADDING = { top: 60, right: 60, bottom: 60, left: 60 };
export default class map_of_patients extends React.Component {
constructor(){
this.state={}
}
fitAllMarkers() {
this.map.fitToCoordinates(MARKERS, {
edgePadding: DEFAULT_PADDING,
animated: true,
});
}
render() {
return (
<View style={styles.container}>
<MapView
ref={ref => {
this.map = ref;
}}
style={styles.map}
initialRegion={{
latitude: LATITUDE,
longitude: LONGITUDE,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}}
>
{MARKERS.map((marker, i) => (
<Marker key={i} identifier={`id${i}`} coordinate={marker} />
))}
</MapView>
<View style={styles.buttonContainer}>
<TouchableOpacity
onPress={() => this.fitAllMarkers()}
style={[styles.bubble, styles.button]}
>
<Text>Fit All Markers</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
Second I would like to call the function fitAllMarkers into coordinates on start, so I don't have to click somewhere to do it. I tried by calling in inside componentDidMount() but didn't work either.
Third, I would like to zoom in to the region by giving the coordinates from the dynamic array.
I managed to fix all the issues mentioned in the question by doing the following:
-To fit all markers on initialization of the map I did as #Marek Lisik suggested using onMapReady={this.fitAllMarkers.bind(this)}.
-And to read from a dynamic array I managed to pass the array beforehand to the state and by the time the map is initialized it already had some data.
Here is the entire code again with the changes:
constructor(props) {
super(props);
this.state = {
region: {
latitude: 42.65847,
longitude: 21.16070,
latitudeDelta: 0.500,
longitudeDelta: 0.500 * width / height,
},
MARKERS: [
{
latitude: 42.637368,
longitude: 21.148682,
description: "dfsdf",
title: "title"
},
{
latitude: 42.604021,
longitude: 21.261292,
description: "sdfsdf",
title: "title"
},
{
latitude: 42.500833,
longitude: 21.181641,
description: "sdfsdfds",
title: "title"
}
]
};
}
fitAllMarkers() {
this.map.fitToCoordinates(this.state.MARKERS, {
edgePadding: DEFAULT_PADDING,
animated: true,
});
}
render() {
return (
<View style={styles.container}>
<MapView
ref={ref => {
this.map = ref;
}}
style={styles.map}
initialRegion={this.state.region}
onMapReady={this.fitAllMarkers.bind(this)}
>
{this.state.MARKERS.map((marker, i) => (
<Marker key={i} identifier={`id${i}`} coordinate={marker}
description={marker.description}>
</Marker>
))}
</MapView>
<View style={styles.buttonContainer}>
<TouchableOpacity
onPress={() => this.fitAllMarkers()}
style={[styles.bubble, styles.button]}
>
<Text>Fit All Markers</Text>
</TouchableOpacity>
</View>
</View>
);
}
I created the map using react-native-maps.Now i need to get latitude & longitude as a text when click on map.
I tried this way but it gives an error"Can't find variable:coordinate".
export default class Location extends Component {
constructor(props) {
super(props);
this.state = {
markers: []
};
this.handlePress = this.handlePress.bind(this);
}
handlePress(e) {
this.setState({
markers: [
...this.state.markers,
{
coordinate: e.nativeEvent.coordinate,
key: coordinate,
color: randomColor()
}
]
});
console.log(e.nativeEvent);
}
render() {
return (
<MapView
style={styles.map}
initialRegion={{
latitude: 7.8731,
longitude: 80.7718,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421
}}
onPress={e => this.handlePress(e)}
>
{this.state.markers.map(marker => (
<Marker
key={marker.key}
coordinate={marker.coordinate}
pinColor={marker.color}
>
<View style={styles.marker}>
<Text style={styles.text}>{marker.coordinate}</Text>
</View>
</Marker>
))}
</MapView>
);
}
}
How i fix it?
I solved it.
export default class Location extends Component {
constructor(props) {
super(props);
this.state = {
region: {
latitude: LATITUDE,
longitude: LONGITUDE,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
},
markers: {
coordinate: {
latitude: 4,
longitude: 4,
},
key: id,
color: randomColor(),
}
};
}
onMapPress(e) {
this.setState({
markers:
{
coordinate: e.nativeEvent.coordinate,
key: id++,
color: randomColor(),
},
});
SaveAddress=()=>{
console.log(JSON.stringify(this.state.markers[0].coordinate.latitude))
}
}
render() {
return (
<MapView
provider={this.props.provider}
style={styles.map}
initialRegion={this.state.region}
onPress={e => this.onMapPress(e)}
>
<Marker
key={this.state.markers.key}
coordinate={this.state.markers.coordinate}
pinColor={this.state.markers.color}
>
<View style={styles.marker}>
<Text style={styles.text}>
{JSON.stringify(this.state.markers.coordinate)}</Text>
</View>
</Marker>
</MapView>
);
}
}
Add an onPress event to the map. like below. It will return the coordinates of pressed location in the map.
onPress={ (event) => console.log(event.nativeEvent.coordinate) }
So the code will be,
<MapView style = {styles.map}
initialRegion = {{
latitude: 7.8731,
longitude: 80.7718,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421, }}
onPress={ (event) => console.log(event.nativeEvent.coordinate) }
/>
I have below code base
componentDidMount() {
//firebase.firestore().collection('locations').doc('test').set({ test: 'test' })
Geolocation.getCurrentPosition(
(position) => {
if (position) {
this.setState({
region: {
latitude: Number(position.coords.latitude),
longitude: Number(position.coords.longitude),
latitudeDelta: 0.003,
longitudeDelta: 0.003,
},
});
}
alert(JSON.stringify(this.state.region))
},
(error) => alert(JSON.stringify(error)),
{ enableHighAccuracy: true, timeout: 20000, maximumAge: 1000, distanceFilter: 100 }
);
this.unsubscribe = this.locationRef.onSnapshot(this.getCollectionData);
// firebase.firestore().collection("locations").get().then(QuerySnapshot => { });
}
And Map
render() {
return (<View style={{
flex: 1,
flexDirection: 'column',
}}>
<HeaderNavigationBar {...this.props} />
<MapView showsUserLocation={true}
// ref={map => this.map = map}
initialRegion={this.state.region}
style={styles.container}
>
{this.state.AllLocations.map((marker, index) => (
<MapView.Marker key={index}
coordinate={marker.locations.coords}
title={marker.Title}
/>
))}
</MapView>
</View>);
}
componentDidMount is correcty updating the state, but map is not showing correct position,it is taking as it was set in construction
constructor(props) {
super(props);
this.locationRef = firebase.firestore().collection('locations');
this.unsubscribe = null;
this.state = {
isLoading: true,
AllLocations: [],
region: {
latitude: 0,
longitude: 0,
latitudeDelta: 0.003,
longitudeDelta: 0.003,
}
};
}
Please help
thanks
initialRegion is used for the initial render of map.
In your case, You are getting the user location after your map render, Thats the reason its not getting updated.
To counter this there are two ways.
1: use a loading state till you get your use location, and prevent the map render before you get the location.
2: Use region, for example region = {this.state.region}.
<MapView showsUserLocation={true}
// ref={map => this.map = map}
initialRegion={this.state.region}
region={this.state.region}
style={styles.container}
>
{this.state.AllLocations.map((marker, index) => (
<MapView.Marker key={index}
coordinate={marker.locations.coords}
title={marker.Title}
/>
))}
</MapView>
Both will work, it depends on your usecase.
Try this
{this.state.region ? (<MapView
style={[styles.map]}
initialRegion={this.state.region}
region={this.state.region}
provider={PROVIDER_GOOGLE}
>
{this.state.AllLocations.map((marker, index) => (
<MapView.Marker key={index}
coordinate={marker.locations.coords}
title={marker.Title}
/>
))}
</MapView>) : (
<MapView
loadingEnabled={true}
style={styles.map}
showsMyLocationButton={true}
provider={PROVIDER_GOOGLE}
style={[styles.map]}
>
</MapView>
)
}
Set region: null in the constructor it will work