Map styles
Styles and patterns used in the maps
Data-driven styles
To modify the width of circles and lines based on the zoom level, Mapbox data-driven styles were used to set stops set with pairs of zoom level and matching pixel width. These pairs are stops and are set as show in the following code for styling lines in a vector tile layer.
The stops
key above contain a 2D array which holds pairs of zoom levels and widths in pixels. Out-of-the-box, Mapbox will use calculate the values between these stops to adjust the circle radius linearly between stops levels.
react-map-gl-draw
does not support data-driven styles out-of-the-box so a couple of functions were written to calculate the width of circles in the drawing UI when the zoom level updates. These two functions are called getCircleRadiusByZoom
and linearInterpolation
.
The first step is to traverse the first level of the stops
array and find where the currentZoom
level fits into the brackets made up by pairs in the second depth of the array. If the currentZoom
value falls below the first set of level of zoom, the smallest pixel width is return. Similarly, if the currentZoom
is above the highest level of zoom, the highest pixel width is returned. If the currentZoom
is somewhere in between the minimum and maximum, the value is compared to two elements of the first level of the array which creates a bracket of zooms and widths. This provides four of the known values described next.
Next, the calculation come down to a bit of Algebra to find the unknown, currentWidth
, from the known values - minZoom
, currentZoom
, maxZoom
, minWidth
, and maxWidth
. This was all stitched together by translating the formula for linear interpolation into code. More here.
Note that the base
key in the above sample code is set to 1. If any other value was used, the linearInterpolation
function would need to be updated to account for the different rate of change in the output of the function. Luckily, the base of 1 makes it simple and looks nice.
Map control styles
One challenge with adding custom controls to the map is mouse events propagating through the control and interacting with the map below them. This can lead to users accidentally selecting features or interacting with the map when they were really trying to zoom, use a geocoder, or select a layer to show on the map. This was managed in a few ways depending on the control and whether it is a control native to react-map-gl
or not.
Native controls
For native controls, like the NavigationControl component, react-map-gl
adds props that can be adjusted to control the behavior of mouse events. One example is captureClick
which can be set to false to stop propagation of a click to the map below. See a simplified version of this from the app below.
Custom controls
Custom controls built with JSX can also be added to the map, but they also present the same challenge of mouse event propagation. For these controls, the solution is to render them outside of the open and close component tags of the map instance. Using CSS, you can position the rendered controls over the map and tune where they are placed in relation to the other controls. See a simplified version of this from the app below.
Geocoder controls
When using react-map-gl-geocoder
, the Geocoder component contains a prop called containerRef
where you can pass a ref that is tied to an external JSX element to tell the library where to place the geocoder input box. The styles of the geocoder input can also be customized using CSS to set where it is positioned on top of the map. This is show in a simplified version below and also in an example in the documentation.
Last updated