Showing different markers on different points in react native maps - react-native

I have a react native app where i'm using react-native-maps.Here, i want to show different marker images for different points according to their coordinate indexes. But its showing just one same marker for all. Here is the code i have now:
{this.state.coordinates.map((coordinate, index) =>
<MapView.Marker key={`coordinate_${index}`}
coordinate={coordinate}
title={coordinate.title}
description={coordinate.description}
image={require(../assests/1.png)}>
</MapView.Marker>
)}
I have more images named 2.png , 3.png etc. How can i show these images in different coordinates such as for index 0 ,it will get 1.png. For index 1,it will get 2.png. What is the wayout from here?

You are hard coding image index to 1, this code should work:
{this.state.coordinates.map((coordinate, index) =>
<MapView.Marker key={`coordinate_${index}`}
coordinate={coordinate}
title={coordinate.title}
description={coordinate.description}
image={require(`../assests/${index}.png`)}>
</MapView.Marker>
)}

Try this code. I hope it solved your problem.
First form the marker array.
For Ex.
var marker1 = {
coordinates: {
latitude: ...,
longitude: ...
},
key: MARKER1,
image: require(../assests/1.png)
};
var marker2 = {
coordinates: {
latitude: ...,
longitude: ...
},
key: MARKER2,
image: require(../assests/2.png)
};
//Push it into your marker array
this.state.markers.push(marker1);
this.state.markers.push(marker2);
//Render
<MapView
...
{this.state.markers.map(marker => (
<MapView.Marker
coordinate={marker.coordinates}
key={marker.key}>
<Image source={marker.image} style={width:50, height:50}/>
</MapView.Marker>
))}
</MapView>

Related

React Native Google map and programmatically change pin colors

I built an app with React Native. The app maily shows markers in several locations.
<MapView
style={styles.map}
initialRegion={initialPosition}
showsUserLocation={true}
followsUserLocation={true}
showsCompass={true}
showsPointsOfInterest={false}
customMapStyle={mapStyle}
ref={(current) => (map.current = current)}
>
{
props.data.map((marker: any, index: number) => (
marker.single_point ?
<Marker
key={index}
coordinate={{
latitude: Number(marker.single_point.split(',')[0].trim()),
longitude: Number(marker.single_point.split(',')[1].trim()),
}}
ref={markerRefs[index]}
onPress={() => {
setPin('aqua' === pin ? 'gold' : 'aqua')
markerRefs[index].current.redraw()
}}
pinColor={pin}
>
</Marker>
: null
)
)}
</MapView>
I added a state variable to change the pin color once clicked. The issue here is that the set of coordinates are sent to the map component as a prop.
I did a copy of the prop and add to every object in the array an element/key named pincol
const [propCopy, setPropCopy] = useState([])
setPropCopy( props.data.map(v => ({...v, pincol: 'red'})))
but I'm getting the following error
ERROR Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
I did this because my idea is to setPropCopy[index].pincol to green from the Marker onPress method.
What am I doing wrong? Thank you
You need to call set in a useEffect to avoid too many re-renders issues like this:
const [propCopy, setPropCopy] = useState([]);
useEffect(() => {
setPropCopy( props.data.map(v => ({...v, pincol: 'red'})))
}, []);

react-native-maps callout content not updated on change

I am trying to update the content of a <Callout> tag on a google maps view using the react-native-maps npm package. I would like to dynamically load extra content after a map marker is pressed. I have simplified my code to the example below:-
I have already tried using the tracksInfoWindowChanges attribute on the marker, and the showCallout() method to attempt to manually trigger a re-render. I am using android so the iOS only options such as redrawCallout() method are not available.
const MyMap = (props) => {
const [marker, setMarker] = useState({});
return (
<MapView
showsUserLocation
zoomControlEnabled={true}
zoomEnabled={true}
region={{
latitude: 0,
longitude: 0,
latitudeDelta: 0.03,
longitudeDelta: 0.03,
}}>
<Marker
coordinate={{latitude: 0, longitude: 0}}
onPress={(e) => {
setMarker({ title: 'Test'});
}}>
<View>
<Icon name='key' size={40} color='#384963'</Icon>
</View>
<Callout>
<Text>{marker.title}</Text>
</Callout>
</Marker>
</MapView>
);
}
Is it possible to trigger a re-render of the content after the Callout has been rendered once? From debugging I can see that the component renders after I update my marker state, however the content is not visible until I manually close the Callout and re-opened it.
I am having the exact same problem. The suggestion for android, as per this issue, is to use showCallout(). Below is my work-around for this issue using showCallout():
const MyMap = (props) => {
const [marker, setMarker] = useState({});
let markerRef = useRef(null);
useEffect(() => {
if (markerRef.current) {
markerRef.current.showCallout();
}
})
return (
<MapView
showsUserLocation
zoomControlEnabled={true}
zoomEnabled={true}
region={{
latitude: 0,
longitude: 0,
latitudeDelta: 0.03,
longitudeDelta: 0.03,
}}>
<Marker
ref={markerRef}
coordinate={{latitude: 0, longitude: 0}}
onPress={(e) => {
setMarker({title: `Test ${Math.floor(Math.random() * 10)}`});
}}>
<View>
<Icon name='key' size={40} color='#384963' />
</View>
<Callout>
<Text>{marker.title}</Text>
</Callout>
</Marker>
</MapView>
);
}
Briefly, I added a markerRef and call markerRef.current.showCallout() in useEffect(). What this achieves, if I am not mistaken, is that each time MyMap gets re-rendered, the callout will be forced to show again. I also changed the onPress such that the effect of callout re-rendering is more apparent.
Note 1: Each time the marker is pressed, the callout shall have its text updated.
Note 2: If you have multiple markers and callouts, and you want to update only the callout that is currently in view, you will have to add another check in useEffect(). Something like: if (markerRef.current && selectedMarker === 'markerID') to make sure that only the desired callout is re-rendered continuously.

React Native maps marker change destroys map state

I am using React Native maps, with real-time marker. This is my code.
const markers = useSelector(state => state.map.markers);
const animate = useCallback(() => {
if (map) {
map.animateToRegion({
latitude: location.latitude,
longitude: location.longitude,
latitudeDelta: location.latitudeDelta,
longitudeDelta: location.longitudeDelta,
});
}
}, [location, map]);
useEffect(() => {
if (location && isMapReady) {
animate();
}
}, [animate, location, isMapReady]);
const onMapLayout = () => {
setIsMapReady(true);
};
<MapView
ref={mapView => {
map = mapView;
}}
provider={PROVIDER_GOOGLE}
region={location}
onPress={() => {
animate();
}}
onMapReady={onMapLayout}>
{isMapReady &&
markers.length !== 0 &&
markers.map((marker, index) => (
<MarkerAnimated
key={index}
coordinate={{
latitude: marker.lat,
longitude: marker.lon,
}}
/>
))}
</MapView>
So whenever the state changes the markers get updated and the marker is changed in real-time in the maps.
But with one issue: the real-time comes in like 10 seconds, within that time, if I try to drag maps to somewhere or zoom in or out of the maps, whenever the marker state changes all these changes will be lost, the zoom will be reset to the old one, and I'll move back to the initial location, etc.
How can I fix this? I want to continue scrolling inside the map, zoom in and out, meanwhile, the markers need to get updated on the app.
This seems very late but if you set the region to initialRegion in your map view it fixes this problem.

Rendering custom annotation with following view on Mapbox in react native

Custom annotation on mapbox
How can I achieve this using Mapbox and Annotations in React Native. I have tried nesting the annotations, rendering as polyline but am not getting the desired result. Can anyone help with resolving this?
Check out this working code. Finally was able to figure out the correct way to do it :
Inside my render() :
<Mapbox.MapView
ref={map => { this.map = map; }}
styleURL={Mapbox.StyleURL.Basic}
zoomLevel={15}
centerCoordinate={[11.256, 43.770]}
style={{flex: 1}}
showUserLocation={true}
userTrackingMode={Mapbox.UserTrackingModes.Follow}>
{this.state.markers.map(marker => (
<Mapbox.PointAnnotation
key= {marker.title}
id= {marker.title}
coordinate={marker.coordinates}>
<View style={styles.annotationContainer}>
<View style={styles.annotationFill} />
</View>
<Mapbox.Callout title={marker.title} />
</Mapbox.PointAnnotation>
))}
</Mapbox.MapView>
Function to update this.state.markers :
_getAnnotations = (key, location) => {
let newelement = {
title: key,
coordinates: location,
};
this.setState(prevState => ({
markers: [...prevState.markers, newelement]
}))
}
Geoquery trigger :
this.state.geoQuery.on("key_entered", (key, location, distance) => {
this._getAnnotations(key,location);
});

react-native-maps marker throwing and IndexOutofBoundsException: Invalid index 1, size is 0

I'm trying to get a react-native-maps Marker to show up on my Android device (it work's perfectly fine on iOS) but it's throwing me an IndexOutofBoundsException: Invalid index 1, size is 0 error at render time.
http://i.imgur.com/owletnw.png
render() {
const INITIAL_REGION = { latitude: 52.221835, longitude: 21.000896, latitudeDelta: 0.0922, longitudeDelta: 0.0421 };
return (
<MapView ref="map"
showsUserLocation = { true }
loadingEnabled = { true }
showsTraffic = { false }
showsBuildings = { false }
style = { styles.map, {flex: 1} }
initialRegion = { INITIAL_REGION }
>
<View style={{flex: 0.1, flexDirection: 'row', position: 'absolute', justifyContent: 'space-between', backgroundColor: 'transparent'}}>
<Button onPress={() => this.showWithinRadius(5)}>Wszystkie ~5km</Button>
<Button onPress={() => this.showWithinRadius(10)}>Wszystkie ~10km</Button>
<Button onPress={() => this.showWithinRadius(15)}>Wszystkie ~15km</Button>
<Button onPress={() => this.showAllOffers()}>Wszystkie oferty</Button>
</View>
{ console.log(this.state.offerLocation) }
<Marker
coordinate = { {latitude: 52.259216308593, longitude: 21.041564941406} }
title = { this.props.selectedOffer.offer.title }
/>
</MapView>
);
}
Any idea what could be causing this behaviour? I've tried deleting the View block from the MapView component but that doesn't seem to help. In the actual app I'm planning to pass the coordinates by an object which seem to works in the fitToCoordinates method.
No solutions were given to this issue but it helped me understand and fix mine.
I don't think the View is the problem but the order in which the components are rendered.
It seems like, on Android, render order matters.
So, rendering Markers as first child of MapView fixed it for me.
It is also mentioned here :
https://github.com/react-community/react-native-maps/issues/291
Hope I'm helping !
Nevermind, it was in fact the View component inside the MapView that was causing the issue.
Put the inner View and the market inside one View, this will solve the problem.
For me, the solution was removing any view from (can be a view, an image, etc).
It had no connection to the object neither to the order.