Mapbox Integration
Visualise GPS position data from your microcontroller with an integrated Mapbox GL element, and dynamically updating position markers.

Mapbox is a paid, 3rd party service. We demonstrate it's use here due to popularity and breadth of tooling available.
Alternative/OSS mapping options are available through a combination of OpenStreetMap and
react-leaflet.
Mapbox Setup
Getting a API key
Create a free Mapbox account, then generate an access token which we'll use later.
Installation
We're going to add the following libraries using yarn,
yarn add mapbox-gl @types/mapbox-gl react-map-glwhich will add the dependancies to the project's package.json file (versions may differ from time of writing),
"@types/mapbox-gl": "^2","mapbox-gl": "^2.7.1","react-map-gl": "^7.0.10",then run arc install before starting the sandbox environment.
Hardware Position Data
This example assumes the GPS latitude and longitude are available as numbers in a structure, with working codecs.
In this example, the gps message contains the lat and lon members which are sent from hardware as single-precision floating point values,
export type GPSData = { fix_ok: boolean lat: number, lon: number, speed: number, }Usage
Adding a map
Create a new component file map.tsx:
- Use
useMessageDataSourceto provide streaming access to ourgpsdata, - Create a
mapRefreference to persist data between renders, - Add a barebones
react-map-glmap component,
import React, { useEffect, useRef, useState } from 'react'import { useDataSourceSubscription } from '@electricui/components-desktop-charts'import { useMessageDataSource } from '@electricui/core-timeseries'import Map, { Marker, MapRef } from 'react-map-gl/dist/es5'import 'mapbox-gl/dist/mapbox-gl.css'export function SatelliteView() { const positionDS = useMessageDataSource('gps') let mapZoom: number = 14.3 const mapRef = useRef<MapRef>() return ( <Map initialViewState={{ latitude: -25.43, longitude: 133.15, zoom: 2 }} mapboxAccessToken="token_goes_here" mapStyle="mapbox://styles/mapbox/satellite-streets-v11" ref={mapRef as any} > // Annotations go here later </Map> )}
initialViewState={{ latitude: -25.43, longitude: 133.15, zoom: 2 }}is set to the centre of Australia for this example - consider setting this to the user's last known location or sensible starting location.
When using the SatelliteView component in your layout, ensure it has properly defined width and height (use an additional div) to ensure stable layout behaviour!
Custom Marker Icon
Any SVG icon would work, but lets create a simple SVG based red balloon icon to act as our marker graphic, call it Pin.tsx.
import * as React from 'react'const ICON = `M20.2,15.7L20.2,15.7c1.1-1.6,1.8-3.6,1.8-5.7c0-5.6-4.5-10-10-10S2,4.5,2,10c0,2,0.6,3.9,1.6,5.4c0,0.1,0.1,0.2,0.2,0.3 c0,0,0.1,0.1,0.1,0.2c0.2,0.3,0.4,0.6,0.7,0.9c2.6,3.1,7.4,7.6,7.4,7.6s4.8-4.5,7.4-7.5c0.2-0.3,0.5-0.6,0.7-0.9 C20.1,15.8,20.2,15.8,20.2,15.7z`const pinStyle = { fill: '#d00', stroke: 'none',}export function Pin(props: { size: number }) { const { size = 20 } = props return ( <svg height={size} viewBox="0 0 24 24" style={pinStyle}> <path d={ICON} /> </svg> )}Hardware driven marker positioning
Inside our SatelliteView's map component, we need to create a react-map-gl Marker component and some glue code to position it with our hardware information.
By creating a lastPos reference, we can write some logic to perform a position update:
- The
useDataSourceSubscription()hook allows us to run code when new data arrives, - If the GPS position has changed,
- Cache the position for later in a variable called
lastPos, using theuseRefhook, - Update the position for our Marker,
- Re-center the map on the latest position (optional)
- Cache the position for later in a variable called
First make sure we've imported our Pin icon, and the Marker component:
import { Pin } from './Pin'import Map, { Marker, MapRef } from 'react-map-gl/dist/es5'We'll put the update code just under the mapRef we created earlier:
const lastPos = useRef({ latitude: gpsData.lat, longitude: gpsData.lon,})const [marker, setMarker] = useState(lastPos.current)useDataSourceSubscription(positionDS, data => { if (lastPos.current.latitude !== data.lat || lastPos.current.longitude !== data.lon) { lastPos.current = { latitude: data.lat, longitude: data.lon, } setMarker({ latitude: data.lat, longitude: data.lon }) mapRef.current?.flyTo({ center: { lat: data.lat, lon: data.lon }, zoom: mapZoom, }) }}, false)Finally, add the Marker and Pin components as child of the Map component.
<Marker longitude={marker.longitude} latitude={marker.latitude} anchor="bottom"> <Pin size={20} /></Marker>
That's it. To push your map integration further, have a look at the react-map-gl docs.
