How to create web maps with OpenLayers and React

How to create web maps with OpenLayers and React

In this article I will explain step-by-step how you can create a web map with OpenLayers and React without any third party packages. So let`s start with a new project.

First of all you will need a new react-app with OpenLayers which can be done with:

npx create-react-app openlayers-react
cd openlayers-react
yarn add ol

The project now should look somehow like this:

project-overview.png

In the /src you can now create a subfolder called /map where you will need three files.

React Context

Before you devote yourself to a map component you can create a file mapContext.js.

import React from "react"
const MapContext = new React.createContext()
export default MapContext

With mapContext.js you can create a React Context which provides a way to pass data through the component tree without having to pass props down manually at every level.

To do so you need to import the mapContext in any component where you would like to access the map which with:

import MapContext from "../Map/MapContext";
const { map } = useContext(MapContext)

That's actually super helpful because with the context you can for example add a drawing interaction or adjust the map view with data coming from any API without needing to pass any props to the component.

Map Component

Now you can create a map.js in the same folder and import the OpenLayers Map class aswell as the View class. You also will need the previously created MapContextwhich will wrap the map.

import React, { useRef, useState, useEffect } from "react"
import MapContext from "./mapContext"
import View from "ol/View"
import Map from "ol/Map"

const MapWrapper = ({ children, zoom, center }) => {
  const mapRef = useRef()
  const [map, setMap] = useState(null)

  // on component mount
  useEffect(() => {
    let options = {
      view: new View({ zoom, center }),
      layers: [],
      controls: [],
      overlays: [],
    }

    let mapObject = new Map(options)
    mapObject.setTarget(mapRef.current)
    setMap(mapObject)

    return () => mapObject.setTarget(undefined)
  }, [])

  // zoom change handler
  useEffect(() => {
    if (!map) return

    map.getView().setZoom(zoom)
  }, [zoom])

  // center change handler
  useEffect(() => {
    if (!map) return

    map.getView().setCenter(center)
  }, [center])

  return (
    <MapContext.Provider value={{ map }}>
      <div
        ref={mapRef}
        className="ol-map"
        style={{ height: "100vh", width: "100%" }}
      >
        {children}
      </div>
    </MapContext.Provider>
  )
}

export default MapWrapper

Inside the map component you will find three useEffect hooks. These are used to be able to (re-) render the map in case any map options like

  • layers,
  • controls,
  • overays,
  • zoom or
  • the center

have changed. However you still ned some data to visualize something on the map. Therefore you can now create a folder layers with a tileLayer.js file which will be used to display tiled images like OpenStreetMap.

Layers

You will need the TileLayer from OpenLayers and here we will use the map context for the first time, because you need to check if the map exists and if it does you can add layers to it.


import { useContext, useEffect } from "react";
import MapContext from "../Map/MapContext";
import OLTileLayer from "ol/layer/Tile";

const TileLayer = ({ source, zIndex = 0 }) => {
	const { map } = useContext(MapContext);

	useEffect(() => {
		if (!map) return;

		let tileLayer = new OLTileLayer({
			source,
			zIndex,
		});

		map.addLayer(tileLayer);
		tileLayer.setZIndex(zIndex);

		return () => {
			if (map) {
				map.removeLayer(tileLayer);
			}
		};
	}, [map]);

	return null;
};

export default TileLayer;

This TileLayer component can receive props for a layer source and zIndex to be able to add different sources like and to adjust the order of the layers. Actually you now already have everything to build a simple map. For that you will need to put the Layer and Map component in any page like for example App.js. As layer source you would add OSM in this example.

import React, { useState } from "react";
import Map from "./Map/Map";
import TileLayer from "./Layers/TileLayer";
import { fromLonLat } from "ol/proj";
import "./App.css";
import OSM from 'ol/source/OSM';

const App = () => {
  const [center, setCenter] = useState([0,0]);
  const [zoom, setZoom] = useState(3);

  return (
    <div>
      <Map center={fromLonLat(center)} zoom={zoom}>
          <TileLayer source={new OSM} zIndex={0} />
      </Map>
    </div>
  );
};

export default App;

After running npm run start should be able to see your web map created with OpenLayers and React at http://localhost:3000/ .

Due to the fact that you created the MapContext you can now add basically as many features as you like without headache. If you want to add a drawing feature you could do it like this:

import React, { useEffect, useState, useContext } from "react"
import Draw from 'ol/interaction/Draw';
import MapContext from "../Map/MapContext";
import { Vector as VectorLayer} from 'ol/layer';
import { Vector as VectorSource} from 'ol/source';

const Features = () => {
    const [interactionAdded, setInteractionAdded] = useState(false)
	const { map } = useContext(MapContext);

    const source = new VectorSource({wrapX: false});

    const vector = new VectorLayer({
        source: source,
    });

    let draw; // global so we can remove it later

    function addInteraction() {
        draw = new Draw({
        source: source,
        type: "Polygon"
        });
        map.addInteraction(draw);
        setInteractionAdded(true)
    }
  
	return (
          <button onClick={addInteraction}>Add draw</button>
    );
};

export default Features;

If you have any questions about this post feel free to contact me on my socials.

About The Author

Max Dietrich
Max Dietrich

Geospatial Developer

Currently i work as IT-Architect at Bayernwerk. Beside that I ride my mountain bike in the alps, code and design my website and publish new content on my website whenever i can.

0 Webmentions

Have you published a response to this? Send me a webmention by letting me know the URL.

Found no Webmentions yet. Be the first!

Newsletter

Continue Reading

  • How to create web maps with Leaflet, React and functional components

    In this article I will explain how you can create a basic web map with Leaflet and React by using functional components without any third party packages. Continue reading...
  • How i constantly track my location and display a web-map with all the locations

    Inspired by Aaron Parecki and who he has been tracking his location since 2008 with an iPhone app and a server side tracking API i decided to go for a similar approach. I wanted to track my position constantly with my Android smartphone and use the data to display a map with all locations i have ever been to. Continue reading...
  • Fetching and storing activities from Garmin Connect with Strapi and visualizing them with NextJS

    Step-by-step guide explaining how to fetch data from Garmin Connect, store it in Strapi and visualize it with NextJS and React-Leaflet. Continue reading...