How To Build a Crime Map With Markers using React and MapBox API

image

I found it quite difficult to find a blog or video that shows me how I can upload map markers explicitly inside of a React component using the mapbox-gl js library.

Today, I will show you how to install, load and customize your map with styles and markers.

Create your account.

Go to https://account.mapbox.com/access-tokens/ to create your access token after signing up for your free account. You’ll use this token to initialize your map inside of your react component

Install MapBox GL to Your React Project

yarn add mapbox-gl

Create your map component

Create a fill called Map.js. and copy the code below and assign your API token to mapboxgl.accessToken

// Map.js

import React, {useEffect, useState, useRef} from "react";
import 'mapbox-gl/dist/mapbox-gl.css';
import mapboxgl from 'mapbox-gl'; // or "const mapboxgl = require('mapbox-gl');"
import {fraud, murder} from '../../data/crimes'

mapboxgl.accessToken = 'your access token'

export const Map = () => {
    const map = useRef(null);
    const mapContainer = useRef(null);

    const [lng, setLng] = useState(-83.04);
    const [lat, setLat] = useState(42.33);
    const [zoom, setZoom] = useState(6);
    
    useEffect(() => {
        map.current = new mapboxgl.Map({
            container: mapContainer.current,
            style: 'mapbox://styles/christopherclemmons2020/cl37v6xlr000u14mlzlfbuc80',
            center: [lng, lat],
            zoom: zoom
        });
        map.current.addControl(new mapboxgl.NavigationControl());
        map.current.on('load', () => {
        
            map.current.addLayer(createDataLayer('murder', murder, '#fbb03b')); 
            map.current.addLayer(createDataLayer('fraud', fraud, '#e55e5e'));
            
            map.current.addControl(
                new mapboxgl.GeolocateControl({
                    positionOptions: {
                        enableHighAccuracy: true
                    },
                    // When active the map will receive updates to the device's location as it changes.
                    trackUserLocation: true,
                    // Draw an arrow next to the location dot to indicate which direction the device is heading.
                    showUserHeading: true
                })
            );
        })

        //cleanup function
        return () => map.current.remove()
    })

    return (
        <>
            <div 
                style={{height:'100vh', width:'70vw' }} 
                ref={mapContainer} 
                className="map-container"
            ></div>
        </>
    ) 
}

We use the useRef hook to ensure we have a reference to the DOM elements before we start rendering data to it. In our useEffect hook, we are checking to make sure that a map instance doesn’t already exist before rendering the app. It won’t cause an issue to the client by not checking for this but it reduces the server calls which overall reduces server costs as well.

Next we set out lat , lng and zoom variables so we can set a view to the map when it renders. Our map will load right over motor city Detroit!

Go Lions!

addLayer will create our category that will contain the dataset from ourfraud dataset. We will also use our custom createDataLayer function to help use with handing larger data collections in the future.

fraud is our sample data set in the format that will work with our map. This will be parsed into the map on render that will render our dots based on geometry.coordinates

const fraud = {
    'type': 'FeatureCollection',
    'features': [
        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [
                -77.034084142948,
                38.909671288923
                ]
            },
            "properties": {
                "phoneFormatted": "(202) 234-7336",
                "phone": "2022347336",
                "address": "1471 P St NW",
                "city": "Washington DC",
                "country": "United States",
                "crossStreet": "at 15th St NW",
                "postalCode": "20005",
                "state": "D.C."
            }
        },
        {
            "type": "Feature",
            "geometry": {
              "type": "Point",
              "coordinates": [
                -77.049766,
                38.900772
              ]
            },
            "properties": {
                "phoneFormatted": "(202) 507-8357",
                "phone": "2025078357",
                "address": "2221 I St NW",
                "city": "Washington DC",
                "country": "United States",
                "crossStreet": "at 22nd St NW",
                "postalCode": "20037",
                "state": "D.C."
            }
        }
    ]
}

export {fraud}

createDataLayer will load our map makers that correspond to our data. This will also allow us to categorize data if we wanted to focus in on it. For example, we may want to filter crimes by location, date or severity. I separated the logic to make the Map.js file more compact but its not required. This code can be placed inside the map component.

const createDataLayer = (id, data, color) => {
    return {
        'id': id,
        'type': 'circle',
        'source': {
            'type': 'geojson',
            'data': data
        },
        'paint': {
            'circle-color': color
        }
    }
}

export {createDataLayer}

Final Results

Here you have it! Now you should be able to see your data points right inside of your React application.

image

Conclusion

I am always looking to improve the way that I teach and code. Feel free to share your thought and make suggestions by emailing me at christopher.clemmons2020@gmail.com. Want to start a project with me? Visit my startup’s webpage at digyt.co/contact to get in touch with me.

Cheers!