React Native Mapbox (rnmapbox/maps v10) Usage

ajmal_hasan

Ajmal Hasan

Posted on October 18, 2022

React Native Mapbox (rnmapbox/maps v10) Usage

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, MarkerView, SymbolLayer in Mapbox.

Sample Picture

INSTALLATION:

Installation Steps


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]
            ]
          ]
        ]
      }
    },
*
*
*
]}
Enter fullscreen mode Exit fullscreen mode

markers.js

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

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;

Enter fullscreen mode Exit fullscreen mode
💖 💪 🙅 🚩
ajmal_hasan
Ajmal Hasan

Posted on October 18, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related