包详细信息

rlayers

mmomtchev18.3kISC3.7.1

React Components for OpenLayers

react, maps, openlayers, gis

自述文件

rlayers - React Components for OpenLayers 6+

Update: since Oct 24 I am homeless and living in my van. I lost access to most of my computer hardware. The eviction from my home has been timed for maximum effect as I was not present when it happened. I have only a single set of clothes and no means to get anything else. Please, if you use my software, consider asking everyone in the geospatial community if they are taking part in this extortion and why.

logo

License: ISC npm version Node.js CI codecov downloads

© OpenStreetMap contributors Kartendaten: © OpenStreetMap-Mitwirkende, SRTM | Kartendarstellung: © OpenTopoMap (CC-BY-SA)

rlayers is an opinionated set of React components for OpenLayers.

It's design policy is:

  • Fully Typescript-typed
  • Do everything that faces the user the React way and not the OpenLayers way - onClick and onPointerEnter/onPoinerLeave handlers are typical examples
  • If it does not face the user, it does not need to be React way - internally it uses inheritance, following the OpenLayers classes, over composition
  • Simple things should be simple to do, performance optimizations should not get in the way unless needed
  • If taking shortcuts when updating the components, always err on the safe side but do provide an override method that allows to come close to the raw OpenLayers performance
  • Expose all the advanced OpenLayers features
  • Try to be as much SSR-friendly as possible (this feature is currently in POC stage, see below)
  • The current target is OpenLayers 6+
  • Avoid dependencies when built except for React and OpenLayers (the examples have some dependencies) - currently the single one is lru-cache at 8Kbytes

Long term support of this project

The birth of this project is related to a huge extortion in the geography community linked to a sexual harassment affair covered up by the French Judiciary. It is maintained as a free service to the geography community so that it can remain as a remainder to a number of companies - including Camptocamp, ESRI, Mapbox and Makina Corpus - that the most noble way to claim size bragging rights is to produce good software. You can safely use this framework in your projects, be assured that it will be maintained very well and for many years to come. It's companion project on the server-side is gdal-async.

Alternatives

<- Light-Weight --- Feature-Rich ->

pigeon-maps - react-leaflet - rlayers

Among the completely free and open source alternatives for creating maps with React, on a scale going from the most light-weight to the most feature-rich solution, rlayers is the right-most one.

It offers the full power OpenLayers - dynamic reprojections, comprehensive event handlers, a very rich set of supported formats, interfaces and layer types and a very good performance for very complex maps. This comes at the price of a quite significant total bundle size.

Installation

npm --save install rlayers ol react react-dom

Compatibility

React is a peer dependency and should be installed separately.

React is supported from version 16.8.0 with rlayers@2.x and from version 18 with rlayers@3.x.

Starting with version 3.0, rlayers always comes with a fixed OpenLayers version which is usually the latest one at the time of the release.


When using dynamic styles with React 18, you may get a warning in the console in debug mode: https://github.com/mmomtchev/rlayers/issues/40. You can safely ignore it as has no functional consequences - React 18, including the concurrent renderer, is fully supported.

Usage

rlayers is a set of reusable React components that can be nested in various ways to create map applications for the web through React composition in the true spirit of React. The components are based on a simplified model of the OpenLayers classes: for example the layers and the sources abstraction levels have been fused into one single level and the map and the view are also represented by a single component.

In order to avoid confusion between the OpenLayers classes and the rlayers classes which sometimes have the same names - all rlayers classes are prefixed with R. If a class begins with R, it is from rlayers, otherwise it is an OpenLayers class.

The most important element is the <RMap>. Every other element, except <RStyle>, requires a parent to function - an <RLayer> must be part of a map, an <RFeature> must be part of an <RLayerVector>, an <RControl> must also be part of a map.

Simple step-by-step example

This is the simple overlay example - https://mmomtchev.github.io/rlayers/#/overlays

import React from 'react';
import {fromLonLat} from 'ol/proj';
import {Point} from 'ol/geom';
import 'ol/ol.css';

import {RMap, ROSM, RLayerVector, RFeature, ROverlay, RStyle} from 'rlayers';
import locationIcon from './svg/location.svg';

// Create a map, its size is set in the CSS class example-map
<RMap className='example-map' initial={{center: fromLonLat([2.364, 48.82]), zoom: 11}}>
    {/* Use an OpenStreetMap background */}
    <ROSM />
    {/* Create a single layer for holding vector features */}
    <RLayerVector zIndex={10}>
        {/* Create a style for rendering the features */}
        <RStyle.RStyle>
            {/* Consisting of a single icon, that is slightly offset
             * so that its center falls over the center of the feature */}
            <RStyle.RIcon src={locationIcon} anchor={[0.5, 0.8]} />
        </RStyle.RStyle>
        {/* Create a single feature in the vector layer */}
        <RFeature
            {/* Its geometry is a point geometry over the monument */}
            geometry={new Point(fromLonLat([2.295, 48.8737]))}
            {/* Bind an onClick handler */}
            onClick={(e) =>
                {/* e.map is the underlying OpenLayers map - we call getView().fit()
                to pan/zoom the map over the monument with a small animation */}
                e.map.getView().fit(e.target.getGeometry().getExtent(), {
                    duration: 250,
                    maxZoom: 15
                })
            }
        >
            {/* The icon is an SVG image that represents the feature on the map
            while an overlay allows us to add a normal HTML element over the feature */}
            <ROverlay className='example-overlay'>
                Arc de Triomphe
                <br />
                <em>&#11017; click to zoom</em>
            </ROverlay>
        </RFeature>
    </RLayerVector>
</RMap>

Check examples/static_pages.html for a fully self-contained static HTML page using rlayers.

You can also check the GPLed XC-DB for a larger and more complex project entirely implemented using React, Redux and rlayers.

Contexts

Composition works by using React Contexts. Every nested element uses the context of its nearest parent.

The underlying OpenLayers objects can be accessed using the useOL() hook - check the Geolocation example to see how.

Currently useOL() has an RContextType and can contain the following elements:

  • map provided by a map, every other element, except an RStyle must have a map parent
  • layer and source provided by all layers - not required for anything at the moment, but can be used to access the underlying OpenLayers objects
  • vectorlayer and vectorsource provided by vector layers only - required for <RFeature>
  • vectortilelayer provided by vector tile layers only
  • location and feature provided by a map feature - required for <ROverlay> and <RPopup>
  • style provided by a style definition - the only one which can be outside of a map

Additionally, useRLayersComponent() allows retrieving the containing rlayers component.

Accessing the underlying OpenLayers objects and API

The underlying OpenLayers objects can be accessed in a number of different ways:

  • Through the context objects by using React.Context.Consumer
  • In an event handler that is a normal function and not an arrow lambda, this will contain the rlayers component and this.context will contain the context - this is an alternative to using useOL()
  • In all event handlers, OpenLayers will pass the target object in event.target and the map in event.map - the popups example uses this method
  • And finally, accessing arbitrary elements, even outside their contexts, is possible by using React references - React.RefObjects. The high performance example contains an example of this. The underlying OpenLayers objects can be accessed through the ol property of every component. Additionaly, for layer objects, the underlying OpenLayers source can be accessed through source:
    const layerRef = React.createRef() as React.RefObject<RLayerVector>;
    Then after rendering:
    <RLayerVector ref={layerRef} />
    layerRef.current.ol will contain the OpenLayers layer and layerRef.current.source will contain the source. This is the only way of accessing the object outside its context.

Styles

Style definitions can be placed anywhere inside the DOM and can be referenced with a React reference. rlayers includes two special types for dealing with styles:

  • RStyleRef which is an alias to React.RefObject<RStyle> is a React reference to an <RStyle> element. It can be transparently used everywhere where a classical OpenLayers StyleLike is required
  • RStyleLike is the new union type that allows for StyleLike or a RStyleRef

A style placed inside a vector layer will be automatically applied to that vector layer.

A style can either be static or dynamic. A static style depends only on its properties. A dynamic style is a function that takes an OpenLayers Feature object as its input and returns a Style. A dynamic style creates a new object for every rendered feature, so this must be taken into account. A simple caching mechanism is also provided, based on a user-supplied hash function. It can greatly improve performance when the majority of the features use relatively few different styles.

You can refer to

Classical OpenLayers StyleLike objects are supported too, but this is not the React way. Still, if you need every last bit of performance, writing an optimized OpenLayers style function is the best solution.

Performance

React is a wonderful framework that makes it very easy to write complex web applications without having to manually handle all the interdependencies between the various components. This is the reason why it is called React - components automatically React to changes in other components. In the true spirit of React, rlayers prefers to err on the safe side - always updating when there is a chance that the component needs updating - making it easy on the beginner who wants simple interface while still allowing the experienced engineer to achieve the performance he needs.

When high performance is required, particular care must be taken that the component properties do not change without a reason. This is especially true when the pointermove event is used. In these cases one should avoid using anonymous objects, arrays or functions as properties.

Take for example this:

<RFeature
    geometry={new Point(fromLonLat([2.295, 48.858])}
    onClick={(e: MapBrowserEvent) => process(e.target)}
/>

This is a feature that will be re-evaluated at every frame. Its geometry appears to be a constant, but it is in fact an anonymous object that is created at every frame - even if it always holds the same value. Passing a constant is one way around this, but the true React way is using the two tools React provides: React.useMemo and React.useCallback. They memoize the value and take care to always return a reference to the same object unless one of the listed dependencies is modified.

This is a much better performing code that won't rerender the feature component:

<RFeature
    geometry={React.useMemo(new Point(fromLonLat([2.295, 48.858]), [/* no deps */])}
    onClick={React.useCallback((e: MapBrowserEvent) => process(e.target), [/* no deps */])}
/>

Anonymous objects, arrays and especially lambdas in the properties of a component are prime candidates for memoization. Sometimes, you can also memoize whole components or groups of components - for a very significant performance boost.

Generally, if you are binding code to the pointermove event and your performance is not good enough, this is the first thing you should be looking at - which components update at every pointermove and why.

These 3 examples run code on various high-frequency events, take a look at them:

  • Clustering runs the styling function every time the map is panned or zoomed
  • Drop a pin runs code on every pointermove that carefully avoids rerendering
  • Geo data updates components at every pointerenter/pointerleave
  • The high performance example is a complex example that runs lots of code and updates components at every pointermove

Also, when searching for features listening on pointermove/pointerenter/pointerleave events, rlayers 2.0.0 and later, is able to eliminate very early feature layers that do not contain features listenening for those events. If your map contains a large number of features, and only a handful of these use pointermove events - try to group them in a separate layer.

Examples

The examples can be found here: https://mmomtchev.github.io/rlayers/

Server-Side Rendering

Server-side rendering of map components is difficult - there is still no comprehensive solution. Besides the obvious complexities of rendering on canvas outside the browser, one of the major issues is that server-side rendering runs before the browser layout flowing - and thus must work independent of layout and resolution.

The best solution is to use a WMS-compatible server (such as Geoserver) and to serve prerendered maps over WMS - eventually replacing the initial image by a canvas.

An intermediate solution, which does not require extensive server-side investment (such as Geoserver), but is limited to static layout(s), is to prerender one (or one per screen size) image to be used as a temporary place-holder until the map is loading. In this case, at least some devices, will get an ugly looking map for the first few seconds.

Pushing the initial tiles is also an option:

  • when combined with a WMS-server it could deliver pixel-perfect maps with on the first HTTP request
  • without a WMS-server it could still avoid doing a large number of HTTP requests on the first load

Currently, server-side rendering of raster layers on fixed map sizes has reached POC status and an online demo is accessible at https://rlayers-ssr.meteo.guru/. The code can be found in the ssr branch of this project. The next.js project can be found at https://github.com/mmomtchev/rlayers-ssr-demo.git. This is still not a user-friendly, install-and-run project. Take a look at pages/index.js if you want see how it is meant to be used.

As of March 2022, SSR support is stale and I am not working on it anymore. Should there be any interest in this feature, I could consider adding it.

Next.js

As currently rlayers is an entirely client-side component without SSR support, using it with Next.js requires some special configuration that has tendency to change from version to version.

rlayers has been found to work with all recent Next.js versions, including Next.js 14 in app mode.

Check rlayers-npm-tests for working examples.

Google Maps API Support

The Google Maps API is not open and although it is now supported out-of-the-box by OpenLayers starting from version 9.0, it requires a paid subscription (with an eventual trial period). Adding Google Maps support is not possible unless someone is willing to sponsor it.

API

You can browse the full documentation at https://mmomtchev.github.io/rlayers/api.

License

ISC

更新日志

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[3.7.1] 2025-06-26

  • OpenLayers 10.6.1

[3.7.0] 2025-06-17

  • OpenLayers 10.6.0

[3.6.1] 2025-04-22

  • Relax the requirements of RLayerHeatmap because in OpenLayers 10.5.0 WebGLVectorPointsRenderer does not overload getRenderer() with a more specific type

[3.6.0] 2025-04-02

  • OpenLayers 10.5.0 support
  • Add React 19.1 to the CI
  • Fix #399, attributions are not displayed for layers other than OSM

[3.5.0] 2025-02-05

  • OpenLayers 10.4.0 support

[3.4.0] 2025-02-02

  • React 19 support

[3.3.1] 2025-01-10

  • Fix RLayerVectorImage in OpenLayers 10.3.1

[3.3.0] 2025-01-09

  • OpenLayers 10.3.1 support
  • Fix #280, <RContol.RCustom> cannot be used as anchor for inserting new elements

[3.2.0] 2024-09-24

  • Fixed package-lock.json
  • OpenLayers 10.2.0 support
  • Fix #276, extending rlayers example needs ol-mapbox-style

[3.1.0] 2024-07-27

  • OpenLayers 10 support
  • Fix #270, dynamic <RStyle> elements with a render property do not always update properly

[3.0.1] 2024-05-23

  • Drop React 16/17, React 18 is now required
  • For OpenLayers 9.2.3

[3.0.0] 2024-05-22

  • Do not support multiple OpenLayers versions, link each rlayers to one OpenLayers minor version
  • Support light-weight RenderFeatures, these are opt-out for vector tile layers and opt-in for vector layers
  • Use the same level of typing as OpenLayers
  • For OpenLayers 9.2.2

[2.3.2] 2024-05-22

  • Fix #252, stop event propagation when the event handler returns false
  • Add React 18.3 as a test target

[2.3.1] 2024-03-24

  • Support OpenLayers 9.1.0
  • Fix #220, use correct event names for RModify in TypeScript

[2.3.0] 2024-02-27

  • Support OpenLayers 9.0.0

[2.2.0] 2023-11-20

  • Support OpenLayers 8.2.0
  • Support using TypeScript generics to specify the feature geometry type for vector layers

[2.1.0] 2023-09-13

  • Support OpenLayers 8.0.0 and 8.1.0
  • Replace RLayerStamen with RLayerStadia which requires OpenLayers 8.0.0

[2.0.0] 2023-08-16

  • Vastly improved event handling performance avoiding expensive forEachFeatureAtPixel on layers that do not have event handlers
  • Add the useOL() and useRLayersComponent() component hooks allowing to easily access the containing OpenLayers and rlayers components
  • Layer event handlers are now independent of feature event handlers, if both the feature and its containing layer have declared an event handler, both will be called
  • Fix onClick handlers on RLayerVectorTiles layers
  • Use TypeScript protected and private to restrict methods that are not expected to be directly used from user code
  • Support all positioning options in ROverlay
  • Support OpenLayers 7.5.1

[1.5.3] 2023-07-30

  • Fix #169, support TypeScript 5.x
  • Add onFeaturesLoadStart and onFeaturesLoadError events to all vector layers
  • Fix #163, DOMException when removing an ROverlay

[1.5.2] 2023-06-11

  • Support OpenLayers 7.4.0

[1.5.1] 2023-05-30

  • Declare RLayerRasterMBTiles and RLayerVectorMBTiles as public exports

[1.5.0] 2023-05-30

  • Add remote .mbtiles support with ol-mbtiles
  • Add new rlayers-specific events to RLayerWMTS and RLayerRasterMBTiles to distinguish them from the OpenLayers onSourceReady events
  • Fix #149, RLayerWMTS visible property does not trigger a refresh
  • Add onPointerLeave handlers for vector tiles

[1.4.9] 2023-03-28

  • Support OpenLayers 7.3.0
  • Allow using projections other than Web Mercator with RVectorTile (the vector tile projection must still match the view projection)
  • Fix [#133], certain layer properties do not update if they transition from a defined value to undefined
  • Fix #144, update WMTS layers only when the metadata is changed
  • Add a RLayerGeoTIFF custom component, resolves #145

[1.4.8] 2023-01-08

  • Support additional <RMap> properties, including disabling of the rotation
  • Fix [#109], interactions do not support JSX styles
  • Add event callbacks to RDraw and RModify
  • Allow setting of wrapX for vector layers
  • Fix [#121], reprojected tile layers do not automatically refresh when setting the URL

[1.4.7] 2022-12-23

  • Support OpenLayers 7.2.2

[1.4.6] 2022-12-23

  • Support OpenLayers 7.2.0
  • Add RLayerImage for displaying a static image as a map
  • Add RBackground for setting background properties of RText
  • Support all Text parameters in OpenLayers
  • Add RGraticule for displaying a graticule

[1.4.5] 2022-10-26

  • Fix [#85], dynamic properties of RText are not updated
  • Fix [#89], support inline styles in RLayerVectorTile

[1.4.4] 2022-09-26

  • Support OpenLayers 7.1.0
  • Fix [#80], error when compiling the layers control with TypeScript 4.8
  • Fix [#82], direct enumeration of features does not work with clustering

[1.4.3] 2022-08-19

  • Support OpenLayers 7.0.0
  • Support React 18.2
  • Correctly use &nbsp as a default placeholder value for RMousePosition

[1.4.2] 2022-07-29

  • Support OpenLayers 6.15 and 6.15.1

[1.4.1] 2022-05-26

  • Allow direct import of rlayers/control/layers.css

[1.4.0] 2022-05-26

  • Provide an alternative ES6 modules version of rlayers with a package.json exports map
  • Provide a CDN bundle version for direct inclusion in static pages
  • Render the examples in React 18 mode when available
  • New documentation subsystem based on documentation.js with documentation-hipster
  • Expose the interpolation option for RLayerTile and RLayerTileWebGL [#59]
  • Add RMousePosition control for ol.control.MousePosition [#60]
  • Add RZoomToExtent control for ol.control.ZoomToExtent [#61]
  • Always use ProjectionLike type for projections [#62]
  • Support setting and reading the resolution in RView

[1.3.4] 2022-04-26

  • TypeScript compatibility with @types/react@18.0.0

[1.3.3] 2022-04-26

  • Support React 18
  • Support OpenLayers 6.14.1

[1.3.2] 2022-03-26

  • Support OpenLayers 6.14
  • Fix [#37], build directly in package root

[1.3.1] 2022-02-27

  • Support OpenLayers 6.13
  • Add RLayerVectorImage supporting OpenLayers VectorImage layers

[1.3.0] 2022-01-28

  • Support OpenLayers 6.12
  • Initial WebGL support
  • Fix [#27], do not lose the event handlers when updating properties that require replacing the underlying OpenLayers object, impacts the interactions and RFeature with feature= property

[1.2.1] 2022-01-04

  • Restrict OpenLayers to 6.6 to 6.9.99 for the rlayers 1.2 branch, OpenLayers 6.10 requires rlayers 1.3
  • Fix [#20], renderBuffer parameter ignored

[1.2.0] 2021-12-16

  • Have strict types for this and event.target whenever possible in the event handlers
  • Support replacing the bound OpenLayers feature object when updating an RFeature
  • Fix wrong this object in RLayerWMTS.onSourceReady
  • Implement collapsing via React property setting for RAttribution and ROverview, waiting on https://github.com/openlayers/openlayers/issues/13050 for a two-way external control

[1.1.1] 2021-11-01

  • Fix published package

[1.1.0] 2021-10-29

  • Drop support for OpenLayers <6.6
  • Switch to the built-in TypeScript bindings in OpenLayers >=6.6
  • Add basic WMS/TileWMS support
  • Support zIndex in RStyle
  • Fix [#13]: Dynamic rendering in RStyleArray
  • Fix [#14]: Set the projection in RLayerTileWMS
  • Fix [#15]: Properly unmount vector layers

[1.0.4] 2021-07-10

  • Fix [#3]: Path to the example css for the layers control
  • Fix [#4]: More tsc strict: true mode compatibility fixes for React 17

[1.0.3] 2021-06-24

  • Fix [#2]: Support importing from tsc with strict: true

[1.0.2] 2021-06-02

  • Fix [#1]: Crash when removing <RFeature> from <RLayerVector>, reported by @jonas-peeters

[1.0.1] 2021-04-09

  • Solve a version conflict in the NPM registry

[1.0.0] 2021-04-09

  • First release