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
Related
I am trying to implement to search and pinpoint a location in react native. I am using react-native-maps and react-native-google-places-autocomplete packages for their obvious usages.
First I have initiated region in the state as:
constructor(){
this.state={
mapRegion: {
latitude: this.props.latitude ? this.props.latitude : 27.7172,
longitude: this.props.longitude ? this.props.longitude : 85.3240,
latitudeDelta: 0.005,
longitudeDelta: 0.005,
},
}
}
I have tried to update the region on pressing the autocompleted search results as well as upon marker drag. But I get an error as:
Error: You attempted to set the key latitude with the value '27' on an object that is meant to be immutable and has been frozen.
I have implemented the code as:
<GooglePlacesAutocomplete
placeholder='Search'
fetchDetails={true}
onPress={(data, details = null) => {
let tempMapRegion = this.state.mapRegion;
tempMapRegion.latitude = details.geometry.location.lat;
tempMapRegion.longitude = details.geometry.location.lng;
this.setState({ mapRegion: tempMapRegion })
}}
query={{
key: AppSettings.googleMapApiKey,
language: 'en',
}}
/>
<MapView
initialRegion={{
latitude: this.props.latitude ? this.props.latitude : 27.7172,
longitude: this.props.longitude ? this.props.longitude : 85.3240,
latitudeDelta: 0.005,
longitudeDelta: 0.005,
}}
region={this.state.mapRegion}
onRegionChange={(e) => { this.onRegionChange(e) }}
style={{ height: 300, width: 300 }}
>
<Marker draggable
coordinate={this.state.mapRegion}
onDragEnd={(e) => {
let tempMapRegion = this.state.mapRegion;
tempMapRegion.latitude = e.nativeEvent.coordinate.latitude
tempMapRegion.longitude = e.nativeEvent.coordinate.longitude
this.setState({ mapRegion: tempMapRegion })
}}
/>
</MapView>
The onRegionChange in the MapView works smoothly and marker is dragged automatically to the centre, but the reverse process brings up the above error.
What is causing this error and how do I get past this?
<View style={{ padding: 2, }}>
<GooglePlacesAutocomplete
placeholder='Search'
fetchDetails={true}
onPress={(data, details = null) => {
let tempMapRegion = this.state.mapRegion;
tempMapRegion.latitude = details.geometry.location.lat;
tempMapRegion.longitude = details.geometry.location.lng;
this.map.animateToRegion(this.newRegion(tempMapRegion));
this.setState({ address: data.description })
}}
query={{
key: AppSettings.googleMapApiKey,
language: 'en',
}}
/>
</View>
<MapView
provider={this.props.provider}
ref={ref => { this.map = ref; }}
mapType={MAP_TYPES.TERRAIN}
initialRegion={this.state.mapRegion}
onRegionChangeComplete={(e) => { this.onRegionChange(e) }}
style={{ height: width, width: width, marginTop: -5 }}
>
<Marker draggable
coordinate={this.state.mapRegion}
onDragEnd={(e) => {
let tempMapRegion = this.state.mapRegion;
tempMapRegion.latitude = e.nativeEvent.coordinate.latitude
tempMapRegion.longitude = e.nativeEvent.coordinate.longitude
this.map.animateToRegion(this.newRegion(tempMapRegion));
// this.setState({ mapRegion: tempMapRegion })
}}
/>
</MapView>
So, what basically did the thing, was using the animateToRegion property of the mapview. It basically animates the view to the mentioned region and then calls the onRegionChange. I had stumbled upon this answer a number if times but it hadnt worked. Weirdly this only works in the build version and not while debugging, not on the emulator at least.
Thanks to https://stackoverflow.com/a/53836679/5379191 this answer for showing the way though.
newRegion(tempMapRegion) {
return {
...this.state.mapRegion,
...this.regionCoordinate(tempMapRegion),
};
}
regionCoordinate(tempMapRegion) {
return {
latitude: tempMapRegion.latitude,
longitude: tempMapRegion.longitude,
};
}
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 implemented a MapView with react-native-maps. I'm trying to change Marker's pinColor by clicking on it.
Note: I have large amounts of markers. So I don't think refreshing all view can be a good solution. I need directly change the selected marker's color.
I didn't find how to do it. I tried below code:
class TestMap extends React.Component {
constructor(props) {
this.state = {
testColor: "#FFFFFF",
userLatitude:0,
userLongitude:0,
data:[]
}
}
render() {
return (
<MapView
provider={PROVIDER_GOOGLE}
showsTraffic={true}
showsBuildings={true}
toolbarEnabled={true}
loadingEnabled={true}
style={styles.map}
initialRegion={{
latitude: this.state.userLatitude,
longitude: this.state.userLongitude,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA
}}
onPoiClick={this.onPoiClick}
showsUserLocation={true}
followsUserLocation={true}
showsMyLocationButton={true}
loadingBackgroundColor="#FEA405"
loadingIndicatorColor="white"
onLongPress={e => this.onMapPress(e)}
enableZoomControl
>
{this.ListMarkers()}
</MapView>
)};
ListMarkers() {
return this.state.data.map((data, i) => {
return (
<Marker
key={i}
onPress={e => this.onPressMarker(e, i, data)}
coordinate={{
longitude: data.LONGITUDE,
latitude: data.LATITUDE
}}
pinColor={this.state.testColor}
/>
)}
)};
onPressMarker(e, index, data) {
this.setState({testColor:"#000000"});
}
}
I expect the color of marker should change after clicking on it but it is not working.
Thanks for your help.
You can set the selected pin in the state and use a different style in that case, if you have some id in your data you can use that value instead of the index:
constructor(props) {
this.state = {
selectedPin: -1,
}
}
ListMarkers = () => {
return this.state.data.map((data, i) => {
return (
<Marker
key={i}
onPress={e => this.onPressMarker(e, i, data)}
coordinate={{
longitude: data.LONGITUDE,
latitude: data.LATITUDE
}}
pinColor={ i === this.state.selectedPin ? '#FF0000' : '#FFFFFF'}
/>
)}
)};
onPressMarker = (e, index, data)=> {
this.setState({selectedPin:index});
}
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.