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
Show file tree
Hide file tree
Changes from 95 commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
1c2f3dd
WIP: Test support for vector tiles
QSparks Jun 7, 2024
fe4a662
Update dist
QSparks Jun 7, 2024
479eb74
WIP: Add styling to vector tiles
QSparks Jul 16, 2024
a171ed3
WIP: Styling and labels
QSparks Jul 19, 2024
799cbbd
Add WMS label layer
QSparks Jul 24, 2024
2d6b94b
Update dist
QSparks Jul 24, 2024
7d13031
Add additional label layers
QSparks Jul 25, 2024
cbf547c
Use true CRS extent for EPSG 3005 tile matrix bounds
QSparks Jul 26, 2024
70bba72
Revert "Use true CRS extent for EPSG 3005 tile matrix bounds"
QSparks Jul 26, 2024
0533638
Use canvas rendering, remove DOM-dependent click logging
QSparks Jul 26, 2024
e17e93d
Move label layergroup to omt-mbtiles workspace
QSparks Jul 30, 2024
72de698
Move labelsLayer wms opts to BCVectorBaseMap, styling
QSparks Aug 7, 2024
b752143
Include wmsLayerOptions parameter in GenericVectorBaseMap
QSparks Aug 7, 2024
9f25136
Import Leaflet in BCVectorBaseMap
QSparks Aug 7, 2024
5c26589
Adjust zoom filters for the 3005 basemap
QSparks Aug 7, 2024
f072d08
Add aeroway styling
QSparks Aug 7, 2024
f262da8
Update dist
QSparks Aug 7, 2024
127dcfa
Adjust transportation, aeroway and waterway styling
QSparks Aug 7, 2024
e860436
Revert "Adjust transportation, aeroway and waterway styling"
QSparks Aug 7, 2024
dd25faa
Add aeroway weight and lower waterway zoom filter
QSparks Aug 7, 2024
224b08b
Pass zoom and center props to VectorGridLayer, extend landcover styling
QSparks Aug 8, 2024
7dded99
Test label layer caching
QSparks Aug 8, 2024
bdec5b2
Add zoom-dependent transportation styling
QSparks Aug 8, 2024
b8c0216
Revert "Add zoom-dependent transportation styling"
QSparks Aug 8, 2024
b662af6
Update styling
QSparks Aug 9, 2024
4876b80
Update parks style, remove tiled option from WMS labels
QSparks Aug 9, 2024
f54bc17
Revert parks style
QSparks Aug 9, 2024
392aca4
Use label partials for tiled wms layer
QSparks Aug 9, 2024
f1f9273
Remove label partials option
QSparks Aug 9, 2024
990ba96
Fix boundary styling logic
QSparks Aug 9, 2024
e08b28a
Return array of styles
QSparks Aug 12, 2024
3ea1fea
Pass buffer parameter to label WMS
QSparks Aug 12, 2024
aa1ab56
Add simplification tolerance to vectorTileOptions
QSparks Aug 13, 2024
f191104
Test forced polygon rendering
QSparks Aug 13, 2024
4e7ea37
Add styling for wood
QSparks Aug 13, 2024
bfef079
Styling Revisions: boundary, landuse, landcover
QSparks Aug 14, 2024
d020aba
Styling Revisions: water, landcover
QSparks Aug 14, 2024
ddcbefa
Styling Revisions: water z level, colors
QSparks Aug 14, 2024
98f6efe
Styling Revisions: water
QSparks Aug 14, 2024
1e2b49f
Styling: revise drawing order
QSparks Aug 15, 2024
9d65ab2
Styling: waterway, boundary
QSparks Aug 15, 2024
4421dd2
Debug: water and boundary layer only
QSparks Aug 15, 2024
0ef9ca5
Debug: water layer only
QSparks Aug 15, 2024
2aa47a5
Debug: water style typo
QSparks Aug 15, 2024
f3ba95a
Re-add boundary & labels, fix typo in aeroway style
QSparks Aug 15, 2024
bca6ab4
Re-add landuse & parks
QSparks Aug 15, 2024
d8fbfcf
Style: boundary and landuse, log park properties
QSparks Aug 15, 2024
7ae34d6
Remove park logging and filter point features
QSparks Aug 15, 2024
d913ea6
Enable landcover and waterways, render parks at Z level 6
QSparks Aug 16, 2024
0ce6ce5
Add more landcover, aeroways and transport
QSparks Aug 16, 2024
17bf14d
Styling and draw order
QSparks Aug 16, 2024
7a22b7d
Add landcover sub-classes
QSparks Aug 19, 2024
d041cb8
Revert park opacity to 1
QSparks Aug 19, 2024
15bb605
Adjust landuse order and transportation zoom range
QSparks Aug 20, 2024
b005f06
Test setting background of MapContainer and reduce transportation weight
QSparks Aug 20, 2024
0379024
Extend landuse classes and adjust colours
QSparks Aug 20, 2024
f859893
Fix typo
QSparks Aug 20, 2024
de53fb9
Remove zoom constraint on farmland, grass and wetland
QSparks Aug 20, 2024
03af1b2
Test SVG labels
QSparks Aug 21, 2024
fd78f74
Test SVG labels: get bounds from map
QSparks Aug 21, 2024
153a838
Test SVG labels: add crs code string to wmsLayerOptions
QSparks Aug 21, 2024
011fb44
Test SVG labels: reproject bounds for WMS
QSparks Aug 21, 2024
f3db579
Revert "Test SVG labels: reproject bounds for WMS"
QSparks Aug 21, 2024
5367ad7
Revert "Test SVG labels: add crs code string to wmsLayerOptions"
QSparks Aug 21, 2024
7bbd136
Revert "Test SVG labels: get bounds from map"
QSparks Aug 21, 2024
c8accb1
Revert "Test SVG labels"
QSparks Aug 21, 2024
77d99e4
Refactor 3005.js style
QSparks Aug 21, 2024
70890e4
Lighten basemap colours
QSparks Aug 22, 2024
511a005
Use nonTiledLayer for labels
QSparks Aug 22, 2024
cdeb810
Use CRS string for wmsLayerOptions
QSparks Aug 23, 2024
8eaa40b
Use project CRS in wmsOptions
QSparks Aug 23, 2024
b384966
Add transformation to custom CRS def
QSparks Aug 23, 2024
4a301ff
Define custom getImageURL function
QSparks Aug 23, 2024
d46b0b9
Define custom getImageURLAsync function
QSparks Aug 23, 2024
6e7f9c6
Define custom WMS layer
QSparks Aug 23, 2024
0a5f1bd
Use proj4 image overlay
QSparks Aug 23, 2024
57eb18c
Zoom filter optimization: landcover and waterways
QSparks Aug 23, 2024
100d703
Revert "Zoom filter optimization: landcover and waterways"
QSparks Aug 26, 2024
7cb2a80
Revert "Use proj4 image overlay"
QSparks Aug 26, 2024
c7c0574
Revert "Define custom WMS layer"
QSparks Aug 26, 2024
b2670f9
Revert "Define custom getImageURLAsync function"
QSparks Aug 26, 2024
d1387ab
Revert "Define custom getImageURL function"
QSparks Aug 26, 2024
7ce71c1
Revert "Add transformation to custom CRS def"
QSparks Aug 26, 2024
ec1e45f
Revert "Use project CRS in wmsOptions"
QSparks Aug 26, 2024
6bbec5f
Revert "Use CRS string for wmsLayerOptions"
QSparks Aug 26, 2024
35183b0
Revert "Use nonTiledLayer for labels"
QSparks Aug 26, 2024
d5b961d
Reduce label buffer, use text antialiasing and increase dpi
QSparks Aug 26, 2024
60ca0c6
Use 8-bit png palette, add achromatic legend
QSparks Aug 27, 2024
745cc4d
Get legend from env variable
QSparks Aug 28, 2024
96e2707
Increase achromatic contrast, use text antialiasing
QSparks Aug 30, 2024
d2b5770
Revert "Increase achromatic contrast, use text antialiasing"
QSparks Aug 30, 2024
1ea50c2
Use Darker palette for achromatic style
QSparks Aug 30, 2024
a55277b
Darken transport layers in achromatic style
QSparks Sep 3, 2024
ee2226c
Darken water layers in achromatic style
QSparks Sep 3, 2024
6cc5373
Use explicit case for achromatic legend in legends.js
QSparks Sep 3, 2024
0dc6986
Rename files and legend env var, DRY up styling
QSparks Sep 4, 2024
f491356
Refactor GenericVectorBaseMap into separate modules
QSparks Sep 5, 2024
15c4149
Add zIndex to labels layer
QSparks Sep 5, 2024
25c1727
Refactor BCVectorBaseMap as functional component
QSparks Sep 5, 2024
abcfb38
Replace labelsLayer component with inline WMSTileLayer
QSparks Sep 5, 2024
ddd866b
Generalize VectorGridLayer, move instance options to BCVectorBaseMap
QSparks Sep 5, 2024
77ecaec
Modularize map config in BCVectorBaseMap
QSparks Sep 6, 2024
80d895a
Darken inland water for achromatic style
QSparks Sep 13, 2024
8647c0a
Vector Styling: Improve road details
QSparks Sep 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
946 changes: 736 additions & 210 deletions dist/index.cjs.js

Large diffs are not rendered by default.

966 changes: 745 additions & 221 deletions dist/index.esm.js

Large diffs are not rendered by default.

18,515 changes: 718 additions & 17,797 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^14.2.1",
"leaflet.vectorgrid": "^1.3.0",
"lodash": "^4.17.21",
"proj4": "^2.8.0",
"proj4leaflet": "^1.0.2",
Expand Down
74 changes: 74 additions & 0 deletions src/components/BCVectorBaseMap/BCVectorBaseMap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import GenericVectorBaseMap from '../GenericVectorBaseMap';
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.

static propTypes = {
center: PropTypes.shape({
lat: PropTypes.number.isRequired,
lng: PropTypes.number.isRequired,
}).isRequired,
zoom: PropTypes.number.isRequired,
minZoom: PropTypes.number,
maxZoom: PropTypes.number,
mapRef: PropTypes.func,
};

static defaultProps = {
mapRef: () => null,
};

static tileset = {
url: process.env.REACT_APP_BC_VECTOR_BASE_MAP_TILES_URL,
projection: {
code: 'EPSG:3005',
proj4def: '+proj=aea +lat_1=50 +lat_2=58.5 +lat_0=45 +lon_0=-126 +x_0=1000000 +y_0=0 +ellps=GRS80 +datum=NAD83 +units=m +no_defs',
},
tileMatrix: {
metersPerUnit: 1,
tileMatrixMinX: -20037508,
tileMatrixMaxX: 20037508,
tileWidth: 256,
tileMatrixMinY: -20037508,
tileMatrixMaxY: 20037508,
numResolutions: 14,
},
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
};

static initialViewport = {
center: {
lat: 55.0,
lng: -125,
},
zoom: 6,
};

render() {
const { children, ...rest } = this.props;
const wmsLayerOptions = {
service: "WMS",
layers: 'OMT-NA-TEXT-ZF-LG', //OpenMapTiles, North America, Text only, Zoom filtered, Layer group
format: 'image/png8',
transparent: true,
version: '1.1.0',
crs: L.CRS.EPSG3005,
tiled: true,
buffer: 2,
formatOptions: 'dpi:300;antialiasing:off',
};
return (
<GenericVectorBaseMap
tileset={BCVectorBaseMap.tileset}
vectorTileStyling={vectorTileStyling}
wmsUrl={process.env.REACT_APP_LABELS_WMS_URL}
wmsLayerOptions={wmsLayerOptions}
{...rest}
>
{children}
</GenericVectorBaseMap>
);
}
}
6 changes: 6 additions & 0 deletions src/components/BCVectorBaseMap/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "BCVectorBaseMap",
"version": "0.0.0",
"private": true,
"main": "./BCVectorBaseMap.js"
}
160 changes: 160 additions & 0 deletions src/components/GenericVectorBaseMap/GenericVectorBaseMap.js
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.

Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { MapContainer, useMap } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
import 'leaflet.vectorgrid';
import L from 'leaflet';
import 'proj4';
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.)

const map = useMap();

useEffect(() => {
const wmsLayer = L.tileLayer.wms(wmsUrl, wmsOptions).addTo(map);
return () => {
map.removeLayer(wmsLayer);
};
}, [map]);

return null;
};

const VectorGridLayer = ({ tilesUrl, vectorTileStyling, zoom, center, crs, wmsUrl, wmsOptions }) => {

const map = useMap();


useEffect(() => {
const vectorTileOptions = {
rendererFactory: L.canvas.tile,
interactive: false,
tolerance: function (zoom) {
if (zoom < 10) return 4;
if (zoom < 14) return 2;
return 1;
},
getFeatureId: (feature) => feature.properties.id,
/* Not included in vector style below:
- omt_place,
- omt_aeroway,
- omt_aerodrome_label,
- omt_building,
- omt_poi,
- omt_mountain_peak,
- omt_transportation_name,
- omt_water_name.
*/
vectorTileLayerStyles: {
omt_park: vectorTileStyling.park,
omt_landcover: vectorTileStyling.landcover,
omt_water: vectorTileStyling.water,
omt_boundary: vectorTileStyling.boundary,
omt_landuse: vectorTileStyling.landuse,
omt_waterway: vectorTileStyling.waterway,
omt_aeroway: vectorTileStyling.aeroway,
omt_transportation: vectorTileStyling.transportation,

}
};

try {
const vectorGrid = L.vectorGrid.protobuf(tilesUrl, vectorTileOptions).addTo(map);
return () => {
map.removeLayer(vectorGrid);
};
} catch (error) {
console.error('Error setting up vector grid:', error);
}
}, [map]);

return null;
};

VectorGridLayer.propTypes = {
tilesUrl: PropTypes.string.isRequired,
vectorTileStyling: PropTypes.object.isRequired,
zoom: PropTypes.number.isRequired,
center: PropTypes.shape({
lat: PropTypes.number.isRequired,
lng: PropTypes.number.isRequired
}).isRequired,
crs: PropTypes.instanceOf(L.CRS).isRequired,
wmsUrl: PropTypes.string.isRequired,
wmsOptions: PropTypes.object.isRequired,
};

const GenericVectorBaseMap = ({
tileset: { url, projection, tileMatrix },
center,
zoom,
mapRef,
vectorTileStyling,
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?

children,
...rest
}) => {
const crs = new L.Proj.CRS(
projection.code,
projection.proj4def,
{ ...projCRSOptions(tileMatrix), ...projection.options }
);

return (
<MapContainer
style={{ backgroundColor: '#EEEEEE' }} // alternative?:'#e7e5cf'
crs={crs}
minZoom={0}
maxZoom={tileMatrix.numResolutions}
center={center}
zoom={zoom}
ref={mapRef}
{...rest}
>
<VectorGridLayer
tilesUrl={url}
vectorTileStyling={vectorTileStyling}
zoom={zoom}
center={center}
crs={crs}
wmsUrl={wmsUrl}
wmsOptions={wmsLayerOptions}
{...rest}
/>
<LabelsLayer wmsUrl={wmsUrl} wmsOptions={wmsLayerOptions} {...rest} />
{children}
</MapContainer>
);
};

GenericVectorBaseMap.propTypes = {
tileset: PropTypes.shape({
url: PropTypes.string.isRequired,
projection: PropTypes.shape({
code: PropTypes.string.isRequired,
proj4def: PropTypes.string.isRequired,
options: PropTypes.object,
}).isRequired,
tileMatrix: PropTypes.object.isRequired,
}).isRequired,
center: PropTypes.shape({
lat: PropTypes.number.isRequired,
lng: PropTypes.number.isRequired,
}).isRequired,
zoom: PropTypes.number.isRequired,
mapRef: PropTypes.func,
vectorTileStyling: PropTypes.object.isRequired,
wmsUrl: PropTypes.string.isRequired,
wmsLayerOptions: PropTypes.shape({
layers: PropTypes.string,
format: PropTypes.string,
transparent: PropTypes.bool,
version: PropTypes.string,
crs: PropTypes.instanceOf(L.CRS)
}).isRequired,
children: PropTypes.node,
};

export default GenericVectorBaseMap;
6 changes: 6 additions & 0 deletions src/components/GenericVectorBaseMap/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "GenericVectorBaseMap",
"version": "0.0.0",
"private": true,
"main": "./GenericVectorBaseMap.js"
}
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export { default as GenericBaseMap } from './components/GenericBaseMap';
export { default as GenericVectorBaseMap } from './components/GenericVectorBaseMap';
export { default as BCBaseMap } from './components/BCBaseMap';
export { default as BCVectorBaseMap } from './components/BCVectorBaseMap';
export { default as YNWTBaseMap } from './components/YNWTBaseMap';
export { default as SetView } from './components/SetView';
export { default as StaticControl } from './components/StaticControl';
Expand Down
Loading