Monday, October 14, 2019

Simple example of a ReactJS and OpenLayers map component

OpenLayers (https://openlayers.org/) is a "high-performance, feature-packed (Javascript) library for all your mapping needs" for web sites. I spent some time figuring out how to use it with ReactJS. As most of the available examples in the official documentation are for plain vanilla HTML/Javascript, I stumbled a few times trying to get it to work. This post summarises the steps to get a minimal OpenLayers - React component to work. 


Installing OpenLayers for ReactJS
Assuming a ReactJS project has been created e.g. /path/to/React/project/, the first thing is to install the prerequisite software.
  1. Install OpenLayers package for node. Open a Terminal, change directory to the project root directory and type in the following command.

    $ npm install ol --save
  2. Optional. Install Proj4Js and Material-UI.

    $ npm install proj4 @material-ui/core --save
Create the React component for OpenLayers map
Under the ReactJS project e.g. /path/to/React/project/src/components/, create the OpenLayers React component e.g. OLMapFragment.js.

Code listing of OLMapFragment.js
import React from 'react'
import Grid from '@material-ui/core/Grid'

// Start Openlayers imports
import { 
    Map,
    View
 } from 'ol'
import {
    GeoJSON,
    XYZ
} from 'ol/format'
import {
    Tile as TileLayer,
    Vector as VectorLayer
} from 'ol/layer'
import {
    Vector as VectorSource,
    OSM as OSMSource,
    XYZ as XYZSource,
    TileWMS as TileWMSSource
} from 'ol/source'
import {
    Select as SelectInteraction,
    defaults as DefaultInteractions
} from 'ol/interaction'
import { 
    Attribution,
    ScaleLine,
    ZoomSlider,
    Zoom,
    Rotate,
    MousePosition,
    OverviewMap,
    defaults as DefaultControls
} from 'ol/control'
import {
    Style,
    Fill as FillStyle,
    RegularShape as RegularShapeStyle,
    Stroke as StrokeStyle
} from 'ol/style'

import { 
    Projection,
    get as getProjection
 } from 'ol/proj'

// End Openlayers imports

class OLMapFragment extends React.Component {
    constructor(props) {
        super(props)
        this.updateDimensions = this.updateDimensions.bind(this)
    }
    updateDimensions(){
        const h = window.innerWidth >= 992 ? window.innerHeight : 400
        this.setState({height: h})
    }
    componentWillMount(){
        window.addEventListener('resize', this.updateDimensions)
        this.updateDimensions()
    }
    componentDidMount(){

        // Create an Openlayer Map instance with two tile layers
        const map = new Map({
            //  Display the map in the div with the id of map
            target: 'map',
            layers: [
                new TileLayer({
                    source: new XYZSource({
                        url: 'https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png',
                        projection: 'EPSG:3857'
                    })
                }),
                new TileLayer({
                    source: new TileWMSSource({
                        url: 'https://ahocevar.com/geoserver/wms',
                        params: {
                            layers: 'topp:states',
                            'TILED': true,
                        },
                        projection: 'EPSG:4326'
                    }),
                    name: 'USA'
                }),
            ],
            // Add in the following map controls
            controls: DefaultControls().extend([
                new ZoomSlider(),
                new MousePosition(),
                new ScaleLine(),
                new OverviewMap()
            ]),
            // Render the tile layers in a map view with a Mercator projection
            view: new View({
                projection: 'EPSG:3857',
                center: [0, 0],
                zoom: 2
            })
        })
    }
    componentWillUnmount(){
        window.removeEventListener('resize', this.updateDimensions)
    }
    render(){
        const style = {
            width: '100%',
            height:this.state.height,
            backgroundColor: '#cccccc',
        }
        return (
            <Grid container>
                <Grid item xs={12}>
                    <div id='map' style={style} >
                    </div>
                </Grid>
            </Grid>
        )
    }
}
export default OLMapFragment

Use the React OpenLayers map component
Now in the ReactJS project's index.js file under /path/to/React/project/src/, import in the newly created OLMapFragment component and render it.

Listing of index.js
import React from 'react'
import { render} from 'react-dom'
import OLMapFragment from './js/components/OLMapFragment'

render(
    <OLMapFragment />
    ,
    document.getElementById('react-container')
)

Create the default index.html file
Finally, include links to OpenLayer's style sheets in the project's index.html file under /path/to/React/project/src/.
Listing of index.html file
<!DOCTYPE html>
<html class="no-js" lang="en">

<head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content='ie=edge'>
    <title>Openlayers React</title>
    <meta name="description" content="Explore planet Mars">
    <meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no" />
    <!-- material-ui prerequisites -->
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
    <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
    <!-- end material-ui prerequisites -->

    <link rel="stylesheet" href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css"
        type="text/css" />

</head>

<body>
    <!--[if lte IE 8]
        <p class="chromeframe">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com">upgrade your browser</a></p>
        <![endif]-->
    <div id='react-container'></div>

</body>

</html>

Now, run the ReactJS project with a web server and open the web page with an Internet browser.

The web map tiles are rendered in the React OpenLayers component.

1 comment: