rlayers

repository·main·Indexed 42 days ago

https://github.com/mmomtchev/rlayers

An opinionated set of React components for OpenLayers 6+, providing a React-idiomatic API for complex maps. Version 3.9.0 supports React 18+ and exposes advanced OpenLayers features like dynamic reprojections and various layer types through a composition-based hierarchy (e.g., RMap, RLayerVector, RFeature).

Tokens
13.9K
Snippets
26
Records
56
Agent score
52%

What's inside rlayers

  1. Overview of rlayers design principles

    main

    rlayers is a feature-rich library for creating complex maps in React. Its design follows these principles:

    • React-centric API: User-facing interactions use standard React patterns (e.g., onClick, onPointerEnter, onPointerLeave).
    • OpenLayers-centric Internals: Internally, the library uses inheritance following OpenLayers classes rather than composition to maintain performance and feature parity.
    • Full Feature Exposure: It exposes advanced OpenLayers features, including dynamic reprojections and a wide variety of layer types and formats.
    • Performance: It balances simplicity with performance, providing override methods for users who need to approach raw OpenLayers performance levels.
    • Minimal Dependencies: Aside from react and ol, the only dependency is lru-cache (~8KB).
  2. How rlayers components and composition work

    main

    rlayers uses React composition to build map applications. It simplifies the OpenLayers class model by fusing certain abstractions (e.g., layers and sources are a single level; map and view are a single component).

    Key Rules of Composition:

    • Prefixing: All rlayers classes are prefixed with R (e.g., RMap, RLayerVector) to distinguish them from standard OpenLayers classes.
    • Hierarchy: Most components require a parent to function:
      • <RMap> is the root element.
      • <RLayer> must be a child of <RMap>.
      • <RFeature> must be a child of an <RLayerVector>.
      • <RControl> must be a child of <RMap>.
      • <RStyle> is the only component that can exist outside of a map context.
    • Contexts: Composition is powered by React Contexts. Every nested element uses the context of its nearest parent.
  3. Server-Side Rendering (SSR) considerations for rlayers

    main

    Rendering map components on the server is complex because OpenLayers relies on browser-specific APIs (like Canvas and layout engines) that are unavailable in a Node.js environment.

    Currently, there is no comprehensive SSR solution for rlayers. Recommended strategies for handling initial map loads include:

    1. WMS-compatible server (e.g., GeoServer): Serve prerendered maps over WMS to provide an initial image that is later replaced by a client-side canvas.
    2. Static Placeholders: Prerender one or more static images (one per screen size) to serve as temporary placeholders until the client-side map loads.
    3. Tile Pushing: Push initial tiles to avoid a high volume of HTTP requests during the first load.

    Note: Experimental SSR support for raster layers on fixed map sizes exists in the ssr branch of this repository, but as of March 2022, this support is considered stale and is not actively maintained.

  4. Configure and use styles with RStyle

    main

    Styles in rlayers can be static or dynamic and can be placed anywhere in the DOM if referenced via a React reference.

    • Automatic Application: A style placed inside a vector layer is automatically applied to that layer.
    • Static Styles: Depend only on their own properties.
    • Dynamic Styles: A function that takes an OpenLayers Feature object as input and returns a Style. Note that dynamic styles create a new object for every rendered feature.
    • Caching: A caching mechanism is provided based on a user-supplied hash function to improve performance when many features share similar styles.
    • Types:
      • RStyleRef: An alias to React.RefObject<RStyle>. Can be used anywhere a classical OpenLayers StyleLike is required.
      • RStyleLike: A union type allowing for either StyleLike or RStyleRef.
  5. Optimize rlayers performance

    main

    To achieve high performance, especially when using high-frequency events like pointermove, avoid passing anonymous objects, arrays, or functions (lambdas) as component properties. These cause unnecessary re-renders because their references change on every render cycle.

    Use React.useMemo and React.useCallback to stabilize references:

    Bad (re-evaluates every frame):

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

    Good (memoized):

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

    Performance Tips:

    • Memoize whole components or groups of components for significant boosts.
    • If performance is poor during pointermove, identify which components are updating at every frame.
    • Group features that listen to pointermove/pointerenter/pointerleave into a separate layer. rlayers 2.0.0+ can eliminate early feature layers that do not contain listeners for these events.
  6. Install rlayers

    main

    To use rlayers, you must install it along with its peer dependencies: ol (OpenLayers), react, and react-dom.

    Note that rlayers is an opinionated set of React components designed to provide the full power of OpenLayers while following React patterns (e.g., using onClick instead of OpenLayers-specific event listeners).

    npm --save install rlayers ol react react-dom
  7. Use rlayers with Next.js

    main

    Because rlayers is an entirely client-side component without built-in Server-Side Rendering (SSR) support, you must use special configuration when integrating it with Next.js to prevent errors during the server-side render phase.

    rlayers is compatible with recent Next.js versions, including Next.js 14 using the app router. For implementation details, refer to the working examples in the rlayers-npm-tests repository.

  8. Use RLayerBaseVector for vector layers

    main

    The RLayerBaseVector class is an abstract base component designed to be extended for specific vector layer types (e.g., RLayerVector, RLayerVectorTile). It provides common logic for managing an OpenLayers Vector source and handling feature-related events.

    When using components derived from RLayerBaseVector, the component provides a RContext.Provider that makes the OpenLayers layer instance (ol), the source (source), and the component instance (rLayer) available to child components via React context. This allows nested <RFeature> components to access the layer and source automatically.

    // Example conceptual usage of a component extending RLayerBaseVector
    <RLayerVector 
      url="/api/features.json" 
      format={new GeoJSON()}
      onClick={(e) => console.log('Feature clicked:', e.feature)}
    >
      {/* Nested RFeature components can now access the layer context */}
      <RFeature />
    </RLayerVector>
  9. How RLayer provides context to children

    main
    The RLayer component acts as a provider for its children. It uses RContext to expose the underlying OpenLayers Layer instance, the Source, and the RLayer component instance itself. This allows child components (such as source-specific components or specialized layer components) to access the map context and the layer's internal state without manual prop drilling.
  10. Create a simple map with an overlay

    main

    This example demonstrates how to set up a basic map using OpenStreetMap, a vector layer, a styled feature, and an HTML overlay.

    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>
  11. Access underlying OpenLayers objects

    main

    You can access the raw OpenLayers objects through several methods:

    1. useOL() hook: Retrieves the context object based on the current component's position in the tree. Available RContextType elements include:
      • map: Provided by a map (required for most components).
      • layer and source: Provided by all layers.
      • vectorlayer and vectorsource: Provided by vector layers (required for <RFeature>).
      • vectortilelayer: Provided by vector tile layers.
      • location and feature: Provided by a map feature (required for <ROverlay> and <RPopup>).
      • style: Provided by a style definition.
    2. Event Handlers: In all event handlers, OpenLayers passes the target object in event.target and the map in event.map.
    3. React Refs: Access objects outside of their context using React.RefObject. Every component has an .ol property containing the OpenLayers object. For layer objects, the underlying source is available via .source.

    Example using Refs:

    const layerRef = React.createRef() as React.RefObject<RLayerVector>;
    // ... in render
    <RLayerVector ref={layerRef} />
    // ... later
    const olLayer = layerRef.current.ol;
    const olSource = layerRef.current.source;