Interactive map helper functions

The objective of this modeule is to create some helper function to help us set the correct center and zoom level in folium (and other) maps.
cml = open_cml_sample()
Warning

These functions were originally created for Plotly, which uses Mapbox internally. This is unlike Leaflet, which Folium uses internally. This is why the estimated zoom may differ slightly from the actual required zoom. Please feel free to report any bugs you find.

When creating the map, we want the initial view to be centred and zoomed properly so that all the links are clearly visible at once. While the geopandas.explore function does this automatically, in some cases we might want to create a Map instance and then add geometries or tiles to it. To provide a good user experience, we need to be able to centre and zoom the map to the correct position. We will start by computing the centre.


get_center


def get_center(
    cml:xarray.core.dataarray.DataArray | xarray.core.dataset.Dataset
)->tuple:

Get the center point of a set of CML in OpenSense standard

get_center(cml)
(4.0407554999999995, 9.743189999999998)

To ensure that all links are clearly visible on the map, it is important to calculate an appropriate zoom level based on the spatial extent of the data. In particular, we need to determine the zoom required to fit the latitude and longitude ranges of the links within the map view.

We will first compute the zoom level required to represent the latitude. The zoom in meters per pixel can be estimated as follows:

\[ \text{meters\_per\_pixel} = \frac{v_0 \cdot \cos\left(lat_{center}\right)}{2^{\text{zoom\_level}}} \]

We need a zoom such that:

\[ \text{meters\_per\_pixel} = \frac{|lat_{\max} - lat_{\min}| \cdot \text{meters\_per\_degree}}{\text{height\_pixels}} \]

Thus, the required zoom level for latitude can be computed as:

\[ \text{zoom\_level} = \log_2 \left( \frac{v_0 \cdot \cos\left(lat_{center}\right) \cdot \text{height\_pixels}}{|lat_{\max} - lat_{\min}| \cdot \text{meters\_per\_degree}} \right) \]

Where:

  • \(v_0\) = initial resolution (meters/pixel at zoom level 0). It can be obtained from plotly website
  • \(\text{height\_pixels}\) = height of the map layout in pixels
  • \(lat_{\max}, lat_{\min}\) = maximum and minimum latitude in the data
  • \(\text{meters\_per\_degree}\) = number of meters per degree of latitude (typically 111320 m/degree)
  • \(lat_{center}\) = center latitude of the bounding box, i.e. \(lat_{center} = \frac{lat_{\max} + lat_{\min}}{2}\)

Implementing this in python is straightforward.

Next, we need to estimate the required zoom level for longitude. Unlike latitude, the number of meters per degree of longitude changes with latitude, and we also need to use the map’s width (in pixels) instead of its height. The formula for meters per pixel along the longitude direction is:

\[ \text{meters\_per\_pixel} = \frac{|lon_{\max} - lon_{\min}| \cdot \text{meters\_per\_degree} \cdot \cos\left(lat_{center}\right)}{\text{width\_pixels}} \]

From this, we can derive the formula for the required zoom level:

\[ \text{zoom\_level} = \log_2 \left( \frac{v_0 \cdot \text{width\_pixels}}{|lon_{\max} - lon_{\min}| \cdot \text{meters\_per\_degree}} \right) \]

where \(\text{width\_pixels}\) is the width of the map layout in pixels.

As before, implementing this calculation in Python is straightforward.

So, the final zoom level for the map should be set to the minimum of the zoom levels calculated for latitude and longitude. This approach ensures that all links are fully visible within the map’s bounding box, both vertically and horizontally.


get_zoom_start


def get_zoom_start(
    cml, height:int=1200, width:int=1200
):

Call self as a function.

get_zoom_start(cml)
12.406704259110562
folium.Map(get_center(cml), zoom_start=get_zoom_start(cml))