-
Notifications
You must be signed in to change notification settings - Fork 0
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
base: master
Are you sure you want to change the base?
Changes from 95 commits
1c2f3dd
fe4a662
479eb74
a171ed3
799cbbd
2d6b94b
7d13031
cbf547c
70bba72
0533638
e17e93d
72de698
b752143
9f25136
5c26589
f072d08
f262da8
127dcfa
e860436
dd25faa
224b08b
7dded99
bdec5b2
b8c0216
b662af6
4876b80
f54bc17
392aca4
f1f9273
990ba96
e08b28a
3ea1fea
aa1ab56
f191104
4e7ea37
bfef079
d020aba
ddcbefa
98f6efe
1e2b49f
9d65ab2
4421dd2
0ef9ca5
2aa47a5
f3ba95a
bca6ab4
d8fbfcf
7ae34d6
d913ea6
0ce6ce5
17bf14d
7a22b7d
d041cb8
15bb605
b005f06
0379024
f859893
de53fb9
03af1b2
fd78f74
153a838
011fb44
f3db579
5367ad7
7bbd136
c8accb1
77d99e4
70890e4
511a005
cdeb810
8eaa40b
b384966
4a301ff
d46b0b9
6e7f9c6
0a5f1bd
57eb18c
100d703
7cb2a80
c7c0574
b2670f9
d1387ab
7ce71c1
ec1e45f
6bbec5f
35183b0
d5b961d
60ca0c6
745cc4d
96e2707
d2b5770
1ea50c2
a55277b
ee2226c
6cc5373
0dc6986
f491356
15c4149
25c1727
abcfb38
ddd866b
77ecaec
80d895a
8647c0a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
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 { | ||
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: '© <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> | ||
); | ||
} | ||
} |
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" | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 }) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if the |
||
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; |
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" | ||
} |
There was a problem hiding this comment.
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.