DEV Community

Ajmal Hasan
Ajmal Hasan

Posted on • Updated on

 

Mapbox Api Integration with React Native

In this post we will be integrating multiple mapbox api's like Search, Retrieve, Geocoding, Reverse Geocoding, POIs, Directions.

Image description

Directions

The Mapbox Directions API will show you how to get where you're going. With the Directions API, you can:

Calculate optimal driving, walking, and cycling routes using traffic- and incident-aware routing
Produce turn-by-turn instructions
Produce routes with up to 25 coordinates for the driving, driving-traffic, walking, and cycling profiles
Calculate routes for electric vehicles to reach destinations with optimal charging stops as well as battery prediction
https://docs.mapbox.com/api/navigation/directions/

Search

The Mapbox Search Service includes two APIs: the Mapbox Geocoding API and the Mapbox Search API(to enable mapbox search feature dev need to connect with mapbox sales for enabling it).
https://docs.mapbox.com/api/search/

1. Geocoding
The Mapbox Geocoding API allows you to do forward and reverse geocoding operations. Forward Geocoding takes text in the form of an address or place and converts it to geographic coordinates (latitude/longitude). Reverse geocoding takes geographic coordinates (latitude/longitude) and converts it into an address or place in text form.
https://docs.mapbox.com/api/search/geocoding/

2. Search
The Mapbox Search API enables search suggestions and feature retrieval for an interactive Search experience.
The Search API includes six different endpoints: /suggest, /retrieve, /forward, /permanent/forward, /reverse, and /permanent/reverse.
https://docs.mapbox.com/api/search/search/

API COLLECTION:

  1. GET(Direction api)
curl --location --request GET 'https://api.mapbox.com/directions/v5/mapbox/driving/-122.42,37.78;-77.03,38.91?geometries=geojson&access_token=pk.eyJ1IjoiZGVlcGFrMTIyMSIsImEiOiJjbDhiZXQ5cmEwcXhzM3FvNTFhNDNpNms3In0.x6FPXW7P3lIWTKPs6PMaJA'
Enter fullscreen mode Exit fullscreen mode
  1. GET(Gives suggestion list of inputted text)
curl --location --request GET 'https://api.mapbox.com/search/v1/suggest/mec?language=ar&session_token=&country=SA&access_token=sk.eyJ1Ijoid2FzYWx0bWFwcyIsImEiOiJjbGM2OXUzODkwMjhhM3BtZ2x4dmtvd21hIn0.uLcik68kLtKKCqKvpLzD6A'
Enter fullscreen mode Exit fullscreen mode
  1. POST(Retrieve suggested item's more info)
curl --location -g --request POST 'https://api.mapbox.com/search/v1/retrieve?session_token=[GENERATED-UUID]&access_token=pk.eyJ1Ijoid2FzYWx0bWFwcyIsImEiOiJjbDlkdHhtOW8xbjVzM3dwOGFzcm0xbmI1In0.aKPN7qWIPGX8hbKcOV_hbA' \
--header 'Content-Type: application/json' \
--data-raw '{
                    "id": "GpLnHR-w8BMB2AxARJ1nZYvXueJQn4PQ1GKu3mT11_5uLytWHFBsI7LnCBhFmrtosuecy3HLMPRTbcte0bBJYqe4XmLO3DCCJoeqcrJwzBeK_n1Hrfq5zBS034KiwOObxR1Wmv4MTKFW0vanZjPlO3j9QOJZJLjrIaW2Olfdc91WmEAqB7yv2TxTpZFfOMmrvqhVWtRJyB_tgq5NzndtDxlG1wsXAl_1urUn0VsmEe3VpBkyOm6MnBZwUKWMq3RELeTduG4jG1zNElVvjjS_aiZfg_VXsfbhit5J4efCwiResxecDKrLlaDbLRuDGOeFOC8XCb5_eM1O35zZEi7U_HujRi_v5bMinyfvnQfo0kWJTIVmgvHyfUS5eyzOzdaaEWwoblfFfo4MxQA-j_wdFOMmsDvyHL9BwOaGufjaWuN-6gW0eZBqrY3tJnwfYgY9liRlUMdc8FxCuzg0D0QDCduFp8H2nUXI8ALZV2wIN510XgJAg3r80T85quTp80dBRyXvP4R7_9XG5mFXcflsRTeCZsjaRDBVC400ff-06rHPVBKtHtuIGqjWE7p9AEjf17RX9fwY8MFocA1JZrRrTDwHQFFBCV-wbcbUGL4s50BLn2jkkClPNr0-yiDt14AJg3JP0NDPJa73hjRtC2i5Jm74VqPy_XK632rgeMHKB8IDyabJjKPJvQk-EPE0LL3YgR-JH4wC4UCOMOxPciuBoZ4hxF3GChra-fiQRVF6M6c4N5r323922nyIYIv9sX1RS3c7DcnWRZ0KsXc5PWD-hd8ZR2uLN8nMVwwak2u0axjh2WzrUeD27Yk_5ZIhNxxWsH8VNNyVfMT5Ig2FT3_FYJj-Hb-KwSv629ohr1kEWeNWH4LhI_jmCAIzzleoVk3j7XrRKtUb8lUqWye4ah7f5GceHl4gANhPM2fdDMZDbO8tUOuepEWoO5BQRGJiChM4h-85cJ5JO7zDSzsaA1WwFQf9EyPdhEaJB8tN8UNAmpF4vtpM3HsvGmnv7AY63oLj0VkusigK-UEPvxu4_3bixk6PDCTwZXsJEpYB_TcXwKXmrPnKbrYmDqt9psvWG8SlziKs3YYyMIs5HoP1eVQpQGmR06YQ8hxw8O2XFQdRUKvptHYDiwuY03R2YGDspiYu1UD2fj7muraZNZsk-VWFjRfuztunsTe-vtuJtqdyilyXGpWgft9t14GXOl-F5HfnMqYP_tzlCW1CedCchrRG_WdDL0IWfxF_LhkK4BjSoh7o2-pb4gLxEJ-k0--xYRIxPZgpRZ9YrHdNi8hXYsPQFwv6x91nw7jSPAZXcwvgYb8VCOgJrca9D6BHzXAD1VXXKi-9h9LnYkUA1cGKQ1p_o4sQU2OLslOD3jgNTOVRzePfkvbqkjXeiIdWlzuz2o2fvdyaxym7jWR-1qiIFzeXIaCeT746pEmLBw=="
                }'
Enter fullscreen mode Exit fullscreen mode
  1. GET(Gives list of POIs)
curl --location --request GET 'https://api.mapbox.com/geocoding/v5/mapbox.places/fast food;college;university.json?type=poi&proximity=-74.70850,40.78375&access_token=pk.eyJ1IjoiZGVlcGFrMTIyMSIsImEiOiJjbDhiZXQ5cmEwcXhzM3FvNTFhNDNpNms3In0.x6FPXW7P3lIWTKPs6PMaJA'
Enter fullscreen mode Exit fullscreen mode
  1. GET(Gives info about inputed text))
curl --location --request GET 'https://api.mapbox.com/geocoding/v5/mapbox.places/Los%20Angeles.json?access_token=pk.eyJ1Ijoid2FzYWx0bWFwcyIsImEiOiJjbDlkdHhtOW8xbjVzM3dwOGFzcm0xbmI1In0.aKPN7qWIPGX8hbKcOV_hbA'
Enter fullscreen mode Exit fullscreen mode
  1. GET(Gives info about provided coordinates)
curl --location --request GET 'https://api.mapbox.com/search/v1/reverse/13.329048,52.51258?language=en&access_token=pk.eyJ1Ijoid2FzYWx0bWFwcyIsImEiOiJjbDlkdHhtOW8xbjVzM3dwOGFzcm0xbmI1In0.aKPN7qWIPGX8hbKcOV_hbA&country=SA'
Enter fullscreen mode Exit fullscreen mode

Code:

Mapview.js

import MapboxGL, {Camera, PointAnnotation, MarkerView} from '@rnmapbox/maps';
import {MAP_BOX_ACCESS_TOKEN} from '../../utils/constants/constants';
import * as turf from '@turf/turf';
import SearchLocationInput from './SearchLocationInput';

MapboxGL.setAccessToken(MAP_BOX_ACCESS_TOKEN);
const ANNOTATION_SIZE = 30;

const MAP_VIEW = ({navigation}) => {
  let _map = useRef(null);
  let camera = useRef(null);
  let pointAnnotation = useRef(null);
  const [poiList, setpoiList] = useState([]);
  const [currentPinCoordinate, setcurrentPinCoordinate] = useState([
    46.6753, 24.7136,
  ]);
  // const [currentSelectedPoi, setCurrentSelectedPoi] = useState({});
  const [routeGeoJOSN, setrouteGeoJOSN] = useState();
  const [centerOfLineString, setcenterOfLineString] = useState();

  useEffect(() => {
    if (currentPinCoordinate && currentPinCoordinate.length > 0) {
      console.log('currentPinCoordinate', currentPinCoordinate);
    }
  }, [currentPinCoordinate]);

  const defaultCamera = {
    centerCoordinate: [46.6753, 24.7136],
    zoomLevel: 15,
  };


  const onPressInterestSite = async name => {
    let response = await fetch(
      'https://api.mapbox.com/geocoding/v5/mapbox.places/mosque.json?' +
        new URLSearchParams({
          type: 'poi',
          proximity: '46.6753,24.7136',
          access_token: MAP_BOX_ACCESS_TOKEN,
          limit: 10,
        }),
    );
    let data = await response.json();
    console.log('onPressInterestSite', data);
    setpoiList(data);
  };

  const onSelectPoiFunc = async payload => {
    console.log('poi:', payload);
    let coords = payload?.center;
    // setCurrentSelectedPoi(payload);

    let response = await fetch(
      `https://api.mapbox.com/directions/v5/mapbox/driving/${currentPinCoordinate[0]},${currentPinCoordinate[1]};${coords[0]},${coords[1]}?` +
        new URLSearchParams({
          geometries: 'geojson',
          access_token: MAP_BOX_ACCESS_TOKEN,
        }),
    );
    let data = await response.json();
    let lineStringGeoJSON = {
      type: 'FeatureCollection',
      features: [
        {
          type: 'Feature',
          properties: {},
          geometry: data?.routes[0]?.geometry,
        },
      ],
    };
    setrouteGeoJOSN(lineStringGeoJSON);

    let allCoordinates = lineStringGeoJSON?.features[0]?.geometry?.coordinates;
    console.log(
      'onPressInterestSiteDirection',
      data,
      lineStringGeoJSON,
      allCoordinates[Math.round(allCoordinates.length / 2)],
    );
    let featuresCenter = turf.points(allCoordinates);

    let center = turf.center(featuresCenter);
    setcenterOfLineString(center?.geometry?.coordinates);
  };

  const RouteLineString = () => (
    <MapboxGL.ShapeSource id="routeLine" shape={routeGeoJOSN}>
      <MapboxGL.LineLayer id="lines" style={styles.mapBox} />
    </MapboxGL.ShapeSource>
  );

  const CustomCalloutView = ({message}) => {
    return (
      <View style={styles.callOutContainer}>
        <View style={styles.callOutInner} />
        <View style={[styles.triangle, styles.arrowDown]} />
      </View>
    );
  };

  const onDragEnd = payload => {
    let centerCoordinate = payload?.geometry?.coordinates;
    setcurrentPinCoordinate(centerCoordinate);
    camera?.current?.setCamera({
      centerCoordinate,
      zoomLevel: defaultCamera?.zoomLevel,
      animationDuration: 1000,
      animationMode: 'flyTo',
    });
  };

  const selectedItemResDropdown = payload => {
    console.log('SELECTED_DROPDOWN_ITEM_RESPONSE', payload);
    let centerCoordinate = payload?.geometry?.coordinates;
    setcurrentPinCoordinate(centerCoordinate);
    camera?.current?.setCamera({
      centerCoordinate,
      zoomLevel: defaultCamera?.zoomLevel,
      animationDuration: 3000,
      animationMode: 'flyTo',
    });
  };

  return (
    <View style={styles.page}>
      <TouchableOpacity
        style={styles.tempGoback}
        onPress={() => navigation.goBack()}
      />

      {/* REUSABLE MARKER SEARCH COMPONENT */}
      <SearchLocationInput
        textInputText={''}
        selectedItemRes={res => selectedItemResDropdown(res)}
        currentPinCoordinate={currentPinCoordinate}
      />
      {/* REUSABLE MARKER SEARCH COMPONENT */}

      <View style={styles.container}>
        <MapboxGL.MapView
          ref={_map}
          style={styles.map}
          styleURL={MapboxGL.StyleURL.Street}
          localizeLabels={true}
          attributionEnabled={false}
          logoEnabled={false}>
          <Camera ref={camera} defaultSettings={defaultCamera} />
          <PointAnnotation
            key={1}
            id={'1'}
            coordinate={currentPinCoordinate}
            draggable
            onDragEnd={onDragEnd}
            ref={pointAnnotation}>
            <View style={styles.annotationContainer}>
              <Image
                source={{uri: 'https://reactnative.dev/img/tiny_logo.png'}}
                style={styles.imgStyle}
                onLoad={() => pointAnnotation.current?.refresh()}
              />
            </View>
          </PointAnnotation>
          {poiList?.features && poiList?.features.length > 0
            ? poiList?.features.map((value, index) => (
                <MarkerView key={value.id} coordinate={value?.center}>
                  <Pressable onPress={() => onSelectPoiFunc(value)} key={index}>
                    <View style={styles.marker} />
                  </Pressable>
                </MarkerView>
              ))
            : null}

          {routeGeoJOSN && <RouteLineString />}
          {centerOfLineString && (
            <MapboxGL.MarkerView
              id="selectedFeatureMarkerView"
              coordinate={centerOfLineString}>
              <CustomCalloutView />
            </MapboxGL.MarkerView>
          )}
        </MapboxGL.MapView>
      </View>
      <View style={styles.bottomContainer}>
        <Button title={'SHOPPING'} onPress={onPressInterestSite} />
        <Button title={'RESTAURANT'} onPress={onPressInterestSite} />
        <Button title={'MOSQUE'} onPress={onPressInterestSite} />
        <Button title={'HOSPITAL'} onPress={onPressInterestSite} />
      </View>
    </View>
  );
};

export default MAP_VIEW;

const styles = StyleSheet.create({});
Enter fullscreen mode Exit fullscreen mode

SearchLocationInput.js

import React, {useEffect, useState} from 'react';
import {
  View,
  Text,
  FlatList,
  TextInput,
  StyleSheet,
  TouchableOpacity,
} from 'react-native';
import {MAP_BOX_ACCESS_TOKEN} from '../../../utils/constants/constants';
import {isRTL} from '../../../i18n';
import {useFirstRender} from '../../newTabsGallery/galleryComponents/reusableHooks';

const SearchLocationInput = ({
  textInputText = '',
  selectedItemRes,
  currentPinCoordinate = [],
}) => {
  const [resultList, setresultList] = useState([]);
  const [inputText, setinputText] = useState(textInputText);
  const firstRender = useFirstRender();

  useEffect(() => {
    if (!firstRender) {
      reverseGeocodingData();
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [currentPinCoordinate]);

  const reverseGeocodingData = async (bool = true) => {
    // bool true = poi, bool false = region
    let searchParams = new URLSearchParams({
      access_token: MAP_BOX_ACCESS_TOKEN,
      language: isRTL() ? 'ar' : 'en',
      country: 'SA',
      limit: 1,
      // type: 'poi',
    });
    let response = await fetch(
      bool
        ? `https://api.mapbox.com/geocoding/v5/mapbox.places/${currentPinCoordinate[0]},${currentPinCoordinate[1]}.json?` +
            searchParams
        : `https://api.mapbox.com/search/v1/reverse/${currentPinCoordinate[0]},${currentPinCoordinate[1]}?` +
            searchParams,
    );
    let result = await response.json();
    let resData = bool
      ? result?.features[0]?.text
      : result?.features[0]?.properties?.feature_name;
    console.log('reverseGeocodingData', resData);
    setinputText(resData);
  };

  const searchItems = async text => {
    try {
      setinputText(text);
      if (text.length > 2) {
        let response = await fetch(
          `https://api.mapbox.com/search/v1/suggest/${text}?` +
            new URLSearchParams({
              access_token: MAP_BOX_ACCESS_TOKEN,
              session_token: '',
              language: isRTL() ? 'ar' : 'en',
              country: 'SA',
              // types:
              //   'country, region, prefecture, postcode, district, place, city, locality, oaza, neighborhood, chome, block, street,  address',
            }),
        );
        let result = await response.json();
        setresultList(
          result?.suggestions.length > 0 ? result?.suggestions : [],
        );
      } else {
        setresultList([]);
      }
    } catch ({message}) {
      setinputText('');
      setresultList([]);
      console.log('searchItemsSiteERROR', message);
    }
  };

  const onPressListItem = async item => {
    try {
      let options = {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(item?.action?.body),
      };
      let response = await fetch(
        'https://api.mapbox.com/search/v1/retrieve?' +
          new URLSearchParams({
            access_token: MAP_BOX_ACCESS_TOKEN,
            session_token: '',
          }),
        options,
      );
      let result = await response.json();
      selectedItemRes(result?.features[0]);
      setinputText(result?.features[0]?.properties?.feature_name);
      setresultList([]);
    } catch ({message}) {
      console.log('onPressListItemERROR', message);
    }
  };

  const renderSeparator = () => {
    return (
      <View
        style={{
          height: 1,
          width: '100%',
          backgroundColor: '#CED0CE',
        }}
      />
    );
  };

  return (
    <View
      style={{
        flex: 1,
        width: '98%',
        position: 'absolute',
        top: 100,
        zIndex: 1,
        backgroundColor: 'white',
      }}>
      <TextInput
        style={{height: 60, borderColor: '#000', borderWidth: 1}}
        placeholder="   Type Here...Key word"
        onChangeText={searchItems}
        value={inputText}
      />
      {inputText && (
        <FlatList
          data={resultList}
          renderItem={({item}) => (
            <TouchableOpacity onPress={() => onPressListItem(item)}>
              <Text style={{padding: 10}}>{item?.feature_name} </Text>
            </TouchableOpacity>
          )}
          keyExtractor={(item, index) => index + ''}
          ItemSeparatorComponent={renderSeparator}
        />
      )}
    </View>
  );
};

export default SearchLocationInput;

const styles = StyleSheet.create({});

Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
rhenaldkarrel profile image
Rhenald Karrel

Does this API is free for development?

Visualizing Promises and Async/Await 🤯

async await

☝️ Check out this all-time classic DEV post