Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for Vector Tile Basemaps #26

Open
wants to merge 104 commits into
base: master
Choose a base branch
from
Open

Conversation

QSparks
Copy link

@QSparks QSparks commented Sep 3, 2024

Pull Request: Add Support for Vector Tile Basemaps

Summary:

This PR introduces support for vector tile base maps in our map applications to support dynamic, client-side styling. There may be some improvements to feature styling and labels based on feedback from CSG and RCI, but there are no significant changes planned for the components in this PR.

Key Changes:

  1. GenericVectorBaseMap.js:

    • Created a new component, GenericVectorBaseMap, to handle the rendering of a vector basemap and a wms label layer.
    • Integrates client-styled vector tiles with pre-styled Place and POI labels via WMS.
    • Accepts custom projections.
    • The vector tile layer is rendered using the sub-component VectorGridLayer that is a wrapper for Leaflet.VectorGrid.
    • The label layer is added using the WMSTileLayer wrapper from react-leaflet
  2. BCVectorBaseMap.js:

    • A BC-specific vector tile base map component, for rendering maps in the British Columbia region using the EPSG:3005 projection.
    • Extends GenericVectorBaseMap with region-specific configurations, such as a custom BC Albers projection definition and extent-tuned feature rendering based on zoom level.
  3. vectorTileStyling.js:

    • Defines functions to dynamically style vector tiles based on map features (e.g., boundaries, landcover, transportation), feature classes (e.g., mountain rank, road type) and zoom levels.
    • Uses a legendSelector module to apply consistent, predefined styles.
  4. legendColored.js, legendAchromatic and legendSelector.js:

    • Introduced coloured and greyscale legend definitions (legendColored, legendAchromatic).
    • Implemented a dynamic legend selection legendSelector mechanism based on an environment variable (REACT_APP_LEGEND_TYPE), allowing for easy switching between different styles.

Associated Environment variables:

  1. REACT_APP_BC_VECTOR_BASE_MAP_TILES_URL

    • Used to make wmts requests to a GeoWebCache instance running behind GeoServer.
    • Returns tiles in the mapbox-vector-tile format.
  2. REACT_APP_LABELS_WMS_URL

    • Used for WMS request to get styled label tiles from GeoServer.

    Example:

    REACT_APP_BC_VECTOR_BASE_MAP_TILES_URL=https://beehive.pacificclimate.org/geoserver/gwc/service/wmts?REQUEST=GetTile&TILES=True&SERVICE=WMTS&VERSION=1.0.0&LAYER=OMT-NA-ZF-LG&TILEMATRIXSET=EPSG:3005&TILEMATRIX=EPSG:3005:{z}&TILEROW={y}&TILECOL={x}&FORMAT=application/vnd.mapbox-vector-tile
    REACT_APP_LABELS_WMS_URL=https://beehive.pacificclimate.org/geoserver/gwc/service/wms
    REACT_APP_LEGEND_TYPE=achromatic
    

Purpose:

These changes add vector tile base map support, enabling:

  • Single tile source for all of our map projections and styles.
  • Greater flexibility in customizing map styles and features, offering app-specific control over base map elements.

Testing Performed:

  • Ensured vector tile base maps are correctly rendered using demo apps of initial colour and greyscale map styles and our custom EPSG 3005 projection.
  • Some compatibility checking across different browsers and devices.

Demos:

Greyscale with 0.6 opacity raster overlay:
https://beehive.pacificclimate.org/plan2adapt/app
Colour with point data:
https://beehive.pacificclimate.org/weather-anomaly-viewer

Updates Following Feedback:

  • Refactor BCVectorBaseMap as a functional component.
  • Use WMSTileLayer for labels instead of our own custom component.
  • Improve file and environment variable naming.
  • Break apart GenericVectorBaseMap by moving VectorGridLayer into a separate module.
  • DRY styling, using default styling parameters, and destructuring assignment

QSparks added 30 commits June 7, 2024 12:33
@QSparks QSparks requested a review from rod-glover September 3, 2024 19:44
@QSparks QSparks self-assigned this Sep 3, 2024
Copy link
Contributor

@rod-glover rod-glover left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really excellent work, and with such a good result. I've suggested fairly minor changes.

import vectorTileStyling from '../../styles/3005.js';
import L from 'leaflet';

export default class BCVectorBaseMap extends PureComponent {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Class-based components are deprecated in React. I encourage you not to create new ones using them. Writing (and rewriting) components as functional components is straightforward and brings a lot of benefits.

import 'proj4leaflet';
import { projCRSOptions } from '../../utils/crs';

const LabelsLayer = ({ wmsUrl, wmsOptions }) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to create our own component for this when Leaflet contains TileLayer.WMS?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My intention when creating a separate component for the label layer was to render the labels independently of the feature layer. This way, if one layer is rendered faster than the other it would give users a higher perceived performance by presenting some information in a tile space. This got me thinking of the layer independence and I've added a zIndex option so that labels will render on top of the climate rasters, making them even more readable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get the reason for having a separate label layer and feature layer. That's excellent.

But the label layer is just a WMS layer, and that is already built in to Leaflet. I think you can just use that, passing in the appropriate parameters to make it fetch the label tiles. What am I missing here?

(Possibly you are not distinguishing between a component and what it renders, which is parallel to the distinction between a class and an instance. I'm saying we don't need a new component, but we do need of course a separate instance of it for the label layer.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice clean breakdown into components here.

This module file is pretty long ... which is generally to be avoided when possible. Consider splitting out the components that are internal here into separate modules and importing them where needed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I've split GenericVectorBaseMaps apart so that the labels and vectorgrid layer are separate modules.

src/styles/3005.js Outdated Show resolved Hide resolved
src/styles/3005.js Outdated Show resolved Hide resolved
src/styles/3005_legend.js Outdated Show resolved Hide resolved
src/styles/legends.js Outdated Show resolved Hide resolved
@QSparks QSparks force-pushed the add-BCVectorBaseMap branch from a44c25e to 255a6be Compare September 4, 2024 21:11
@QSparks QSparks force-pushed the add-BCVectorBaseMap branch from 255a6be to 0dc6986 Compare September 4, 2024 21:17
@QSparks QSparks requested a review from rod-glover September 5, 2024 19:04
Copy link
Contributor

@rod-glover rod-glover left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, Quintin! And the code is nice and tight now, good separation of abstraction from usage.

<WMSTileLayer
url={wmsUrl}
{...wmsLayerOptions}
/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

mapRef,
vectorTileOptions,
wmsUrl,
wmsLayerOptions,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if the wms... props should be given names that indicate their use, something more specific like wmsLabelLayerUrl, etc.? Or do you think that the GenericVectorBaseMap is/could be a more generalized tool, where the WMS layer would be used for other things?

@rod-glover
Copy link
Contributor

rod-glover commented Sep 9, 2024

FYI, @Nospamas has #25 in flight that moves us from using env vars to using props to specify things like tile source URLs. He may be in touch with you to discuss a bit of what you've done here. This does not require you to change anything in this PR; that belongs elsewhere.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants