# Map styles

## Data-driven styles

To modify the width of circles and lines based on the zoom level, Mapbox [data-driven styles](https://docs.mapbox.com/mapbox-gl-js/example/data-driven-circle-colors/) were used to set stops set with pairs of zoom level and matching pixel width. These pairs are [stops](https://docs.mapbox.com/mapbox-gl-js/style-spec/other/#function-stops) and are set as show in the following code for styling lines in a vector tile layer.

```
return {
    id: this.layerIdName,
    type: "line",
    layout: {
        "line-join": "round",
        "line-cap": "round",
    },
    paint: {
        "line-color": "green",
    "line-width": {
        base: 1,
        stops: [
          [10, 1], // [zoom, width in px]
          [13, 2],
          [16, 10],
          [18, 25],
        ],
      },              
    },
}
```

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`.&#x20;

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.&#x20;

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](https://matthew-brett.github.io/teaching/linear_interpolation.html).&#x20;

{% hint style="info" %}
Note that the [`base`](https://docs.mapbox.com/mapbox-gl-js/style-spec/other/#function-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.
{% endhint %}

## 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](https://visgl.github.io/react-map-gl/docs/api-reference/navigation-control) component, `react-map-gl` adds props that can be adjusted to control the behavior of mouse events. One example is [`captureClick`](https://visgl.github.io/react-map-gl/docs/api-reference/navigation-control#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.

```
<Box className={classes.mapBox}>
  <ReactMapGL>
    <div className={classes.navStyle}>
      {/* captureClick prop set to false */}
      <NavigationControl captureClick={false} />
    </div>
    {/* Map sources and layers here */}
  </ReactMapGL>
</Box>
```

### 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.

```
<Box className={classes.mapBox}>
  {/* Render custom control outside ReactMapGL component */}
  {renderLayerSelect()}
  <ReactMapGL>
    {/* Map sources and layers here */}
  </ReactMapGL>
</Box>
```

### Geocoder controls

When using `react-map-gl-geocoder`, the Geocoder component contains a prop called [`containerRef`](https://github.com/SamSamskies/react-map-gl-geocoder#props) 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](https://github.com/SamSamskies/react-map-gl-geocoder#ignore-map-events-example).

```
<Box className={classes.mapBox}>
  {/* Render custom control outside ReactMapGL component */}
  {/* and add a ref created with useRef */}
  <div
    ref={mapControlContainerRef}
    style={{
      display: "flex",
      height: 50,
      position: "absolute",
      alignItems: "center",
      right: 32,
    }}
  />
  <ReactMapGL>
    <Geocoder
      mapRef={mapRef}
      onViewportChange={handleGeocoderViewportChange}
      mapboxApiAccessToken={MAPBOX_TOKEN}
      {/* The ref created for the div above is passed here */}
      containerRef={mapControlContainerRef}
    />
  </ReactMapGL>
</Box>
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://atd-dts.gitbook.io/moped-documentation/dev-guides/maps-and-geospatial-data/v1-archive/map-styles.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
