Nervous Penpal Message

    @mappedin/react-native-sdk
    TypeScript icon, indicating that this package has built-in type declarations

    5.0.0-beta.0 • Public • Published

    Mappedin React Native SDK

    Resources

    Installation

    NPM:

    npm install @mappedin/react-native-sdk react-native-webview

    YARN:

    yarn add @mappedin/react-native-sdk react-native-webview

    Update cocoapods: cd ios && pod install

    Usage:

    We provide both a declarative API using props and an imperative API with methods. For example, to listen to polygon clicks, we provide a prop onPolygonClicked; in order to focus the map onto this polygon, we provide an imperative API method focusOn.

    Component Signature:

    const MiMapView = (props: TMapViewProps) => React.ReactElement;

    Props:

    Documented in Detail here

    Prop Description Signature Required
    options Options to initialize MapView with TMiMapViewOptions Yes
    ref Exposes Imperative API MapViewStore React.MutableRefObject<MapViewStore> No
    onPolygonClicked Gets called when clicking interactive polygon ({ polygon: MappedinPolygon }) => void No
    onDataLoaded Fired when Mappedin data is loaded ({ venueData: Mappedin }) => void No
    onFirstMapLoaded Fires when map can first be interacted with () => void No
    onBlueDotStateChanged Fires when BlueDot state changes ({ stateChange: TBlueDotStateChange }) => void No
    onBlueDotPositionUpdated Fires when BlueDot position changes ({ update: TBlueDotPositionUpdate }) => void No
    onStateChanged Fires when SDK State changes ({ state: STATE }) => void No

    Imperative API

    Documented in Detail here: MapViewStore

    Render map

    Example:

    import { Mappedin } from '@mappedin/react-native-sdk';
    
    const options = {
      clientId: '****',
      clientSecret: '***',
      venue: 'venue-slug',
      perspective: 'Website',
    };
    
    // Render map
    const App = () => {
      return <MiMapView style={{ flex: 1 }} options={options} />;
    };

    Example:

    import { MiMapView } from '@mappedin/react-native-sdk';
    import type { Mappedin } from '@mappedin/react-native-sdk';
    
    const App = () => {
      const [venueData, setVenueData] = React.useState<Mappedin>({});
    
      return (
        <MiMapView
          options={options}
          onDataLoaded={({ venueData }) => {
            setVenueData(venueData);
          }}
        />
      );
    };

    Fetching venue data outside of the MiMapView component

    It is possible to fetch venue data (locations, maps, etc), outside of the MiMapView component. This can be helpful for cases where a map isn't always needed or there is a need to fetch the data first, and render the map later.

    import { getVenue, MiMapView } from '@mappedin/react-native-sdk';
    
    const options = {
      clientId: '****',
      clientSecret: '***',
      venue: 'venue-slug',
      perspective: 'Website',
    };
    
    const App = () => {
      const [venueData, setVenueData] = React.useState<Mappedin>({});
    
      useEffect(() => {
        async function init() {
          const venueData = await getVenue(options);
          console.log(venueData.locations);
          setVenueData(venueData);
        }
      }, []);
    
      return (
        {venueData != null &&
          <MiMapView
            style={{ flex: 1 }}
            options={options}
            // pass venueData in
            venueData={venueData}
          />
        }
      );
    };

    Listen to polygon clicks

    import { MiMapView } from '@mappedin/react-native-sdk';
    import type { MappedinLocation, Mappedin } from '@mappedin/react-native-sdk';
    
    const App = () => {
      const [venueData, setVenueData] = React.useState<Mappedin>({});
      const [
        selectedLocation,
        setSelectedLocation,
      ] = React.useState<MappedinLocation>({});
    
      return (
        <MiMapView
          style={{ flex: 1 }}
          options={options}
          onDataLoaded={({ venueData: Mappedin }) => {
            setVenueData(venueData);
          }}
          onPolygonClicked={({ polygon }) => {
            setSelectedLocation(polygon.locations[0]);
          }}
        />
      );
    };

    Rotate the map

    import { MiMapView } from '@mappedin/react-native-sdk';
    import type { MapViewStore } from '@mappedin/react-native-sdk';
    
    // Imperative API
    const mapView = React.useRef<MapViewStore>();
    
    const App = () => {
      return (
        <MiMapView
          style={{ flex: 1 }}
          ref={mapView}
          options={options}
          onFirstMapLoaded={() => {
            mapView.current!.Camera.set({ positionOptions: { rotation: 90 });
          }}
        />
      );
    };

    Focus on the polygon and highlight it

    import { MiMapView } from '@mappedin/react-native-sdk';
    import type {
      MappedinPolygon,
      MapViewStore,
      Mappedin,
    } from '@mappedin/react-native-sdk';
    
    const App = () => {
      const [venueData, setVenueData] = React.useState<Mappedin>({});
      const [
        selectedLocation,
        setSelectedLocation,
      ] = React.useState<MappedinLocation>({});
    
      // Imperative API
      const mapView = React.useRef<MapViewStore>();
    
      return (
        <MiMapView
          style={{ flex: 1 }}
          ref={mapView}
          options={options}
          onDataLoaded={({ venueData: Mappedin }) => {
            setVenueData(venueData);
          }}
          onPolygonClicked={async ({ polygon: MappedinPolygon }) => {
            setSelectedLocation(polygon.locations[0]);
            mapView.current.clearAllPolygonColors();
            mapView.current.setPolygonColor(polygon, 'red');
            // lets wait for the focusOn to complete
            await mapView.current.Camera.focusOn({ targets: {
                polygons: [polygon],
              }
            });
            console.log('success!');
          }}
        />
      );
    };

    Enable Blue Dot and listen to update events

    import { MiMapView } from '@mappedin/react-native-sdk';
    import type {
      TBlueDotUpdate,
      MapViewStore,
      MappedinMap,
      MappedinNode,
    } from '@mappedin/react-native-sdk';
    
    // Imperative API
    const mapView = React.useRef<MapViewStore>();
    
    const App = () => {
      return (
        <MiMapView
          style={{ flex: 1 }}
          ref={mapView}
          options={options}
          onFirstMapLoaded={() => {
            mapView.current.BlueDot.enable();
          }}
          onBlueDotStateChanged={({
            stateChange,
          }: {
            stateChange: TBlueDotStateChange;
          }) => {
            // This event can be used to get real time updates as to what the nearest node is
            const {
              map,
              nearestNode,
            }: {
              map: MappedinMap;
              nearestNode: MappedinNode;
            } = stateChange;
          }}
        />
      );
    };

    Set SDK into "FOLLOW" mode

    Setting the SDK into follow mode will lock the camera to follow the BlueDot at the center of the screen. Interacting with the map will put the SDK back into EXPLORE mode and emit onStateChanged event.

    import { MiMapView } from '@mappedin/react-native-sdk';
    import type {
      TBlueDotUpdate,
      MapViewStore,
      MappedinLocation,
      MappedinNode,
    } from '@mappedin/react-native-sdk';
    import { STATE } from '@mappedin/react-native-sdk';
    
    // Imperative API
    const mapView = React.useRef<MapViewStore>();
    const nearestNode = React.useRef<MappedinNode>();
    const [
      selectedLocation,
      setSelectedLocation,
    ] = React.useState<MappedinLocation>({});
    
    const App = () => {
      return (
        <>
          <MiMapView
            style={{ flex: 1 }}
            ref={mapView}
            options={options}
            onStateChanged={({ state }) => {
              console.log('Current State is', state);
            }}
            onDataLoaded={() => {
              mapView.current.setState(STATE.FOLLOW);
              mapView.current.BlueDot.enable();
            }}
            onBlueStateChanged={({ stateChange }) => {
              nearestNode.current = stateChange.nearestNode;
            }}
            onPolygonClicked={({ polygon: MappedinPolygon }) => {
              setSelectedLocation(polygon.locations[0]);
            }}
          />
        </>
      );
    };

    Get Directions

    This API can be used to get a directions object from any MappedinLocation | MappedinNode | MappedinPolygon to any MappedinLocation | MappedinNode | MappedinPolygon. This can be used to show Text directions, as well as draw paths.

    Example:

    ...
    <Button
      title="Get Directions"
      onPress={async () => {
        try {
          const directions = selectedLocation.directionsTo(destinationLocation);
          console.log(directions);
        } catch (e) {
          console.error(e);
        }
      }}
    ></Button>
    ...

    Get Directions from One Location to Another and show path and connections on map

    import { MiMapView } from '@mappedin/react-native-sdk';
    import type {
      TBlueDotUpdate,
      MapViewStore,
      MappedinLocation,
      MappedinNode,
    } from '@mappedin/react-native-sdk';
    
    // Imperative API
    const mapView = React.useRef<MapViewStore>();
    const [destination, setDestination] = React.useRef<MappedinLocation>();
    const [departure, setDeparture] = React.useState<MappedinLocation>({});
    
    const App = () => {
      return (
        <>
          <MiMapView
            style={{ flex: 1 }}
            ref={mapView}
            options={options}
            onDataLoaded={() => {
              mapView.current.BlueDot.enable();
            }}
            onPolygonClicked={({ polygon: MappedinPolygon }) => {
              // first, let's set departure
              if (departure == null) {
                setDeparture(polygon.locations[0]);
              } else {
                // then, destination
                setDestination(polygon.locations[0]);
              }
            }}
          />
          <Button
            title="Get Directions"
            onPress={async () => {
              try {
                // get Directions
                const directions = departure.directionsTo(destination);
                // draw path on map
                await controller.current?.Journey.draw(directions);
                // focus on the path
                controller.current?.Camera.focusOn({ targets: {
                    nodes: directions.path,
                  }
                });
              } catch (e) {
                console.error(e);
              }
            }}
          ></Button>
          <Button
            title="Reset"
            onPress={() => {
              mapView.current.clearAllPaths();
              setDeparture(undefined);
              setDestination(undefined);
            }}
          ></Button>
        </>
      );
    };

    Preparing SVGs for use as assets in markers/tooltips/etc

    SVG is a popular format for image assets, which means there are a lot of proprietary, broken, or unnecessary tags when getting SVGs online.

    Before using SVGs with our SDKs, they need to get “cleaned up” - this also drastically reduces their file size, so win-win.

    Preparing

    1. Go to https://jakearchibald.github.io/svgomg/
    2. Drag and drop your SVG
    3. All default settings will work well, but we also recommend enabling “prefer viewBox to width/height”
    4. You can now download the clean SVG or copy and paste its contents.

    Once the SVGs are prepared, they need to be wrapped in a div element which will give them an explicit size. This allows markers to be any desired size and the SVG will grow/shrink appropriately. The element can also add a background, shadows, and any other styles.

    `
    <div style="width: 32px; height: 32px;">
      <svg xmlns="http://www.w3.org/2000/svg">...</svg>
    </div>
    `

    Adding a Coordinate by lat/lon

    import { MiMapView } from '@mappedin/react-native-sdk';
    import type {
      MapViewStore,
    } from '@mappedin/react-native-sdk';
    
    // Imperative API
    const mapView = React.useRef<MapViewStore>();
    
    const App = () => {
      return (
        <>
          <MiMapView
            style={{ flex: 1 }}
            ref={mapView}
            options={options}
            onFirstMapLoaded={() => {
              // create a coordinate
              const coordinate = mapView.current.venueData.maps[1].createCoordinate(43.52117572969203, -80.53869317220372);
    
              const coordinate2 = mapView.current.venueData.maps[1].createCoordinate(44.52117572969203, -79.53869317220372);
              // find distance between coordinates:
              console.log(coordinate.absoluteDistanceTo(coordinate2));
    
              // coordinates can also be used to place markers:
              mapView.current.createMarker(coordinate, ...)
            }}
          />
      )
    }

    Customizing appearance of labels

    Labels are added on map load by default, but this can be overriden by setting labelAllLocationsOnInit to false and taking control of labeling. We provide 2 themes, and full control over further customization.

    import { MiMapView, labelThemes } from '@mappedin/react-native-sdk';
    import type {
      MapViewStore,
    } from '@mappedin/react-native-sdk';
    
    // Imperative API
    const mapView = React.useRef<MapViewStore>();
    
    const App = () => {
      return (
        <>
          <MiMapView
            style={{ flex: 1 }}
            ref={mapView}
            options={{ ...options, labelAllLocationsOnInit: false }}
            onFirstMapLoaded={() => {
              // use one of the themes
              mapView.current.labelAllLocations({ appearance: labelThemes.lightOnDark })
              // OR, customize a set of properties
              mapView.current.labelAllLocations({ appearance: { text: { size: 12 }} })
              // OR, do both
              mapView.current.labelAllLocations({ appearance:
              {
                ...labelThemes.lightOnDark,
                margin: 20,
                text: {
                  ...labelThemes.lightOnDark.text,
                  size: 20
                }
                }
              })
            }}
          />
      )
    }

    Adding/Removing markers

    import { MiMapView } from '@mappedin/react-native-sdk';
    import type {
      MapViewStore,
    } from '@mappedin/react-native-sdk';
    
    // Imperative API
    const mapView = React.useRef<MapViewStore>();
    const markerId = React.useRef(null);
    
    const App = () => {
      return (
        <>
          <MiMapView
            style={{ flex: 1 }}
            ref={mapView}
            options={options}
            onDataLoaded={() => {
              mapView.current.BlueDot.enable();
            }}
            onPolygonClicked={({ polygon }) => {
              if (markerId != null) {
                mapView.current.removeMarker(markerId);
              }
              // Let's add a marker anchored to the top of the first entrance to polygon
              markerId.current = mapView.current.createMarker(polygon.entrances[0], `
                <div style="width: 32px; height: 32px;">
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 293.334 293.334"><g fill="#010002"><path d="M146.667 0C94.903 0 52.946 41.957 52.946 93.721c0 22.322 7.849 42.789 20.891 58.878 4.204 5.178 11.237 13.331 14.903 18.906 21.109 32.069 48.19 78.643 56.082 116.864 1.354 6.527 2.986 6.641 4.743.212 5.629-20.609 20.228-65.639 50.377-112.757 3.595-5.619 10.884-13.483 15.409-18.379a94.561 94.561 0 0016.154-24.084c5.651-12.086 8.882-25.466 8.882-39.629C240.387 41.962 198.43 0 146.667 0zm0 144.358c-28.892 0-52.313-23.421-52.313-52.313 0-28.887 23.421-52.307 52.313-52.307s52.313 23.421 52.313 52.307c0 28.893-23.421 52.313-52.313 52.313z"/><circle cx="146.667" cy="90.196" r="21.756"/></g></svg>
                </div>
                `,
              {
                anchor: MARKER_ANCHOR.TOP,
              })
            }}
          />
      )
    }

    Get nearest node to point on screen

    import { MiMapView } from '@mappedin/react-native-sdk';
    import type { MapViewStore } from '@mappedin/react-native-sdk';
    
    // Imperative API
    const mapView = React.useRef<MapViewStore>();
    
    const App = () => {
      return (
        <TouchableWithoutFeedback
          style={{ flex: 1 }}
          onPress={async ({ locationX, locationY }) => {
            // get nearest node to X,Y screen coordinate
            const node = await mapView.current?.getNearestNodeByScreenCoordinates(
              locationX,
              locationY,
            );
          }}
        >
          <MiMapView style={{ flex: 1 }} ref={mapView} options={options} />
        </TouchableWithoutFeedback>
      );
    };

    Get Directions from BlueDot and show path and connections on map

    import { MiMapView } from '@mappedin/react-native-sdk';
    import type {
      TBlueDotUpdate,
      MapViewStore,
      MappedinLocation,
      MappedinNode,
    } from '@mappedin/react-native-sdk';
    
    // Imperative API
    const mapView = React.useRef<MapViewStore>();
    const nearestNode = React.useRef<MappedinNode>();
    const [
      selectedLocation,
      setSelectedLocation,
    ] = React.useState<MappedinLocation>({});
    
    const App = () => {
      return (
        <>
          <MiMapView
            style={{ flex: 1 }}
            ref={mapView}
            options={options}
            onDataLoaded={() => {
              mapView.current.BlueDot.enable();
            }}
            onBlueDotStateChanged={({ stateChange }) => {
              nearestNode.current = stateChange.nearestNode;
            }}
            onPolygonClicked={({ polygon: MappedinPolygon }) => {
              setSelectedLocation(polygon.locations[0]);
            }}
          />
          <Button
            title="Get Directions"
            onPress={async () => {
              try {
                // get Directions
                const directions = nearestNode.current.directionsTo(selectedLocation);
                // draw path and connections on map
                await controller.current?.Journey.draw(directions);
                // focus on the path
                controller.current?.Camera.focusOn({ targets: {
                    nodes: directions.path,
                  }
                });
              } catch (e) {
                console.error(e);
              }
            }}
          ></Button>
        </>
      );
    };

    Use react-native-location to power BlueDot

    import RNLocation from 'react-native-location';
    
    const App = () => {
      ...
    
      RNLocation.requestPermission({
        ios: 'whenInUse',
        android: {
          detail: 'coarse',
        },
      }).then((granted) => {
        if (granted) {
          // Enable blue dot
          mapView.current.BlueDot.enable();
          RNLocation.subscribeToLocationUpdates((locations) => {
            const {
              accuracy,
              floor,
              latitude,
              longitude,
              timestamp,
            } = locations[0];
    
            const location = {
              timestamp,
              coords: {
                latitude,
                longitude,
                accuracy,
                floorLevel: floor,
              },
            };
            // pass locations in
            mapView.current.overrideLocation(location);
          });
        }
      });
    
      ...
    };

    MiniMap

    Component Signature:

    const MiMiniMap = (props: TMiniMapProps) => React.ReactElement;

    Props:

    Documented in Detail here

    Prop Description Signature Required
    options Options to initialize MapView with TMiMapViewOptions Yes
    location Options to initialize MapView with MappedinLocation | string Yes
    onLoad Gets called when minimap is rendered () => void No
    polygonHighlightColor Color to use when highlighting polygon string No
    focusOptions Override focus Options when generating minimap TFocusOptions No

    Example:

    const App = () => {
      return (
        <MiMiniMap
          style={{ width: 500, height: 200 }}
          options={options: TGetVenueOptions}
          /**
           * Called when miniMap is rendered
           */
          onLoad={() => {}}
          /**
           * What color to use when highlighting polygon
           */
          polygonHighlightColor={string}
          /**
           * Finer control over focus
           */
          focusOptions={options: TFocusOptions}
          location={selectedLocation: MappedinLocation | MappedinLocation["id"]}
        />
      );
    };

    [experimental] Fetching an offline Venue bundle

    It is possible to download the venue bundle with all assets built in, which allows for caching/offline solutions.

    Note 1: This must be enabled by Mappedin's Customer Solutions team.

    Note 2: This may slow down map rendering for large venues, especially those with many images. We have improvements to this on our roadmap.

    import {
      getVenueBundle,
      MiMapView,
      Mappedin,
    } from '@mappedin/react-native-sdk';
    import { View } from 'react-native';
    import React, { useEffect } from 'react';
    import fs from 'react-native-fs';
    
    const options = {
      clientId: 'clientId',
      clientSecret: 'clientSecret',
      venue: 'venue-slug',
      perspective: 'Website',
    };
    
    const App = () => {
      const [venueData, setVenueData] = React.useState<Mappedin>();
    
      useEffect(() => {
        async function init() {
          const path = fs.DocumentDirectoryPath + '/bundle.json';
          try {
            // let's check if cache exists
            if (!(await fs.exists(path))) {
              console.log('cache doesnt exist');
              const venue = await getVenueBundle(options);
              setVenueData(venue);
              fs.writeFile(path, venue.toString());
            } else {
              console.log('cache exists, using');
              const venueString = await fs.readFile(path);
              const venue = new Mappedin(options);
              // hydrate the instance with cached data
              venue.hydrate(venueString);
              setVenueData(venue);
            }
          } catch (e) {
            console.error(e);
          }
        }
        init();
      }, []);
    
      return (
        <View style={{ flex: 1 }}>
          {venueData != null && (
            <MiMapView
              style={{ flex: 1 }}
              options={options}
              // pass venueData in
              venueData={venueData}
            />
          )}
        </View>
      );
    };

    Keywords

    none

    Install

    npm i @mappedin/react-native-sdk

    DownloadsWeekly Downloads

    353

    Version

    5.0.0-beta.0

    License

    UNLICENSED

    Unpacked Size

    3.24 MB

    Total Files

    6

    Last publish

    Collaborators

    • mappedin-ops
    • mappedin-engineering