React Native Mapbox (rnmapbox/maps v10) Usage
Tags: reactnative, map, mobile, reactnativemaps
Published: 2022-10-18 18:29:26

**MapView, Camera, PointAnnotation, ShapeSource, MarkerView, SymbolLayer, and LineLayer** are components in the React Native Mapbox library that allow you to display maps and add markers, annotations, and other features to them.

Here's a brief overview of each component:

**MapView**: This component is the main component used to display a map. It provides various props for controlling the map's appearance and behavior.

**Camera**: This component is used to control the map's camera position and zoom level. It can be used to animate the camera to a specific location or zoom level.

**PointAnnotation**: This component is used to add a marker to the map. You can customize the marker's appearance and add a callout that appears when the user taps on the marker.

**ShapeSource**: This component is used to add a shape to the map, such as a polygon or line. You can customize the appearance of the shape and add data to it.

**MarkerView**: This component is used to create a custom marker for the PointAnnotation component. It allows you to use an image or a custom component as the marker.

**SymbolLayer**: This component is used to add symbols to the map, such as icons or text. You can customize the appearance of the symbols and add data to them.

**LineLayer**: This component is used to add lines to the map. You can customize the appearance of the lines and add data to them.

In summary, these components provide a way to display maps and add various features to them in a React Native app using the Mapbox library.


The below code explains how to use [ShapeSource](https://github.com/rnmapbox/maps/blob/main/docs/ShapeSource.md), [MarkerView](https://github.com/rnmapbox/maps/blob/main/docs/MarkerView.md), [SymbolLayer](https://github.com/rnmapbox/maps/blob/main/docs/SymbolLayer.md) in Mapbox.


![Sample Picture](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v6oh2bvai6qs5l3owpv0.png)
---

## INSTALLATION:
[Installation Steps](https://dev.to/ajmal_hasan/react-native-mapbox-v10-installation-7ga)

---

## USAGE:

**Sample Data:(Note: geojson data only)**

`polygons.js`
```
{
  "type": "FeatureCollection",
  "name": "GeoJsonData",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "description": null,
      },
      "geometry": {
        "type": "MultiPolygon",
        "coordinates": [
          [
            [
              [46.6973896223821, 24.7938909593501],
              [46.69730684623009, 24.79405349682493],
              [46.69722194475514, 24.79401653642232],
              [46.69730416732871, 24.79385517629931],
              [46.6973896223821, 24.7938909593501]
            ]
          ]
        ]
      }
    },
*
*
*
]}
```
`markers.js`

```
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [
          36.53387,
          28.41464
        ]
      },
      "properties": {
        "name_ar": "تبوك",
        "name_en": "Tabuk",
        "avail":"A"
      }
    }, 
     *
     *
     *
  ]
}
```

**Mapbox.js**

```
import MapboxGL, {Camera} from '@rnmapbox/maps';
import {MAP_BOX_ACCESS_TOKEN} from '../../utils/constants/constants';
import * as turf from '@turf/turf';
import PolygonsGEOJSON from './polygons.json';
import MarkersGEOJSON from './markers.json';

MapboxGL.setAccessToken(MAP_BOX_ACCESS_TOKEN);

const MapBoxSRP = ({navigation, route}) => {

  let _map = useRef(null);
  const camera = useRef(null);
  const dispatch = useDispatch();
  const [localKmlData, setLocalKmlData] = useState(null);
  const [isSateliteStyle, setSateliteStyle] = useState(true);
  const [selectedDist, setSelectedDist] = useState();
  const defaultCamera = {
    centerCoordinate: [46.6254765922189, 24.942461593578678],
    zoomLevel: 8,
  };

//mapbox components style
    const style_MB = {
    polygonBG: {
      fillColor: COLOR_PURPLE_100_OPACITY,
    },
    polygonBorder: {
      lineColor: COLOR_WHITE,
      lineWidth: 1,
    },
    countStyle: {
      circleStrokeColor: COLOR_WHITE,
      textField: ['get', 'point_count'],
      iconImage: IMAGES.CIRCLE_GRADIENT,
      textColor: COLOR_WHITE,
      textFont: ['Open Sans SemiBold'],
    },
    dotMarkerStyle: {
      circleRadius: 4,
      circleStrokeWidth: 1,
      circleStrokeColor: COLOR_WHITE,
    },
    calloutMarkerStyle: {
      iconImage: IMAGES.MAP_POINTER,
      textField: '22',
      iconTextFit: 'both',
      iconTextFitPadding: [5, 5, 5, 5],
      textSize: 16,
      iconAllowOverlap: true,
      textAllowOverlap: true,
      textColor: COLOR_BLACK,
      textFont: ['Open Sans SemiBold'],
      textOffset: [0, -1],
    },
    deselectText: {
      textColor: COLOR_WHITE,
      textHaloColor: COLOR_BLACK,
      textHaloWidth: 1,
    },
    selectedPolygonBG: {
      fillColor: COLOR_SECONDARY_DISABLE_OPACITY,
    },
    selectedPolygonBorder: {
      lineColor: COLOR_WHITE,
      lineWidth: 3,
    },
  };

//on screen load move view to the collection of all polygons without cropping any polygon
  useEffect(() => {
    try {
      setTimeout(() => {
        if (Object.keys(PolygonsGEOJSON).length > 0) {
          let bboxPolygon = turf.bbox(PolygonsGEOJSON);
          let minCoor = [bboxPolygon[0], bboxPolygon[1]];
          let maxCoor = [bboxPolygon[2], bboxPolygon[3]];
          console.log('onMapLoad', bboxPolygon);
          camera?.current?.fitBounds(minCoor, maxCoor, [50, 20], 100);
          setLocalKmlData(PolygonsGEOJSON);
        }
      }, 1000);
    } catch ({message}) {
      console.log('onMapLoadERROR', message);
    }
  }, []);

//focus the view to the selected polygon with bound
  const onPressPolygon = e => {
    const feature = e?.features[0];
    console.log('onPressPolygon', feature);

    var bboxPolygon = turf.bbox(feature);
    let minCoor = [bboxPolygon[0], bboxPolygon[1]];
    let maxCoor = [bboxPolygon[2], bboxPolygon[3]];
    camera?.current?.fitBounds(minCoor, maxCoor, 20, 2000);
    setSelectedDist(feature);
  };

  const setMapTypeFunc = () => {
    setSateliteStyle(v => !v);
  };

const SelectedPolygon = () =>
    selectedDist ? (
      <MapboxGL.ShapeSource id="selectedNYC" shape={selectedDist}>
        <MapboxGL.FillLayer
          sourceID="selectedNYC"
          id="nycSelectedFillRed"
          style={style_MB.selectedPolygonBG}
        />
        <MapboxGL.LineLayer
          sourceID="selectedNYC"
          id="nycFillLine2"
          style={style_MB.selectedPolygonBorder}
        />
      </MapboxGL.ShapeSource>
    ) : null;

  const SelectedPolygonProperties = () => (
    <MapboxGL.ShapeSource
      id="nyc1"
      shape={Cities}
      cluster={true}
      // clusterProperties={{
      //   sum: [
      //     ['+', ['accumulated'], ['get', 'sum']],
      //     ['get', 'count'],
      //   ],
      // }}
      onPress={items => {
        onPressPolygon(items);
      }}>
      <MapboxGL.CircleLayer
        sourceID="nyc1"
        id="dot"
        style={style_MB.dotMarkerStyle}
        // minZoomLevel={14}
      />
      <MapboxGL.SymbolLayer
        sourceID="nyc1"
        id="callout"
        style={{
        ...style_MB.calloutMarkerStyle,
        textField: '{name_en}',
        }}
        minZoomLevel={10}
      />
      {/* <MapboxGL.SymbolLayer
  filter={['has', 'point_count']}
  sourceID="nyc1"
  id="countCluster"
  style={style_MB.countStyle}
  maxZoomLevel={14}
/> */}
    </MapboxGL.ShapeSource>
  );

//Custom Marker
  const RenderAnnotationsProperties = () => {
    return selectedPolygonPropeties?.features.map((v, i) => (
      <MapboxGL.MarkerView
        key={i}
        id="pointAnnotation"
        // allowOverlap={true}
        coordinate={v?.geometry?.coordinates}>
        <TouchableOpacity onPress={() => setselectedMarkerId(i)}>
          <View
            style={{
              padding: 10,
              backgroundColor: selectedMarkerId === i ? COLOR_RED : COLOR_WHITE,
            }}>
            <Text style={{textColor: COLOR_BLACK}}>
              {v?.properties?.conversionUnit +
                ' ' +
                v?.properties?.conversionPrice}
            </Text>
          </View>
          <View style={styles.dotProp} />
        </TouchableOpacity>
      </MapboxGL.MarkerView>
    ));
  };

  return (
    <View style={styles.page}>
      <View style={styles.container}>
        <MapboxGL.MapView
          ref={_map}
          onPress={onPressMapView}
          // onDidFinishLoadingMap={onMapLoad}
          style={styles.map}
          styleURL={
            !isSateliteStyle
              ? MapboxGL.StyleURL.Satellite
              : MapboxGL.StyleURL.Street
          }
          localizeLabels={locale: I18nManager.isRTL ? 'ar' : 'en'}
          attributionEnabled={false}
          compassEnabled={true}
          logoEnabled={false}
          scaleBarEnabled={isIOS() ? true : false}
          >
           <Camera
              maxBounds={{
              ne: [60.54, 41.5],
              sw: [32.05, 9.77],
              }}
              ref={camera}
              maxZoomLevel={20}
              minZoomLevel={4}
              centerCoordinate={[46.67, 24.71]}
            />
          {!localKmlData ? null : (
            <>
              <MapboxGL.ShapeSource
                id="nyc"
                // url={localKmlData}
                shape={localKmlData}
                onPress={items => {
                  onPressPolygon(items);
                }}>
                <MapboxGL.FillLayer 
                id="nycFill" 
                style={style_MB.polygonBG} 
                filter={['!=', 'avail', 'A']} //show non-sold items, avail will be present in properties attribute of geojson
 />
                <MapboxGL.LineLayer
                  sourceID="nyc"
                  id="nycFillLine"
                  style={style_MB.polygonBorder}
                />
              </MapboxGL.ShapeSource>
              <SelectedPolygon />
              <SelectedPolygonProperties />
              {selectedPolygonPropeties ? (
                <RenderAnnotationsProperties />
              ) : null}
            </>
          )}
        </MapboxGL.MapView>
      </View>
      <TouchableOpacity
        style={styles.mapType(isSateliteStyle)}
        onPress={setMapTypeFunc}>
        <CustomIcon
          color={!isSateliteStyle ? COLOR_GRAY_70 : COLOR_SECONDARY}
          name={ICONOGRAPHY.FLOORS}
          size={20}
        />
      </TouchableOpacity>
    </View>
  );
};

export default MapBoxSRP;

```