Hi there,
Just a question/recommandation/idea of improvement: because I struggle quite a lot to try and put a simple marker on my map, and it looks so complicated for such a basic feature (why would I need a GeoJSON ?), why not create an abstraction of it and create a MapboxGL.Marker component such as the react-native-maps/marker ?
+1
In CustomIcon.js example show how add 1 icon (as marker i supose) but if i want 2 markers with 2 diferent icons how can i add with MapboxGL.geoUtils.addToFeatureCollection?
You can pretty easily do your own implementation of Marker
class CustomMarkers extends React.Component<Props, OwnState> {
constructor(props: Props) {
super(props);
}
render() {
const { coordinates, icon, size } = this.props;
const features = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [coordinates.longitude, coordinates.latitude],
}
}
return (
<MapboxGL.ShapeSource
id='markers_shapesource'
shape={{ type: 'FeatureCollection', features }}
>
<MapboxGL.SymbolLayer
id="markers_symbollayer"
style={{
iconImage: icon,
iconSize: size
}}
/>
</MapboxGL.ShapeSource>
);
}
}
You can also set a custom children instead of default icon.
I think GeoJSON is more optimize to render multiples marker than render each marker individually. Probably the reason why this is the default way to render markers.
Great, thanks for yout code, I will have a look and try that !
Just a few questions though:
style={{
iconImage: icon,
iconSize: size
}}
the icon prop you show here would be an image, like a .png or a .jpg ? could it be some SVG ?
You can also set a custom children instead of default icon.
does it means that I could pass a SVG as a child of MapboxGL.SymbolLayer ?
Yeah it's suppose to be png or jpg.
Actually I don't think it's possible to display svg but you can display other layers instead of SymbolLayer.
for example, UserLocation marker is display like that (source) :
[
<CircleLayer
key="mapboxUserLocationPluseCircle"
id="mapboxUserLocationPluseCircle"
style={layerStyles.normal.pluse}
/>,
<CircleLayer
key="mapboxUserLocationWhiteCircle"
id="mapboxUserLocationWhiteCircle"
style={layerStyles.normal.background}
/>,
<CircleLayer
key="mapboxUserLocationBlueCicle"
id="mapboxUserLocationBlueCicle"
aboveLayerID="mapboxUserLocationWhiteCircle"
style={layerStyles.normal.foreground}
/>,
]
Maybe some layers allows you to display SVG but I havn't seen it yet.
And how can i check who is my parent feature in
I supose i have to do something like:
<MapboxGL.SymbolLayer id="markers_symbollayer">
<Image source={myIcon} />
</MapboxGL.SymbolLayer>
But to set icon I have to know who is my parent Feature, how can i know it?
I think mapbox can't display Image from react-native, you have to put your image in style like in my example above.
If I understand your question, in your top component you have :
<CustomMarker
coordinates={{ longitude: 40.3543, latitude: 2.5732}}
>
<MapboxGL.SymbolLayer
id="markers_symbollayer"
style={{
iconImage: icon,
iconSize: size
}}
/>
</CustomMarker>
and your CustomMarker class look like :
class CustomMarkers extends React.Component<Props, OwnState> {
constructor(props: Props) {
super(props);
}
render() {
const { coordinates } = this.props;
const features = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [coordinates.longitude, coordinates.latitude],
}
}
return (
<MapboxGL.ShapeSource
id='markers_shapesource'
shape={{ type: 'FeatureCollection', features }}
>
{this.props.children}
</MapboxGL.ShapeSource>
);
}
}
note that you have to provide a unique id for each marker so pass it as props can be useful.
PS : I don't have use this lib for a long time, so I can be wrong on some points.
@ethaqnix I just checked to day your proposal, and it's easy and it works great, thanks !
Just a small fix to your code: features needs to be an array
const features = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [coordinates.longitude, coordinates.latitude],
}
}
Actually I don't think it's possible to display svg but you can display other layers instead of
SymbolLayer.
I tried to put a SVG icon (I mean inline SVG) as a child to MapboxGL.SymbolLayer, and at a first glance it seems to work ! So I'll keep going in that direction to see if it is going well...
But I still have a doubt regarding performances: will it be a problem to use a MapboxGL.ShapeSource for every MapboxGL.SymbolLayer and custom icon we put on the map ?
In old mapbox versions you can set icon as "feature" props, and if icon prop is equal to Images key, you can custom icon for each "feature".
With this version we cant do that?
@ethaqnix I still have an issues with your solution:
Mapview doesn't update when a ShapeSource is added: I was able to show the newly added Marker by updating the Mapview key, do you know why ?If you have some clues there it would be great !
This may be unrelated to this issue specifically.. but if we use a pattern such as the Marker abstraction here to migrate away from the deprecated
So thanks to all your help I could build a proper components to show custom markers, see the code below.
My only big issue is: it doesn't update when a new marker is dynamically added, even with the trick to set a key prop on ShapeSource updating properly. I think I tried a lot of times but I can't still figure out what is wrong there...
import React from 'react';
import MapboxGL from '@react-native-mapbox-gl/maps';
import { View } from 'react-native';
import styles from './markerStyles';
const getFeatureId = ({ properties: { _id } }) => _id;
const getDataId = data => data._id
const createFeature = data => ({
// https://github.com/react-native-mapbox-gl/maps/blob/master/docs/ShapeSource.md -> shapeSource prop
// https://geojson.org
// this has a geoJSON shape
type: 'Feature',
properties: {
_id: data._id,
whatever: 'you want'
},
geometry: {
type: 'Point',
coordinates: data.coordinates, // [lat, long]
}
})
class MyMarkers extends React.Component {
onPress = (e) => {
const feature = e.nativeEvent.payload;
const dataId = getFeatureId(feature)
console.log({聽feature, dataId })
// TODO
}
render() {
const { datas } = this.props;
const features = datas.map(createFeature)
return (
<MapboxGL.ShapeSource
id='markersShape'
shape={{ type: 'FeatureCollection', features }}
onPress={this.onPress}
>
{datas.map(data => (
<MapboxGL.SymbolLayer
id={getDataId(data)}
key={getDataId(data)}
sourceID='markersShape'
filter={['==', '_id', getDataId(data)]}
style={{
iconSize: 1,
iconAllowOverlap: true
}}
>
<View
style={styles.marker}
pointerEvents='none' // this is important for the onPress prop of ShapeSource to work
>
{/* ... place what you want as a marker here, even SVG from react-native-svg for instance */}
</View>
</MapboxGL.SymbolLayer>
))}
</MapboxGL.ShapeSource>
)
}
}
export default MyMarkers;
Sorry, I currently use GeoJSON to display all markers together and they all render whatever the zoom.
And don't know neither why you can't add marker dynamically, maybe the map doesn't render marker newly added (only updated IDs known on the first render).
In this case rerender MapView when you add marker can be a solution, don't seems to be the good way but I don't have any other ideas.
Yeah I though the same, but the map takes a while to rerender, as a user experience it is not the best... is there a way to not rerender the map background but rerender the children only ?
@ethaqnix Coul'd You please give an example of code ?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Most helpful comment
So thanks to all your help I could build a proper components to show custom markers, see the code below.
My only big issue is: it doesn't update when a new marker is dynamically added, even with the trick to set a
keyprop onShapeSourceupdating properly. I think I tried a lot of times but I can't still figure out what is wrong there...