rnmapbox / maps

A Mapbox react native module for creating custom maps
MIT License
2.22k stars 838 forks source link

No way to dynamic images to Symbollayer markers. #2465

Closed rootcstar closed 1 year ago

rootcstar commented 1 year ago

New Feature

There should be a way to set dynamic images on the SymbolLayer markers.

For example, we want to show posts from users on the map and we want to show user images on each marker. There is no way to do it.

Why

It is important because we won't need to loop all markers with a point annonation. It would help us save rendering time also.

Gnative commented 1 year ago

@sammyoguzhan would this work for you ?

You can create an Mapbox Image

<Mapbox.Images
  images={{
    'user-profile-01': require('../../map/img/user-profile-01.png'),
  }}
/>

and then reference this in the Symbol Layer.

<Mapbox.SymbolLayer
  style={{
    iconImage: 'user-profile-01',
  }}
/>
Ballardian commented 1 year ago

@sammyoguzhan You can dynamically set images using a ShapeSource and a Symbol layer. I can send you the code if you need, but I'm having some issues with styling. @Gnative:

  1. It's not possible to set the absolute height or width or icons/images (or style the image at all e.g. border radius), you can only scale. My images are uploaded by users and are different sizes, is there a way to solve this? This is possible using the React mapbox library.
  2. The images are displayed correctly (sometimes), but change to negatives upon zooming, is there a way to solve this?

Thanks in advance!

image
mfazekas commented 1 year ago

As noted in comments there is way to set dynamic images, using Images component onImageMissing callback, and using expressions in iconImage property. Closing.

rootcstar commented 1 year ago

I didnt mean icons i mean the pictures users have posted. I want to show dynamically.

rootcstar commented 1 year ago

@sammyoguzhan You can dynamically set images using a ShapeSource and a Symbol layer. I can send you the code if you need, but I'm having some issues with styling. @Gnative:

  1. It's not possible to set the absolute height or width or icons/images (or style the image at all e.g. border radius), you can only scale. My images are uploaded by users and are different sizes, is there a way to solve this? This is possible using the React mapbox library.
  2. The images are displayed correctly (sometimes), but change to negatives upon zooming, is there a way to solve this?

Thanks in advance!

image

can you send me the code please ?

Gnative commented 1 year ago

@Ballardian you will want to process user uploaded images server side to resize and crop to a format that is useful for you.

@sammyoguzhan would this code below help you to use image and style them as you would like..

Markers, PointAnnotation can accept child views that you can render as you like and give you the properties of those components. Drag n Drop, Anchor positioning etc.

import React from 'react';
import { View, Text, Image } from 'react-native';
import MapboxGL, {
    MapView,
    Camera,
    ShapeSource,
    LineLayer,
    PointAnnotation,
    AnnotationContent,
    Images,
    SymbolLayer,
    MarkerView,
} from '@rnmapbox/maps';

const MAP_STYLE_OUTDOORS = 'mapbox://styles/mapbox/outdoors-v11'

class BugReportExample extends React.Component {

    render() {
        return (
            <>
                <MapView
                    style={{ flex: 1 }}
                    styleURL={MAP_STYLE_OUTDOORS}
                >
                    <Camera
                        animationDuration={0}
                        center={[10.141653, 47.158206]}
                    />
                    <MarkerView
                        coordinate={[10.141653, 47.158206]}
                        anchor={{ x: 0.5, y: 1 }}
                        id="marker"
                    >
                        <View style={{ backgroundColor: 'white', borderRadius: 5, borderColor: 'white', borderWidth:2, width:60, height:60 }}>
                            <Image source={{uri:"https://s3.eu-central-1.amazonaws.com/contours/photos/100_square/5c3c7d9b8bb09.jpg" }} style={{width:56, height:56, borderRadius: 5, resizeMode: 'stretch'}} />
                        </View>
                    </MarkerView>
                </MapView>
            </>
        );
    }
}

export default BugReportExample;

Edit - I forgot you don't want to loop markers.

rootcstar commented 1 year ago

I am using ShapeSource and cluster. I wanna impleement it into this. can i do it ?


<ShapeSource
            id="allMarkers"
            shape={shape}
            cluster={true}
            clusterRadius={80}
            clusterMaxZoom={14}
            onPress={async (pressedShape) => {
              onSourceLayerPress(pressedShape);
            }}
            ref={_sourceRef}
          >
            <MapboxGL.SymbolLayer
              id="pointCount"
              minZoomLevel={6}
              style={{ textField: "{point_count}", textColor: "#fff" }}
            />
            <MapboxGL.CircleLayer
              id="clusteredPoints"
              minZoomLevel={6}
              belowLayerID="pointCount"
              filter={[">", "point_count", 1]}
              style={{
                circleColor: "#ff0d0d",
                circleRadius: 18,
              }}
Ballardian commented 1 year ago

@Gnative thanks for the quick reply! I have 1000's of user images and I would want to style the icon anyway, so it wouldn't be feasible to style on server side, is there no way to implement styling on icons in a SymbolLayer (now or in future)? I did look at MarkerViews originally, but as I may have many markers it could hurt performance. Also, as you can see from the screenshot, I'm using clustering - is this possible with MarkerViews?

@sammyoguzhan, apologies for the formatting: `const {points, isLoading, viewport, mapRef} = useMap(); return (

{!isLoading && ( { setImages({...images, [url]: {uri: url}}); }} style={styles.images} /> )}

); };

const styles = StyleSheet.create({ page: { flex: 1, }, map: { height: 600, width: 500, }, images: { height: 60, width: 60, }, singlePoint: { iconImage: ['get', 'profile_image'], iconSize: 0.05, iconOpacity: 1, iconAllowOverlap: true, }, clusteredPoints: { circlePitchAlignment: 'map', circleColor: '#A59ADD', circleRadius: [ 'step', ['get', 'point_count'], 20, 100, 25, 250, 30, 750, 40, ], circleOpacity: 0.84, circleStrokeWidth: 0, circleStrokeColor: 'red', }, clusterCount: { textField: '{point_count}', textSize: 14, textPitchAlignment: 'map', }, });`

useMap: ` useEffect(() => { if (markerData && user) { const points = markerData.map(marker => ({ type: 'Feature', properties: { cluster: false, id: marker?.id, profile_image: marker?.friend?.profileImage, ... }, geometry: { type: 'Point', coordinates: [ parseFloat(longitude), parseFloat(latitude), ], }, })); const userPoint = { type: 'Feature', properties: { cluster: false, id: user?.id, profile_image: user?.profileImage, ... }, geometry: { type: 'Point', coordinates: [ parseFloat(longitude), parseFloat(latitude), ], }, }; points.push(userPoint); setPoints(points); } }, [markerData, user]);

return { mapRef, markerData, user, userLocation, isLoading, points, setIsLoading, setPoints, viewport, setViewport, selectedMarker, setSelectedMarker, fetchFriendDestinations, fetchUser, }; };`

mfazekas commented 1 year ago

See Images component and image missing callback

Gnative commented 1 year ago

Hi @Ballardian @sammyoguzhan , I'm not a maintainer of this package and also not all knowledgeable on Mapbox. Saying this I don't think this is possible with GeoJson how you would like this with the SymbolLayer and setting the icon. My understanding of the SymbolLayer is to have a small amount of icons (pngs) for performance.

If you have 1000's of user points and worried about performance using the Marker component you could have clustering up to a certain zoom level and then switch to user markers, base on the map boundaries, that you query your database for. You could filter and limit the number of markers based on the current zoom levels, something like how AirB'n'B does this, or a mix of both techniques.

With the amount of points you are talking about it would also be worth looking at uploading your user data to Mapbox as a dataset or running your own tile server as well such as Tegola. This way you are only pulling in the necessary data for the current map boundaries rather than having to load in all your data at once and worrying about rendering performance with React/Native.

Gnative commented 1 year ago

Also @Ballardian you'll want to pre-process your uploaded user images server side to resize and crop them to the format you need them to be. Never trust a user to upload an image in the format, size you need.

Ballardian commented 1 year ago

Okay thanks @Gnative. So just to confirm, styling of icons in a SymbolLayer is not possible? Is there a clustering library you would recommend that is compatible with rnmapbox?

thierryskoda commented 1 year ago

@Ballardian I'm looking to do the same thing. Have you found any solutions? Thank you so much 🙏

eybel commented 3 months ago

After few days of trying to implement a custom image on each SymbolLayer I got close to the best possible scenario but its not enough. At this moment the library is not good for this purpose. For everything else seems solid, which is not a minor thing but if you are reading this post and want to try to use a custom image per marker, I dont suggest you do it. I have to invoke each thumbnail generated (server side or other library) image then display it without having the "style" prop to use, thus this make the map look real bad. I suggest to use icons and markers and not images.

I hope in the future they could add the Image component with style props so we can make the round at least.

mfazekas commented 3 months ago

@eybel we have the Image component that accepts a RN component as a child where you can use style. https://rnmapbox.github.io/docs/components/Image

eybel commented 3 months ago

@eybel we have the Image component that accepts a RN component as a child where you can use style. https://rnmapbox.github.io/docs/components/Image

Yes I know but I couldnt make it work. How do you use it then? I was able to use the component but I have no idea how to use that Images component. Could you guys provide an example? Or update the docs a littble bit on that?

What I am trying to achieve is to have round images as markers and not square ones (they look real bad). That is why I need to style my marker image.