diff --git a/api/basemaps/index.html b/api/basemaps/index.html index b322faa..6ea194b 100644 --- a/api/basemaps/index.html +++ b/api/basemaps/index.html @@ -1228,15 +1228,12 @@

>>> from maplibre import Map, MapOptions
 >>> from maplibre.basemaps import Carto
 
-
>>> map = Map(MapOptions(style=Carto.DARK_MATTER))
+    
>>> m = Map(MapOptions(style=Carto.DARK_MATTER))
 
Source code in maplibre/basemaps.py -
 8
- 9
-10
-11
+              
11
 12
 13
 14
@@ -1256,30 +1253,33 @@ 

28 29 30 -31

class Carto(Enum):
-    """Carto basemap styles
-
-    Attributes:
-        DARK_MATTER: dark-matter
-        POSITRON: positron
-        VOYAGER: voyager
-        POSITRON_NOLABELS: positron-nolabels
-        DARK_MATTER_NOLABELS: dark-matter-nolabels
-        VOYAGER_NOLABELS: voyager-nolabels
-
-    Examples:
-        >>> from maplibre import Map, MapOptions
-        >>> from maplibre.basemaps import Carto
-
-        >>> map = Map(MapOptions(style=Carto.DARK_MATTER))
-    """
+31
+32
+33
+34
class Carto(Enum):
+    """Carto basemap styles
+
+    Attributes:
+        DARK_MATTER: dark-matter
+        POSITRON: positron
+        VOYAGER: voyager
+        POSITRON_NOLABELS: positron-nolabels
+        DARK_MATTER_NOLABELS: dark-matter-nolabels
+        VOYAGER_NOLABELS: voyager-nolabels
+
+    Examples:
+        >>> from maplibre import Map, MapOptions
+        >>> from maplibre.basemaps import Carto
 
-    DARK_MATTER = "dark-matter"
-    POSITRON = "positron"
-    VOYAGER = "voyager"
-    POSITRON_NOLABELS = "positron-nolabels"
-    DARK_MATTER_NOLABELS = "dark-matter-nolabels"
-    VOYAGER_NOLABELS = "voyager-nolabels"
+        >>> m = Map(MapOptions(style=Carto.DARK_MATTER))
+    """
+
+    DARK_MATTER = "dark-matter"
+    POSITRON = "positron"
+    VOYAGER = "voyager"
+    POSITRON_NOLABELS = "positron-nolabels"
+    DARK_MATTER_NOLABELS = "dark-matter-nolabels"
+    VOYAGER_NOLABELS = "voyager-nolabels"
 
@@ -1379,10 +1379,7 @@

Source code in maplibre/basemaps.py -
38
-39
-40
-41
+            
41
 42
 43
 44
@@ -1392,20 +1389,23 @@ 

48 49 50 -51

def construct_basemap_style(
-    name: str = "nice-style", sources: dict = {}, layers: list = []
-) -> dict:
-    """Construct a basemap style
-
-    Args:
-        name (str): The name of the basemap style.
-        sources (dict): The sources to be used for the basemap style.
-        layers (list): The layers to be used for the basemap style.
-    """
-    layers = [
-        layer.to_dict() if isinstance(layer, Layer) else layer for layer in layers
-    ]
-    return {"name": name, "version": 8, "sources": sources, "layers": layers}
+51
+52
+53
+54
def construct_basemap_style(
+    name: str = "nice-style", sources: dict = {}, layers: list = []
+) -> dict:
+    """Construct a basemap style
+
+    Args:
+        name (str): The name of the basemap style.
+        sources (dict): The sources to be used for the basemap style.
+        layers (list): The layers to be used for the basemap style.
+    """
+    layers = [
+        layer.to_dict() if isinstance(layer, Layer) else layer for layer in layers
+    ]
+    return {"name": name, "version": 8, "sources": sources, "layers": layers}
 
diff --git a/api/controls/index.html b/api/controls/index.html index 0d36ae3..0088089 100644 --- a/api/controls/index.html +++ b/api/controls/index.html @@ -1331,7 +1331,7 @@

- Bases: BaseModel

+ Bases: MapLibreBaseModel

Popup

@@ -1384,7 +1384,7 @@

31 32 33 -34

class Popup(BaseModel):
+34
class Popup(MapLibreBaseModel):
     """Popup
 
     Attributes:
@@ -1431,7 +1431,7 @@ 

- Bases: BaseModel

+ Bases: MapLibreBaseModel

Popup options

@@ -1446,7 +1446,7 @@

19 20 21 -22

class PopupOptions(BaseModel):
+22
class PopupOptions(MapLibreBaseModel):
     """Popup options"""
 
     anchor: str = None
@@ -1492,7 +1492,7 @@ 

- Bases: BaseModel

+ Bases: MapLibreBaseModel

Marker

@@ -1558,7 +1558,7 @@

58 59 60 -61

class Marker(BaseModel):
+61
class Marker(MapLibreBaseModel):
     """Marker
 
     Attributes:
@@ -1607,7 +1607,7 @@ 

- Bases: BaseModel

+ Bases: MapLibreBaseModel

Marker options

@@ -1624,7 +1624,7 @@

44 45 46 -47

class MarkerOptions(BaseModel):
+47
class MarkerOptions(MapLibreBaseModel):
     """Marker options"""
 
     anchor: str = None
@@ -1788,17 +1788,13 @@ 

>>> from maplibre import Map
 >>> from maplibre.controls import FullscreenControl, ControlPosition
 
-
>>> map = Map()
->>> map.add_control(FullscreenControl(), ControlPosition.BOTTOM_LEFT)
+    
>>> m = Map()
+>>> m.add_control(FullscreenControl(), ControlPosition.BOTTOM_LEFT)
 
Source code in maplibre/controls.py -
104
-105
-106
-107
-108
+              
108
 109
 110
 111
@@ -1806,19 +1802,23 @@ 

113 114 115 -116

class FullscreenControl(Control):
-    """Fullscreen control
-
-    Examples:
-        >>> from maplibre import Map
-        >>> from maplibre.controls import FullscreenControl, ControlPosition
+116
+117
+118
+119
+120
class FullscreenControl(Control):
+    """Fullscreen control
 
-        >>> map = Map()
-        >>> map.add_control(FullscreenControl(), ControlPosition.BOTTOM_LEFT)
-    """
+    Examples:
+        >>> from maplibre import Map
+        >>> from maplibre.controls import FullscreenControl, ControlPosition
 
-    # _name: str = ControlType.FULLSCREEN.value
-    pass
+        >>> m = Map()
+        >>> m.add_control(FullscreenControl(), ControlPosition.BOTTOM_LEFT)
+    """
+
+    # _name: str = ControlType.FULLSCREEN.value
+    pass
 
@@ -1847,15 +1847,15 @@

Source code in maplibre/controls.py -
class ScaleControl(Control):
-    """Scale control"""
-
-    max_width: int = Field(None, serialization_alias="maxWidth")
-    unit: Literal["imperial", "metric", "nautical"] = "metric"
+              
class ScaleControl(Control):
+    """Scale control"""
+
+    max_width: int = Field(None, serialization_alias="maxWidth")
+    unit: Literal["imperial", "metric", "nautical"] = "metric"
 
@@ -1900,19 +1900,19 @@

Source code in maplibre/controls.py -
130
-131
-132
-133
-134
+              
class NavigationControl(Control):
-    """Navigation control"""
-
-    # _name: str = ControlType.NAVIGATION.value
-    show_compass: bool = Field(True, serialization_alias="showCompass")
-    show_zoom: bool = Field(True, serialization_alias="showZoom")
-    visualize_pitch: bool = Field(False, serialization_alias="visualizePitch")
+136
+137
+138
+139
+140
class NavigationControl(Control):
+    """Navigation control"""
+
+    # _name: str = ControlType.NAVIGATION.value
+    show_compass: bool = Field(True, serialization_alias="showCompass")
+    show_zoom: bool = Field(True, serialization_alias="showZoom")
+    visualize_pitch: bool = Field(False, serialization_alias="visualizePitch")
 
@@ -1957,23 +1957,23 @@

Source code in maplibre/controls.py -
119
-120
-121
-122
-123
+              
class GeolocateControl(Control):
-    """Geolocate control"""
-
-    # _name: str = ControlType.GEOLOCATE.value
-    position_options: dict = Field(None, serialization_alias="positionOptions")
-    show_accuracy_circle: bool = Field(True, serialization_alias="showAccuracyCircle")
-    show_user_heading: bool = Field(False, serialization_alias="showUserHeading")
-    show_user_location: bool = Field(True, serialization_alias="showUserLocation")
-    track_user_location: bool = Field(False, serialization_alias="trackUserLocation")
+127
+128
+129
+130
+131
class GeolocateControl(Control):
+    """Geolocate control"""
+
+    # _name: str = ControlType.GEOLOCATE.value
+    position_options: dict = Field(None, serialization_alias="positionOptions")
+    show_accuracy_circle: bool = Field(True, serialization_alias="showAccuracyCircle")
+    show_user_heading: bool = Field(False, serialization_alias="showUserHeading")
+    show_user_location: bool = Field(True, serialization_alias="showUserLocation")
+    track_user_location: bool = Field(False, serialization_alias="trackUserLocation")
 
@@ -2055,25 +2055,25 @@

Source code in maplibre/controls.py -
169
-170
-171
-172
-173
+              
class InfoBoxControl(Control):
-    """InfoBox control
-
-    Attributes:
-        content (str): Content (HTML or plain text) to be displayed in the info box.
-        css_text (str): Optional inline style declaration of the control.
-    """
-
-    content: str
-    css_text: str = Field(None, serialization_alias="cssText")
+178
+179
+180
+181
+182
class InfoBoxControl(Control):
+    """InfoBox control
+
+    Attributes:
+        content (str): Content (HTML or plain text) to be displayed in the info box.
+        css_text (str): Optional inline style declaration of the control.
+    """
+
+    content: str
+    css_text: str = Field(None, serialization_alias="cssText")
 
@@ -2166,29 +2166,29 @@

Source code in maplibre/controls.py -
@@ -1231,7 +1231,7 @@

@@ -1264,18 +1264,19 @@

@@ -1303,101 +1304,159 @@

Source code in maplibre/layer.py -

155
-156
-157
-158
-159
+              
class LayerSwitcherControl(Control):
-    """Layer switcher control
-
-    Attributes:
-        layer_ids (list): A list of layer ids to be shown in the layer switcher control.
-        theme (Literal["default", "simple"]): The theme of the layer switcher control.
-        css_text (str): Optional inline style declaration of the control.
-    """
-
-    layer_ids: list = Field([], serialization_alias="layerIds")
-    theme: Literal["default", "simple"] = "default"
-    css_text: str = Field(None, serialization_alias="cssText")
+166
+167
+168
+169
+170
class LayerSwitcherControl(Control):
+    """Layer switcher control
+
+    Attributes:
+        layer_ids (list): A list of layer ids to be shown in the layer switcher control.
+        theme (Literal["default", "simple"]): The theme of the layer switcher control.
+        css_text (str): Optional inline style declaration of the control.
+    """
+
+    layer_ids: list = Field([], serialization_alias="layerIds")
+    theme: Literal["default", "simple"] = "default"
+    css_text: str = Field(None, serialization_alias="cssText")
 
diff --git a/api/layer/index.html b/api/layer/index.html index f0b4c9e..e3c1eb0 100644 --- a/api/layer/index.html +++ b/api/layer/index.html @@ -1169,7 +1169,7 @@

- Bases: BaseModel

+ Bases: MapLibreBaseModel

Layer properties

@@ -1220,7 +1220,7 @@

-

The filter expression that is applied to the source of the layer.

+

A filter expression that is applied to the source of the layer.

-

The layout properties of the layer.

+

The layout properties for the layer.

-

The paint properties of the layer.

+

The paint properties for the layer.

source - str | Source + str | Source | GeoDataFrame
-

The name (unique ID) of a source or a source object to be used for the layer.

+

The name (unique ID) of a source, a source object or a GeoDataFrame +to be used for the layer.

39
-40
-41
-42
-43
-44
-45
-46
-47
-48
-49
-50
-51
-52
-53
-54
-55
-56
-57
-58
-59
-60
-61
-62
-63
-64
-65
-66
-67
-68
-69
-70
-71
-72
-73
-74
-75
-76
-77
-78
-79
-80
-81
-82
-83
-84
-85
-86
class Layer(BaseModel):
-    """Layer properties
-
-    Notes:
-        See [layers](https://maplibre.org/maplibre-style-spec/layers/) for more details on the
-        `paint` and `layout` properties of the layers.
-
-    Attributes:
-        id (str): **Required.** The unique ID of the layer. Defaults to `str(uuid4())`.
-        type (str | LayerType): **Required.** The type of the layer.
-        filter (list): The filter expression that is applied to the source of the layer.
-        layout (dict): The layout properties of the layer.
-        max_zoom (int): The maximum zoom level for the layer.
-        min_zoom (int): The minimum zoom level for the layer.
-        paint (dict): The paint properties of the layer.
-        source (str | Source): The name (unique ID) of a source or a source object to be used for the layer.
-        source_layer (str): The layer to use from a vector tile source.
-
-    Examples:
-        >>> from maplibre.layer import Layer, LayerType
-
-        >>> layer = Layer(id="test-layer", type=LayerType.CIRCLE, source="test-source")
-    """
-
-    id: str = Field(default_factory=lambda: str(uuid4()))
-    type: LayerType
-    filter: list = None
-    layout: dict = None
-    max_zoom: int = Field(None, serialization_alias="maxzoom")
-    metadata: dict = None
-    min_zoom: int = Field(None, serialization_alias="minzoom")
-    paint: dict = None
-    source: Union[str, Source, dict, None] = None
-    source_layer: str = Field(None, serialization_alias="source-layer")
-
-    @field_validator("source")
-    def validate_source(cls, v):
-        if isinstance(v, Source):
-            return v.to_dict()
-
-        return v
-
-    @field_validator("paint", "layout")
-    def fix_paint(cls, v):
-        if isinstance(v, dict):
-            return fix_keys(v)
-
-        return v
+              
class Layer(MapLibreBaseModel):
+    """Layer properties
+
+    Notes:
+        See [layers](https://maplibre.org/maplibre-style-spec/layers/) for more details on the
+        `paint` and `layout` properties of the layers.
+
+    Attributes:
+        id (str): **Required.** The unique ID of the layer. Defaults to `str(uuid4())`.
+        type (str | LayerType): **Required.** The type of the layer.
+        filter (list): A filter expression that is applied to the source of the layer.
+        layout (dict): The layout properties for the layer.
+        max_zoom (int): The maximum zoom level for the layer.
+        min_zoom (int): The minimum zoom level for the layer.
+        paint (dict): The paint properties for the layer.
+        source (str | Source | GeoDataFrame): The name (unique ID) of a source, a source object or a GeoDataFrame
+            to be used for the layer.
+        source_layer (str): The layer to use from a vector tile source.
+
+    Examples:
+        >>> from maplibre.layer import Layer, LayerType
+
+        >>> layer = Layer(id="test-layer", type=LayerType.CIRCLE, source="test-source")
+    """
+
+    id: Optional[str] = Field(default_factory=lambda: str(uuid4()))
+    type: LayerType
+    filter: Optional[list] = None
+    layout: Optional[dict] = None
+    max_zoom: Optional[int] = Field(None, serialization_alias="maxzoom")
+    metadata: Optional[dict] = None
+    min_zoom: Optional[int] = Field(None, serialization_alias="minzoom")
+    paint: Optional[dict] = None
+    source: Union[str, Source, dict, GeoDataFrame, None] = None
+    source_layer: Optional[str] = Field(None, serialization_alias="source-layer")
+
+    # TODO: Use model_post_init and set bounds if source is a GeoDataFrame
+    @field_validator("source")
+    def validate_source(cls, v):
+        if GeoDataFrame is not None and isinstance(v, GeoDataFrame):
+            return SimpleFeatures(v).to_source().to_dict()
+
+        if isinstance(v, Source):
+            return v.to_dict()
+
+        return v
+
+    @field_validator("paint", "layout")
+    def fix_paint(cls, v):
+        if isinstance(v, dict):
+            return fix_keys(v)
+
+        return v
+
+    @property
+    def bounds(self) -> tuple | None:
+        try:
+            bounds = get_bounds(self.source["data"])
+        except Exception as e:
+            # print(e)
+            bounds = None
+
+        return bounds
+
+    def set_paint_props(self, **props) -> Layer:
+        if self.paint is None:
+            self.paint = dict()
+
+        self.paint = self.paint | fix_keys(props)
+        return self
+
+    def set_layout_props(self, **props) -> Layer:
+        if self.layout is None:
+            self.layout = dict()
+
+        self.paint = self.paint | fix_keys(props)
+        return self
 
@@ -1511,14 +1570,7 @@

Source code in maplibre/layer.py -
+ + + + + + + + + + + + + + + + + +
13
-14
-15
-16
-17
-18
-19
-20
+              
20
 21
 22
 23
@@ -1534,30 +1586,37 @@ 

33 34 35 -36

class LayerType(Enum):
-    """Rendering type of layer
-
-    Attributes:
-        CIRCLE: A filled circle.
-        FILL: A filled polygon with an optional stroked border.
-        FILL_EXTRUSION: An extruded polygon.
-        LINE: A stroked line.
-        SYMBOL: An icon or a text label.
-        RASTER: Raster map textures such as satellite imagery.
-        HEATMAP: A heatmap.
-        HILLSHADE: A Client-side hillshading visualization based on DEM data.
-        BACKGROUND: A background color or pattern.
-    """
-
-    CIRCLE = "circle"
-    FILL = "fill"
-    FILL_EXTRUSION = "fill-extrusion"
-    LINE = "line"
-    SYMBOL = "symbol"
-    RASTER = "raster"
-    HEATMAP = "heatmap"
-    HILLSHADE = "hillshade"
-    BACKGROUND = "background"
+36
+37
+38
+39
+40
+41
+42
+43
class LayerType(Enum):
+    """Rendering type of layer
+
+    Attributes:
+        CIRCLE: A filled circle.
+        FILL: A filled polygon with an optional stroked border.
+        FILL_EXTRUSION: An extruded polygon.
+        LINE: A stroked line.
+        SYMBOL: An icon or a text label.
+        RASTER: Raster map textures such as satellite imagery.
+        HEATMAP: A heatmap.
+        HILLSHADE: A Client-side hillshading visualization based on DEM data.
+        BACKGROUND: A background color or pattern.
+    """
+
+    CIRCLE = "circle"
+    FILL = "fill"
+    FILL_EXTRUSION = "fill-extrusion"
+    LINE = "line"
+    SYMBOL = "symbol"
+    RASTER = "raster"
+    HEATMAP = "heatmap"
+    HILLSHADE = "hillshade"
+    BACKGROUND = "background"
 
diff --git a/api/map/index.html b/api/map/index.html index c6deaef..a276567 100644 --- a/api/map/index.html +++ b/api/map/index.html @@ -663,6 +663,24 @@ + + +
  • + + + fit_bounds + + + +
  • + +
  • + + + save + + +
  • @@ -1408,6 +1426,24 @@ +
  • + +
  • + + + fit_bounds + + + +
  • + +
  • + + + save + + +
  • @@ -1603,6 +1639,48 @@

    MapOptions()

  • sources + dict + +
    +

    Sources to be added to the map. Keys are source IDs.

    +
    +
    + None +
    layers + list + +
    +

    Layers to be added to the map.

    +
    +
    + None +
    controls + list + +
    +

    Controls to be added to the map.

    +
    +
    + None +
    **kwargs @@ -1623,41 +1701,15 @@

    Examples:

    >>> from maplibre.map import Map, MapOptions
    -
    -
    >>> map_options = MapOptions(center=(9.5, 51.31667), zoom=8)
    ->>> map = Map(map_options)
    ->>> dict(map)
    -{'mapOptions': {'center': (9.5, 51.31667), 'style': 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json', 'zoom': 8}, 'calls': []}
    +>>> map_options = MapOptions(center=(9.5, 51.31667), zoom=8)
    +>>> m = Map(map_options)
    +>>> dict(m)
    +{'mapOptions': {'center': (9.5, 51.31667), 'style': 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json', 'zoom': 8}, 'calls': []}
     
    Source code in maplibre/map.py -
     62
    - 63
    - 64
    - 65
    - 66
    - 67
    - 68
    - 69
    - 70
    - 71
    - 72
    - 73
    - 74
    - 75
    - 76
    - 77
    - 78
    - 79
    - 80
    - 81
    - 82
    - 83
    - 84
    - 85
    - 86
    - 87
    +              
     87
      88
      89
      90
    @@ -1918,293 +1970,434 @@ 

    345 346 347 -348

    class Map(object):
    -    """Map
    -
    -    Args:
    -        map_options (MapOptions): Map options.
    -        **kwargs: Keyword arguments that are appended to the `MapOptions` object.
    -
    -    Examples:
    -        >>> from maplibre.map import Map, MapOptions
    -
    -        >>> map_options = MapOptions(center=(9.5, 51.31667), zoom=8)
    -        >>> map = Map(map_options)
    -        >>> dict(map)
    -        {'mapOptions': {'center': (9.5, 51.31667), 'style': 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json', 'zoom': 8}, 'calls': []}
    -    """
    -
    -    MESSAGE = "not implemented yet"
    -
    -    def __init__(self, map_options: MapOptions = MapOptions(), **kwargs):
    -        self.map_options = map_options.to_dict() | kwargs
    -        self._message_queue = []
    -
    -    def __iter__(self):
    -        for k, v in self.to_dict().items():
    -            yield k, v
    -
    -    def to_dict(self) -> dict:
    -        return {"mapOptions": self.map_options, "calls": self._message_queue}
    -
    -    """
    -    @property
    -    def sources(self) -> list:
    -        return [item["data"] for item in self._calls if item["name"] == "addSource"]
    -
    -    @property
    -    def layers(self) -> list:
    -        return [item["data"] for item in self._calls if item["name"] == "addLayer"]
    -    """
    -
    -    # TODO: Rename to add_map_call
    -    def add_call_(self, func_name: str, params: list) -> None:
    -        self._message_queue.append(
    -            {"name": "applyFunc", "data": {"funcName": func_name, "params": params}}
    -        )
    +348
    +349
    +350
    +351
    +352
    +353
    +354
    +355
    +356
    +357
    +358
    +359
    +360
    +361
    +362
    +363
    +364
    +365
    +366
    +367
    +368
    +369
    +370
    +371
    +372
    +373
    +374
    +375
    +376
    +377
    +378
    +379
    +380
    +381
    +382
    +383
    +384
    +385
    +386
    +387
    +388
    +389
    +390
    +391
    +392
    +393
    +394
    +395
    +396
    +397
    +398
    +399
    +400
    +401
    +402
    +403
    +404
    +405
    +406
    +407
    +408
    +409
    +410
    +411
    +412
    +413
    +414
    +415
    +416
    +417
    +418
    +419
    +420
    +421
    +422
    +423
    +424
    +425
    +426
    +427
    +428
    +429
    +430
    +431
    class Map(object):
    +    """Map
    +
    +    Args:
    +        map_options (MapOptions): Map options.
    +        sources (dict): Sources to be added to the map. Keys are source IDs.
    +        layers (list): Layers to be added to the map.
    +        controls (list): Controls to be added to the map.
    +        **kwargs: Keyword arguments that are appended to the `MapOptions` object.
    +
    +    Examples:
    +        >>> from maplibre.map import Map, MapOptions
    +        >>> map_options = MapOptions(center=(9.5, 51.31667), zoom=8)
    +        >>> m = Map(map_options)
    +        >>> dict(m)
    +        {'mapOptions': {'center': (9.5, 51.31667), 'style': 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json', 'zoom': 8}, 'calls': []}
    +    """
    +
    +    MESSAGE = "not implemented yet"
     
    -    def add_call(self, method_name: str, *args) -> None:
    -        """Add a method call that is executed on the map instance
    -
    -        Args:
    -            method_name (str): The name of the map method to be executed.
    -            *args (any): The arguments to be passed to the map method.
    -        """
    -        # TODO: Pass as dict? {"name": method_name, "args": args}
    -        call = [method_name, args]
    -        self._message_queue.append(call)
    -
    -    def add_control(
    -        self,
    -        control: Control,
    -        position: [str | ControlPosition] = ControlPosition.TOP_RIGHT,
    -    ) -> None:
    -        """Add a control to the map
    -
    -        Args:
    -            control (Control): The control to be added to the map.
    -            position (str | ControlPosition): The position of the control.
    -        """
    -        self.add_call(
    -            "addControl",
    -            control.type,
    -            control.to_dict(),
    -            ControlPosition(position).value,
    -        )
    +    def __init__(
    +        self,
    +        map_options: MapOptions = MapOptions(),
    +        sources: dict = None,
    +        layers: list = None,
    +        controls: list = None,
    +        **kwargs,
    +    ):
    +        self.map_options = (
    +            map_options.to_dict() | kwargs
    +        )  # MapOptions(**kwargs).to_dict() # need to fix MapWidget, because height is passed as kwarg
    +        self._message_queue = []
    +        self.add_layers(layers, sources)
    +        if controls:
    +            for control in controls:
    +                self.add_control(control)
    +
    +    def __iter__(self):
    +        for k, v in self.to_dict().items():
    +            yield k, v
    +
    +    def to_dict(self) -> dict:
    +        return {"mapOptions": self.map_options, "calls": self._message_queue}
    +
    +    """
    +    @property
    +    def sources(self) -> list:
    +        return [item["data"] for item in self._calls if item["name"] == "addSource"]
     
    -    def add_source(self, id: str, source: [Source | dict]) -> None:
    -        """Add a source to the map
    -
    -        Args:
    -            id (str): The unique ID of the source.
    -            source (Source | dict): The source to be added to the map.
    -        """
    -        if isinstance(source, Source):
    -            source = source.to_dict()
    -
    -        self.add_call("addSource", id, source)
    -
    -    def add_layer(self, layer: [Layer | dict], before_id: str = None) -> None:
    -        """Add a layer to the map
    -
    -        Args:
    -            layer (Layer | dict): The Layer to be added to the map.
    -            before_id (str): The ID of an existing layer to insert the new layer before,
    -                resulting in the new layer appearing visually beneath the existing layer.
    -                If `None`, the new layer will appear above all other layers.
    -        """
    -        if isinstance(layer, Layer):
    -            layer = layer.to_dict()
    +    @property
    +    def layers(self) -> list:
    +        return [item["data"] for item in self._calls if item["name"] == "addLayer"]
    +    """
    +
    +    # TODO: Rename to add_map_call
    +    """
    +    def add_call_(self, func_name: str, params: list) -> None:
    +        self._message_queue.append(
    +            {"name": "applyFunc", "data": {"funcName": func_name, "params": params}}
    +        )
    +    """
    +
    +    def add_call(self, method_name: str, *args) -> None:
    +        """Add a method call that is executed on the map instance
    +
    +        Args:
    +            method_name (str): The name of the map method to be executed.
    +            *args (any): The arguments to be passed to the map method.
    +        """
    +        # TODO: Pass as dict? {"name": method_name, "args": args}
    +        call = [method_name, args]
    +        self._message_queue.append(call)
     
    -        self.add_call("addLayer", layer, before_id)
    -
    -    def add_marker(self, marker: Marker) -> None:
    -        """Add a marker to the map
    -
    -        Args:
    -            marker (Marker): The marker to be added to the map.
    -        """
    -        self.add_call("addMarker", marker.to_dict())
    -
    -    def add_popup(self, layer_id: str, prop: str = None, template: str = None) -> None:
    -        """Add a popup to the map
    -
    -        Args:
    -            layer_id (str): The layer to which the popup is added.
    -            prop (str): The property of the source to be displayed. If `None`, all properties are displayed.
    -            template (str): A mustache template. If supplied, `prop` is ignored.
    -        """
    -        self.add_call("addPopup", layer_id, prop, template)
    -
    -    def add_tooltip(
    -        self, layer_id: str, prop: str = None, template: str = None
    -    ) -> None:
    -        """Add a tooltip to the map
    -
    -        Args:
    -            layer_id (str): The layer to which the tooltip is added.
    -            prop (str): The property of the source to be displayed. If `None`, all properties are displayed.
    -            template (str): A mustache template. If supplied, `prop` is ignored.
    -
    -        Examples:
    -            >>> map = Map()
    -            >>> # ...
    -            >>> map.add_tooltip("test-layer", template="Name: {{ name }}")
    -        """
    -        self.add_call("addTooltip", layer_id, prop, template)
    +    def add_control(
    +        self,
    +        control: Control,
    +        position: [str | ControlPosition] = None,
    +    ) -> None:
    +        """Add a control to the map
    +
    +        Args:
    +            control (Control): The control to be added to the map.
    +            position (str | ControlPosition): The position of the control.
    +        """
    +        position = position or control.position
    +        self.add_call(
    +            "addControl",
    +            control.type,
    +            control.to_dict(),
    +            ControlPosition(position).value,
    +        )
    +
    +    def add_source(self, id: str, source: [Source | dict | GeoDataFrame]) -> None:
    +        """Add a source to the map
    +
    +        Args:
    +            id (str): The unique ID of the source.
    +            source (Source | dict | GeoDataFrame): The source to be added to the map.
    +        """
    +        if GeoDataFrame is not None and isinstance(source, GeoDataFrame):
    +            source = SimpleFeatures(source).to_source()
    +
    +        if isinstance(source, Source):
    +            source = source.to_dict()
    +
    +        self.add_call("addSource", id, source)
    +
    +    def add_layer(self, layer: [Layer | dict], before_id: str = None) -> None:
    +        """Add a layer to the map
     
    -    def set_filter(self, layer_id: str, filter_: list):
    -        """Update the filter of a layer
    -
    -        Args:
    -            layer_id (str): The name of the layer to be updated.
    -            filter_ (list): The filter expression that is applied to the source of the layer.
    -        """
    -        self.add_call("setFilter", layer_id, filter_)
    +        Args:
    +            layer (Layer | dict): The Layer to be added to the map.
    +            before_id (str): The ID of an existing layer to insert the new layer before,
    +                resulting in the new layer appearing visually beneath the existing layer.
    +                If `None`, the new layer will appear above all other layers.
    +        """
    +        if isinstance(layer, Layer):
    +            layer = layer.to_dict()
     
    -    def set_paint_property(self, layer_id: str, prop: str, value: any) -> None:
    -        """Update the paint property of a layer
    -
    -        Args:
    -            layer_id (str): The name of the layer to be updated.
    -            prop (str): The name of the paint property to be updated.
    -            value (any): The new value of the paint property.
    -        """
    -        self.add_call("setPaintProperty", layer_id, prop, value)
    -
    -    def set_layout_property(self, layer_id: str, prop: str, value: any) -> None:
    -        """Update a layout property of a layer
    -
    -        Args:
    -            layer_id (str): The name of the layer to be updated.
    -            prop (str): The name of the layout property to be updated.
    -            value (any): The new value of the layout property.
    -        """
    -        self.add_call("setLayoutProperty", layer_id, prop, value)
    -
    -    def set_data(self, source_id: str, data: dict) -> None:
    -        """Update the data of a GeoJSON source
    -
    -        Args:
    -            source_id (str): The name of the source to be updated.
    -            data (dict): The data of the source.
    +        self.add_call("addLayer", layer, before_id)
    +
    +    def add_layers(self, layers: list = None, sources: dict = None):
    +        layers = layers or []
    +        sources = sources or dict()
    +        for source_id, source in sources.items():
    +            self.add_source(source_id, source)
    +
    +        for layer in layers:
    +            self.add_layer(layer)
    +
    +    def add_marker(self, marker: Marker) -> None:
    +        """Add a marker to the map
    +
    +        Args:
    +            marker (Marker): The marker to be added to the map.
    +        """
    +        self.add_call("addMarker", marker.to_dict())
    +
    +    def add_popup(self, layer_id: str, prop: str = None, template: str = None) -> None:
    +        """Add a popup to the map
    +
    +        Args:
    +            layer_id (str): The layer to which the popup is added.
    +            prop (str): The property of the source to be displayed. If `None`, all properties are displayed.
    +            template (str): A mustache template. If supplied, `prop` is ignored.
             """
    -        self.add_call("setSourceData", source_id, data)
    +        self.add_call("addPopup", layer_id, prop, template)
     
    -    def set_visibility(self, layer_id: str, visible: bool = True) -> None:
    -        """Update the visibility of a layer
    -
    -        Args:
    -            layer_id (str): The name of the layer to be updated.
    -            visible (bool): Whether the layer is visible or not.
    -        """
    -        value = "visible" if visible else "none"
    -        self.add_call("setLayoutProperty", layer_id, "visibility", value)
    +    def add_tooltip(
    +        self, layer_id: str, prop: str = None, template: str = None
    +    ) -> None:
    +        """Add a tooltip to the map
    +
    +        Args:
    +            layer_id (str): The layer to which the tooltip is added.
    +            prop (str): The property of the source to be displayed. If `None`, all properties are displayed.
    +            template (str): A mustache template. If supplied, `prop` is ignored.
     
    -    def to_html(self, title: str = "My Awesome Map", **kwargs) -> str:
    -        """Render to html
    -
    -        Args:
    -            title (str): The Title of the HTML document.
    -            **kwargs (Any): Additional keyword arguments that are passed to the template.
    -                Currently, `style` is the only supported keyword argument.
    -
    -        Examples:
    -            >>> from maplibre import Map
    -
    -            >>> map = Map()
    -            >>> with open("/tmp/map.html", "w") as f:
    -            ...     f.write(map.to_html(style="height: 800px;") # doctest: +SKIP
    -        """
    -        js_lib = read_internal_file("srcjs", "pywidget.js")
    -        js_snippet = Template(js_template).render(data=json.dumps(self.to_dict()))
    -        css = read_internal_file("srcjs", "pywidget.css")
    -        headers = [f"<style>{css}</style>"]
    -
    -        # Deck.GL headers
    -        add_deckgl_headers = "addDeckOverlay" in [
    -            item[0] for item in self._message_queue
    -        ]
    -        # TODO: Set version in constants
    -        deckgl_headers = (
    -            [
    -                '<script src="https://unpkg.com/deck.gl@9.0.16/dist.min.js"></script>',
    -                '<script src="https://unpkg.com/@deck.gl/json@9.0.16/dist.min.js"></script>',
    -            ]
    -            if add_deckgl_headers
    -            else []
    -        )
    -
    -        # Mapbox Draw headers
    -        add_mapbox_draw_headers = "addMapboxDraw" in [
    -            item[0] for item in self._message_queue
    -        ]
    -        # TODO: Set version in constants
    -        mapbox_draw_headers = (
    -            [
    -                # "<script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.3/mapbox-gl-draw.js'></script>",
    -                # "<link rel='stylesheet' href='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.3/mapbox-gl-draw.css' type='text/css' />",
    -                "<script src='https://www.unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.js'></script>",
    -                "<link rel='stylesheet' href='https://www.unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.css' type='text/css' />",
    -            ]
    -            if add_mapbox_draw_headers
    -            else []
    -        )
    -
    -        output = Template(html_template).render(
    -            js="\n".join([js_lib, js_snippet]),
    -            title=title,
    -            headers=headers + deckgl_headers + mapbox_draw_headers,
    -            **kwargs,
    -        )
    -        return output
    +        Examples:
    +            >>> m = Map()
    +            >>> # ...
    +            >>> m.add_tooltip("test-layer", template="Name: {{ name }}")
    +        """
    +        self.add_call("addTooltip", layer_id, prop, template)
    +
    +    def set_filter(self, layer_id: str, filter_: list):
    +        """Update the filter of a layer
    +
    +        Args:
    +            layer_id (str): The name of the layer to be updated.
    +            filter_ (list): The filter expression that is applied to the source of the layer.
    +        """
    +        self.add_call("setFilter", layer_id, filter_)
    +
    +    def set_paint_property(self, layer_id: str, prop: str, value: any) -> None:
    +        """Update the paint property of a layer
    +
    +        Args:
    +            layer_id (str): The name of the layer to be updated.
    +            prop (str): The name of the paint property to be updated.
    +            value (any): The new value of the paint property.
    +        """
    +        self.add_call("setPaintProperty", layer_id, prop, value)
    +
    +    def set_layout_property(self, layer_id: str, prop: str, value: any) -> None:
    +        """Update a layout property of a layer
    +
    +        Args:
    +            layer_id (str): The name of the layer to be updated.
    +            prop (str): The name of the layout property to be updated.
    +            value (any): The new value of the layout property.
    +        """
    +        self.add_call("setLayoutProperty", layer_id, prop, value)
    +
    +    def set_data(self, source_id: str, data: dict | GeoDataFrame) -> None:
    +        """Update the data of a GeoJSON source
    +
    +        Args:
    +            source_id (str): The name of the source to be updated.
    +            data (dict): The data of the source.
    +        """
    +        if isinstance(data, GeoDataFrame):
    +            data = SimpleFeatures(data).to_source().data
    +
    +        self.add_call("setSourceData", source_id, data)
    +
    +    def set_visibility(self, layer_id: str, visible: bool = True) -> None:
    +        """Update the visibility of a layer
    +
    +        Args:
    +            layer_id (str): The name of the layer to be updated.
    +            visible (bool): Whether the layer is visible or not.
    +        """
    +        value = "visible" if visible else "none"
    +        self.add_call("setLayoutProperty", layer_id, "visibility", value)
     
    -    # -------------------------
    -    # Plugins
    -    # -------------------------
    -    def add_deck_layers(self, layers: list[dict], tooltip: str | dict = None) -> None:
    -        """Add Deck.GL layers to the layer stack
    -
    -        Args:
    -            layers (list[dict]): A list of dictionaries containing the Deck.GL layers to be added.
    -            tooltip (str | dict): Either a single mustache template string applied to all layers
    -                or a dictionary where keys are layer ids and values are mustache template strings.
    -        """
    -        self.add_call("addDeckOverlay", layers, tooltip)
    -
    -    def set_deck_layers(self, layers: list[dict], tooltip: str | dict = None) -> None:
    -        """Update Deck.GL layers
    -
    -        Args:
    -            layers (list[dict]): A list of dictionaries containing the Deck.GL layers to be updated.
    -                New layers will be added. Missing layers will be removed.
    -            tooltip (str | dict): Must be set to keep tooltip even if it did not change.
    -        """
    -        self.add_call("setDeckLayers", layers, tooltip)
    -
    -    def add_mapbox_draw(
    -        self,
    -        options: dict | MapboxDrawOptions = None,
    -        position: str | ControlPosition = ControlPosition.TOP_LEFT,
    -        geojson: dict = None,
    -    ) -> None:
    -        """Add MapboxDraw controls to the map
    -
    -        Note:
    -            See [MapboxDraw API Reference](https://github.com/mapbox/mapbox-gl-draw/blob/main/docs/API.md)
    -                for available options.
    -
    -        Args:
    -            options (dict | MapboxDrawOptions): MapboxDraw options.
    -            position (str | ControlPosition): The position of the MapboxDraw controls.
    -            geojson (dict): A GeoJSON Feature, FeatureCollection or Geometry to be added to the draw layer.
    -        """
    -        if isinstance(options, MapboxDrawOptions):
    -            options = options.to_dict()
    -
    -        self.add_call(
    -            "addMapboxDraw", options or {}, ControlPosition(position).value, geojson
    -        )
    +    def fit_bounds(
    +        self,
    +        bounds: tuple | list = None,
    +        data: GeoDataFrame = None,
    +        animate=False,
    +        **kwargs,
    +    ) -> None:
    +        """Pan and zoom the map to contain its visible area within the specified geographical bounds"""
    +        kwargs["animate"] = animate
    +        if data is not None:
    +            bounds = tuple(data.total_bounds)
    +
    +        self.add_call("fitBounds", bounds, kwargs)
    +
    +    def to_html(self, title: str = "My Awesome Map", **kwargs) -> str:
    +        """Render to html
    +
    +        Args:
    +            title (str): The Title of the HTML document.
    +            **kwargs (Any): Additional keyword arguments that are passed to the template.
    +                Currently, `style` is the only supported keyword argument.
    +
    +        Examples:
    +            >>> from maplibre import Map
    +            >>> m = Map()
    +            >>> with open("/tmp/map.html", "w") as f:
    +            ...     f.write(m.to_html(style="height: 800px;") # doctest: +SKIP
    +        """
    +        js_lib = read_internal_file("srcjs", "pywidget.js")
    +        js_snippet = Template(js_template).render(data=json.dumps(self.to_dict()))
    +        css = read_internal_file("srcjs", "pywidget.css")
    +        headers = [f"<style>{css}</style>"]
    +
    +        # Deck.GL headers
    +        add_deckgl_headers = "addDeckOverlay" in [
    +            item[0] for item in self._message_queue
    +        ]
    +        # TODO: Set version in constants
    +        deckgl_headers = (
    +            [
    +                # '<script src="https://unpkg.com/h3-js"></script>',
    +                '<script src="https://unpkg.com/h3-js@4.1.0/dist/h3-js.umd.js"></script>',
    +                '<script src="https://unpkg.com/deck.gl@9.0.16/dist.min.js"></script>',
    +                '<script src="https://unpkg.com/@deck.gl/json@9.0.16/dist.min.js"></script>',
    +            ]
    +            if add_deckgl_headers
    +            else []
    +        )
    +
    +        # Mapbox Draw headers
    +        add_mapbox_draw_headers = "addMapboxDraw" in [
    +            item[0] for item in self._message_queue
    +        ]
    +        # TODO: Set version in constants
    +        mapbox_draw_headers = (
    +            [
    +                # "<script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.3/mapbox-gl-draw.js'></script>",
    +                # "<link rel='stylesheet' href='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.3/mapbox-gl-draw.css' type='text/css' />",
    +                "<script src='https://www.unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.js'></script>",
    +                "<link rel='stylesheet' href='https://www.unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.css' type='text/css' />",
    +            ]
    +            if add_mapbox_draw_headers
    +            else []
    +        )
    +
    +        output = Template(html_template).render(
    +            js="\n".join([js_lib, js_snippet]),
    +            title=title,
    +            headers=headers + deckgl_headers + mapbox_draw_headers,
    +            **kwargs,
    +        )
    +        return output
    +
    +    def save(self, filename: str = None, preview=True, **kwargs):
    +        """Save the map to an HTML file"""
    +        return save_map(self, filename, preview, **kwargs)
    +
    +    # -------------------------
    +    # Plugins
    +    # -------------------------
    +    def add_deck_layers(
    +        self, layers: list[dict | "pydeck.Layer"], tooltip: str | dict = None
    +    ) -> None:
    +        """Add Deck.GL layers to the layer stack
    +
    +        Args:
    +            layers (list[dict | "pydeck.Layer"]): A list of dictionaries containing the Deck.GL layers to be added.
    +            tooltip (str | dict): Either a single mustache template string applied to all layers
    +                or a dictionary where keys are layer ids and values are mustache template strings.
    +        """
    +        layers = parse_deck_layers(layers)
    +        self.add_call("addDeckOverlay", layers, tooltip)
    +
    +    def set_deck_layers(
    +        self, layers: list[dict | "pydeck.Layer"], tooltip: str | dict = None
    +    ) -> None:
    +        """Update Deck.GL layers
    +
    +        Args:
    +            layers (list[dict | "pydeck.Layer"]): A list of dictionaries containing the Deck.GL layers to be updated.
    +                New layers will be added. Missing layers will be removed.
    +            tooltip (str | dict): Must be set to keep tooltip even if it did not change.
    +        """
    +        layers = parse_deck_layers(layers)
    +        self.add_call("setDeckLayers", layers, tooltip)
    +
    +    def add_mapbox_draw(
    +        self,
    +        options: dict | MapboxDrawOptions = None,
    +        position: str | ControlPosition = ControlPosition.TOP_LEFT,
    +        geojson: dict = None,
    +    ) -> None:
    +        """Add MapboxDraw controls to the map
    +
    +        Note:
    +            See [MapboxDraw API Reference](https://github.com/mapbox/mapbox-gl-draw/blob/main/docs/API.md)
    +                for available options.
    +
    +        Args:
    +            options (dict | MapboxDrawOptions): MapboxDraw options.
    +            position (str | ControlPosition): The position of the MapboxDraw controls.
    +            geojson (dict): A GeoJSON Feature, FeatureCollection or Geometry to be added to the draw layer.
    +        """
    +        if isinstance(options, MapboxDrawOptions):
    +            options = options.to_dict()
    +
    +        self.add_call(
    +            "addMapboxDraw", options or {}, ControlPosition(position).value, geojson
    +        )
     
    @@ -2281,25 +2474,25 @@

    Source code in maplibre/map.py -
    @@ -2367,39 +2560,41 @@

    Source code in maplibre/map.py -

    def add_call(self, method_name: str, *args) -> None:
    -    """Add a method call that is executed on the map instance
    -
    -    Args:
    -        method_name (str): The name of the map method to be executed.
    -        *args (any): The arguments to be passed to the map method.
    -    """
    -    # TODO: Pass as dict? {"name": method_name, "args": args}
    -    call = [method_name, args]
    -    self._message_queue.append(call)
    +            
    def add_call(self, method_name: str, *args) -> None:
    +    """Add a method call that is executed on the map instance
    +
    +    Args:
    +        method_name (str): The name of the map method to be executed.
    +        *args (any): The arguments to be passed to the map method.
    +    """
    +    # TODO: Pass as dict? {"name": method_name, "args": args}
    +    call = [method_name, args]
    +    self._message_queue.append(call)
     
    @@ -2312,7 +2505,7 @@

    - add_control(control, position=ControlPosition.TOP_RIGHT) + add_control(control, position=None)

    @@ -2359,7 +2552,7 @@

    - TOP_RIGHT + None
    def add_control(
    -    self,
    -    control: Control,
    -    position: [str | ControlPosition] = ControlPosition.TOP_RIGHT,
    -) -> None:
    -    """Add a control to the map
    -
    -    Args:
    -        control (Control): The control to be added to the map.
    -        position (str | ControlPosition): The position of the control.
    -    """
    -    self.add_call(
    -        "addControl",
    -        control.type,
    -        control.to_dict(),
    -        ControlPosition(position).value,
    -    )
    +            
    def add_control(
    +    self,
    +    control: Control,
    +    position: [str | ControlPosition] = None,
    +) -> None:
    +    """Add a control to the map
    +
    +    Args:
    +        control (Control): The control to be added to the map.
    +        position (str | ControlPosition): The position of the control.
    +    """
    +    position = position or control.position
    +    self.add_call(
    +        "addControl",
    +        control.type,
    +        control.to_dict(),
    +        ControlPosition(position).value,
    +    )
     
    @@ -2437,7 +2632,7 @@

    layers - list[dict] + list[dict | 'pydeck.Layer']
    @@ -2468,23 +2663,29 @@

    Source code in maplibre/map.py -
    def add_deck_layers(self, layers: list[dict], tooltip: str | dict = None) -> None:
    -    """Add Deck.GL layers to the layer stack
    -
    -    Args:
    -        layers (list[dict]): A list of dictionaries containing the Deck.GL layers to be added.
    -        tooltip (str | dict): Either a single mustache template string applied to all layers
    -            or a dictionary where keys are layer ids and values are mustache template strings.
    -    """
    -    self.add_call("addDeckOverlay", layers, tooltip)
    +            
    def add_deck_layers(
    +    self, layers: list[dict | "pydeck.Layer"], tooltip: str | dict = None
    +) -> None:
    +    """Add Deck.GL layers to the layer stack
    +
    +    Args:
    +        layers (list[dict | "pydeck.Layer"]): A list of dictionaries containing the Deck.GL layers to be added.
    +        tooltip (str | dict): Either a single mustache template string applied to all layers
    +            or a dictionary where keys are layer ids and values are mustache template strings.
    +    """
    +    layers = parse_deck_layers(layers)
    +    self.add_call("addDeckOverlay", layers, tooltip)
     
    @@ -2554,31 +2755,31 @@

    Source code in maplibre/map.py -
    def add_layer(self, layer: [Layer | dict], before_id: str = None) -> None:
    -    """Add a layer to the map
    -
    -    Args:
    -        layer (Layer | dict): The Layer to be added to the map.
    -        before_id (str): The ID of an existing layer to insert the new layer before,
    -            resulting in the new layer appearing visually beneath the existing layer.
    -            If `None`, the new layer will appear above all other layers.
    -    """
    -    if isinstance(layer, Layer):
    -        layer = layer.to_dict()
    -
    -    self.add_call("addLayer", layer, before_id)
    +            
    def add_layer(self, layer: [Layer | dict], before_id: str = None) -> None:
    +    """Add a layer to the map
    +
    +    Args:
    +        layer (Layer | dict): The Layer to be added to the map.
    +        before_id (str): The ID of an existing layer to insert the new layer before,
    +            resulting in the new layer appearing visually beneath the existing layer.
    +            If `None`, the new layer will appear above all other layers.
    +    """
    +    if isinstance(layer, Layer):
    +        layer = layer.to_dict()
    +
    +    self.add_call("addLayer", layer, before_id)
     
    @@ -2665,51 +2866,51 @@

    Source code in maplibre/map.py -
    def add_mapbox_draw(
    -    self,
    -    options: dict | MapboxDrawOptions = None,
    -    position: str | ControlPosition = ControlPosition.TOP_LEFT,
    -    geojson: dict = None,
    -) -> None:
    -    """Add MapboxDraw controls to the map
    -
    -    Note:
    -        See [MapboxDraw API Reference](https://github.com/mapbox/mapbox-gl-draw/blob/main/docs/API.md)
    -            for available options.
    -
    -    Args:
    -        options (dict | MapboxDrawOptions): MapboxDraw options.
    -        position (str | ControlPosition): The position of the MapboxDraw controls.
    -        geojson (dict): A GeoJSON Feature, FeatureCollection or Geometry to be added to the draw layer.
    -    """
    -    if isinstance(options, MapboxDrawOptions):
    -        options = options.to_dict()
    -
    -    self.add_call(
    -        "addMapboxDraw", options or {}, ControlPosition(position).value, geojson
    -    )
    +            
    def add_mapbox_draw(
    +    self,
    +    options: dict | MapboxDrawOptions = None,
    +    position: str | ControlPosition = ControlPosition.TOP_LEFT,
    +    geojson: dict = None,
    +) -> None:
    +    """Add MapboxDraw controls to the map
    +
    +    Note:
    +        See [MapboxDraw API Reference](https://github.com/mapbox/mapbox-gl-draw/blob/main/docs/API.md)
    +            for available options.
    +
    +    Args:
    +        options (dict | MapboxDrawOptions): MapboxDraw options.
    +        position (str | ControlPosition): The position of the MapboxDraw controls.
    +        geojson (dict): A GeoJSON Feature, FeatureCollection or Geometry to be added to the draw layer.
    +    """
    +    if isinstance(options, MapboxDrawOptions):
    +        options = options.to_dict()
    +
    +    self.add_call(
    +        "addMapboxDraw", options or {}, ControlPosition(position).value, geojson
    +    )
     
    @@ -2763,19 +2964,19 @@

    Source code in maplibre/map.py -
    def add_marker(self, marker: Marker) -> None:
    -    """Add a marker to the map
    -
    -    Args:
    -        marker (Marker): The marker to be added to the map.
    -    """
    -    self.add_call("addMarker", marker.to_dict())
    +            
    def add_marker(self, marker: Marker) -> None:
    +    """Add a marker to the map
    +
    +    Args:
    +        marker (Marker): The marker to be added to the map.
    +    """
    +    self.add_call("addMarker", marker.to_dict())
     
    @@ -2857,23 +3058,23 @@

    Source code in maplibre/map.py -
    def add_popup(self, layer_id: str, prop: str = None, template: str = None) -> None:
    -    """Add a popup to the map
    -
    -    Args:
    -        layer_id (str): The layer to which the popup is added.
    -        prop (str): The property of the source to be displayed. If `None`, all properties are displayed.
    -        template (str): A mustache template. If supplied, `prop` is ignored.
    -    """
    -    self.add_call("addPopup", layer_id, prop, template)
    +            
    def add_popup(self, layer_id: str, prop: str = None, template: str = None) -> None:
    +    """Add a popup to the map
    +
    +    Args:
    +        layer_id (str): The layer to which the popup is added.
    +        prop (str): The property of the source to be displayed. If `None`, all properties are displayed.
    +        template (str): A mustache template. If supplied, `prop` is ignored.
    +    """
    +    self.add_call("addPopup", layer_id, prop, template)
     
    @@ -2925,7 +3126,7 @@

    source - Source | dict + Source | dict | GeoDataFrame
    @@ -2941,27 +3142,33 @@

    Source code in maplibre/map.py -
    def add_source(self, id: str, source: [Source | dict]) -> None:
    -    """Add a source to the map
    -
    -    Args:
    -        id (str): The unique ID of the source.
    -        source (Source | dict): The source to be added to the map.
    -    """
    -    if isinstance(source, Source):
    -        source = source.to_dict()
    -
    -    self.add_call("addSource", id, source)
    +            
    def add_source(self, id: str, source: [Source | dict | GeoDataFrame]) -> None:
    +    """Add a source to the map
    +
    +    Args:
    +        id (str): The unique ID of the source.
    +        source (Source | dict | GeoDataFrame): The source to be added to the map.
    +    """
    +    if GeoDataFrame is not None and isinstance(source, GeoDataFrame):
    +        source = SimpleFeatures(source).to_source()
    +
    +    if isinstance(source, Source):
    +        source = source.to_dict()
    +
    +    self.add_call("addSource", id, source)
     
    @@ -3044,44 +3251,44 @@

    Examples:

    -
    >>> map = Map()
    +    
    >>> m = Map()
     >>> # ...
    ->>> map.add_tooltip("test-layer", template="Name: {{ name }}")
    +>>> m.add_tooltip("test-layer", template="Name: {{ name }}")
     
    Source code in maplibre/map.py -
    def add_tooltip(
    -    self, layer_id: str, prop: str = None, template: str = None
    -) -> None:
    -    """Add a tooltip to the map
    -
    -    Args:
    -        layer_id (str): The layer to which the tooltip is added.
    -        prop (str): The property of the source to be displayed. If `None`, all properties are displayed.
    -        template (str): A mustache template. If supplied, `prop` is ignored.
    -
    -    Examples:
    -        >>> map = Map()
    -        >>> # ...
    -        >>> map.add_tooltip("test-layer", template="Name: {{ name }}")
    -    """
    -    self.add_call("addTooltip", layer_id, prop, template)
    +            
    def add_tooltip(
    +    self, layer_id: str, prop: str = None, template: str = None
    +) -> None:
    +    """Add a tooltip to the map
    +
    +    Args:
    +        layer_id (str): The layer to which the tooltip is added.
    +        prop (str): The property of the source to be displayed. If `None`, all properties are displayed.
    +        template (str): A mustache template. If supplied, `prop` is ignored.
    +
    +    Examples:
    +        >>> m = Map()
    +        >>> # ...
    +        >>> m.add_tooltip("test-layer", template="Name: {{ name }}")
    +    """
    +    self.add_call("addTooltip", layer_id, prop, template)
     
    @@ -3093,24 +3300,100 @@

    -

    - set_data(source_id, data) +

    + fit_bounds(bounds=None, data=None, animate=False, **kwargs)

    -

    Update the data of a GeoJSON source

    +

    Pan and zoom the map to contain its visible area within the specified geographical bounds

    - - -

    Parameters:

    - - - - - +
    + Source code in maplibre/map.py +
    NameType
    def fit_bounds(
    +    self,
    +    bounds: tuple | list = None,
    +    data: GeoDataFrame = None,
    +    animate=False,
    +    **kwargs,
    +) -> None:
    +    """Pan and zoom the map to contain its visible area within the specified geographical bounds"""
    +    kwargs["animate"] = animate
    +    if data is not None:
    +        bounds = tuple(data.total_bounds)
    +
    +    self.add_call("fitBounds", bounds, kwargs)
    +
    + + + + + + +
    + + + +

    + save(filename=None, preview=True, **kwargs) + +

    + + +
    + +

    Save the map to an HTML file

    + +
    + Source code in maplibre/map.py +
    def save(self, filename: str = None, preview=True, **kwargs):
    +    """Save the map to an HTML file"""
    +    return save_map(self, filename, preview, **kwargs)
    +
    +
    +
    + +
    + + +
    + + + +

    + set_data(source_id, data) + +

    + + +
    + +

    Update the data of a GeoJSON source

    + + + +

    Parameters:

    + + + + + @@ -3149,21 +3432,27 @@

    Source code in maplibre/map.py -

    NameType Description Default
    def set_data(self, source_id: str, data: dict) -> None:
    -    """Update the data of a GeoJSON source
    -
    -    Args:
    -        source_id (str): The name of the source to be updated.
    -        data (dict): The data of the source.
    -    """
    -    self.add_call("setSourceData", source_id, data)
    +            
    def set_data(self, source_id: str, data: dict | GeoDataFrame) -> None:
    +    """Update the data of a GeoJSON source
    +
    +    Args:
    +        source_id (str): The name of the source to be updated.
    +        data (dict): The data of the source.
    +    """
    +    if isinstance(data, GeoDataFrame):
    +        data = SimpleFeatures(data).to_source().data
    +
    +    self.add_call("setSourceData", source_id, data)
     
    @@ -3201,7 +3490,7 @@

    layers - list[dict] + list[dict | 'pydeck.Layer']
    @@ -3232,23 +3521,29 @@

    Source code in maplibre/map.py -
    def set_deck_layers(self, layers: list[dict], tooltip: str | dict = None) -> None:
    -    """Update Deck.GL layers
    -
    -    Args:
    -        layers (list[dict]): A list of dictionaries containing the Deck.GL layers to be updated.
    -            New layers will be added. Missing layers will be removed.
    -        tooltip (str | dict): Must be set to keep tooltip even if it did not change.
    -    """
    -    self.add_call("setDeckLayers", layers, tooltip)
    +            
    def set_deck_layers(
    +    self, layers: list[dict | "pydeck.Layer"], tooltip: str | dict = None
    +) -> None:
    +    """Update Deck.GL layers
    +
    +    Args:
    +        layers (list[dict | "pydeck.Layer"]): A list of dictionaries containing the Deck.GL layers to be updated.
    +            New layers will be added. Missing layers will be removed.
    +        tooltip (str | dict): Must be set to keep tooltip even if it did not change.
    +    """
    +    layers = parse_deck_layers(layers)
    +    self.add_call("setDeckLayers", layers, tooltip)
     
    @@ -3316,21 +3611,21 @@

    Source code in maplibre/map.py -
    def set_filter(self, layer_id: str, filter_: list):
    -    """Update the filter of a layer
    -
    -    Args:
    -        layer_id (str): The name of the layer to be updated.
    -        filter_ (list): The filter expression that is applied to the source of the layer.
    -    """
    -    self.add_call("setFilter", layer_id, filter_)
    +            
    def set_filter(self, layer_id: str, filter_: list):
    +    """Update the filter of a layer
    +
    +    Args:
    +        layer_id (str): The name of the layer to be updated.
    +        filter_ (list): The filter expression that is applied to the source of the layer.
    +    """
    +    self.add_call("setFilter", layer_id, filter_)
     
    @@ -3412,23 +3707,23 @@

    Source code in maplibre/map.py -
    def set_layout_property(self, layer_id: str, prop: str, value: any) -> None:
    -    """Update a layout property of a layer
    -
    -    Args:
    -        layer_id (str): The name of the layer to be updated.
    -        prop (str): The name of the layout property to be updated.
    -        value (any): The new value of the layout property.
    -    """
    -    self.add_call("setLayoutProperty", layer_id, prop, value)
    +            
    def set_layout_property(self, layer_id: str, prop: str, value: any) -> None:
    +    """Update a layout property of a layer
    +
    +    Args:
    +        layer_id (str): The name of the layer to be updated.
    +        prop (str): The name of the layout property to be updated.
    +        value (any): The new value of the layout property.
    +    """
    +    self.add_call("setLayoutProperty", layer_id, prop, value)
     
    @@ -3510,23 +3805,23 @@

    Source code in maplibre/map.py -
    def set_paint_property(self, layer_id: str, prop: str, value: any) -> None:
    -    """Update the paint property of a layer
    -
    -    Args:
    -        layer_id (str): The name of the layer to be updated.
    -        prop (str): The name of the paint property to be updated.
    -        value (any): The new value of the paint property.
    -    """
    -    self.add_call("setPaintProperty", layer_id, prop, value)
    +            
    def set_paint_property(self, layer_id: str, prop: str, value: any) -> None:
    +    """Update the paint property of a layer
    +
    +    Args:
    +        layer_id (str): The name of the layer to be updated.
    +        prop (str): The name of the paint property to be updated.
    +        value (any): The new value of the paint property.
    +    """
    +    self.add_call("setPaintProperty", layer_id, prop, value)
     
    @@ -3594,23 +3889,23 @@

    Source code in maplibre/map.py -
    def set_visibility(self, layer_id: str, visible: bool = True) -> None:
    -    """Update the visibility of a layer
    -
    -    Args:
    -        layer_id (str): The name of the layer to be updated.
    -        visible (bool): Whether the layer is visible or not.
    -    """
    -    value = "visible" if visible else "none"
    -    self.add_call("setLayoutProperty", layer_id, "visibility", value)
    +            
    def set_visibility(self, layer_id: str, visible: bool = True) -> None:
    +    """Update the visibility of a layer
    +
    +    Args:
    +        layer_id (str): The name of the layer to be updated.
    +        visible (bool): Whether the layer is visible or not.
    +    """
    +    value = "visible" if visible else "none"
    +    self.add_call("setLayoutProperty", layer_id, "visibility", value)
     
    @@ -3681,127 +3976,128 @@

    Examples:

    >>> from maplibre import Map
    -
    -
    >>> map = Map()
    ->>> with open("/tmp/map.html", "w") as f:
    -...     f.write(map.to_html(style="height: 800px;")
    +>>> m = Map()
    +>>> with open("/tmp/map.html", "w") as f:
    +...     f.write(m.to_html(style="height: 800px;")
     
    Source code in maplibre/map.py -
    def to_html(self, title: str = "My Awesome Map", **kwargs) -> str:
    -    """Render to html
    -
    -    Args:
    -        title (str): The Title of the HTML document.
    -        **kwargs (Any): Additional keyword arguments that are passed to the template.
    -            Currently, `style` is the only supported keyword argument.
    -
    -    Examples:
    -        >>> from maplibre import Map
    -
    -        >>> map = Map()
    -        >>> with open("/tmp/map.html", "w") as f:
    -        ...     f.write(map.to_html(style="height: 800px;") # doctest: +SKIP
    -    """
    -    js_lib = read_internal_file("srcjs", "pywidget.js")
    -    js_snippet = Template(js_template).render(data=json.dumps(self.to_dict()))
    -    css = read_internal_file("srcjs", "pywidget.css")
    -    headers = [f"<style>{css}</style>"]
    -
    -    # Deck.GL headers
    -    add_deckgl_headers = "addDeckOverlay" in [
    -        item[0] for item in self._message_queue
    -    ]
    -    # TODO: Set version in constants
    -    deckgl_headers = (
    -        [
    -            '<script src="https://unpkg.com/deck.gl@9.0.16/dist.min.js"></script>',
    -            '<script src="https://unpkg.com/@deck.gl/json@9.0.16/dist.min.js"></script>',
    -        ]
    -        if add_deckgl_headers
    -        else []
    -    )
    -
    -    # Mapbox Draw headers
    -    add_mapbox_draw_headers = "addMapboxDraw" in [
    -        item[0] for item in self._message_queue
    -    ]
    -    # TODO: Set version in constants
    -    mapbox_draw_headers = (
    -        [
    -            # "<script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.3/mapbox-gl-draw.js'></script>",
    -            # "<link rel='stylesheet' href='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.3/mapbox-gl-draw.css' type='text/css' />",
    -            "<script src='https://www.unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.js'></script>",
    -            "<link rel='stylesheet' href='https://www.unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.css' type='text/css' />",
    -        ]
    -        if add_mapbox_draw_headers
    -        else []
    -    )
    -
    -    output = Template(html_template).render(
    -        js="\n".join([js_lib, js_snippet]),
    -        title=title,
    -        headers=headers + deckgl_headers + mapbox_draw_headers,
    -        **kwargs,
    -    )
    -    return output
    +            
    def to_html(self, title: str = "My Awesome Map", **kwargs) -> str:
    +    """Render to html
    +
    +    Args:
    +        title (str): The Title of the HTML document.
    +        **kwargs (Any): Additional keyword arguments that are passed to the template.
    +            Currently, `style` is the only supported keyword argument.
    +
    +    Examples:
    +        >>> from maplibre import Map
    +        >>> m = Map()
    +        >>> with open("/tmp/map.html", "w") as f:
    +        ...     f.write(m.to_html(style="height: 800px;") # doctest: +SKIP
    +    """
    +    js_lib = read_internal_file("srcjs", "pywidget.js")
    +    js_snippet = Template(js_template).render(data=json.dumps(self.to_dict()))
    +    css = read_internal_file("srcjs", "pywidget.css")
    +    headers = [f"<style>{css}</style>"]
    +
    +    # Deck.GL headers
    +    add_deckgl_headers = "addDeckOverlay" in [
    +        item[0] for item in self._message_queue
    +    ]
    +    # TODO: Set version in constants
    +    deckgl_headers = (
    +        [
    +            # '<script src="https://unpkg.com/h3-js"></script>',
    +            '<script src="https://unpkg.com/h3-js@4.1.0/dist/h3-js.umd.js"></script>',
    +            '<script src="https://unpkg.com/deck.gl@9.0.16/dist.min.js"></script>',
    +            '<script src="https://unpkg.com/@deck.gl/json@9.0.16/dist.min.js"></script>',
    +        ]
    +        if add_deckgl_headers
    +        else []
    +    )
    +
    +    # Mapbox Draw headers
    +    add_mapbox_draw_headers = "addMapboxDraw" in [
    +        item[0] for item in self._message_queue
    +    ]
    +    # TODO: Set version in constants
    +    mapbox_draw_headers = (
    +        [
    +            # "<script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.3/mapbox-gl-draw.js'></script>",
    +            # "<link rel='stylesheet' href='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.3/mapbox-gl-draw.css' type='text/css' />",
    +            "<script src='https://www.unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.js'></script>",
    +            "<link rel='stylesheet' href='https://www.unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.css' type='text/css' />",
    +        ]
    +        if add_mapbox_draw_headers
    +        else []
    +    )
    +
    +    output = Template(html_template).render(
    +        js="\n".join([js_lib, js_snippet]),
    +        title=title,
    +        headers=headers + deckgl_headers + mapbox_draw_headers,
    +        **kwargs,
    +    )
    +    return output
     
    @@ -3830,7 +4126,7 @@

    - Bases: BaseModel

    + Bases: MapLibreBaseModel

    Map options

    @@ -3841,27 +4137,7 @@

    Source code in maplibre/map.py -
    19
    -20
    -21
    -22
    -23
    -24
    -25
    -26
    -27
    -28
    -29
    -30
    -31
    -32
    -33
    -34
    -35
    -36
    -37
    -38
    -39
    +              
    39
     40
     41
     42
    @@ -3881,47 +4157,77 @@ 

    56 57 58 -59

    class MapOptions(BaseModel):
    -    """Map options
    -
    -    Note:
    -        See [MapOptions](https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/MapOptions) for more details.
    -    """
    -
    -    model_config = ConfigDict(
    -        validate_assignment=True, extra="forbid", use_enum_values=False
    -    )
    -    antialias: bool = None
    -    attribution_control: bool = Field(None, serialization_alias="attributionControl")
    -    bearing: Union[int, float] = None
    -    bearing_snap: int = Field(None, serialization_alias="bearingSnap")
    -    bounds: tuple = None
    -    box_zoom: bool = Field(None, serialization_alias="boxZoom")
    -    center: tuple = None
    -    click_tolerance: int = Field(None, serialization_alias="clickTolerance")
    -    custom_attribution: bool = Field(None, serialization_alias="customAttribution")
    -    double_click_zoom: bool = Field(None, serialization_alias="doubleClickZoom")
    -    fade_duration: int = Field(None, serialization_alias="fadeDuration")
    -    fit_bounds_options: dict = Field(None, serialization_alias="fitBoundsOptions")
    -    hash: Union[bool, str] = None
    -    interactive: bool = None
    -    keyword: bool = None
    -    max_bounds: tuple = Field(None, serialization_alias="maxBounds")
    -    max_pitch: int = Field(None, serialization_alias="maxPitch")
    -    max_zoom: int = Field(None, serialization_alias="maxZoom")
    -    min_pitch: int = Field(None, serialization_alias="minPitch")
    -    min_zoom: int = Field(None, serialization_alias="minZoom")
    -    pitch: Union[int, float] = None
    -    scroll_zoom: bool = Field(None, serialization_alias="scrollZoom")
    -    style: Union[str, Carto, dict] = construct_carto_basemap_url(Carto.DARK_MATTER)
    -    zoom: Union[int, float] = None
    -
    -    @field_validator("style")
    -    def validate_style(cls, v):
    -        if isinstance(v, Carto):
    -            return construct_carto_basemap_url(v)
    -
    -        return v
    +59
    +60
    +61
    +62
    +63
    +64
    +65
    +66
    +67
    +68
    +69
    +70
    +71
    +72
    +73
    +74
    +75
    +76
    +77
    +78
    +79
    +80
    +81
    +82
    +83
    +84
    class MapOptions(MapLibreBaseModel):
    +    """Map options
    +
    +    Note:
    +        See [MapOptions](https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/MapOptions) for more details.
    +    """
    +
    +    model_config = ConfigDict(
    +        validate_assignment=True, extra="forbid", use_enum_values=False
    +    )
    +    antialias: bool = None
    +    attribution_control: bool = Field(None, serialization_alias="attributionControl")
    +    bearing: Union[int, float] = None
    +    bearing_snap: int = Field(None, serialization_alias="bearingSnap")
    +    bounds: tuple = None
    +    box_zoom: bool = Field(None, serialization_alias="boxZoom")
    +    center: tuple = None
    +    click_tolerance: int = Field(None, serialization_alias="clickTolerance")
    +    custom_attribution: bool = Field(None, serialization_alias="customAttribution")
    +    double_click_zoom: bool = Field(None, serialization_alias="doubleClickZoom")
    +    fade_duration: int = Field(None, serialization_alias="fadeDuration")
    +    fit_bounds_options: dict = Field(None, serialization_alias="fitBoundsOptions")
    +    hash: Union[bool, str] = None
    +    interactive: bool = None
    +    keyword: bool = None
    +    max_bounds: tuple = Field(None, serialization_alias="maxBounds")
    +    max_pitch: int = Field(None, serialization_alias="maxPitch")
    +    max_zoom: int = Field(None, serialization_alias="maxZoom")
    +    min_pitch: int = Field(None, serialization_alias="minPitch")
    +    min_zoom: int = Field(None, serialization_alias="minZoom")
    +    pitch: Union[int, float] = None
    +    scroll_zoom: bool = Field(None, serialization_alias="scrollZoom")
    +    style: Union[str, Carto, MapTiler, dict] = construct_carto_basemap_url(
    +        Carto.DARK_MATTER
    +    )
    +    zoom: Union[int, float] = None
    +
    +    @field_validator("style")
    +    def validate_style(cls, v):
    +        if isinstance(v, Carto):
    +            return construct_carto_basemap_url(v)
    +
    +        if isinstance(v, MapTiler):
    +            return construct_maptiler_basemap_url(v)
    +
    +        return v
     
    @@ -4128,7 +4434,11 @@

    Source code in maplibre/ipywidget.py -
    16
    +              
    12
    +13
    +14
    +15
    +16
     17
     18
     19
    @@ -4194,73 +4504,91 @@ 

    79 80 81 -82

    class MapWidget(AnyWidget, Map):
    -    """MapWidget
    +82
    +83
    +84
    +85
    +86
    +87
    +88
    +89
    class MapWidget(AnyWidget, Map):
    +    """MapWidget
    +
    +    Use this class to display and update maps in Jupyter Notebooks.
    +
    +    See `maplibre.Map` for available methods.
     
    -    Use this class to display and update maps in Jupyter Notebooks.
    -
    -    See `maplibre.Map` for available methods.
    -
    -    Examples:
    -        >>> from maplibre import MapOptions
    -        >>> from maplibre.ipywidget import MapWidget as Map
    -        >>> m = Map(MapOptions(center=(-123.13, 49.254), zoom=11, pitch=45))
    -        >>> m # doctest: +SKIP
    -    """
    -
    -    _esm = join(Path(__file__).parent, "srcjs", "ipywidget.js")
    -    # _css = join(Path(__file__).parent, "srcjs", "maplibre-gl.css")
    -    _css = join(Path(__file__).parent, "srcjs", "ipywidget.css")
    -    _use_message_queue = True
    -    _rendered = traitlets.Bool(False, config=True).tag(sync=True)
    -    map_options = traitlets.Dict().tag(sync=True)
    -    calls = traitlets.List().tag(sync=True)
    -    height = traitlets.Union([traitlets.Int(), traitlets.Unicode()]).tag(sync=True)
    -
    -    # Interactions Map
    -    clicked = traitlets.Dict().tag(sync=True)
    -    view_state = traitlets.Dict().tag(sync=True)
    -
    -    # Interactions MapboxDraw plugin
    -    draw_features_selected = traitlets.List().tag(sync=True)
    -    draw_feature_collection_all = traitlets.Dict().tag(sync=True)
    -
    -    def __init__(self, map_options=MapOptions(), **kwargs) -> None:
    -        self.calls = []
    -        AnyWidget.__init__(self, **kwargs)
    -        Map.__init__(self, map_options, **kwargs)
    -
    -    @traitlets.default("height")
    -    def _default_height(self):
    -        return "400px"
    -
    -    @traitlets.validate("height")
    -    def _validate_height(self, proposal):
    -        height = proposal["value"]
    -        if isinstance(height, int):
    -            return f"{height}px"
    -
    -        return height
    -
    -    @traitlets.observe("_rendered")
    -    def _on_rendered(self, change):
    -        self.send({"calls": self._message_queue, "msg": "init"})
    -        self._message_queue = []
    +    Examples:
    +        >>> from maplibre import MapOptions
    +        >>> from maplibre.ipywidget import MapWidget as Map
    +        >>> m = Map(MapOptions(center=(-123.13, 49.254), zoom=11, pitch=45))
    +        >>> m # doctest: +SKIP
    +    """
    +
    +    _esm = join(Path(__file__).parent, "srcjs", "ipywidget.js")
    +    # _css = join(Path(__file__).parent, "srcjs", "maplibre-gl.css")
    +    _css = join(Path(__file__).parent, "srcjs", "ipywidget.css")
    +    _use_message_queue = True
    +    # _rendered = traitlets.Bool(False, config=True).tag(sync=True)
    +    _rendered = traitlets.Bool(False).tag(config=True).tag(sync=True)
    +    map_options = traitlets.Dict().tag(sync=True)
    +    calls = traitlets.List().tag(sync=True)
    +    height = traitlets.Union([traitlets.Int(), traitlets.Unicode()]).tag(sync=True)
    +
    +    # Interactions Map
    +    clicked = traitlets.Dict().tag(sync=True)
    +    view_state = traitlets.Dict().tag(sync=True)
    +
    +    # Interactions MapboxDraw plugin
    +    draw_features_selected = traitlets.List().tag(sync=True)
    +    draw_feature_collection_all = traitlets.Dict().tag(sync=True)
    +
    +    def __init__(
    +        self,
    +        map_options=MapOptions(),
    +        sources: dict = None,
    +        layers: list = None,
    +        controls: list = None,
    +        height: int | str = 400,
    +        **kwargs,
    +    ) -> None:
    +        self.calls = []
    +        AnyWidget.__init__(self, height=height, **kwargs)
    +        Map.__init__(self, map_options, sources, layers, controls, **kwargs)
    +
    +    """
    +    @traitlets.default("height")
    +    def _default_height(self):
    +        return "400px"
    +    """
    +
    +    @traitlets.validate("height")
    +    def _validate_height(self, proposal):
    +        height = proposal["value"]
    +        if isinstance(height, int):
    +            return f"{height}px"
     
    -    def use_message_queue(self, value: bool = True) -> None:
    -        self._use_message_queue = value
    -
    -    def add_call(self, method_name: str, *args) -> None:
    -        call = [method_name, args]
    -        if not self._rendered:
    -            if not self._use_message_queue:
    -                self.calls = self.calls + [call]
    -                return
    +        return height
    +
    +    @traitlets.observe("_rendered")
    +    def _on_rendered(self, change):
    +        self.send({"calls": self._message_queue, "msg": "init"})
    +        self._message_queue = []
    +
    +    def use_message_queue(self, value: bool = True) -> None:
    +        self._use_message_queue = value
     
    -            self._message_queue.append(call)
    -            return
    -
    -        self.send({"calls": [call], "msg": "custom call"})
    +    def add_call(self, method_name: str, *args) -> None:
    +        call = [method_name, args]
    +        if not self._rendered:
    +            if not self._use_message_queue:
    +                self.calls = self.calls + [call]
    +                return
    +
    +            self._message_queue.append(call)
    +            return
    +
    +        self.send({"calls": [call], "msg": "custom call"})
     
    @@ -4321,7 +4649,7 @@

    - Bases: BaseModel

    + Bases: MapLibreBaseModel

    MapboxDraw controls

    @@ -4336,7 +4664,7 @@

    13 14 15 -16

    class MapboxDrawControls(BaseModel):
    +16
    class MapboxDrawControls(MapLibreBaseModel):
         """MapboxDraw controls"""
     
         point: bool = False
    @@ -4382,7 +4710,7 @@ 

    - Bases: BaseModel

    + Bases: MapLibreBaseModel

    MapboxDraw Options

    @@ -4396,7 +4724,7 @@

    23 24 25 -26

    class MapboxDrawOptions(BaseModel):
    +26
    class MapboxDrawOptions(MapLibreBaseModel):
         """MapboxDraw Options"""
     
         display_controls_default: bool = Field(
    diff --git a/api/sources/index.html b/api/sources/index.html
    index 39bc857..6a2ae39 100644
    --- a/api/sources/index.html
    +++ b/api/sources/index.html
    @@ -1274,10 +1274,7 @@ 

    Source code in maplibre/sources.py -
    32
    -33
    -34
    -35
    +              
    35
     36
     37
     38
    @@ -1302,35 +1299,48 @@ 

    57 58 59 -60

    class GeoJSONSource(Source):
    -    """GeoJSON Source
    -
    -    Examples:
    -        >>> from maplibre.sources import GeoJSONSource
    +60
    +61
    +62
    +63
    +64
    +65
    +66
    +67
    +68
    class GeoJSONSource(Source):
    +    """GeoJSON Source
     
    -        >>> geojson = "https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson"
    -        >>> source = GeoJSONSource(data=geojson)
    -    """
    -
    -    data: Union[str, dict]
    -    attribution: str = None
    -    buffer: int = None
    -    cluster: bool = None
    -    cluster_max_zoom: int = Field(None, serialization_alias="clusterMaxZoom")
    -    cluster_min_points: int = Field(None, serialization_alias="clusterMinPoints")
    -    cluster_properties: dict = Field(None, serialization_alias="clusterProperties")
    -    cluster_radius: int = Field(None, serialization_alias="clusterRadius")
    -    filter: list = None
    -    generate_id: bool = Field(None, serialization_alias="generateId")
    -    line_metrics: bool = Field(None, serialization_alias="lineMetrics")
    -    maxzoom: int = None
    -    promote_id: Union[str, dict] = Field(None, serialization_alias="promoteId")
    -    tolerance: float = None
    -
    -    @computed_field
    -    @property
    -    def type(self) -> str:
    -        return SourceType.GEOJSON.value
    +    Examples:
    +        >>> from maplibre.sources import GeoJSONSource
    +
    +        >>> geojson = "https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson"
    +        >>> source = GeoJSONSource(data=geojson)
    +    """
    +
    +    data: Union[str, dict]
    +    attribution: Optional[str] = None
    +    buffer: Optional[int] = None
    +    cluster: Optional[bool] = None
    +    cluster_max_zoom: Optional[int] = Field(None, serialization_alias="clusterMaxZoom")
    +    cluster_min_points: Optional[int] = Field(
    +        None, serialization_alias="clusterMinPoints"
    +    )
    +    cluster_properties: Optional[dict] = Field(
    +        None, serialization_alias="clusterProperties"
    +    )
    +    cluster_radius: Optional[int] = Field(None, serialization_alias="clusterRadius")
    +    filter: Optional[list] = None
    +    generate_id: Optional[bool] = Field(None, serialization_alias="generateId")
    +    line_metrics: Optional[bool] = Field(None, serialization_alias="lineMetrics")
    +    min_zoom: Optional[int] = Field(None, serialization_alias="minzoom")
    +    max_zoom: Optional[int] = Field(None, serialization_alias="maxzoom")
    +    promote_id: Union[str, dict, None] = Field(None, serialization_alias="promoteId")
    +    tolerance: Optional[float] = None
    +
    +    @computed_field
    +    @property
    +    def type(self) -> str:
    +        return SourceType.GEOJSON.value
     
    @@ -1388,15 +1398,7 @@

    Source code in maplibre/sources.py -
    63
    -64
    -65
    -66
    -67
    -68
    -69
    -70
    -71
    +              
    71
     72
     73
     74
    @@ -1415,34 +1417,42 @@ 

    87 88 89 -90

    class RasterTileSource(Source):
    -    """Raster tile source
    -
    -    Examples:
    -        >>> from maplibre.sources import RasterTileSource
    -
    -        >>> raster_tile_source = RasterTileSource(
    -        ...     tiles=["https://tile.openstreetmap.org/{z}/{x}/{y}.png"],
    -        ...     tile_size=256,
    -        ...     min_zoom=0,
    -        ...     max_zoom=19,
    -        ... )
    -    """
    +90
    +91
    +92
    +93
    +94
    +95
    +96
    +97
    +98
    class RasterTileSource(Source):
    +    """Raster tile source
    +
    +    Examples:
    +        >>> from maplibre.sources import RasterTileSource
     
    -    attribution: str = None
    -    bounds: tuple = None
    -    max_zoom: int = Field(None, serialization_alias="maxzoom")
    -    min_zoom: int = Field(None, serialization_alias="minzoom")
    -    scheme: str = None
    -    tile_size: int = Field(None, serialization_alias="tileSize")
    -    tiles: Union[tuple, list] = None
    -    url: str = None
    -    volatile: bool = None
    -
    -    @computed_field
    -    @property
    -    def type(self) -> str:
    -        return SourceType.RASTER.value
    +        >>> raster_tile_source = RasterTileSource(
    +        ...     tiles=["https://tile.openstreetmap.org/{z}/{x}/{y}.png"],
    +        ...     tile_size=256,
    +        ...     min_zoom=0,
    +        ...     max_zoom=19,
    +        ... )
    +    """
    +
    +    attribution: str = None
    +    bounds: tuple = None
    +    max_zoom: int = Field(None, serialization_alias="maxzoom")
    +    min_zoom: int = Field(None, serialization_alias="minzoom")
    +    scheme: str = None
    +    tile_size: int = Field(None, serialization_alias="tileSize")
    +    tiles: Union[tuple, list] = None
    +    url: str = None
    +    volatile: bool = None
    +
    +    @computed_field
    +    @property
    +    def type(self) -> str:
    +        return SourceType.RASTER.value
     
    @@ -1487,23 +1497,23 @@

    Source code in maplibre/sources.py -
    11
    -12
    -13
    -14
    -15
    -16
    -17
    -18
    -19
    class SourceType(Enum):
    -    """Source types"""
    -
    -    RASTER = "raster"
    -    VECTOR = "vector"
    -    RASTER_DEM = "raster-dem"
    -    GEOJSON = "geojson"
    -    IMAGE = "image"
    -    VIDEO = "video"
    +              
    20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    class SourceType(Enum):
    +    """Source types"""
    +
    +    RASTER = "raster"
    +    VECTOR = "vector"
    +    RASTER_DEM = "raster-dem"
    +    GEOJSON = "geojson"
    +    IMAGE = "image"
    +    VIDEO = "video"
     
    @@ -1569,15 +1579,7 @@

    Source code in maplibre/sources.py -
     93
    - 94
    - 95
    - 96
    - 97
    - 98
    - 99
    -100
    -101
    +              
    101
     102
     103
     104
    @@ -1603,41 +1605,49 @@ 

    124 125 126 -127

    class VectorTileSource(Source):
    -    """Vector tile source
    -
    -    Examples:
    -        >>> from maplibre.sources import VectorTileSource
    -        >>> from maplibre import LayerType, Layer
    -
    -        >>> vector_tile_source = VectorTileSource(
    -        ...     tiles=["https://demotiles.maplibre.org/tiles/{z}/{x}/{y}.pbf"],
    -        ...     min_zoom=0,
    -        ...     max_zoom=6,
    -        ... )
    -
    -        >>> layer = Layer(
    -        ...     type=LayerType.LINE,
    -        ...     id="countries",
    -        ...     source=vector_tile_source,
    -        ...     source_layer="countries",
    -        ...     paint={"fill-color": "lightgreen", "fill-outline-color": "black"},
    +127
    +128
    +129
    +130
    +131
    +132
    +133
    +134
    +135
    class VectorTileSource(Source):
    +    """Vector tile source
    +
    +    Examples:
    +        >>> from maplibre.sources import VectorTileSource
    +        >>> from maplibre import LayerType, Layer
    +
    +        >>> vector_tile_source = VectorTileSource(
    +        ...     tiles=["https://demotiles.maplibre.org/tiles/{z}/{x}/{y}.pbf"],
    +        ...     min_zoom=0,
    +        ...     max_zoom=6,
             ... )
    -    """
    -
    -    attribution: str = None
    -    bounds: tuple = None
    -    max_zoom: int = Field(None, serialization_alias="maxzoom")
    -    min_zoom: int = Field(None, serialization_alias="minzoom")
    -    scheme: str = None
    -    tiles: Union[tuple, list] = None
    -    url: str = None
    -    volatile: bool = None
    -
    -    @computed_field
    -    @property
    -    def type(self) -> str:
    -        return SourceType.VECTOR.value
    +
    +        >>> layer = Layer(
    +        ...     type=LayerType.LINE,
    +        ...     id="countries",
    +        ...     source=vector_tile_source,
    +        ...     source_layer="countries",
    +        ...     paint={"fill-color": "lightgreen", "fill-outline-color": "black"},
    +        ... )
    +    """
    +
    +    attribution: str = None
    +    bounds: tuple = None
    +    max_zoom: int = Field(None, serialization_alias="maxzoom")
    +    min_zoom: int = Field(None, serialization_alias="minzoom")
    +    scheme: str = None
    +    tiles: Union[tuple, list] = None
    +    url: str = None
    +    volatile: bool = None
    +
    +    @computed_field
    +    @property
    +    def type(self) -> str:
    +        return SourceType.VECTOR.value
     
    diff --git a/changelog/index.html b/changelog/index.html index a28acac..3aab7c6 100644 --- a/changelog/index.html +++ b/changelog/index.html @@ -513,9 +513,9 @@
    • - + - maplibre v0.2.7 (unreleased) + maplibre v0.2.7 @@ -1233,9 +1233,9 @@
      • - + - maplibre v0.2.7 (unreleased) + maplibre v0.2.7 @@ -1377,9 +1377,60 @@

        Changelog for MapLibre for Python

        -

        maplibre v0.2.7 (unreleased)

        +

        maplibre v0.2.7

        • +

          Add basemaps.MapTiler

          +
        • +
        • +

          Add maplibre.__future__

          +
        • +
        • +

          Add Map.fit_bounds

          +
        • +
        • +

          Add expression helpers maplibre.expressions:

          +
        • +
        • interpolate
        • +
        • step_expr
        • +
        • quantile_expr
        • +
        • match_expr
        • +
        • color_step_expr
        • +
        • color_quantile_expr
        • +
        • color_match_expr
        • +
        • filter_expr
        • +
        • +

          range_filter

          +
        • +
        • +

          Add support for pydeck.Layer for

          +
        • +
        • Map.add_deck_layers and
        • +
        • +

          Map.set_deck_layers

          +
        • +
        • +

          Add sources.SimpleFeatures for geopandas.GeoDataFrame sources

          +
        • +
        • +

          Support geopandas.GeoDataFrame as source in

          +
        • +
        • Layer and
        • +
        • +

          Map.add_source

          +
        • +
        • +

          Add more parameters to Map class for simpler map initialization:

          +
        • +
        • layers: list
        • +
        • sources: dict
        • +
        • +

          controls: list

          +
        • +
        • +

          Add position attribute to Control classes

          +
        • +
        • Add sources.VectorTileSource (Martenz)

        • diff --git a/examples/getting_started/basic_usage.py b/examples/getting_started/basic_usage.py index 7f76d7b..3ca28c1 100644 --- a/examples/getting_started/basic_usage.py +++ b/examples/getting_started/basic_usage.py @@ -1,5 +1,3 @@ -import webbrowser - from maplibre import Layer, LayerType, Map, MapOptions from maplibre.sources import GeoJSONSource @@ -23,9 +21,4 @@ ) ) -temp_file = "/tmp/pymaplibregl.html" - -with open(temp_file, "w") as f: - f.write(m.to_html(style="height: 800px;")) - -webbrowser.open(temp_file) +m.save(preview=True) diff --git a/index.html b/index.html index 576788b..62ae861 100644 --- a/index.html +++ b/index.html @@ -1238,37 +1238,30 @@

          Installation

    Basic usage

    Standalone

    -
    import webbrowser
    -
    -from maplibre import Layer, LayerType, Map, MapOptions
    -from maplibre.sources import GeoJSONSource
    -
    -vancouver_blocks = GeoJSONSource(
    -    data="https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/geojson/vancouver-blocks.json",
    -)
    -
    -map_options = MapOptions(
    -    center=(-123.1256, 49.24658),
    -    zoom=12,
    -    hash=True,
    -    pitch=35,
    -)
    -
    -m = Map(map_options)
    -m.add_layer(
    -    Layer(
    -        type=LayerType.LINE,
    -        source=vancouver_blocks,
    -        paint={"line-color": "white"},
    -    )
    -)
    -
    -temp_file = "/tmp/pymaplibregl.html"
    -
    -with open(temp_file, "w") as f:
    -    f.write(m.to_html(style="height: 800px;"))
    -
    -webbrowser.open(temp_file)
    +
    from maplibre import Layer, LayerType, Map, MapOptions
    +from maplibre.sources import GeoJSONSource
    +
    +vancouver_blocks = GeoJSONSource(
    +    data="https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/geojson/vancouver-blocks.json",
    +)
    +
    +map_options = MapOptions(
    +    center=(-123.1256, 49.24658),
    +    zoom=12,
    +    hash=True,
    +    pitch=35,
    +)
    +
    +m = Map(map_options)
    +m.add_layer(
    +    Layer(
    +        type=LayerType.LINE,
    +        source=vancouver_blocks,
    +        paint={"line-color": "white"},
    +    )
    +)
    +
    +m.save(preview=True)
     

    Shiny integration

    from maplibre import Map, MapContext, output_maplibregl, render_maplibregl
    diff --git a/jupyter/index.html b/jupyter/index.html
    index 544b099..a27679e 100644
    --- a/jupyter/index.html
    +++ b/jupyter/index.html
    @@ -1079,7 +1079,7 @@
     
       

    Jupyter

    -

    Use MapWidget in your Juyper Notebook:

    +

    Use MapWidget in your Jupyter Notebook:

    import ipywidgets as widgets
     
     from maplibre import Layer, LayerType
    diff --git a/objects.inv b/objects.inv
    index 7c482fc..301f7aa 100644
    Binary files a/objects.inv and b/objects.inv differ
    diff --git a/search/search_index.json b/search/search_index.json
    index 8788e59..b506212 100644
    --- a/search/search_index.json
    +++ b/search/search_index.json
    @@ -1 +1 @@
    -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"MapLibre for Python","text":"

    MapLibre for Python provides Python bindings for MapLibre GL JS.

    It integrates seamlessly into Shiny for Python and Jupyter.

    "},{"location":"#installation","title":"Installation","text":"
    # Stable\npip install maplibre\n\npip install \"maplibre[all]\"\n\n# Dev\npip install git+https://github.com/eoda-dev/py-maplibregl@dev\n
    "},{"location":"#basic-usage","title":"Basic usage","text":""},{"location":"#standalone","title":"Standalone","text":"
    import webbrowser\n\nfrom maplibre import Layer, LayerType, Map, MapOptions\nfrom maplibre.sources import GeoJSONSource\n\nvancouver_blocks = GeoJSONSource(\n    data=\"https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/geojson/vancouver-blocks.json\",\n)\n\nmap_options = MapOptions(\n    center=(-123.1256, 49.24658),\n    zoom=12,\n    hash=True,\n    pitch=35,\n)\n\nm = Map(map_options)\nm.add_layer(\n    Layer(\n        type=LayerType.LINE,\n        source=vancouver_blocks,\n        paint={\"line-color\": \"white\"},\n    )\n)\n\ntemp_file = \"/tmp/pymaplibregl.html\"\n\nwith open(temp_file, \"w\") as f:\n    f.write(m.to_html(style=\"height: 800px;\"))\n\nwebbrowser.open(temp_file)\n
    "},{"location":"#shiny-integration","title":"Shiny integration","text":"
    from maplibre import Map, MapContext, output_maplibregl, render_maplibregl\nfrom maplibre.controls import Marker\nfrom shiny import App, reactive, ui\n\napp_ui = ui.page_fluid(\n    output_maplibregl(\"maplibre_map\", height=600),\n    ui.div(\"Click on map to set a marker\"),\n)\n\n\ndef server(input, output, session):\n    @render_maplibregl\n    def maplibre_map():\n        return Map()\n\n    @reactive.Effect\n    @reactive.event(input.maplibre_map_clicked)\n    async def coords():\n        async with MapContext(\"maplibre_map\") as m:\n            input_value = input.maplibre_map_clicked()\n            print(input_value)\n            lng_lat = tuple(input_value[\"coords\"].values())\n            marker = Marker(lng_lat=lng_lat)\n            m.add_marker(marker)\n            m.add_call(\"flyTo\", {\"center\": lng_lat})\n\n\napp = App(app_ui, server)\n\nif __name__ == \"__main__\":\n    app.run()\n
    "},{"location":"#jupyter-widget","title":"Jupyter widget","text":"
    from maplibre.ipywidget import MapWidget as Map\n\nm = Map()\nm\n
    "},{"location":"changelog/","title":"Changelog for MapLibre for Python","text":""},{"location":"changelog/#maplibre-v027-unreleased","title":"maplibre v0.2.7 (unreleased)","text":"
    • Add sources.VectorTileSource (Martenz)

    • Shiny

    • Add input.{output_id}_view_state dict containing {\"center\", \"zoom\", \"bounds\", \"pitch\", \"bearing\"}
    • Rename input.{output_id} to input.{output_id}_clicked
    • Rename input.{output_id}_layer_{layer_id} to input.{output_id}_feature_clicked returning layer_id

    • Ipywidget

    • Add Map.view_state dict containing {\"center\", \"zoom\", \"bounds\", \"pitch\", \"bearing\"} (#89)
    • Remove Map.center, Map.zoom, Map.bounds
    "},{"location":"changelog/#maplibre-v026","title":"maplibre v0.2.6","text":"
    • Add function in maplibre.utils to save map and display it in the browser
    • Add streamlit.components.v1.iframe component
    "},{"location":"changelog/#maplibre-v025","title":"maplibre v0.2.5","text":"
    • Add custom LayerSwitcherControl (#69)
    • Add custom InfoBoxControl (#74)
    "},{"location":"changelog/#maplibre-v024","title":"maplibre v0.2.4","text":"
    • Add MapboxDraw plugin (#59)
    • Add Shiny input concerned to MapboxDraw:
    • <output_id>.draw_features_selected
    • Add interactive attributes to IpyWidget concerned to MapboxDraw:
    • Map.draw_features_selected (list)
    • Map.draw_feature_collection_all (dict)
    "},{"location":"changelog/#maplibre-v023","title":"maplibre v0.2.3","text":"
    • Add interactive attributes to IpyWidget
    • Map.center
    • Map.bounds
    • Map.zoom
    • Map.lat_lng > Map.clicked (rename)
    • Change map option types
    • MapOptions.zoom: int > Union[int, float]
    • MapOptions.bearing: int > Union[int, float]
    • MapOptions.pitch: int > Union[int, float]
    • Add conda badges and installation instructions
    "},{"location":"changelog/#maplibre-v022","title":"maplibre v0.2.2","text":"
    • Add support for PMTiles (#55)
    "},{"location":"changelog/#maplibre-v021","title":"maplibre v0.2.1","text":"
    • Do not add navigation control by default (#31)
    "},{"location":"changelog/#maplibre-v020","title":"maplibre v0.2.0","text":"
    • Support Deck.GL layers (#28)
    "},{"location":"changelog/#maplibre-v016","title":"maplibre v0.1.6","text":"
    • Add before_id parameter to add_layer method (#45, #47)
    • Add example showing how to insert a layer before labels
    "},{"location":"changelog/#maplibre-v015","title":"maplibre v0.1.5","text":"
    • Update deprecated render function to support shiny>=0.7.0
    "},{"location":"changelog/#maplibre-v014","title":"maplibre v0.1.4","text":"
    • anywidget>=0.9.0 (#36)
    "},{"location":"changelog/#maplibre-v013","title":"maplibre v0.1.3","text":"
    • Display all properties in popup and tooltip if prop = None (#26)
    • Support mustache templates for popups and tooltips (#27)
    "},{"location":"changelog/#maplibre-v012","title":"maplibre v0.1.2","text":"
    • Add Map.set_data
    • Add Map.set_visibility
    • Do not import ipywidget.MapWidget in __init__ and skip tests for MapWidget, because it causes a core dumped error, see anywidget issue
    • Remove requests dependency
    • Remove dead code
    • Add more examples
    "},{"location":"changelog/#maplibre-v011","title":"maplibre v0.1.1","text":"
    • Initial PyPI release
    "},{"location":"deckgl/","title":"Deck.GL Layers","text":"

    Deck.GL layers can be added to the map with Map.add_deck_layers.

    They are defined as a dictionary, where classes got the @@type prefix and getter props the @@= prefix. They are inserted into the layer stack of the maplibre context. Therefore, you can also pass a beforeId prop.

    Here is an example corresponding to the Deck.GL GridLayer API Example:

    grid_layer = {\n    \"@@type\": \"GridLayer\", # JS: new GridLayer\n    \"id\": \"my-awsome-grid-layer\",\n    \"data\": \"https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json\",\n    \"extruded\": True,\n    \"getPosition\": \"@@=COORDINATES\", # JS: d => d.COORDINATES\n    \"getColorWeight\": \"@@=SPACES\", # JS: d => d.SPACES\n    \"getElevationWeight\": \"@@=SPACES\", # JS: d => d.SPACES\n    \"elevationScale\": 4,\n    \"cellSize\": 200,\n    \"pickable\": True,\n    \"beforeId\": \"first-labels-layer\" # optional\n    }\n

    The Code above will generate the following JavaScript code:

    const gridLayer = new GridLayer({\n  id: 'GridLayer',\n  data: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json',\n  extruded: true,\n  getPosition: d => d.COORDINATES,\n  getColorWeight: d => d.SPACES,\n  getElevationWeight: d => d.SPACES,\n  elevationScale: 4,\n  cellSize: 200,\n  pickable: true\n});\n

    See also:

    • Deck.GL Layer
    • Deck.GL Multiple Layers
    "},{"location":"jupyter/","title":"Jupyter","text":"

    Use MapWidget in your Juyper Notebook:

    import ipywidgets as widgets\n\nfrom maplibre import Layer, LayerType\nfrom maplibre.sources import GeoJSONSource\nfrom maplibre.controls import ScaleControl, Marker\nfrom maplibre.ipywidget import MapWidget as Map\n\n# Create a source\nearthquakes = GeoJSONSource(\n    data=\"https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson\"\n)\n\n# Create a layer\nlayer_id = \"earthquakes\"\n\nearthquake_circles = Layer(\n    type=LayerType.CIRCLE,\n    id=layer_id,\n    source=earthquakes,\n    paint={\"circle-color\": \"yellow\"}\n)\n\n# Render map\nm = Map()\nm.add_control(ScaleControl(), position=\"bottom-left\")\nm.add_layer(earthquake_circles)\nm.add_tooltip(layer_id, \"mag\")\nm.add_marker(Marker(lng_lat=(100.507, 13.745)))\nm\n\n# Change radius\n_ = widgets.interact(\n    lambda radius: m.set_paint_property(layer_id, \"circle-radius\", radius),\n    radius=(1, 8, 1)\n)\n\n# Change color\n_ = widgets.interact(\n    lambda color: m.set_paint_property(layer_id, \"circle-color\", color),\n    color=[\"green\", \"yellow\", \"orange\", \"red\"]\n)\n\n# Set filter on magnitude\n_ = widgets.interact(\n    lambda mag_min: m.set_filter(layer_id, [\">=\", [\"get\", \"mag\"], mag_min]),\n    mag_min=(1, 8, 1)\n)\n
    "},{"location":"layers/","title":"Layers","text":"

    The paint and layout properties for the layers depend on the type of the layer. They are passed as a dict corresponding to the Layer Style Spec.

    For example, to set the radius and the color for a circle layer, the paint property looks like this:

    paint = {\n    \"circle-radius\": 5,\n    \"circle-color\": \"yellow\"\n}\n

    The value for any layout property, paint property, or filter may also be specified as an expression. For details see Expressions.

    For example, if the source of your layer has a color property that contains the color of the feature, you can use the following expression:

    paint = {\n    \"circle-radius\": 5,\n    \"circle-color\": [\"get\", \"color\"]\n}\n

    A more complex expression where the color depends on the type property of the layer's source might look like this:

    paint={\n        \"circle-color\": [\n            \"match\",\n            [\"get\", \"type\"],\n            # darkred if type == \"mid\"\n            \"mid\",\n            \"darkred\",\n            # darkgreen if type == \"major\"\n            \"major\",\n            \"darkgreen\",\n            # else blue\n            \"darkblue\",\n        ]\n}\n

    Filter features of a source according to its magnitude property:

    # Only show features where magnitude >= 5\nfilter = [\">=\", [\"get\", \"magnitude\"], 5]\n
    "},{"location":"shiny/","title":"Shiny","text":""},{"location":"shiny/#input-and-output","title":"Input and output","text":"

    Use output_maplibregl in the UI and render_maplibregl in the server section of your Shiny for Python app:

    from shiny import App, ui\nfrom maplibre import output_maplibregl, render_maplibregl, Map\n\napp_ui = ui.page_fluid(\n    ui.panel_title(\"MapLibre\"),\n    output_maplibregl(\"maplibre\", height=600)\n)\n\n\ndef server(input, output, session):\n    @render_maplibregl\n    def maplibre():\n        m = Map()\n        return m\n\n\napp = App(app_ui, server)\n
    "},{"location":"shiny/#reactivity","title":"Reactivity","text":""},{"location":"shiny/#input-events","title":"Input events","text":"

    MapLibre for Python provides the following reactive inputs:

    • input.{output_id}_clicked: Sends coordinates of the clicked location on the map.
    • input.{output_id}_feature_clicked: Sends the properties of the clicked feature and its layer id.
    • input.{output_id}_view_state: Sends the current view state. Fired when the view state is changed.
    "},{"location":"shiny/#map-updates","title":"Map updates","text":"

    Use MapContext to update your Map object.

    "},{"location":"shiny/#example","title":"Example","text":"
    import json\n\nfrom maplibre import (\n    Layer,\n    LayerType,\n    Map,\n    MapContext,\n    output_maplibregl,\n    render_maplibregl,\n)\nfrom maplibre.controls import NavigationControl\nfrom maplibre.sources import GeoJSONSource\nfrom shiny import App, reactive, render, ui\n\nLAYER_ID = \"earthquakes\"\nCIRCLE_RADIUS = 5\n\napp_ui = ui.page_fluid(\n    ui.panel_title(\"MapLibre for Python\"),\n    output_maplibregl(\"mapgl\", height=600),\n    ui.div(\"Click on the map to print the coords.\", style=\"padding: 10px;\"),\n    ui.output_text_verbatim(\"coords\", placeholder=True),\n    ui.div(\"Click on a feature to print its props.\", style=\"padding: 10px;\"),\n    ui.output_text_verbatim(\"props\", placeholder=True),\n    ui.div(\"Move map or zoom to update view state.\", style=\"padding: 10px;\"),\n    ui.output_text_verbatim(\"view_state\", placeholder=True),\n    ui.input_slider(\"radius\", \"Radius\", value=CIRCLE_RADIUS, min=1, max=10),\n)\n\ncircle_layer = Layer(\n    type=LayerType.CIRCLE,\n    id=LAYER_ID,\n    source=GeoJSONSource(\n        data=\"https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson\"\n    ),\n    paint={\"circle-color\": \"yellow\"},\n)\n\n\ndef server(input, output, session):\n    @render_maplibregl\n    def mapgl():\n        m = Map(zoom=3, pitch=40)\n        m.add_control(NavigationControl())\n        m.add_layer(circle_layer)\n        return m\n\n    @render.text\n    def coords():\n        return str(input.mapgl_clicked())\n\n    @render.text\n    def view_state():\n        return json.dumps(input.mapgl_view_state(), indent=2)\n\n    @render.text\n    def props():\n        return str(input.mapgl_feature_clicked())\n\n    @reactive.Effect\n    @reactive.event(input.radius)\n    async def radius():\n        async with MapContext(\"mapgl\") as m:\n            m.set_paint_property(LAYER_ID, \"circle-radius\", input.radius())\n\n\napp = App(app_ui, server)\n

    Run this example:

    poetry run uvicorn docs.examples.getting_started.reactivity:app --reload\n
    "},{"location":"streamlit/","title":"Streamlit","text":"

    Use maplibre.streamlit.st_maplibre to add a MapLibre map to your Streamlit app:

    import streamlit as st\nfrom maplibre import Map, MapOptions\nfrom maplibre.basemaps import Carto\nfrom maplibre.controls import NavigationControl\nfrom maplibre.streamlit import st_maplibre\n\n\ndef create_layer(cell_size: int = 200) -> dict:\n    return {\n        \"@@type\": \"GridLayer\",\n        \"id\": \"GridLayer\",\n        \"data\": \"https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json\",\n        \"extruded\": True,\n        \"getPosition\": \"@@=COORDINATES\",\n        \"getColorWeight\": \"@@=SPACES\",\n        \"getElevationWeight\": \"@@=SPACES\",\n        \"elevationScale\": 4,\n        \"cellSize\": cell_size,\n        \"pickable\": True,\n    }\n\n\nmap_options = MapOptions(\n    style=Carto.POSITRON,\n    center=(-122.4, 37.74),\n    zoom=12,\n    hash=True,\n    pitch=40,\n)\n\n\nst.title(\"SF Bike Parking\")\n\ncell_size = st.slider(\"cell size\", 100, 600, value=200, step=5)\n\nm = Map(map_options)\nm.add_control(NavigationControl())\nm.add_deck_layers([create_layer(cell_size)], tooltip=\"Number of points: {{ count }}\")\n\nst_maplibre(m)\n
    "},{"location":"api/basemaps/","title":"Basemaps","text":""},{"location":"api/basemaps/#maplibre.basemaps.Carto","title":"maplibre.basemaps.Carto","text":"

    Bases: Enum

    Carto basemap styles

    Attributes:

    • DARK_MATTER \u2013

      dark-matter

    • POSITRON \u2013

      positron

    • VOYAGER \u2013

      voyager

    • POSITRON_NOLABELS \u2013

      positron-nolabels

    • DARK_MATTER_NOLABELS \u2013

      dark-matter-nolabels

    • VOYAGER_NOLABELS \u2013

      voyager-nolabels

    Examples:

    >>> from maplibre import Map, MapOptions\n>>> from maplibre.basemaps import Carto\n
    >>> map = Map(MapOptions(style=Carto.DARK_MATTER))\n
    Source code in maplibre/basemaps.py
    class Carto(Enum):\n    \"\"\"Carto basemap styles\n\n    Attributes:\n        DARK_MATTER: dark-matter\n        POSITRON: positron\n        VOYAGER: voyager\n        POSITRON_NOLABELS: positron-nolabels\n        DARK_MATTER_NOLABELS: dark-matter-nolabels\n        VOYAGER_NOLABELS: voyager-nolabels\n\n    Examples:\n        >>> from maplibre import Map, MapOptions\n        >>> from maplibre.basemaps import Carto\n\n        >>> map = Map(MapOptions(style=Carto.DARK_MATTER))\n    \"\"\"\n\n    DARK_MATTER = \"dark-matter\"\n    POSITRON = \"positron\"\n    VOYAGER = \"voyager\"\n    POSITRON_NOLABELS = \"positron-nolabels\"\n    DARK_MATTER_NOLABELS = \"dark-matter-nolabels\"\n    VOYAGER_NOLABELS = \"voyager-nolabels\"\n
    "},{"location":"api/basemaps/#maplibre.basemaps.construct_basemap_style","title":"maplibre.basemaps.construct_basemap_style(name='nice-style', sources={}, layers=[])","text":"

    Construct a basemap style

    Parameters:

    Name Type Description Default name str

    The name of the basemap style.

    'nice-style' sources dict

    The sources to be used for the basemap style.

    {} layers list

    The layers to be used for the basemap style.

    [] Source code in maplibre/basemaps.py
    def construct_basemap_style(\n    name: str = \"nice-style\", sources: dict = {}, layers: list = []\n) -> dict:\n    \"\"\"Construct a basemap style\n\n    Args:\n        name (str): The name of the basemap style.\n        sources (dict): The sources to be used for the basemap style.\n        layers (list): The layers to be used for the basemap style.\n    \"\"\"\n    layers = [\n        layer.to_dict() if isinstance(layer, Layer) else layer for layer in layers\n    ]\n    return {\"name\": name, \"version\": 8, \"sources\": sources, \"layers\": layers}\n
    "},{"location":"api/controls/","title":"Markers and controls","text":""},{"location":"api/controls/#maplibre.controls.Popup","title":"maplibre.controls.Popup","text":"

    Bases: BaseModel

    Popup

    Attributes:

    Name Type Description text str

    The Text of the popup.

    options PopupOptions | dict

    Popup options.

    Source code in maplibre/controls.py
    class Popup(BaseModel):\n    \"\"\"Popup\n\n    Attributes:\n        text: The Text of the popup.\n        options (PopupOptions | dict): Popup options.\n    \"\"\"\n\n    text: str\n    options: Union[PopupOptions, dict] = {}\n
    "},{"location":"api/controls/#maplibre.controls.PopupOptions","title":"maplibre.controls.PopupOptions","text":"

    Bases: BaseModel

    Popup options

    Source code in maplibre/controls.py
    class PopupOptions(BaseModel):\n    \"\"\"Popup options\"\"\"\n\n    anchor: str = None\n    close_button: bool = Field(False, serialization_alias=\"closeButton\")\n    close_on_click: bool = Field(None, serialization_alias=\"closeOnClick\")\n    close_on_move: bool = Field(None, serialization_alias=\"closeOnMove\")\n    max_width: int = Field(None, serialization_alias=\"maxWidth\")\n    offset: Union[int, list, dict] = None\n
    "},{"location":"api/controls/#maplibre.controls.Marker","title":"maplibre.controls.Marker","text":"

    Bases: BaseModel

    Marker

    Attributes:

    Name Type Description lng_lat tuple | list

    Required. The longitude and latitude of the marker.

    popup Popup | dict

    The Popup that is displayed when a user clicks on the marker.

    options MarkerOptions | dict

    Marker options.

    Source code in maplibre/controls.py
    class Marker(BaseModel):\n    \"\"\"Marker\n\n    Attributes:\n        lng_lat (tuple |list): **Required.** The longitude and latitude of the marker.\n        popup (Popup | dict): The Popup that is displayed when a user clicks on the marker.\n        options (MarkerOptions | dict): Marker options.\n    \"\"\"\n\n    lng_lat: Union[tuple, list] = Field(None, serialization_alias=\"lngLat\")\n    popup: Union[Popup, dict] = None\n    options: Union[MarkerOptions, dict] = {}\n
    "},{"location":"api/controls/#maplibre.controls.MarkerOptions","title":"maplibre.controls.MarkerOptions","text":"

    Bases: BaseModel

    Marker options

    Source code in maplibre/controls.py
    class MarkerOptions(BaseModel):\n    \"\"\"Marker options\"\"\"\n\n    anchor: str = None\n    color: str = None\n    draggable: bool = None\n    offset: Union[tuple, list] = None\n    pitch_alignment: str = Field(None, serialization_alias=\"pitchAlignment\")\n    rotation: int = None\n    rotation_alignment: str = Field(None, serialization_alias=\"rotationAlignment\")\n    scale: int = None\n
    "},{"location":"api/controls/#maplibre.controls.ControlPosition","title":"maplibre.controls.ControlPosition","text":"

    Bases: Enum

    Control position

    Attributes:

    • TOP_LEFT \u2013

      top-left

    • TOP_RIGHT \u2013

      top-right

    • BOTTOM_LEFT \u2013

      bottom-left

    • BOTTOM_RIGHT \u2013

      bottom-right

    Source code in maplibre/controls.py
    class ControlPosition(Enum):\n    \"\"\"Control position\n\n    Attributes:\n        TOP_LEFT: top-left\n        TOP_RIGHT: top-right\n        BOTTOM_LEFT: bottom-left\n        BOTTOM_RIGHT: bottom-right\n    \"\"\"\n\n    TOP_LEFT = \"top-left\"\n    TOP_RIGHT = \"top-right\"\n    BOTTOM_LEFT = \"bottom-left\"\n    BOTTOM_RIGHT = \"bottom-right\"\n
    "},{"location":"api/controls/#maplibre.controls.FullscreenControl","title":"maplibre.controls.FullscreenControl","text":"

    Bases: Control

    Fullscreen control

    Examples:

    >>> from maplibre import Map\n>>> from maplibre.controls import FullscreenControl, ControlPosition\n
    >>> map = Map()\n>>> map.add_control(FullscreenControl(), ControlPosition.BOTTOM_LEFT)\n
    Source code in maplibre/controls.py
    class FullscreenControl(Control):\n    \"\"\"Fullscreen control\n\n    Examples:\n        >>> from maplibre import Map\n        >>> from maplibre.controls import FullscreenControl, ControlPosition\n\n        >>> map = Map()\n        >>> map.add_control(FullscreenControl(), ControlPosition.BOTTOM_LEFT)\n    \"\"\"\n\n    # _name: str = ControlType.FULLSCREEN.value\n    pass\n
    "},{"location":"api/controls/#maplibre.controls.ScaleControl","title":"maplibre.controls.ScaleControl","text":"

    Bases: Control

    Scale control

    Source code in maplibre/controls.py
    class ScaleControl(Control):\n    \"\"\"Scale control\"\"\"\n\n    max_width: int = Field(None, serialization_alias=\"maxWidth\")\n    unit: Literal[\"imperial\", \"metric\", \"nautical\"] = \"metric\"\n
    "},{"location":"api/controls/#maplibre.controls.NavigationControl","title":"maplibre.controls.NavigationControl","text":"

    Bases: Control

    Navigation control

    Source code in maplibre/controls.py
    class NavigationControl(Control):\n    \"\"\"Navigation control\"\"\"\n\n    # _name: str = ControlType.NAVIGATION.value\n    show_compass: bool = Field(True, serialization_alias=\"showCompass\")\n    show_zoom: bool = Field(True, serialization_alias=\"showZoom\")\n    visualize_pitch: bool = Field(False, serialization_alias=\"visualizePitch\")\n
    "},{"location":"api/controls/#maplibre.controls.GeolocateControl","title":"maplibre.controls.GeolocateControl","text":"

    Bases: Control

    Geolocate control

    Source code in maplibre/controls.py
    class GeolocateControl(Control):\n    \"\"\"Geolocate control\"\"\"\n\n    # _name: str = ControlType.GEOLOCATE.value\n    position_options: dict = Field(None, serialization_alias=\"positionOptions\")\n    show_accuracy_circle: bool = Field(True, serialization_alias=\"showAccuracyCircle\")\n    show_user_heading: bool = Field(False, serialization_alias=\"showUserHeading\")\n    show_user_location: bool = Field(True, serialization_alias=\"showUserLocation\")\n    track_user_location: bool = Field(False, serialization_alias=\"trackUserLocation\")\n
    "},{"location":"api/controls/#maplibre.controls.InfoBoxControl","title":"maplibre.controls.InfoBoxControl","text":"

    Bases: Control

    InfoBox control

    Attributes:

    Name Type Description content str

    Content (HTML or plain text) to be displayed in the info box.

    css_text str

    Optional inline style declaration of the control.

    Source code in maplibre/controls.py
    class InfoBoxControl(Control):\n    \"\"\"InfoBox control\n\n    Attributes:\n        content (str): Content (HTML or plain text) to be displayed in the info box.\n        css_text (str): Optional inline style declaration of the control.\n    \"\"\"\n\n    content: str\n    css_text: str = Field(None, serialization_alias=\"cssText\")\n
    "},{"location":"api/controls/#maplibre.controls.LayerSwitcherControl","title":"maplibre.controls.LayerSwitcherControl","text":"

    Bases: Control

    Layer switcher control

    Attributes:

    Name Type Description layer_ids list

    A list of layer ids to be shown in the layer switcher control.

    theme Literal['default', 'simple']

    The theme of the layer switcher control.

    css_text str

    Optional inline style declaration of the control.

    Source code in maplibre/controls.py
    class LayerSwitcherControl(Control):\n    \"\"\"Layer switcher control\n\n    Attributes:\n        layer_ids (list): A list of layer ids to be shown in the layer switcher control.\n        theme (Literal[\"default\", \"simple\"]): The theme of the layer switcher control.\n        css_text (str): Optional inline style declaration of the control.\n    \"\"\"\n\n    layer_ids: list = Field([], serialization_alias=\"layerIds\")\n    theme: Literal[\"default\", \"simple\"] = \"default\"\n    css_text: str = Field(None, serialization_alias=\"cssText\")\n
    "},{"location":"api/layer/","title":"Layer","text":""},{"location":"api/layer/#maplibre.Layer","title":"maplibre.Layer","text":"

    Bases: BaseModel

    Layer properties

    Notes

    See layers for more details on the paint and layout properties of the layers.

    Attributes:

    Name Type Description id str

    Required. The unique ID of the layer. Defaults to str(uuid4()).

    type str | LayerType

    Required. The type of the layer.

    filter list

    The filter expression that is applied to the source of the layer.

    layout dict

    The layout properties of the layer.

    max_zoom int

    The maximum zoom level for the layer.

    min_zoom int

    The minimum zoom level for the layer.

    paint dict

    The paint properties of the layer.

    source str | Source

    The name (unique ID) of a source or a source object to be used for the layer.

    source_layer str

    The layer to use from a vector tile source.

    Examples:

    >>> from maplibre.layer import Layer, LayerType\n
    >>> layer = Layer(id=\"test-layer\", type=LayerType.CIRCLE, source=\"test-source\")\n
    Source code in maplibre/layer.py
    class Layer(BaseModel):\n    \"\"\"Layer properties\n\n    Notes:\n        See [layers](https://maplibre.org/maplibre-style-spec/layers/) for more details on the\n        `paint` and `layout` properties of the layers.\n\n    Attributes:\n        id (str): **Required.** The unique ID of the layer. Defaults to `str(uuid4())`.\n        type (str | LayerType): **Required.** The type of the layer.\n        filter (list): The filter expression that is applied to the source of the layer.\n        layout (dict): The layout properties of the layer.\n        max_zoom (int): The maximum zoom level for the layer.\n        min_zoom (int): The minimum zoom level for the layer.\n        paint (dict): The paint properties of the layer.\n        source (str | Source): The name (unique ID) of a source or a source object to be used for the layer.\n        source_layer (str): The layer to use from a vector tile source.\n\n    Examples:\n        >>> from maplibre.layer import Layer, LayerType\n\n        >>> layer = Layer(id=\"test-layer\", type=LayerType.CIRCLE, source=\"test-source\")\n    \"\"\"\n\n    id: str = Field(default_factory=lambda: str(uuid4()))\n    type: LayerType\n    filter: list = None\n    layout: dict = None\n    max_zoom: int = Field(None, serialization_alias=\"maxzoom\")\n    metadata: dict = None\n    min_zoom: int = Field(None, serialization_alias=\"minzoom\")\n    paint: dict = None\n    source: Union[str, Source, dict, None] = None\n    source_layer: str = Field(None, serialization_alias=\"source-layer\")\n\n    @field_validator(\"source\")\n    def validate_source(cls, v):\n        if isinstance(v, Source):\n            return v.to_dict()\n\n        return v\n\n    @field_validator(\"paint\", \"layout\")\n    def fix_paint(cls, v):\n        if isinstance(v, dict):\n            return fix_keys(v)\n\n        return v\n
    "},{"location":"api/layer/#maplibre.LayerType","title":"maplibre.LayerType","text":"

    Bases: Enum

    Rendering type of layer

    Attributes:

    • CIRCLE \u2013

      A filled circle.

    • FILL \u2013

      A filled polygon with an optional stroked border.

    • FILL_EXTRUSION \u2013

      An extruded polygon.

    • LINE \u2013

      A stroked line.

    • SYMBOL \u2013

      An icon or a text label.

    • RASTER \u2013

      Raster map textures such as satellite imagery.

    • HEATMAP \u2013

      A heatmap.

    • HILLSHADE \u2013

      A Client-side hillshading visualization based on DEM data.

    • BACKGROUND \u2013

      A background color or pattern.

    Source code in maplibre/layer.py
    class LayerType(Enum):\n    \"\"\"Rendering type of layer\n\n    Attributes:\n        CIRCLE: A filled circle.\n        FILL: A filled polygon with an optional stroked border.\n        FILL_EXTRUSION: An extruded polygon.\n        LINE: A stroked line.\n        SYMBOL: An icon or a text label.\n        RASTER: Raster map textures such as satellite imagery.\n        HEATMAP: A heatmap.\n        HILLSHADE: A Client-side hillshading visualization based on DEM data.\n        BACKGROUND: A background color or pattern.\n    \"\"\"\n\n    CIRCLE = \"circle\"\n    FILL = \"fill\"\n    FILL_EXTRUSION = \"fill-extrusion\"\n    LINE = \"line\"\n    SYMBOL = \"symbol\"\n    RASTER = \"raster\"\n    HEATMAP = \"heatmap\"\n    HILLSHADE = \"hillshade\"\n    BACKGROUND = \"background\"\n
    "},{"location":"api/map/","title":"Map","text":""},{"location":"api/map/#maplibre.Map","title":"maplibre.Map","text":"

    Bases: object

    Map

    Parameters:

    Name Type Description Default map_options MapOptions

    Map options.

    MapOptions() **kwargs

    Keyword arguments that are appended to the MapOptions object.

    {}

    Examples:

    >>> from maplibre.map import Map, MapOptions\n
    >>> map_options = MapOptions(center=(9.5, 51.31667), zoom=8)\n>>> map = Map(map_options)\n>>> dict(map)\n{'mapOptions': {'center': (9.5, 51.31667), 'style': 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json', 'zoom': 8}, 'calls': []}\n
    Source code in maplibre/map.py
    class Map(object):\n    \"\"\"Map\n\n    Args:\n        map_options (MapOptions): Map options.\n        **kwargs: Keyword arguments that are appended to the `MapOptions` object.\n\n    Examples:\n        >>> from maplibre.map import Map, MapOptions\n\n        >>> map_options = MapOptions(center=(9.5, 51.31667), zoom=8)\n        >>> map = Map(map_options)\n        >>> dict(map)\n        {'mapOptions': {'center': (9.5, 51.31667), 'style': 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json', 'zoom': 8}, 'calls': []}\n    \"\"\"\n\n    MESSAGE = \"not implemented yet\"\n\n    def __init__(self, map_options: MapOptions = MapOptions(), **kwargs):\n        self.map_options = map_options.to_dict() | kwargs\n        self._message_queue = []\n\n    def __iter__(self):\n        for k, v in self.to_dict().items():\n            yield k, v\n\n    def to_dict(self) -> dict:\n        return {\"mapOptions\": self.map_options, \"calls\": self._message_queue}\n\n    \"\"\"\n    @property\n    def sources(self) -> list:\n        return [item[\"data\"] for item in self._calls if item[\"name\"] == \"addSource\"]\n\n    @property\n    def layers(self) -> list:\n        return [item[\"data\"] for item in self._calls if item[\"name\"] == \"addLayer\"]\n    \"\"\"\n\n    # TODO: Rename to add_map_call\n    def add_call_(self, func_name: str, params: list) -> None:\n        self._message_queue.append(\n            {\"name\": \"applyFunc\", \"data\": {\"funcName\": func_name, \"params\": params}}\n        )\n\n    def add_call(self, method_name: str, *args) -> None:\n        \"\"\"Add a method call that is executed on the map instance\n\n        Args:\n            method_name (str): The name of the map method to be executed.\n            *args (any): The arguments to be passed to the map method.\n        \"\"\"\n        # TODO: Pass as dict? {\"name\": method_name, \"args\": args}\n        call = [method_name, args]\n        self._message_queue.append(call)\n\n    def add_control(\n        self,\n        control: Control,\n        position: [str | ControlPosition] = ControlPosition.TOP_RIGHT,\n    ) -> None:\n        \"\"\"Add a control to the map\n\n        Args:\n            control (Control): The control to be added to the map.\n            position (str | ControlPosition): The position of the control.\n        \"\"\"\n        self.add_call(\n            \"addControl\",\n            control.type,\n            control.to_dict(),\n            ControlPosition(position).value,\n        )\n\n    def add_source(self, id: str, source: [Source | dict]) -> None:\n        \"\"\"Add a source to the map\n\n        Args:\n            id (str): The unique ID of the source.\n            source (Source | dict): The source to be added to the map.\n        \"\"\"\n        if isinstance(source, Source):\n            source = source.to_dict()\n\n        self.add_call(\"addSource\", id, source)\n\n    def add_layer(self, layer: [Layer | dict], before_id: str = None) -> None:\n        \"\"\"Add a layer to the map\n\n        Args:\n            layer (Layer | dict): The Layer to be added to the map.\n            before_id (str): The ID of an existing layer to insert the new layer before,\n                resulting in the new layer appearing visually beneath the existing layer.\n                If `None`, the new layer will appear above all other layers.\n        \"\"\"\n        if isinstance(layer, Layer):\n            layer = layer.to_dict()\n\n        self.add_call(\"addLayer\", layer, before_id)\n\n    def add_marker(self, marker: Marker) -> None:\n        \"\"\"Add a marker to the map\n\n        Args:\n            marker (Marker): The marker to be added to the map.\n        \"\"\"\n        self.add_call(\"addMarker\", marker.to_dict())\n\n    def add_popup(self, layer_id: str, prop: str = None, template: str = None) -> None:\n        \"\"\"Add a popup to the map\n\n        Args:\n            layer_id (str): The layer to which the popup is added.\n            prop (str): The property of the source to be displayed. If `None`, all properties are displayed.\n            template (str): A mustache template. If supplied, `prop` is ignored.\n        \"\"\"\n        self.add_call(\"addPopup\", layer_id, prop, template)\n\n    def add_tooltip(\n        self, layer_id: str, prop: str = None, template: str = None\n    ) -> None:\n        \"\"\"Add a tooltip to the map\n\n        Args:\n            layer_id (str): The layer to which the tooltip is added.\n            prop (str): The property of the source to be displayed. If `None`, all properties are displayed.\n            template (str): A mustache template. If supplied, `prop` is ignored.\n\n        Examples:\n            >>> map = Map()\n            >>> # ...\n            >>> map.add_tooltip(\"test-layer\", template=\"Name: {{ name }}\")\n        \"\"\"\n        self.add_call(\"addTooltip\", layer_id, prop, template)\n\n    def set_filter(self, layer_id: str, filter_: list):\n        \"\"\"Update the filter of a layer\n\n        Args:\n            layer_id (str): The name of the layer to be updated.\n            filter_ (list): The filter expression that is applied to the source of the layer.\n        \"\"\"\n        self.add_call(\"setFilter\", layer_id, filter_)\n\n    def set_paint_property(self, layer_id: str, prop: str, value: any) -> None:\n        \"\"\"Update the paint property of a layer\n\n        Args:\n            layer_id (str): The name of the layer to be updated.\n            prop (str): The name of the paint property to be updated.\n            value (any): The new value of the paint property.\n        \"\"\"\n        self.add_call(\"setPaintProperty\", layer_id, prop, value)\n\n    def set_layout_property(self, layer_id: str, prop: str, value: any) -> None:\n        \"\"\"Update a layout property of a layer\n\n        Args:\n            layer_id (str): The name of the layer to be updated.\n            prop (str): The name of the layout property to be updated.\n            value (any): The new value of the layout property.\n        \"\"\"\n        self.add_call(\"setLayoutProperty\", layer_id, prop, value)\n\n    def set_data(self, source_id: str, data: dict) -> None:\n        \"\"\"Update the data of a GeoJSON source\n\n        Args:\n            source_id (str): The name of the source to be updated.\n            data (dict): The data of the source.\n        \"\"\"\n        self.add_call(\"setSourceData\", source_id, data)\n\n    def set_visibility(self, layer_id: str, visible: bool = True) -> None:\n        \"\"\"Update the visibility of a layer\n\n        Args:\n            layer_id (str): The name of the layer to be updated.\n            visible (bool): Whether the layer is visible or not.\n        \"\"\"\n        value = \"visible\" if visible else \"none\"\n        self.add_call(\"setLayoutProperty\", layer_id, \"visibility\", value)\n\n    def to_html(self, title: str = \"My Awesome Map\", **kwargs) -> str:\n        \"\"\"Render to html\n\n        Args:\n            title (str): The Title of the HTML document.\n            **kwargs (Any): Additional keyword arguments that are passed to the template.\n                Currently, `style` is the only supported keyword argument.\n\n        Examples:\n            >>> from maplibre import Map\n\n            >>> map = Map()\n            >>> with open(\"/tmp/map.html\", \"w\") as f:\n            ...     f.write(map.to_html(style=\"height: 800px;\") # doctest: +SKIP\n        \"\"\"\n        js_lib = read_internal_file(\"srcjs\", \"pywidget.js\")\n        js_snippet = Template(js_template).render(data=json.dumps(self.to_dict()))\n        css = read_internal_file(\"srcjs\", \"pywidget.css\")\n        headers = [f\"<style>{css}</style>\"]\n\n        # Deck.GL headers\n        add_deckgl_headers = \"addDeckOverlay\" in [\n            item[0] for item in self._message_queue\n        ]\n        # TODO: Set version in constants\n        deckgl_headers = (\n            [\n                '<script src=\"https://unpkg.com/deck.gl@9.0.16/dist.min.js\"></script>',\n                '<script src=\"https://unpkg.com/@deck.gl/json@9.0.16/dist.min.js\"></script>',\n            ]\n            if add_deckgl_headers\n            else []\n        )\n\n        # Mapbox Draw headers\n        add_mapbox_draw_headers = \"addMapboxDraw\" in [\n            item[0] for item in self._message_queue\n        ]\n        # TODO: Set version in constants\n        mapbox_draw_headers = (\n            [\n                # \"<script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.3/mapbox-gl-draw.js'></script>\",\n                # \"<link rel='stylesheet' href='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.3/mapbox-gl-draw.css' type='text/css' />\",\n                \"<script src='https://www.unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.js'></script>\",\n                \"<link rel='stylesheet' href='https://www.unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.css' type='text/css' />\",\n            ]\n            if add_mapbox_draw_headers\n            else []\n        )\n\n        output = Template(html_template).render(\n            js=\"\\n\".join([js_lib, js_snippet]),\n            title=title,\n            headers=headers + deckgl_headers + mapbox_draw_headers,\n            **kwargs,\n        )\n        return output\n\n    # -------------------------\n    # Plugins\n    # -------------------------\n    def add_deck_layers(self, layers: list[dict], tooltip: str | dict = None) -> None:\n        \"\"\"Add Deck.GL layers to the layer stack\n\n        Args:\n            layers (list[dict]): A list of dictionaries containing the Deck.GL layers to be added.\n            tooltip (str | dict): Either a single mustache template string applied to all layers\n                or a dictionary where keys are layer ids and values are mustache template strings.\n        \"\"\"\n        self.add_call(\"addDeckOverlay\", layers, tooltip)\n\n    def set_deck_layers(self, layers: list[dict], tooltip: str | dict = None) -> None:\n        \"\"\"Update Deck.GL layers\n\n        Args:\n            layers (list[dict]): A list of dictionaries containing the Deck.GL layers to be updated.\n                New layers will be added. Missing layers will be removed.\n            tooltip (str | dict): Must be set to keep tooltip even if it did not change.\n        \"\"\"\n        self.add_call(\"setDeckLayers\", layers, tooltip)\n\n    def add_mapbox_draw(\n        self,\n        options: dict | MapboxDrawOptions = None,\n        position: str | ControlPosition = ControlPosition.TOP_LEFT,\n        geojson: dict = None,\n    ) -> None:\n        \"\"\"Add MapboxDraw controls to the map\n\n        Note:\n            See [MapboxDraw API Reference](https://github.com/mapbox/mapbox-gl-draw/blob/main/docs/API.md)\n                for available options.\n\n        Args:\n            options (dict | MapboxDrawOptions): MapboxDraw options.\n            position (str | ControlPosition): The position of the MapboxDraw controls.\n            geojson (dict): A GeoJSON Feature, FeatureCollection or Geometry to be added to the draw layer.\n        \"\"\"\n        if isinstance(options, MapboxDrawOptions):\n            options = options.to_dict()\n\n        self.add_call(\n            \"addMapboxDraw\", options or {}, ControlPosition(position).value, geojson\n        )\n
    "},{"location":"api/map/#maplibre.Map.add_call","title":"add_call(method_name, *args)","text":"

    Add a method call that is executed on the map instance

    Parameters:

    Name Type Description Default method_name str

    The name of the map method to be executed.

    required *args any

    The arguments to be passed to the map method.

    () Source code in maplibre/map.py
    def add_call(self, method_name: str, *args) -> None:\n    \"\"\"Add a method call that is executed on the map instance\n\n    Args:\n        method_name (str): The name of the map method to be executed.\n        *args (any): The arguments to be passed to the map method.\n    \"\"\"\n    # TODO: Pass as dict? {\"name\": method_name, \"args\": args}\n    call = [method_name, args]\n    self._message_queue.append(call)\n
    "},{"location":"api/map/#maplibre.Map.add_control","title":"add_control(control, position=ControlPosition.TOP_RIGHT)","text":"

    Add a control to the map

    Parameters:

    Name Type Description Default control Control

    The control to be added to the map.

    required position str | ControlPosition

    The position of the control.

    TOP_RIGHT Source code in maplibre/map.py
    def add_control(\n    self,\n    control: Control,\n    position: [str | ControlPosition] = ControlPosition.TOP_RIGHT,\n) -> None:\n    \"\"\"Add a control to the map\n\n    Args:\n        control (Control): The control to be added to the map.\n        position (str | ControlPosition): The position of the control.\n    \"\"\"\n    self.add_call(\n        \"addControl\",\n        control.type,\n        control.to_dict(),\n        ControlPosition(position).value,\n    )\n
    "},{"location":"api/map/#maplibre.Map.add_deck_layers","title":"add_deck_layers(layers, tooltip=None)","text":"

    Add Deck.GL layers to the layer stack

    Parameters:

    Name Type Description Default layers list[dict]

    A list of dictionaries containing the Deck.GL layers to be added.

    required tooltip str | dict

    Either a single mustache template string applied to all layers or a dictionary where keys are layer ids and values are mustache template strings.

    None Source code in maplibre/map.py
    def add_deck_layers(self, layers: list[dict], tooltip: str | dict = None) -> None:\n    \"\"\"Add Deck.GL layers to the layer stack\n\n    Args:\n        layers (list[dict]): A list of dictionaries containing the Deck.GL layers to be added.\n        tooltip (str | dict): Either a single mustache template string applied to all layers\n            or a dictionary where keys are layer ids and values are mustache template strings.\n    \"\"\"\n    self.add_call(\"addDeckOverlay\", layers, tooltip)\n
    "},{"location":"api/map/#maplibre.Map.add_layer","title":"add_layer(layer, before_id=None)","text":"

    Add a layer to the map

    Parameters:

    Name Type Description Default layer Layer | dict

    The Layer to be added to the map.

    required before_id str

    The ID of an existing layer to insert the new layer before, resulting in the new layer appearing visually beneath the existing layer. If None, the new layer will appear above all other layers.

    None Source code in maplibre/map.py
    def add_layer(self, layer: [Layer | dict], before_id: str = None) -> None:\n    \"\"\"Add a layer to the map\n\n    Args:\n        layer (Layer | dict): The Layer to be added to the map.\n        before_id (str): The ID of an existing layer to insert the new layer before,\n            resulting in the new layer appearing visually beneath the existing layer.\n            If `None`, the new layer will appear above all other layers.\n    \"\"\"\n    if isinstance(layer, Layer):\n        layer = layer.to_dict()\n\n    self.add_call(\"addLayer\", layer, before_id)\n
    "},{"location":"api/map/#maplibre.Map.add_mapbox_draw","title":"add_mapbox_draw(options=None, position=ControlPosition.TOP_LEFT, geojson=None)","text":"

    Add MapboxDraw controls to the map

    Note

    See MapboxDraw API Reference for available options.

    Parameters:

    Name Type Description Default options dict | MapboxDrawOptions

    MapboxDraw options.

    None position str | ControlPosition

    The position of the MapboxDraw controls.

    TOP_LEFT geojson dict

    A GeoJSON Feature, FeatureCollection or Geometry to be added to the draw layer.

    None Source code in maplibre/map.py
    def add_mapbox_draw(\n    self,\n    options: dict | MapboxDrawOptions = None,\n    position: str | ControlPosition = ControlPosition.TOP_LEFT,\n    geojson: dict = None,\n) -> None:\n    \"\"\"Add MapboxDraw controls to the map\n\n    Note:\n        See [MapboxDraw API Reference](https://github.com/mapbox/mapbox-gl-draw/blob/main/docs/API.md)\n            for available options.\n\n    Args:\n        options (dict | MapboxDrawOptions): MapboxDraw options.\n        position (str | ControlPosition): The position of the MapboxDraw controls.\n        geojson (dict): A GeoJSON Feature, FeatureCollection or Geometry to be added to the draw layer.\n    \"\"\"\n    if isinstance(options, MapboxDrawOptions):\n        options = options.to_dict()\n\n    self.add_call(\n        \"addMapboxDraw\", options or {}, ControlPosition(position).value, geojson\n    )\n
    "},{"location":"api/map/#maplibre.Map.add_marker","title":"add_marker(marker)","text":"

    Add a marker to the map

    Parameters:

    Name Type Description Default marker Marker

    The marker to be added to the map.

    required Source code in maplibre/map.py
    def add_marker(self, marker: Marker) -> None:\n    \"\"\"Add a marker to the map\n\n    Args:\n        marker (Marker): The marker to be added to the map.\n    \"\"\"\n    self.add_call(\"addMarker\", marker.to_dict())\n
    "},{"location":"api/map/#maplibre.Map.add_popup","title":"add_popup(layer_id, prop=None, template=None)","text":"

    Add a popup to the map

    Parameters:

    Name Type Description Default layer_id str

    The layer to which the popup is added.

    required prop str

    The property of the source to be displayed. If None, all properties are displayed.

    None template str

    A mustache template. If supplied, prop is ignored.

    None Source code in maplibre/map.py
    def add_popup(self, layer_id: str, prop: str = None, template: str = None) -> None:\n    \"\"\"Add a popup to the map\n\n    Args:\n        layer_id (str): The layer to which the popup is added.\n        prop (str): The property of the source to be displayed. If `None`, all properties are displayed.\n        template (str): A mustache template. If supplied, `prop` is ignored.\n    \"\"\"\n    self.add_call(\"addPopup\", layer_id, prop, template)\n
    "},{"location":"api/map/#maplibre.Map.add_source","title":"add_source(id, source)","text":"

    Add a source to the map

    Parameters:

    Name Type Description Default id str

    The unique ID of the source.

    required source Source | dict

    The source to be added to the map.

    required Source code in maplibre/map.py
    def add_source(self, id: str, source: [Source | dict]) -> None:\n    \"\"\"Add a source to the map\n\n    Args:\n        id (str): The unique ID of the source.\n        source (Source | dict): The source to be added to the map.\n    \"\"\"\n    if isinstance(source, Source):\n        source = source.to_dict()\n\n    self.add_call(\"addSource\", id, source)\n
    "},{"location":"api/map/#maplibre.Map.add_tooltip","title":"add_tooltip(layer_id, prop=None, template=None)","text":"

    Add a tooltip to the map

    Parameters:

    Name Type Description Default layer_id str

    The layer to which the tooltip is added.

    required prop str

    The property of the source to be displayed. If None, all properties are displayed.

    None template str

    A mustache template. If supplied, prop is ignored.

    None

    Examples:

    >>> map = Map()\n>>> # ...\n>>> map.add_tooltip(\"test-layer\", template=\"Name: {{ name }}\")\n
    Source code in maplibre/map.py
    def add_tooltip(\n    self, layer_id: str, prop: str = None, template: str = None\n) -> None:\n    \"\"\"Add a tooltip to the map\n\n    Args:\n        layer_id (str): The layer to which the tooltip is added.\n        prop (str): The property of the source to be displayed. If `None`, all properties are displayed.\n        template (str): A mustache template. If supplied, `prop` is ignored.\n\n    Examples:\n        >>> map = Map()\n        >>> # ...\n        >>> map.add_tooltip(\"test-layer\", template=\"Name: {{ name }}\")\n    \"\"\"\n    self.add_call(\"addTooltip\", layer_id, prop, template)\n
    "},{"location":"api/map/#maplibre.Map.set_data","title":"set_data(source_id, data)","text":"

    Update the data of a GeoJSON source

    Parameters:

    Name Type Description Default source_id str

    The name of the source to be updated.

    required data dict

    The data of the source.

    required Source code in maplibre/map.py
    def set_data(self, source_id: str, data: dict) -> None:\n    \"\"\"Update the data of a GeoJSON source\n\n    Args:\n        source_id (str): The name of the source to be updated.\n        data (dict): The data of the source.\n    \"\"\"\n    self.add_call(\"setSourceData\", source_id, data)\n
    "},{"location":"api/map/#maplibre.Map.set_deck_layers","title":"set_deck_layers(layers, tooltip=None)","text":"

    Update Deck.GL layers

    Parameters:

    Name Type Description Default layers list[dict]

    A list of dictionaries containing the Deck.GL layers to be updated. New layers will be added. Missing layers will be removed.

    required tooltip str | dict

    Must be set to keep tooltip even if it did not change.

    None Source code in maplibre/map.py
    def set_deck_layers(self, layers: list[dict], tooltip: str | dict = None) -> None:\n    \"\"\"Update Deck.GL layers\n\n    Args:\n        layers (list[dict]): A list of dictionaries containing the Deck.GL layers to be updated.\n            New layers will be added. Missing layers will be removed.\n        tooltip (str | dict): Must be set to keep tooltip even if it did not change.\n    \"\"\"\n    self.add_call(\"setDeckLayers\", layers, tooltip)\n
    "},{"location":"api/map/#maplibre.Map.set_filter","title":"set_filter(layer_id, filter_)","text":"

    Update the filter of a layer

    Parameters:

    Name Type Description Default layer_id str

    The name of the layer to be updated.

    required filter_ list

    The filter expression that is applied to the source of the layer.

    required Source code in maplibre/map.py
    def set_filter(self, layer_id: str, filter_: list):\n    \"\"\"Update the filter of a layer\n\n    Args:\n        layer_id (str): The name of the layer to be updated.\n        filter_ (list): The filter expression that is applied to the source of the layer.\n    \"\"\"\n    self.add_call(\"setFilter\", layer_id, filter_)\n
    "},{"location":"api/map/#maplibre.Map.set_layout_property","title":"set_layout_property(layer_id, prop, value)","text":"

    Update a layout property of a layer

    Parameters:

    Name Type Description Default layer_id str

    The name of the layer to be updated.

    required prop str

    The name of the layout property to be updated.

    required value any

    The new value of the layout property.

    required Source code in maplibre/map.py
    def set_layout_property(self, layer_id: str, prop: str, value: any) -> None:\n    \"\"\"Update a layout property of a layer\n\n    Args:\n        layer_id (str): The name of the layer to be updated.\n        prop (str): The name of the layout property to be updated.\n        value (any): The new value of the layout property.\n    \"\"\"\n    self.add_call(\"setLayoutProperty\", layer_id, prop, value)\n
    "},{"location":"api/map/#maplibre.Map.set_paint_property","title":"set_paint_property(layer_id, prop, value)","text":"

    Update the paint property of a layer

    Parameters:

    Name Type Description Default layer_id str

    The name of the layer to be updated.

    required prop str

    The name of the paint property to be updated.

    required value any

    The new value of the paint property.

    required Source code in maplibre/map.py
    def set_paint_property(self, layer_id: str, prop: str, value: any) -> None:\n    \"\"\"Update the paint property of a layer\n\n    Args:\n        layer_id (str): The name of the layer to be updated.\n        prop (str): The name of the paint property to be updated.\n        value (any): The new value of the paint property.\n    \"\"\"\n    self.add_call(\"setPaintProperty\", layer_id, prop, value)\n
    "},{"location":"api/map/#maplibre.Map.set_visibility","title":"set_visibility(layer_id, visible=True)","text":"

    Update the visibility of a layer

    Parameters:

    Name Type Description Default layer_id str

    The name of the layer to be updated.

    required visible bool

    Whether the layer is visible or not.

    True Source code in maplibre/map.py
    def set_visibility(self, layer_id: str, visible: bool = True) -> None:\n    \"\"\"Update the visibility of a layer\n\n    Args:\n        layer_id (str): The name of the layer to be updated.\n        visible (bool): Whether the layer is visible or not.\n    \"\"\"\n    value = \"visible\" if visible else \"none\"\n    self.add_call(\"setLayoutProperty\", layer_id, \"visibility\", value)\n
    "},{"location":"api/map/#maplibre.Map.to_html","title":"to_html(title='My Awesome Map', **kwargs)","text":"

    Render to html

    Parameters:

    Name Type Description Default title str

    The Title of the HTML document.

    'My Awesome Map' **kwargs Any

    Additional keyword arguments that are passed to the template. Currently, style is the only supported keyword argument.

    {}

    Examples:

    >>> from maplibre import Map\n
    >>> map = Map()\n>>> with open(\"/tmp/map.html\", \"w\") as f:\n...     f.write(map.to_html(style=\"height: 800px;\")\n
    Source code in maplibre/map.py
    def to_html(self, title: str = \"My Awesome Map\", **kwargs) -> str:\n    \"\"\"Render to html\n\n    Args:\n        title (str): The Title of the HTML document.\n        **kwargs (Any): Additional keyword arguments that are passed to the template.\n            Currently, `style` is the only supported keyword argument.\n\n    Examples:\n        >>> from maplibre import Map\n\n        >>> map = Map()\n        >>> with open(\"/tmp/map.html\", \"w\") as f:\n        ...     f.write(map.to_html(style=\"height: 800px;\") # doctest: +SKIP\n    \"\"\"\n    js_lib = read_internal_file(\"srcjs\", \"pywidget.js\")\n    js_snippet = Template(js_template).render(data=json.dumps(self.to_dict()))\n    css = read_internal_file(\"srcjs\", \"pywidget.css\")\n    headers = [f\"<style>{css}</style>\"]\n\n    # Deck.GL headers\n    add_deckgl_headers = \"addDeckOverlay\" in [\n        item[0] for item in self._message_queue\n    ]\n    # TODO: Set version in constants\n    deckgl_headers = (\n        [\n            '<script src=\"https://unpkg.com/deck.gl@9.0.16/dist.min.js\"></script>',\n            '<script src=\"https://unpkg.com/@deck.gl/json@9.0.16/dist.min.js\"></script>',\n        ]\n        if add_deckgl_headers\n        else []\n    )\n\n    # Mapbox Draw headers\n    add_mapbox_draw_headers = \"addMapboxDraw\" in [\n        item[0] for item in self._message_queue\n    ]\n    # TODO: Set version in constants\n    mapbox_draw_headers = (\n        [\n            # \"<script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.3/mapbox-gl-draw.js'></script>\",\n            # \"<link rel='stylesheet' href='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.3/mapbox-gl-draw.css' type='text/css' />\",\n            \"<script src='https://www.unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.js'></script>\",\n            \"<link rel='stylesheet' href='https://www.unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.css' type='text/css' />\",\n        ]\n        if add_mapbox_draw_headers\n        else []\n    )\n\n    output = Template(html_template).render(\n        js=\"\\n\".join([js_lib, js_snippet]),\n        title=title,\n        headers=headers + deckgl_headers + mapbox_draw_headers,\n        **kwargs,\n    )\n    return output\n
    "},{"location":"api/map/#maplibre.MapOptions","title":"maplibre.MapOptions","text":"

    Bases: BaseModel

    Map options

    Note

    See MapOptions for more details.

    Source code in maplibre/map.py
    class MapOptions(BaseModel):\n    \"\"\"Map options\n\n    Note:\n        See [MapOptions](https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/MapOptions) for more details.\n    \"\"\"\n\n    model_config = ConfigDict(\n        validate_assignment=True, extra=\"forbid\", use_enum_values=False\n    )\n    antialias: bool = None\n    attribution_control: bool = Field(None, serialization_alias=\"attributionControl\")\n    bearing: Union[int, float] = None\n    bearing_snap: int = Field(None, serialization_alias=\"bearingSnap\")\n    bounds: tuple = None\n    box_zoom: bool = Field(None, serialization_alias=\"boxZoom\")\n    center: tuple = None\n    click_tolerance: int = Field(None, serialization_alias=\"clickTolerance\")\n    custom_attribution: bool = Field(None, serialization_alias=\"customAttribution\")\n    double_click_zoom: bool = Field(None, serialization_alias=\"doubleClickZoom\")\n    fade_duration: int = Field(None, serialization_alias=\"fadeDuration\")\n    fit_bounds_options: dict = Field(None, serialization_alias=\"fitBoundsOptions\")\n    hash: Union[bool, str] = None\n    interactive: bool = None\n    keyword: bool = None\n    max_bounds: tuple = Field(None, serialization_alias=\"maxBounds\")\n    max_pitch: int = Field(None, serialization_alias=\"maxPitch\")\n    max_zoom: int = Field(None, serialization_alias=\"maxZoom\")\n    min_pitch: int = Field(None, serialization_alias=\"minPitch\")\n    min_zoom: int = Field(None, serialization_alias=\"minZoom\")\n    pitch: Union[int, float] = None\n    scroll_zoom: bool = Field(None, serialization_alias=\"scrollZoom\")\n    style: Union[str, Carto, dict] = construct_carto_basemap_url(Carto.DARK_MATTER)\n    zoom: Union[int, float] = None\n\n    @field_validator(\"style\")\n    def validate_style(cls, v):\n        if isinstance(v, Carto):\n            return construct_carto_basemap_url(v)\n\n        return v\n
    "},{"location":"api/map/#maplibre.MapContext","title":"maplibre.MapContext","text":"

    Bases: Map

    MapContext

    Use this class to update a Map instance in a Shiny app. Must be used inside an async function.

    See maplibre.Map for available methods.

    Parameters:

    Name Type Description Default id string

    The id of the map to be updated.

    required session Session

    A Shiny session. If None, the active session is used.

    None Source code in maplibre/mapcontext.py
    class MapContext(Map):\n    \"\"\"MapContext\n\n    Use this class to update a `Map` instance in a Shiny app.\n    Must be used inside an async function.\n\n    See `maplibre.Map` for available methods.\n\n    Args:\n        id (string): The id of the map to be updated.\n        session (Session): A Shiny session.\n            If `None`, the active session is used.\n    \"\"\"\n\n    def __init__(self, id: str, session: Session = None) -> None:\n        self.id = id\n        self._session = require_active_session(session)\n        self.map_options = {}\n        self._message_queue = []\n\n    async def __aenter__(self):\n        return self\n\n    async def __aexit__(self, exc_type, exc_val, exc_tb):\n        await self.render()\n\n    async def render(self):\n        await self._session.send_custom_message(\n            f\"pymaplibregl-{self.id}\", {\"id\": self.id, \"calls\": self._message_queue}\n        )\n
    "},{"location":"api/map/#maplibre.ipywidget.MapWidget","title":"maplibre.ipywidget.MapWidget","text":"

    Bases: AnyWidget, Map

    MapWidget

    Use this class to display and update maps in Jupyter Notebooks.

    See maplibre.Map for available methods.

    Examples:

    >>> from maplibre import MapOptions\n>>> from maplibre.ipywidget import MapWidget as Map\n>>> m = Map(MapOptions(center=(-123.13, 49.254), zoom=11, pitch=45))\n>>> m\n
    Source code in maplibre/ipywidget.py
    class MapWidget(AnyWidget, Map):\n    \"\"\"MapWidget\n\n    Use this class to display and update maps in Jupyter Notebooks.\n\n    See `maplibre.Map` for available methods.\n\n    Examples:\n        >>> from maplibre import MapOptions\n        >>> from maplibre.ipywidget import MapWidget as Map\n        >>> m = Map(MapOptions(center=(-123.13, 49.254), zoom=11, pitch=45))\n        >>> m # doctest: +SKIP\n    \"\"\"\n\n    _esm = join(Path(__file__).parent, \"srcjs\", \"ipywidget.js\")\n    # _css = join(Path(__file__).parent, \"srcjs\", \"maplibre-gl.css\")\n    _css = join(Path(__file__).parent, \"srcjs\", \"ipywidget.css\")\n    _use_message_queue = True\n    _rendered = traitlets.Bool(False, config=True).tag(sync=True)\n    map_options = traitlets.Dict().tag(sync=True)\n    calls = traitlets.List().tag(sync=True)\n    height = traitlets.Union([traitlets.Int(), traitlets.Unicode()]).tag(sync=True)\n\n    # Interactions Map\n    clicked = traitlets.Dict().tag(sync=True)\n    view_state = traitlets.Dict().tag(sync=True)\n\n    # Interactions MapboxDraw plugin\n    draw_features_selected = traitlets.List().tag(sync=True)\n    draw_feature_collection_all = traitlets.Dict().tag(sync=True)\n\n    def __init__(self, map_options=MapOptions(), **kwargs) -> None:\n        self.calls = []\n        AnyWidget.__init__(self, **kwargs)\n        Map.__init__(self, map_options, **kwargs)\n\n    @traitlets.default(\"height\")\n    def _default_height(self):\n        return \"400px\"\n\n    @traitlets.validate(\"height\")\n    def _validate_height(self, proposal):\n        height = proposal[\"value\"]\n        if isinstance(height, int):\n            return f\"{height}px\"\n\n        return height\n\n    @traitlets.observe(\"_rendered\")\n    def _on_rendered(self, change):\n        self.send({\"calls\": self._message_queue, \"msg\": \"init\"})\n        self._message_queue = []\n\n    def use_message_queue(self, value: bool = True) -> None:\n        self._use_message_queue = value\n\n    def add_call(self, method_name: str, *args) -> None:\n        call = [method_name, args]\n        if not self._rendered:\n            if not self._use_message_queue:\n                self.calls = self.calls + [call]\n                return\n\n            self._message_queue.append(call)\n            return\n\n        self.send({\"calls\": [call], \"msg\": \"custom call\"})\n
    "},{"location":"api/map/#maplibre.plugins","title":"maplibre.plugins","text":""},{"location":"api/map/#maplibre.plugins.MapboxDrawControls","title":"MapboxDrawControls","text":"

    Bases: BaseModel

    MapboxDraw controls

    Source code in maplibre/plugins.py
    class MapboxDrawControls(BaseModel):\n    \"\"\"MapboxDraw controls\"\"\"\n\n    point: bool = False\n    line_string: bool = False\n    polygon: bool = False\n    trash: bool = False\n    combine_features: bool = False\n    uncombine_features: bool = False\n
    "},{"location":"api/map/#maplibre.plugins.MapboxDrawOptions","title":"MapboxDrawOptions","text":"

    Bases: BaseModel

    MapboxDraw Options

    Source code in maplibre/plugins.py
    class MapboxDrawOptions(BaseModel):\n    \"\"\"MapboxDraw Options\"\"\"\n\n    display_controls_default: bool = Field(\n        True, serialization_alias=\"displayControlsDefault\"\n    )\n    controls: MapboxDrawControls = None\n    box_select: bool = Field(True, serialization_alias=\"boxSelect\")\n
    "},{"location":"api/sources/","title":"Sources","text":""},{"location":"api/sources/#maplibre.sources","title":"maplibre.sources","text":""},{"location":"api/sources/#maplibre.sources.GeoJSONSource","title":"GeoJSONSource","text":"

    Bases: Source

    GeoJSON Source

    Examples:

    >>> from maplibre.sources import GeoJSONSource\n
    >>> geojson = \"https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson\"\n>>> source = GeoJSONSource(data=geojson)\n
    Source code in maplibre/sources.py
    class GeoJSONSource(Source):\n    \"\"\"GeoJSON Source\n\n    Examples:\n        >>> from maplibre.sources import GeoJSONSource\n\n        >>> geojson = \"https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson\"\n        >>> source = GeoJSONSource(data=geojson)\n    \"\"\"\n\n    data: Union[str, dict]\n    attribution: str = None\n    buffer: int = None\n    cluster: bool = None\n    cluster_max_zoom: int = Field(None, serialization_alias=\"clusterMaxZoom\")\n    cluster_min_points: int = Field(None, serialization_alias=\"clusterMinPoints\")\n    cluster_properties: dict = Field(None, serialization_alias=\"clusterProperties\")\n    cluster_radius: int = Field(None, serialization_alias=\"clusterRadius\")\n    filter: list = None\n    generate_id: bool = Field(None, serialization_alias=\"generateId\")\n    line_metrics: bool = Field(None, serialization_alias=\"lineMetrics\")\n    maxzoom: int = None\n    promote_id: Union[str, dict] = Field(None, serialization_alias=\"promoteId\")\n    tolerance: float = None\n\n    @computed_field\n    @property\n    def type(self) -> str:\n        return SourceType.GEOJSON.value\n
    "},{"location":"api/sources/#maplibre.sources.RasterTileSource","title":"RasterTileSource","text":"

    Bases: Source

    Raster tile source

    Examples:

    >>> from maplibre.sources import RasterTileSource\n
    >>> raster_tile_source = RasterTileSource(\n...     tiles=[\"https://tile.openstreetmap.org/{z}/{x}/{y}.png\"],\n...     tile_size=256,\n...     min_zoom=0,\n...     max_zoom=19,\n... )\n
    Source code in maplibre/sources.py
    class RasterTileSource(Source):\n    \"\"\"Raster tile source\n\n    Examples:\n        >>> from maplibre.sources import RasterTileSource\n\n        >>> raster_tile_source = RasterTileSource(\n        ...     tiles=[\"https://tile.openstreetmap.org/{z}/{x}/{y}.png\"],\n        ...     tile_size=256,\n        ...     min_zoom=0,\n        ...     max_zoom=19,\n        ... )\n    \"\"\"\n\n    attribution: str = None\n    bounds: tuple = None\n    max_zoom: int = Field(None, serialization_alias=\"maxzoom\")\n    min_zoom: int = Field(None, serialization_alias=\"minzoom\")\n    scheme: str = None\n    tile_size: int = Field(None, serialization_alias=\"tileSize\")\n    tiles: Union[tuple, list] = None\n    url: str = None\n    volatile: bool = None\n\n    @computed_field\n    @property\n    def type(self) -> str:\n        return SourceType.RASTER.value\n
    "},{"location":"api/sources/#maplibre.sources.SourceType","title":"SourceType","text":"

    Bases: Enum

    Source types

    Source code in maplibre/sources.py
    class SourceType(Enum):\n    \"\"\"Source types\"\"\"\n\n    RASTER = \"raster\"\n    VECTOR = \"vector\"\n    RASTER_DEM = \"raster-dem\"\n    GEOJSON = \"geojson\"\n    IMAGE = \"image\"\n    VIDEO = \"video\"\n
    "},{"location":"api/sources/#maplibre.sources.VectorTileSource","title":"VectorTileSource","text":"

    Bases: Source

    Vector tile source

    Examples:

    >>> from maplibre.sources import VectorTileSource\n>>> from maplibre import LayerType, Layer\n
    >>> vector_tile_source = VectorTileSource(\n...     tiles=[\"https://demotiles.maplibre.org/tiles/{z}/{x}/{y}.pbf\"],\n...     min_zoom=0,\n...     max_zoom=6,\n... )\n
    >>> layer = Layer(\n...     type=LayerType.LINE,\n...     id=\"countries\",\n...     source=vector_tile_source,\n...     source_layer=\"countries\",\n...     paint={\"fill-color\": \"lightgreen\", \"fill-outline-color\": \"black\"},\n... )\n
    Source code in maplibre/sources.py
    class VectorTileSource(Source):\n    \"\"\"Vector tile source\n\n    Examples:\n        >>> from maplibre.sources import VectorTileSource\n        >>> from maplibre import LayerType, Layer\n\n        >>> vector_tile_source = VectorTileSource(\n        ...     tiles=[\"https://demotiles.maplibre.org/tiles/{z}/{x}/{y}.pbf\"],\n        ...     min_zoom=0,\n        ...     max_zoom=6,\n        ... )\n\n        >>> layer = Layer(\n        ...     type=LayerType.LINE,\n        ...     id=\"countries\",\n        ...     source=vector_tile_source,\n        ...     source_layer=\"countries\",\n        ...     paint={\"fill-color\": \"lightgreen\", \"fill-outline-color\": \"black\"},\n        ... )\n    \"\"\"\n\n    attribution: str = None\n    bounds: tuple = None\n    max_zoom: int = Field(None, serialization_alias=\"maxzoom\")\n    min_zoom: int = Field(None, serialization_alias=\"minzoom\")\n    scheme: str = None\n    tiles: Union[tuple, list] = None\n    url: str = None\n    volatile: bool = None\n\n    @computed_field\n    @property\n    def type(self) -> str:\n        return SourceType.VECTOR.value\n
    "},{"location":"examples/3d_indoor_mapping/","title":"3D Indoor Mapping","text":"
    import sys\nimport webbrowser\n\nfrom maplibre import Layer, LayerType, Map, MapOptions\nfrom maplibre.basemaps import background\nfrom maplibre.sources import GeoJSONSource, RasterTileSource\n\nfile_name = \"/tmp/pymaplibregl_temp.html\"\n\nFLOORPLAN_SOURCE_ID = \"floorplan\"\n\nraster_source = RasterTileSource(\n    tiles=[\"https://tile.openstreetmap.org/{z}/{x}/{y}.png\"],\n    tile_size=256,\n    min_zoom=0,\n    max_zoom=19,\n)\n\nraster_layer = Layer(type=LayerType.RASTER, source=raster_source)\n\nfloorplan_source = GeoJSONSource(\n    data=\"https://maplibre.org/maplibre-gl-js/docs/assets/indoor-3d-map.geojson\"\n)\n\nfloorplan_layer = Layer(\n    type=LayerType.FILL_EXTRUSION,\n    id=\"floorplan\",\n    source=FLOORPLAN_SOURCE_ID,\n    paint={\n        \"fill-extrusion-color\": [\"get\", \"color\"],\n        \"fill-extrusion-height\": [\"get\", \"height\"],\n        \"fill-extrusion-base\": [\"get\", \"base_height\"],\n        \"fill-extrusion-opacity\": 0.5,\n    },\n)\n\nmap_options = MapOptions(\n    style=background(\"yellow\"),\n    center=(-87.61694, 41.86625),\n    zoom=17,\n    pitch=40,\n    bearing=20,\n    antialias=True,\n)\n\n\ndef create_map():\n    m = Map(map_options)\n    m.add_layer(raster_layer)\n    m.add_source(FLOORPLAN_SOURCE_ID, floorplan_source)\n    m.add_layer(floorplan_layer)\n    return m\n\n\nif __name__ == \"__main__\":\n    m = create_map()\n    if len(sys.argv) == 2:\n        file_name = sys.argv[1]\n\n    with open(file_name, \"w\") as f:\n        f.write(m.to_html())\n\n    webbrowser.open(file_name)\n

    Run example:

    poetry run python docs/examples/3d_indoor_mapping/app.py\n
    "},{"location":"examples/airports/","title":"Airport Markers","text":"
    import sys\n\nimport pandas as pd\nfrom maplibre import (\n    Layer,\n    LayerType,\n    Map,\n    MapOptions,\n    output_maplibregl,\n    render_maplibregl,\n)\nfrom maplibre.basemaps import Carto\nfrom maplibre.controls import (\n    Marker,\n    MarkerOptions,\n    NavigationControl,\n    Popup,\n    PopupOptions,\n)\nfrom maplibre.sources import GeoJSONSource\nfrom maplibre.utils import GeometryType, df_to_geojson\nfrom shiny import App, ui\n\nBOUNDS = (-8.92242886, 43.30508298, 13.76496714, 59.87668996)\n\nairports_data = pd.read_json(\n    \"https://github.com/visgl/deck.gl-data/raw/master/examples/line/airports.json\"\n)\n\n\ndef get_color(airport_type: str) -> str:\n    color = \"darkblue\"\n    if airport_type == \"mid\":\n        color = \"darkred\"\n    elif airport_type == \"major\":\n        color = \"darkgreen\"\n\n    return color\n\n\ngeojson = df_to_geojson(\n    airports_data,\n    \"coordinates\",\n    GeometryType.POINT,\n    properties=[\"type\", \"name\", \"abbrev\"],\n)\n\nairport_circles = Layer(\n    type=LayerType.CIRCLE,\n    source=GeoJSONSource(data=geojson),\n    paint={\n        \"circle-color\": [\n            \"match\",\n            [\"get\", \"type\"],\n            \"mid\",\n            \"darkred\",\n            \"major\",\n            \"darkgreen\",\n            \"darkblue\",\n        ],\n        \"circle_radius\": 10,\n        \"circle-opacity\": 0.3,\n    },\n)\n\nmap_options = MapOptions(\n    style=Carto.POSITRON,\n    bounds=BOUNDS,\n    fit_bounds_options={\"padding\": 20},\n    hash=True,\n)\n\npopup_options = PopupOptions(close_button=False)\n\n\ndef create_map() -> Map:\n    m = Map(map_options)\n    m.add_control(NavigationControl())\n    m.add_layer(airport_circles)\n    for _, r in airports_data.iterrows():\n        marker = Marker(\n            lng_lat=r[\"coordinates\"],\n            options=MarkerOptions(color=get_color(r[\"type\"])),\n            popup=Popup(\n                text=r[\"name\"],\n                options=popup_options,\n            ),\n        )\n        m.add_marker(marker)\n    return m\n\n\napp_ui = ui.page_fluid(\n    ui.panel_title(\"Airports\"),\n    output_maplibregl(\"maplibre\", height=600),\n)\n\n\ndef server(input, output, session):\n    @render_maplibregl\n    def maplibre():\n        return create_map()\n\n\napp = App(app_ui, server)\n\nif __name__ == \"__main__\":\n    if len(sys.argv) == 2:\n        with open(sys.argv[1], \"w\") as f:\n            f.write(create_map().to_html())\n    else:\n        app.run()\n

    Run example:

    poetry run uvicorn docs.examples.airports.app:app --reload\n
    "},{"location":"examples/custom_basemap/","title":"Custom basemap","text":"
    import webbrowser\n\nfrom maplibre import Layer, LayerType, Map, MapOptions\nfrom maplibre.basemaps import construct_basemap_style\nfrom maplibre.controls import NavigationControl\nfrom maplibre.sources import GeoJSONSource\n\nfile_name = \"docs/examples/custom_basemap/app.html\"\n\nbg_layer = Layer(\n    type=LayerType.BACKGROUND,\n    id=\"background\",\n    source=None,\n    paint={\"background-color\": \"darkblue\", \"background-opacity\": 0.8},\n)\n\ncountries_source = GeoJSONSource(\n    data=\"https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_110m_admin_0_countries.geojson\"\n)\n\nlines_layer = Layer(\n    type=LayerType.LINE,\n    source=\"countries\",\n    paint={\"line-color\": \"white\", \"line-width\": 1.5},\n)\n\npolygons_layer = Layer(\n    type=LayerType.FILL,\n    source=\"countries\",\n    paint={\"fill-color\": \"darkred\", \"fill-opacity\": 0.8},\n)\n\ncustom_basemap = construct_basemap_style(\n    layers=[bg_layer, polygons_layer, lines_layer],\n    sources={\"countries\": countries_source},\n)\n\n\nmap_options = MapOptions(\n    style=custom_basemap,\n    center=(0, 0),\n    zoom=2,\n)\n\n\ndef create_map():\n    m = Map(map_options)\n    m.add_control(NavigationControl())\n    m.add_layer(\n        Layer(\n            type=LayerType.CIRCLE,\n            id=\"earthquakes\",\n            source=GeoJSONSource(\n                data=\"https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson\"\n            ),\n            paint={\"circle-color\": \"yellow\", \"circle-radius\": 5},\n        )\n    )\n    m.add_popup(\"earthquakes\", \"mag\")\n    return m\n\n\nif __name__ == \"__main__\":\n    m = create_map()\n    with open(file_name, \"w\") as f:\n        f.write(m.to_html())\n\n    webbrowser.open(file_name)\n

    Run example:

    poetry run python docs/examples/custom_basemap/app.py\n
    "},{"location":"examples/deckgl_layer/","title":"Deck.GL Layer","text":"
    # Shiny Express App\n\nimport json\n\nfrom maplibre import Map, MapOptions, render_maplibregl\nfrom maplibre.basemaps import Carto\nfrom maplibre.controls import NavigationControl\nfrom maplibre.ui import use_deckgl\n\n# from shiny import reactive\nfrom shiny.express import input, render, ui\n\nm = Map(\n    MapOptions(\n        style=Carto.POSITRON,\n        center=(-122.4, 37.74),\n        zoom=12,\n        hash=True,\n        pitch=40,\n    )\n)\nm.add_control(NavigationControl())\n\ndeck_grid_layer = {\n    \"@@type\": \"GridLayer\",\n    \"id\": \"GridLayer\",\n    \"data\": \"https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json\",\n    \"extruded\": True,\n    \"getPosition\": \"@@=COORDINATES\",\n    \"getColorWeight\": \"@@=SPACES\",\n    \"getElevationWeight\": \"@@=SPACES\",\n    \"elevationScale\": 4,\n    \"cellSize\": 200,\n    \"pickable\": True,\n}\n\nm.add_deck_layers([deck_grid_layer], tooltip=\"Number of points: {{ count }}\")\n\n# Shiny Express\nuse_deckgl()\n\n\n@render_maplibregl\ndef render_map():\n    return m\n\n\n@render.code\ndef picking_object():\n    obj = input.render_map_layer_GridLayer()\n    print(obj)\n    return json.dumps(obj[\"points\"], indent=2) if obj else \"Pick a feature!\"\n\n\nif __name__ == \"__main__\":\n    with open(\"docs/examples/deckgl_layer/app.html\", \"w\") as f:\n        f.write(m.to_html())\n

    Run example:

    # Run Shiny app\nshiny run docs/examples/deckgl_layer/app.py\n
    "},{"location":"examples/deckgl_multiple_layers/","title":"Multiple Deck.GL Layers","text":"
    # Shiny Express App\n\nimport requests as req\nfrom maplibre import Map, MapOptions, render_maplibregl\nfrom maplibre.basemaps import Carto\nfrom maplibre.controls import NavigationControl\nfrom maplibre.ui import use_deckgl\nfrom shiny.express import input, render, ui\n\ndata = req.get(\n    \"https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_airports.geojson\"\n).json()\n\nm = Map(\n    MapOptions(\n        style=Carto.POSITRON,\n        center=(0.45, 51.47),\n        zoom=4,\n        hash=True,\n        pitch=30,\n    )\n)\nm.add_control(NavigationControl())\n\ndeck_geojson_layer = {\n    \"@@type\": \"GeoJsonLayer\",\n    \"id\": \"airports\",\n    \"data\": data,\n    \"filled\": True,\n    \"pointRadiusMinPixels\": 2,\n    \"pointRadiusScale\": 2000,\n    \"getPointRadius\": \"@@=11 - properties.scalerank\",\n    \"getFillColor\": [200, 0, 80, 180],\n    \"autoHighlight\": True,\n    \"pickable\": True,\n}\n\ndeck_arc_layer = {\n    \"@@type\": \"ArcLayer\",\n    \"id\": \"arcs\",\n    \"data\": [\n        feature\n        for feature in data[\"features\"]\n        if feature[\"properties\"][\"scalerank\"] < 4\n    ],\n    \"getSourcePosition\": [-0.4531566, 51.4709959],  # London\n    \"getTargetPosition\": \"@@=geometry.coordinates\",\n    \"getSourceColor\": [0, 128, 200],\n    \"getTargetColor\": [200, 0, 80],\n    \"getWidth\": 2,\n    \"pickable\": True,\n}\n\nm.add_deck_layers(\n    [deck_geojson_layer, deck_arc_layer],\n    tooltip={\n        \"airports\": \"{{ &properties.name }}\",\n        \"arcs\": \"gps_code: {{ properties.gps_code }}\",\n    },\n)\n\n# Shiny Express\nuse_deckgl()\n\n\n@render_maplibregl\ndef render_map():\n    return m\n\n\nif __name__ == \"__main__\":\n    with open(\"docs/examples/deckgl_multiple_layers/app.html\", \"w\") as f:\n        f.write(m.to_html())\n

    Run example:

    # Run Shiny app\npoetry run shiny run docs/examples/deckgl_multiple_layers/app.py\n
    "},{"location":"examples/earthquake_clusters/","title":"Earthquake Clusters","text":"
    import sys\n\nfrom maplibre import (\n    Layer,\n    LayerType,\n    Map,\n    MapOptions,\n    output_maplibregl,\n    render_maplibregl,\n)\nfrom maplibre.basemaps import Carto\nfrom maplibre.controls import NavigationControl\nfrom maplibre.sources import GeoJSONSource\nfrom shiny import App, reactive, ui\n\nEARTHQUAKE_SOURCE = \"earthquakes\"\nEARTHQUAKE_CIRCLES = \"earthquake-circles\"\nEARTHQUAKE_CLUSTERS = \"earthquake-clusters\"\nEARTHQUAKE_LABELS = \"earthquake-labels\"\n\nCENTER = (-118.0931, 33.78615)\n\nearthquakes_source = GeoJSONSource(\n    data=\"https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson\",\n    cluster=True,\n    cluster_radius=50,\n    cluster_min_points=2,\n    cluster_max_zoom=14,\n    cluster_properties={\n        \"maxMag\": [\"max\", [\"get\", \"mag\"]],\n        \"minMag\": [\"min\", [\"get\", \"mag\"]],\n    },\n)\n\nearthquake_circles = Layer(\n    type=LayerType.CIRCLE,\n    id=EARTHQUAKE_CIRCLES,\n    source=EARTHQUAKE_SOURCE,\n    paint={\"circle-color\": \"darkblue\"},\n    filter=[\"!\", [\"has\", \"point_count\"]],\n)\n\nearthquake_clusters = Layer(\n    type=LayerType.CIRCLE,\n    id=EARTHQUAKE_CLUSTERS,\n    source=EARTHQUAKE_SOURCE,\n    filter=[\"has\", \"point_count\"],\n    paint={\n        \"circle-color\": [\n            \"step\",\n            [\"get\", \"point_count\"],\n            \"#51bbd6\",\n            100,\n            \"#f1f075\",\n            750,\n            \"#f28cb1\",\n        ],\n        \"circle-radius\": [\"step\", [\"get\", \"point_count\"], 20, 100, 30, 750, 40],\n    },\n)\n\nearthquake_labels = Layer(\n    type=LayerType.SYMBOL,\n    id=\"text\",\n    source=EARTHQUAKE_SOURCE,\n    filter=[\"has\", \"point_count\"],\n    layout={\n        \"text-field\": [\"get\", \"point_count_abbreviated\"],\n        \"text-size\": 12,\n    },\n)\n\nmap_options = MapOptions(style=Carto.POSITRON, center=CENTER, zoom=3, hash=True)\n\n\ndef create_map() -> Map:\n    m = Map(map_options)\n    m.add_control(NavigationControl())\n    m.add_source(EARTHQUAKE_SOURCE, earthquakes_source)\n    m.add_layer(earthquake_clusters)\n    m.add_layer(earthquake_circles)\n    m.add_tooltip(EARTHQUAKE_CLUSTERS, \"maxMag\")\n    m.add_layer(earthquake_labels)\n    return m\n\n\napp_ui = ui.page_fluid(\n    ui.panel_title(\"Earthquakes Cluster\"),\n    output_maplibregl(\"maplibre\", height=500),\n)\n\n\ndef server(input, output, session):\n    @render_maplibregl\n    def maplibre():\n        return create_map()\n\n    @reactive.Effect\n    @reactive.event(input.maplibre)\n    async def result():\n        print(f\"result: {input.maplibre()}\")\n\n\napp = App(app_ui, server)\n\nif __name__ == \"__main__\":\n    if len(sys.argv) == 2:\n        file_name = sys.argv[1]\n        with open(file_name, \"w\") as f:\n            f.write(create_map().to_html())\n    else:\n        app.run()\n

    Run example:

    poetry run uvicorn docs.examples.earthquake_clusters.app:app --reload\n
    "},{"location":"examples/every_person_in_manhattan/","title":"Every Person in Manhattan","text":"
    import json\nimport sys\n\nimport pandas as pd\nimport shapely\nfrom maplibre import (\n    Layer,\n    LayerType,\n    Map,\n    MapContext,\n    MapOptions,\n    output_maplibregl,\n    render_maplibregl,\n)\nfrom maplibre.basemaps import Carto\nfrom maplibre.controls import NavigationControl, ScaleControl\nfrom maplibre.sources import GeoJSONSource\nfrom maplibre.utils import df_to_geojson\nfrom shiny import App, reactive, ui\n\nMALE_COLOR = \"rgb(0, 128, 255)\"\nFEMALE_COLOR = \"rgb(255, 0, 128)\"\nLAYER_ID = \"every-person-in-manhattan-circles\"\nCIRCLE_RADIUS = 2\n\npoint_data = pd.read_json(\n    \"https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/scatterplot/manhattan.json\"\n)\n\npoint_data.columns = [\"lng\", \"lat\", \"sex\"]\n\nevery_person_in_manhattan_source = GeoJSONSource(\n    data=df_to_geojson(point_data, properties=[\"sex\"]),\n)\n\nbbox = shapely.bounds(\n    shapely.from_geojson(json.dumps(every_person_in_manhattan_source.data))\n)\n\nevery_person_in_manhattan_circles = Layer(\n    type=LayerType.CIRCLE,\n    id=LAYER_ID,\n    source=every_person_in_manhattan_source,\n    paint={\n        \"circle-color\": [\"match\", [\"get\", \"sex\"], 1, MALE_COLOR, FEMALE_COLOR],\n        \"circle-radius\": CIRCLE_RADIUS,\n    },\n)\n\nmap_options = MapOptions(\n    style=Carto.POSITRON,\n    bounds=tuple(bbox),\n    fit_bounds_options={\"padding\": 20},\n    hash=True,\n)\n\n\ndef create_map() -> Map:\n    m = Map(map_options)\n    m.add_control(NavigationControl())\n    m.add_control(ScaleControl(), position=\"bottom-left\")\n    m.add_layer(every_person_in_manhattan_circles)\n    return m\n\n\napp_ui = ui.page_fluid(\n    ui.panel_title(\"Every Person in Manhattan\"),\n    output_maplibregl(\"maplibre\", height=600),\n    ui.input_slider(\"radius\", \"Radius\", value=CIRCLE_RADIUS, min=1, max=5),\n)\n\n\ndef server(input, output, session):\n    @render_maplibregl\n    def maplibre():\n        return create_map()\n\n    @reactive.Effect\n    @reactive.event(input.radius, ignore_init=True)\n    async def radius():\n        async with MapContext(\"maplibre\") as m:\n            m.set_paint_property(LAYER_ID, \"circle-radius\", input.radius())\n\n\napp = App(app_ui, server)\n\nif __name__ == \"__main__\":\n    if len(sys.argv) == 2:\n        file_name = sys.argv[1]\n        with open(file_name, \"w\") as f:\n            f.write(create_map().to_html())\n    else:\n        app.run()\n

    Run example:

    poetry run uvicorn docs.examples.every_person_in_manhattan.app:app --reload\n
    "},{"location":"examples/geopandas/","title":"GeoPandas","text":"
    import sys\nimport webbrowser\n\nfrom maplibre import Layer, LayerType, Map, MapOptions\nfrom maplibre.controls import NavigationControl\nfrom maplibre.sources import GeoJSONSource\nfrom maplibre.utils import geopandas_to_geojson\n\nimport geopandas as gpd\n\nfile_name = \"/tmp/pymaplibregl_temp.html\"\nLAYER_ID = \"wilderness\"\n\ndf_geo = gpd.read_file(\n    \"zip+https://github.com/Toblerity/Fiona/files/11151652/coutwildrnp.zip\"\n)\n\nwilderness_source = GeoJSONSource(data=geopandas_to_geojson(df_geo))\n\nwilderness_layer = Layer(\n    type=LayerType.FILL,\n    id=LAYER_ID,\n    source=wilderness_source,\n    paint={\"fill-color\": \"darkred\", \"fill-opacity\": 0.5},\n)\n\nmap_options = MapOptions(bounds=df_geo.total_bounds)\n\n\ndef create_map():\n    m = Map(map_options)\n    m.add_control(NavigationControl())\n    m.add_layer(wilderness_layer)\n    m.add_tooltip(LAYER_ID, \"NAME\")\n    return m\n\n\nif __name__ == \"__main__\":\n    m = create_map()\n    if len(sys.argv) == 2:\n        file_name = sys.argv[1]\n\n    with open(file_name, \"w\") as f:\n        f.write(m.to_html())\n\n    webbrowser.open(file_name)\n

    Run example:

    poetry run python docs/examples/geopandas/app.py\n
    "},{"location":"examples/layer_order/","title":"Layer Order","text":"

    Add a data layer below the labels of the basemap.

    # Shiny Express App\n\nimport requests as req\nfrom maplibre import Layer, LayerType, Map, MapOptions, render_maplibregl\nfrom maplibre.basemaps import Carto, construct_carto_basemap_url\nfrom maplibre.controls import NavigationControl\nfrom maplibre.sources import GeoJSONSource\nfrom shiny.express import input, render, ui\n\nstyle = req.get(construct_carto_basemap_url(Carto.VOYAGER)).json()\n\n\nsymbol_ids = [layer[\"id\"] for layer in style[\"layers\"] if layer[\"type\"] == \"symbol\"]\n# print(symbol_ids)\n\nurban_areas = GeoJSONSource(\n    data=\"https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_urban_areas.geojson\"\n)\n\nm = Map(\n    MapOptions(\n        style=style,\n        center=(-89.928, 35.204),\n        # center=(-88.13734351262877, 35.137451890638886),\n        zoom=9,\n        hash=True,\n    )\n)\nm.add_control(NavigationControl())\nm.add_layer(\n    Layer(\n        id=\"urban-areas-fill\",\n        type=LayerType.FILL,\n        source=urban_areas,\n        paint={\"fill-color\": \"pink\", \"fill-opacity\": 1.0},\n    ),\n    before_id=symbol_ids[0],\n)\nfor symbol_id in symbol_ids:\n    m.set_paint_property(symbol_id, \"text-color\", \"purple\")\n\n\n@render_maplibregl\ndef render_map():\n    return m\n\n\nif __name__ == \"__main__\":\n    with open(\"docs/examples/layer_order/app.html\", \"w\") as f:\n        f.write(m.to_html())\n

    Run example:

    poetry run shiny run docs/examples/layer_order/app.py\n
    "},{"location":"examples/layer_switcher/","title":"Layer Switcher","text":"
    # Example taken from here: https://maplibre.org/maplibre-gl-js/docs/examples/pmtiles/\n\nimport webbrowser\n\nfrom maplibre import Layer, LayerType, Map, MapOptions, render_maplibregl\nfrom maplibre.basemaps import construct_basemap_style\nfrom maplibre.controls import (\n    ControlPosition,\n    InfoBoxControl,\n    LayerSwitcherControl,\n    NavigationControl,\n)\nfrom shiny.express import input, render, ui\n\nPMTILES_URL = \"https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles\"\n\npmtiles_source = {\n    \"type\": \"vector\",\n    \"url\": f\"pmtiles://{PMTILES_URL}\",\n    \"attribution\": '\u00a9 <a href=\"https://openstreetmap.org\">OpenStreetMap</a>',\n}\n\ncustom_basemap = construct_basemap_style(\n    sources={\"pmtiles\": pmtiles_source},\n    layers=[\n        Layer(\n            id=\"buildings\",\n            source=\"pmtiles\",\n            source_layer=\"landuse\",\n            type=LayerType.FILL,\n            paint={\"fill-color\": \"red\"},\n            layout={\"visibility\": \"none\"},\n        ),\n        Layer(\n            id=\"roads\",\n            source=\"pmtiles\",\n            source_layer=\"roads\",\n            type=LayerType.LINE,\n            paint={\"line-color\": \"black\"},\n        ),\n        Layer(\n            id=\"mask\",\n            source=\"pmtiles\",\n            source_layer=\"mask\",\n            type=LayerType.FILL,\n            paint={\"fill-color\": \"yellow\"},\n            layout={\"visibility\": \"none\"},\n        ),\n    ],\n)\n\n\nmap_options = MapOptions(\n    style=custom_basemap,\n    # hash=True,\n    # bounds=(11.154026, 43.7270125, 11.3289395, 43.8325455),\n    center=(11.2415, 43.7798),\n    zoom=12.5,\n)\n\n\ndef create_map():\n    m = Map(map_options)\n    m.add_control(NavigationControl())\n    m.add_control(\n        InfoBoxControl(\n            content=\"Toggle layers of PMTiles source\",\n            css_text=\"padding: 10px; background-color: yellow; color: steelblue; font-weight: bold;\",\n        ),\n        ControlPosition.TOP_LEFT,\n    )\n    m.add_control(\n        LayerSwitcherControl(\n            layer_ids=[\"buildings\", \"roads\", \"mask\"],\n            css_text=\"padding: 5px; border: 1px solid darkgrey; border-radius: 4px;\",\n        ),\n        position=ControlPosition.TOP_LEFT,\n    )\n    return m\n\n\n@render_maplibregl\ndef render_map():\n    return create_map()\n\n\nif __name__ == \"__main__\":\n    file_name = \"docs/examples/layer_switcher/app.html\"\n\n    m = create_map()\n    with open(file_name, \"w\") as f:\n        f.write(m.to_html())\n\n    webbrowser.open(file_name)\n

    Run example:

    shiny run docs/examples/layer_switcher/app.py\n
    "},{"location":"examples/mapbox_draw_plugin/","title":"Mapbox Draw Plugin","text":"
    # Shiny Express App\n\nimport json\nimport webbrowser\n\nfrom maplibre import Map, MapOptions, render_maplibregl\nfrom maplibre.basemaps import Carto\nfrom maplibre.controls import ControlPosition, NavigationControl, ScaleControl\nfrom maplibre.plugins import MapboxDrawControls, MapboxDrawOptions\nfrom maplibre.ui import use_mapboxgl_draw\n\n# from shiny import reactive\nfrom shiny.express import input, render, ui\n\ngeojson_feature = {\n    \"id\": \"xyz\",\n    \"type\": \"Feature\",\n    \"properties\": {},\n    \"geometry\": {\n        \"coordinates\": [\n            [\n                [-122.4523683552298, 37.775540942000546],\n                [-122.41910082339776, 37.75932501909665],\n                [-122.43487191413453, 37.72543546737114],\n                [-122.46053073611722, 37.729612763886834],\n                [-122.4523683552298, 37.775540942000546],\n            ]\n        ],\n        \"type\": \"Polygon\",\n    },\n}\n\nm = Map(\n    MapOptions(\n        style=Carto.POSITRON,\n        center=(-122.4, 37.74),\n        zoom=12,\n        hash=True,\n        pitch=40,\n    )\n)\nm.add_control(NavigationControl())\nm.add_control(ScaleControl(), ControlPosition.BOTTOM_LEFT)\n\n# Optional: only activate a given set of controls\ndraw_options = MapboxDrawOptions(\n    display_controls_default=False,\n    controls=MapboxDrawControls(polygon=True, line_string=True, trash=True),\n)\n# Use options from above\n# m.add_mapbox_draw(draw_options, geojson=geojson_feature)\n\n# If no options are passed, all controls are activated by default\nm.add_mapbox_draw(geojson=geojson_feature)\n\n# Shiny Express\nuse_mapboxgl_draw()\n\n\n@render_maplibregl\ndef maplibre():\n    return m\n\n\n@render.code\ndef selected_features():\n    obj = input.maplibre_draw_features_selected()\n    print(obj)\n    return json.dumps(obj[\"features\"], indent=2) if obj else \"Pick some features!\"\n\n\nif __name__ == \"__main__\":\n    filename = \"docs/examples/mapbox_draw_plugin/app.html\"\n    with open(filename, \"w\") as f:\n        f.write(m.to_html())\n\n    webbrowser.open(filename)\n

    Run example:

    shiny run docs/examples/mapbox_draw_plugin/app.py\n
    "},{"location":"examples/pmtiles/","title":"PMTiles","text":"
    # Example taken from here: https://maplibre.org/maplibre-gl-js/docs/examples/pmtiles/\n\nimport webbrowser\n\nfrom maplibre import Layer, LayerType, Map, MapOptions, render_maplibregl\nfrom maplibre.basemaps import construct_basemap_style\nfrom maplibre.controls import NavigationControl\nfrom shiny.express import input, render, ui\n\nPMTILES_URL = \"https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles\"\n\npmtiles_source = {\n    \"type\": \"vector\",\n    \"url\": f\"pmtiles://{PMTILES_URL}\",\n    \"attribution\": '\u00a9 <a href=\"https://openstreetmap.org\">OpenStreetMap</a>',\n}\n\ncustom_basemap = construct_basemap_style(\n    sources={\"pmtiles\": pmtiles_source},\n    layers=[\n        Layer(\n            id=\"buildings\",\n            source=\"pmtiles\",\n            source_layer=\"landuse\",\n            type=LayerType.FILL,\n            paint={\"fill-color\": \"steelblue\"},\n        ),\n        Layer(\n            id=\"roads\",\n            source=\"pmtiles\",\n            source_layer=\"roads\",\n            type=LayerType.LINE,\n            paint={\"line-color\": \"black\"},\n        ),\n        Layer(\n            id=\"mask\",\n            source=\"pmtiles\",\n            source_layer=\"mask\",\n            type=LayerType.FILL,\n            paint={\"fill-color\": \"white\"},\n        ),\n    ],\n)\n\n\nmap_options = MapOptions(\n    style=custom_basemap,\n    bounds=(11.154026, 43.7270125, 11.3289395, 43.8325455),\n)\n\n\ndef create_map():\n    m = Map(map_options)\n    m.add_control(NavigationControl())\n    return m\n\n\n@render_maplibregl\ndef render_map():\n    return create_map()\n\n\nif __name__ == \"__main__\":\n    file_name = \"docs/examples/pmtiles/app.html\"\n\n    m = create_map()\n    with open(file_name, \"w\") as f:\n        f.write(m.to_html())\n\n    webbrowser.open(file_name)\n

    Run example:

    shiny run docs/examples/pmtiles/app.py\n
    "},{"location":"examples/road_safety/","title":"H3 Grid UK Road Safety","text":"
    import sys\nimport webbrowser\n\nimport h3\nimport pandas as pd\nfrom maplibre import (\n    Layer,\n    LayerType,\n    Map,\n    MapContext,\n    MapOptions,\n    output_maplibregl,\n    render_maplibregl,\n)\nfrom maplibre.controls import NavigationControl\nfrom maplibre.sources import GeoJSONSource\nfrom maplibre.utils import df_to_geojson\nfrom shiny import App, reactive, ui\n\nRESOLUTION = 7\nCOLORS = (\n    \"lightblue\",\n    \"turquoise\",\n    \"lightgreen\",\n    \"yellow\",\n    \"orange\",\n    \"darkred\",\n)\n\nroad_safety = pd.read_csv(\n    \"https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv\"\n).dropna()\n\n\ndef create_h3_grid(res=RESOLUTION) -> dict:\n    road_safety[\"h3\"] = road_safety.apply(\n        lambda x: h3.geo_to_h3(x[\"lat\"], x[\"lng\"], resolution=res), axis=1\n    )\n    df = road_safety.groupby(\"h3\").h3.agg(\"count\").to_frame(\"count\").reset_index()\n    df[\"hexagon\"] = df.apply(\n        lambda x: [h3.h3_to_geo_boundary(x[\"h3\"], geo_json=True)], axis=1\n    )\n    df[\"color\"] = pd.cut(\n        df[\"count\"],\n        bins=len(COLORS),\n        labels=COLORS,\n    )\n    return df_to_geojson(\n        df, \"hexagon\", geometry_type=\"Polygon\", properties=[\"count\", \"color\"]\n    )\n\n\nsource = GeoJSONSource(data=create_h3_grid())\n\nmap_options = MapOptions(\n    center=(-1.415727, 52.232395),\n    zoom=7,\n    pitch=40,\n    bearing=-27,\n)\n\nh3_layer = Layer(\n    id=\"road-safety\",\n    type=LayerType.FILL_EXTRUSION,\n    source=source,\n    paint={\n        \"fill-extrusion-color\": [\"get\", \"color\"],\n        \"fill-extrusion-opacity\": 0.7,\n        \"fill-extrusion-height\": [\"*\", 100, [\"get\", \"count\"]],\n    },\n)\n\n\ndef create_map() -> Map:\n    m = Map(map_options)\n    m.add_control(NavigationControl())\n    m.add_layer(h3_layer)\n    m.add_tooltip(\"road-safety\", \"count\")\n    return m\n\n\napp_ui = ui.page_fluid(\n    ui.panel_title(\"Road safety in UK\"),\n    output_maplibregl(\"mapylibre\", height=700),\n    ui.input_slider(\"res\", \"Resolution\", min=4, max=8, step=1, value=RESOLUTION),\n)\n\n\ndef server(input, output, session):\n    @render_maplibregl\n    def mapylibre():\n        return create_map()\n\n    @reactive.Effect\n    @reactive.event(input.res, ignore_init=True)\n    async def resolution():\n        async with MapContext(\"mapylibre\") as m:\n            with ui.Progress() as p:\n                p.set(message=\"H3 calculation in progress\")\n                m.set_data(\"road-safety\", create_h3_grid(input.res()))\n                p.set(1, message=\"Calculation finished\")\n\n\napp = App(app_ui, server)\n\nif __name__ == \"__main__\":\n    filename = sys.argv[1] if len(sys.argv) == 2 else \"/tmp/road_safety.html\"\n    with open(filename, \"w\") as f:\n        m = create_map()\n        f.write(m.to_html(style=\"height: 700px;\"))\n\n    webbrowser.open(filename)\n

    Run example:

    poetry run uvicorn docs.examples.road_safety.app:app --reload\n
    "},{"location":"examples/vancouver_blocks/","title":"Vancouver Property Value","text":"
    import sys\n\nfrom maplibre import (\n    Layer,\n    LayerType,\n    Map,\n    MapContext,\n    MapOptions,\n    output_maplibregl,\n    render_maplibregl,\n)\nfrom maplibre.basemaps import Carto\nfrom maplibre.controls import NavigationControl, ScaleControl\nfrom maplibre.sources import GeoJSONSource\nfrom shiny import App, reactive, ui\n\nSOURCE_ID = \"vancouver-blocks\"\nLAYER_ID_LINES = \"vancouver-blocks-lines\"\nLAYER_ID_FILL = \"vancouver-blocks-fill-extrusion\"\nMAX_FILTER_VALUE = 1000000\n\nvancouver_blocks_source = GeoJSONSource(\n    data=\"https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/geojson/vancouver-blocks.json\"\n)\n\nvancouver_blocks_lines = Layer(\n    type=LayerType.LINE,\n    id=LAYER_ID_LINES,\n    source=SOURCE_ID,\n    paint={\n        \"line-color\": \"white\",\n        \"line-width\": 2,\n    },\n)\n\nvancouver_blocks_fill = Layer(\n    type=LayerType.FILL_EXTRUSION,\n    id=LAYER_ID_FILL,\n    source=SOURCE_ID,\n    paint={\n        \"fill-extrusion-color\": {\n            \"property\": \"valuePerSqm\",\n            \"stops\": [\n                [0, \"grey\"],\n                [1000, \"yellow\"],\n                [5000, \"orange\"],\n                [10000, \"darkred\"],\n                [50000, \"lightblue\"],\n            ],\n        },\n        \"fill-extrusion-height\": [\"*\", 10, [\"sqrt\", [\"get\", \"valuePerSqm\"]]],\n        \"fill-extrusion-opacity\": 0.9,\n    },\n)\n\nmap_options = MapOptions(\n    style=Carto.DARK_MATTER,\n    center=(-123.13, 49.254),\n    zoom=11,\n    pitch=45,\n    bearing=0,\n)\n\n\ndef create_map() -> Map:\n    m = Map(map_options)\n    m.add_control(NavigationControl())\n    m.add_control(ScaleControl(), position=\"bottom-left\")\n    m.add_source(SOURCE_ID, vancouver_blocks_source)\n    m.add_layer(vancouver_blocks_lines)\n    m.add_layer(vancouver_blocks_fill)\n    m.add_tooltip(LAYER_ID_FILL, \"valuePerSqm\")\n    return m\n\n\napp_ui = ui.page_fluid(\n    ui.panel_title(\"Vancouver Property Value\"),\n    ui.div(\n        \"Height of polygons - average property value per square meter of lot\",\n        style=\"padding: 10px;\",\n    ),\n    output_maplibregl(\"maplibre\", height=600),\n    ui.input_select(\n        \"filter\",\n        \"max property value per square meter\",\n        choices=[0, 1000, 5000, 10000, 50000, 100000, MAX_FILTER_VALUE],\n        selected=MAX_FILTER_VALUE,\n    ),\n    ui.input_checkbox_group(\n        \"layers\",\n        \"Layers\",\n        choices=[LAYER_ID_FILL, LAYER_ID_LINES],\n        selected=[LAYER_ID_FILL, LAYER_ID_LINES],\n    ),\n)\n\n\ndef server(input, output, session):\n    @render_maplibregl\n    def maplibre():\n        m = create_map()\n        return m\n\n    @reactive.Effect\n    @reactive.event(input.filter)\n    async def filter():\n        async with MapContext(\"maplibre\") as m:\n            filter_ = [\"<=\", [\"get\", \"valuePerSqm\"], int(input.filter())]\n            m.set_filter(LAYER_ID_FILL, filter_)\n\n    @reactive.Effect\n    @reactive.event(input.layers)\n    async def layers():\n        visible_layers = input.layers()\n        async with MapContext(\"maplibre\") as m:\n            for layer in [LAYER_ID_FILL, LAYER_ID_LINES]:\n                m.set_visibility(layer, layer in visible_layers)\n\n\napp = App(app_ui, server)\n\nif __name__ == \"__main__\":\n    if len(sys.argv) == 2:\n        file_name = sys.argv[1]\n        m = create_map()\n        with open(file_name, \"w\") as f:\n            f.write(m.to_html())\n    else:\n        app.run()\n

    Run example:

    poetry run uvicorn docs.examples.vancouver_blocks.app:app --reload\n
    "},{"location":"examples/vector_tiles/","title":"Vector Tiles","text":"
    # Example taken from here:\n# https://maplibre.org/maplibre-gl-js/docs/API/classes/VectorTileSource/\n# https://maplibre.org/maplibre-style-spec/sources/\n\nimport webbrowser\n\nfrom maplibre import Layer, LayerType, Map, MapOptions, render_maplibregl\nfrom maplibre.basemaps import Carto\nfrom maplibre.controls import NavigationControl\nfrom maplibre.sources import VectorTileSource\nfrom shiny.express import input, render, ui\n\n# Get layer ids and pbf url from here\nVECTOR_TILES_URL = \"https://demotiles.maplibre.org/tiles/tiles.json\"\nLAYER_ID = \"countries\"\n\nvector_source = VectorTileSource(\n    url=VECTOR_TILES_URL,\n    # tiles=[\"https://demotiles.maplibre.org/tiles/{z}/{x}/{y}.pbf\"],\n    min_zoom=0,\n    max_zoom=6,\n)\n\nvector_layer = Layer(\n    type=LayerType.FILL,\n    id=LAYER_ID,\n    source=vector_source,\n    paint={\"fill-color\": \"lightgreen\", \"fill-outline-color\": \"black\"},\n    source_layer=\"countries\",\n)\n\n\ndef create_map():\n    m = Map(MapOptions(style=Carto.POSITRON, center=(11, 42), zoom=3, hash=True))\n    m.add_control(NavigationControl())\n    m.add_layer(vector_layer)\n    m.add_tooltip(LAYER_ID)\n    return m\n\n\n@render_maplibregl\ndef render_map():\n    return create_map()\n\n\nif __name__ == \"__main__\":\n    file_name = \"docs/examples/vector_tiles/app.html\"\n\n    m = create_map()\n    with open(file_name, \"w\") as f:\n        f.write(m.to_html())\n\n    webbrowser.open(file_name)\n

    Run example:

    shiny run docs/examples/vector_tiles/app.py\n
    "},{"location":"examples/where_is_the_iss/","title":"Where is the ISS","text":"
    import requests\nfrom maplibre import (\n    Layer,\n    LayerType,\n    Map,\n    MapContext,\n    MapOptions,\n    output_maplibregl,\n    render_maplibregl,\n)\nfrom maplibre.controls import NavigationControl\nfrom maplibre.sources import GeoJSONSource\nfrom shiny import App, reactive, ui\n\nMAX_FEATURES = 30\nSOURCE_ID_ISS_POSITION = \"iss_position\"\nSOURCE_ID_ISS_LAST_POSITIONS = \"iss-last-positions\"\n\n\ndef where_is_the_iss() -> tuple:\n    r = requests.get(\"https://api.wheretheiss.at/v1/satellites/25544\").json()\n    return (r[\"longitude\"], r[\"latitude\"])\n\n\ndef create_feature(lng_lat: tuple) -> dict:\n    return {\n        \"type\": \"Feature\",\n        \"geometry\": {\n            \"type\": \"Point\",\n            \"coordinates\": lng_lat,\n        },\n        \"properties\": {\"coords\": \", \".join(map(str, lng_lat))},\n    }\n\n\nlng_lat = where_is_the_iss()\nfeature = create_feature(where_is_the_iss())\nfeature_collection = {\"type\": \"FeatureCollection\", \"features\": [feature]}\n\napp_ui = ui.page_fluid(\n    ui.panel_title(\"Where is the ISS\"),\n    ui.div(\"Click on Update to get the current position of the ISS.\"),\n    ui.div(\n        \"The yellow dots show the positions before. Hover the blue dot to display the coordinates.\"\n    ),\n    output_maplibregl(\"mapylibre\", height=600),\n    ui.input_action_button(\"update\", \"Update\"),\n)\n\n\ndef server(input, output, session):\n    @render_maplibregl\n    def mapylibre():\n        m = Map(MapOptions(center=lng_lat, zoom=3))\n        m.add_control(NavigationControl())\n        m.set_paint_property(\"water\", \"fill-color\", \"darkblue\")\n        m.add_source(\n            SOURCE_ID_ISS_LAST_POSITIONS, GeoJSONSource(data=feature_collection)\n        )\n        m.add_layer(\n            Layer(\n                type=LayerType.CIRCLE,\n                source=SOURCE_ID_ISS_LAST_POSITIONS,\n                paint={\"circle-color\": \"yellow\", \"circle-radius\": 5},\n            ),\n        )\n        m.add_source(SOURCE_ID_ISS_POSITION, GeoJSONSource(data=feature))\n        m.add_layer(\n            Layer(\n                type=LayerType.CIRCLE,\n                id=\"iss-position\",\n                source=SOURCE_ID_ISS_POSITION,\n                paint={\"circle-color\": \"lightblue\", \"circle-radius\": 7},\n            )\n        )\n        m.add_tooltip(\"iss-position\", \"coords\")\n        return m\n\n    @reactive.Effect\n    @reactive.event(input.update)\n    async def update():\n        print(\"Fetching new position\")\n        lng_lat = where_is_the_iss()\n        print(lng_lat)\n        if len(feature_collection[\"features\"]) == MAX_FEATURES:\n            feature_collection[\"features\"] = []\n\n        async with MapContext(\"mapylibre\") as m:\n            feature = create_feature(lng_lat)\n            m.set_data(SOURCE_ID_ISS_POSITION, feature)\n            feature_collection[\"features\"].append(feature)\n            m.set_data(SOURCE_ID_ISS_LAST_POSITIONS, feature_collection)\n            m.add_call(\"flyTo\", {\"center\": lng_lat, \"speed\": 0.5})\n\n\napp = App(app_ui, server)\n

    Run example:

    poetry run uvicorn docs.examples.where_is_the_iss.app:app --reload\n
    "},{"location":"examples/wms/","title":"WMS","text":"
    import webbrowser\n\nfrom maplibre import Layer, LayerType, Map, MapOptions\nfrom maplibre.controls import NavigationControl\nfrom maplibre.sources import RasterTileSource\n\nSOURCE_ID = \"wms-test-source\"\nLAYER_ID = \"wms-test-layer\"\n\nwms_source = RasterTileSource(\n    tiles=[\n        \"https://img.nj.gov/imagerywms/Natural2015?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.1&request=GetMap&srs=EPSG:3857&transparent=true&width=256&height=256&layers=Natural2015\"\n    ],\n    tile_size=256,\n)\nwms_layer = Layer(type=LayerType.RASTER, source=SOURCE_ID, id=LAYER_ID)\n\nmap_options = MapOptions(zoom=8, center=(-74.5447, 40.6892))\n\nm = Map(map_options=map_options)\nm.add_control(NavigationControl())\nm.add_source(SOURCE_ID, wms_source)\nm.add_layer(wms_layer)\n\nfilename = \"docs/examples/wms/app.html\"\n\nwith open(filename, \"w\") as f:\n    f.write(m.to_html(style=\"height: 800px;\"))\n\nwebbrowser.open(filename)\n
    "}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"MapLibre for Python","text":"

    MapLibre for Python provides Python bindings for MapLibre GL JS.

    It integrates seamlessly into Shiny for Python and Jupyter.

    "},{"location":"#installation","title":"Installation","text":"
    # Stable\npip install maplibre\n\npip install \"maplibre[all]\"\n\n# Dev\npip install git+https://github.com/eoda-dev/py-maplibregl@dev\n
    "},{"location":"#basic-usage","title":"Basic usage","text":""},{"location":"#standalone","title":"Standalone","text":"
    from maplibre import Layer, LayerType, Map, MapOptions\nfrom maplibre.sources import GeoJSONSource\n\nvancouver_blocks = GeoJSONSource(\n    data=\"https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/geojson/vancouver-blocks.json\",\n)\n\nmap_options = MapOptions(\n    center=(-123.1256, 49.24658),\n    zoom=12,\n    hash=True,\n    pitch=35,\n)\n\nm = Map(map_options)\nm.add_layer(\n    Layer(\n        type=LayerType.LINE,\n        source=vancouver_blocks,\n        paint={\"line-color\": \"white\"},\n    )\n)\n\nm.save(preview=True)\n
    "},{"location":"#shiny-integration","title":"Shiny integration","text":"
    from maplibre import Map, MapContext, output_maplibregl, render_maplibregl\nfrom maplibre.controls import Marker\nfrom shiny import App, reactive, ui\n\napp_ui = ui.page_fluid(\n    output_maplibregl(\"maplibre_map\", height=600),\n    ui.div(\"Click on map to set a marker\"),\n)\n\n\ndef server(input, output, session):\n    @render_maplibregl\n    def maplibre_map():\n        return Map()\n\n    @reactive.Effect\n    @reactive.event(input.maplibre_map_clicked)\n    async def coords():\n        async with MapContext(\"maplibre_map\") as m:\n            input_value = input.maplibre_map_clicked()\n            print(input_value)\n            lng_lat = tuple(input_value[\"coords\"].values())\n            marker = Marker(lng_lat=lng_lat)\n            m.add_marker(marker)\n            m.add_call(\"flyTo\", {\"center\": lng_lat})\n\n\napp = App(app_ui, server)\n\nif __name__ == \"__main__\":\n    app.run()\n
    "},{"location":"#jupyter-widget","title":"Jupyter widget","text":"
    from maplibre.ipywidget import MapWidget as Map\n\nm = Map()\nm\n
    "},{"location":"changelog/","title":"Changelog for MapLibre for Python","text":""},{"location":"changelog/#maplibre-v027","title":"maplibre v0.2.7","text":"
    • Add basemaps.MapTiler

    • Add maplibre.__future__

    • Add Map.fit_bounds

    • Add expression helpers maplibre.expressions:

    • interpolate
    • step_expr
    • quantile_expr
    • match_expr
    • color_step_expr
    • color_quantile_expr
    • color_match_expr
    • filter_expr
    • range_filter

    • Add support for pydeck.Layer for

    • Map.add_deck_layers and
    • Map.set_deck_layers

    • Add sources.SimpleFeatures for geopandas.GeoDataFrame sources

    • Support geopandas.GeoDataFrame as source in

    • Layer and
    • Map.add_source

    • Add more parameters to Map class for simpler map initialization:

    • layers: list
    • sources: dict
    • controls: list

    • Add position attribute to Control classes

    • Add sources.VectorTileSource (Martenz)

    • Shiny

    • Add input.{output_id}_view_state dict containing {\"center\", \"zoom\", \"bounds\", \"pitch\", \"bearing\"}
    • Rename input.{output_id} to input.{output_id}_clicked
    • Rename input.{output_id}_layer_{layer_id} to input.{output_id}_feature_clicked returning layer_id

    • Ipywidget

    • Add Map.view_state dict containing {\"center\", \"zoom\", \"bounds\", \"pitch\", \"bearing\"} (#89)
    • Remove Map.center, Map.zoom, Map.bounds
    "},{"location":"changelog/#maplibre-v026","title":"maplibre v0.2.6","text":"
    • Add function in maplibre.utils to save map and display it in the browser
    • Add streamlit.components.v1.iframe component
    "},{"location":"changelog/#maplibre-v025","title":"maplibre v0.2.5","text":"
    • Add custom LayerSwitcherControl (#69)
    • Add custom InfoBoxControl (#74)
    "},{"location":"changelog/#maplibre-v024","title":"maplibre v0.2.4","text":"
    • Add MapboxDraw plugin (#59)
    • Add Shiny input concerned to MapboxDraw:
    • <output_id>.draw_features_selected
    • Add interactive attributes to IpyWidget concerned to MapboxDraw:
    • Map.draw_features_selected (list)
    • Map.draw_feature_collection_all (dict)
    "},{"location":"changelog/#maplibre-v023","title":"maplibre v0.2.3","text":"
    • Add interactive attributes to IpyWidget
    • Map.center
    • Map.bounds
    • Map.zoom
    • Map.lat_lng > Map.clicked (rename)
    • Change map option types
    • MapOptions.zoom: int > Union[int, float]
    • MapOptions.bearing: int > Union[int, float]
    • MapOptions.pitch: int > Union[int, float]
    • Add conda badges and installation instructions
    "},{"location":"changelog/#maplibre-v022","title":"maplibre v0.2.2","text":"
    • Add support for PMTiles (#55)
    "},{"location":"changelog/#maplibre-v021","title":"maplibre v0.2.1","text":"
    • Do not add navigation control by default (#31)
    "},{"location":"changelog/#maplibre-v020","title":"maplibre v0.2.0","text":"
    • Support Deck.GL layers (#28)
    "},{"location":"changelog/#maplibre-v016","title":"maplibre v0.1.6","text":"
    • Add before_id parameter to add_layer method (#45, #47)
    • Add example showing how to insert a layer before labels
    "},{"location":"changelog/#maplibre-v015","title":"maplibre v0.1.5","text":"
    • Update deprecated render function to support shiny>=0.7.0
    "},{"location":"changelog/#maplibre-v014","title":"maplibre v0.1.4","text":"
    • anywidget>=0.9.0 (#36)
    "},{"location":"changelog/#maplibre-v013","title":"maplibre v0.1.3","text":"
    • Display all properties in popup and tooltip if prop = None (#26)
    • Support mustache templates for popups and tooltips (#27)
    "},{"location":"changelog/#maplibre-v012","title":"maplibre v0.1.2","text":"
    • Add Map.set_data
    • Add Map.set_visibility
    • Do not import ipywidget.MapWidget in __init__ and skip tests for MapWidget, because it causes a core dumped error, see anywidget issue
    • Remove requests dependency
    • Remove dead code
    • Add more examples
    "},{"location":"changelog/#maplibre-v011","title":"maplibre v0.1.1","text":"
    • Initial PyPI release
    "},{"location":"deckgl/","title":"Deck.GL Layers","text":"

    Deck.GL layers can be added to the map with Map.add_deck_layers.

    They are defined as a dictionary, where classes got the @@type prefix and getter props the @@= prefix. They are inserted into the layer stack of the maplibre context. Therefore, you can also pass a beforeId prop.

    Here is an example corresponding to the Deck.GL GridLayer API Example:

    grid_layer = {\n    \"@@type\": \"GridLayer\", # JS: new GridLayer\n    \"id\": \"my-awsome-grid-layer\",\n    \"data\": \"https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json\",\n    \"extruded\": True,\n    \"getPosition\": \"@@=COORDINATES\", # JS: d => d.COORDINATES\n    \"getColorWeight\": \"@@=SPACES\", # JS: d => d.SPACES\n    \"getElevationWeight\": \"@@=SPACES\", # JS: d => d.SPACES\n    \"elevationScale\": 4,\n    \"cellSize\": 200,\n    \"pickable\": True,\n    \"beforeId\": \"first-labels-layer\" # optional\n    }\n

    The Code above will generate the following JavaScript code:

    const gridLayer = new GridLayer({\n  id: 'GridLayer',\n  data: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json',\n  extruded: true,\n  getPosition: d => d.COORDINATES,\n  getColorWeight: d => d.SPACES,\n  getElevationWeight: d => d.SPACES,\n  elevationScale: 4,\n  cellSize: 200,\n  pickable: true\n});\n

    See also:

    • Deck.GL Layer
    • Deck.GL Multiple Layers
    "},{"location":"jupyter/","title":"Jupyter","text":"

    Use MapWidget in your Jupyter Notebook:

    import ipywidgets as widgets\n\nfrom maplibre import Layer, LayerType\nfrom maplibre.sources import GeoJSONSource\nfrom maplibre.controls import ScaleControl, Marker\nfrom maplibre.ipywidget import MapWidget as Map\n\n# Create a source\nearthquakes = GeoJSONSource(\n    data=\"https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson\"\n)\n\n# Create a layer\nlayer_id = \"earthquakes\"\n\nearthquake_circles = Layer(\n    type=LayerType.CIRCLE,\n    id=layer_id,\n    source=earthquakes,\n    paint={\"circle-color\": \"yellow\"}\n)\n\n# Render map\nm = Map()\nm.add_control(ScaleControl(), position=\"bottom-left\")\nm.add_layer(earthquake_circles)\nm.add_tooltip(layer_id, \"mag\")\nm.add_marker(Marker(lng_lat=(100.507, 13.745)))\nm\n\n# Change radius\n_ = widgets.interact(\n    lambda radius: m.set_paint_property(layer_id, \"circle-radius\", radius),\n    radius=(1, 8, 1)\n)\n\n# Change color\n_ = widgets.interact(\n    lambda color: m.set_paint_property(layer_id, \"circle-color\", color),\n    color=[\"green\", \"yellow\", \"orange\", \"red\"]\n)\n\n# Set filter on magnitude\n_ = widgets.interact(\n    lambda mag_min: m.set_filter(layer_id, [\">=\", [\"get\", \"mag\"], mag_min]),\n    mag_min=(1, 8, 1)\n)\n
    "},{"location":"layers/","title":"Layers","text":"

    The paint and layout properties for the layers depend on the type of the layer. They are passed as a dict corresponding to the Layer Style Spec.

    For example, to set the radius and the color for a circle layer, the paint property looks like this:

    paint = {\n    \"circle-radius\": 5,\n    \"circle-color\": \"yellow\"\n}\n

    The value for any layout property, paint property, or filter may also be specified as an expression. For details see Expressions.

    For example, if the source of your layer has a color property that contains the color of the feature, you can use the following expression:

    paint = {\n    \"circle-radius\": 5,\n    \"circle-color\": [\"get\", \"color\"]\n}\n

    A more complex expression where the color depends on the type property of the layer's source might look like this:

    paint={\n        \"circle-color\": [\n            \"match\",\n            [\"get\", \"type\"],\n            # darkred if type == \"mid\"\n            \"mid\",\n            \"darkred\",\n            # darkgreen if type == \"major\"\n            \"major\",\n            \"darkgreen\",\n            # else blue\n            \"darkblue\",\n        ]\n}\n

    Filter features of a source according to its magnitude property:

    # Only show features where magnitude >= 5\nfilter = [\">=\", [\"get\", \"magnitude\"], 5]\n
    "},{"location":"shiny/","title":"Shiny","text":""},{"location":"shiny/#input-and-output","title":"Input and output","text":"

    Use output_maplibregl in the UI and render_maplibregl in the server section of your Shiny for Python app:

    from shiny import App, ui\nfrom maplibre import output_maplibregl, render_maplibregl, Map\n\napp_ui = ui.page_fluid(\n    ui.panel_title(\"MapLibre\"),\n    output_maplibregl(\"maplibre\", height=600)\n)\n\n\ndef server(input, output, session):\n    @render_maplibregl\n    def maplibre():\n        m = Map()\n        return m\n\n\napp = App(app_ui, server)\n
    "},{"location":"shiny/#reactivity","title":"Reactivity","text":""},{"location":"shiny/#input-events","title":"Input events","text":"

    MapLibre for Python provides the following reactive inputs:

    • input.{output_id}_clicked: Sends coordinates of the clicked location on the map.
    • input.{output_id}_feature_clicked: Sends the properties of the clicked feature and its layer id.
    • input.{output_id}_view_state: Sends the current view state. Fired when the view state is changed.
    "},{"location":"shiny/#map-updates","title":"Map updates","text":"

    Use MapContext to update your Map object.

    "},{"location":"shiny/#example","title":"Example","text":"
    import json\n\nfrom maplibre import (\n    Layer,\n    LayerType,\n    Map,\n    MapContext,\n    output_maplibregl,\n    render_maplibregl,\n)\nfrom maplibre.controls import NavigationControl\nfrom maplibre.sources import GeoJSONSource\nfrom shiny import App, reactive, render, ui\n\nLAYER_ID = \"earthquakes\"\nCIRCLE_RADIUS = 5\n\napp_ui = ui.page_fluid(\n    ui.panel_title(\"MapLibre for Python\"),\n    output_maplibregl(\"mapgl\", height=600),\n    ui.div(\"Click on the map to print the coords.\", style=\"padding: 10px;\"),\n    ui.output_text_verbatim(\"coords\", placeholder=True),\n    ui.div(\"Click on a feature to print its props.\", style=\"padding: 10px;\"),\n    ui.output_text_verbatim(\"props\", placeholder=True),\n    ui.div(\"Move map or zoom to update view state.\", style=\"padding: 10px;\"),\n    ui.output_text_verbatim(\"view_state\", placeholder=True),\n    ui.input_slider(\"radius\", \"Radius\", value=CIRCLE_RADIUS, min=1, max=10),\n)\n\ncircle_layer = Layer(\n    type=LayerType.CIRCLE,\n    id=LAYER_ID,\n    source=GeoJSONSource(\n        data=\"https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson\"\n    ),\n    paint={\"circle-color\": \"yellow\"},\n)\n\n\ndef server(input, output, session):\n    @render_maplibregl\n    def mapgl():\n        m = Map(zoom=3, pitch=40)\n        m.add_control(NavigationControl())\n        m.add_layer(circle_layer)\n        return m\n\n    @render.text\n    def coords():\n        return str(input.mapgl_clicked())\n\n    @render.text\n    def view_state():\n        return json.dumps(input.mapgl_view_state(), indent=2)\n\n    @render.text\n    def props():\n        return str(input.mapgl_feature_clicked())\n\n    @reactive.Effect\n    @reactive.event(input.radius)\n    async def radius():\n        async with MapContext(\"mapgl\") as m:\n            m.set_paint_property(LAYER_ID, \"circle-radius\", input.radius())\n\n\napp = App(app_ui, server)\n

    Run this example:

    poetry run uvicorn docs.examples.getting_started.reactivity:app --reload\n
    "},{"location":"streamlit/","title":"Streamlit","text":"

    Use maplibre.streamlit.st_maplibre to add a MapLibre map to your Streamlit app:

    import streamlit as st\nfrom maplibre import Map, MapOptions\nfrom maplibre.basemaps import Carto\nfrom maplibre.controls import NavigationControl\nfrom maplibre.streamlit import st_maplibre\n\n\ndef create_layer(cell_size: int = 200) -> dict:\n    return {\n        \"@@type\": \"GridLayer\",\n        \"id\": \"GridLayer\",\n        \"data\": \"https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json\",\n        \"extruded\": True,\n        \"getPosition\": \"@@=COORDINATES\",\n        \"getColorWeight\": \"@@=SPACES\",\n        \"getElevationWeight\": \"@@=SPACES\",\n        \"elevationScale\": 4,\n        \"cellSize\": cell_size,\n        \"pickable\": True,\n    }\n\n\nmap_options = MapOptions(\n    style=Carto.POSITRON,\n    center=(-122.4, 37.74),\n    zoom=12,\n    hash=True,\n    pitch=40,\n)\n\n\nst.title(\"SF Bike Parking\")\n\ncell_size = st.slider(\"cell size\", 100, 600, value=200, step=5)\n\nm = Map(map_options)\nm.add_control(NavigationControl())\nm.add_deck_layers([create_layer(cell_size)], tooltip=\"Number of points: {{ count }}\")\n\nst_maplibre(m)\n
    "},{"location":"api/basemaps/","title":"Basemaps","text":""},{"location":"api/basemaps/#maplibre.basemaps.Carto","title":"maplibre.basemaps.Carto","text":"

    Bases: Enum

    Carto basemap styles

    Attributes:

    • DARK_MATTER \u2013

      dark-matter

    • POSITRON \u2013

      positron

    • VOYAGER \u2013

      voyager

    • POSITRON_NOLABELS \u2013

      positron-nolabels

    • DARK_MATTER_NOLABELS \u2013

      dark-matter-nolabels

    • VOYAGER_NOLABELS \u2013

      voyager-nolabels

    Examples:

    >>> from maplibre import Map, MapOptions\n>>> from maplibre.basemaps import Carto\n
    >>> m = Map(MapOptions(style=Carto.DARK_MATTER))\n
    Source code in maplibre/basemaps.py
    class Carto(Enum):\n    \"\"\"Carto basemap styles\n\n    Attributes:\n        DARK_MATTER: dark-matter\n        POSITRON: positron\n        VOYAGER: voyager\n        POSITRON_NOLABELS: positron-nolabels\n        DARK_MATTER_NOLABELS: dark-matter-nolabels\n        VOYAGER_NOLABELS: voyager-nolabels\n\n    Examples:\n        >>> from maplibre import Map, MapOptions\n        >>> from maplibre.basemaps import Carto\n\n        >>> m = Map(MapOptions(style=Carto.DARK_MATTER))\n    \"\"\"\n\n    DARK_MATTER = \"dark-matter\"\n    POSITRON = \"positron\"\n    VOYAGER = \"voyager\"\n    POSITRON_NOLABELS = \"positron-nolabels\"\n    DARK_MATTER_NOLABELS = \"dark-matter-nolabels\"\n    VOYAGER_NOLABELS = \"voyager-nolabels\"\n
    "},{"location":"api/basemaps/#maplibre.basemaps.construct_basemap_style","title":"maplibre.basemaps.construct_basemap_style(name='nice-style', sources={}, layers=[])","text":"

    Construct a basemap style

    Parameters:

    Name Type Description Default name str

    The name of the basemap style.

    'nice-style' sources dict

    The sources to be used for the basemap style.

    {} layers list

    The layers to be used for the basemap style.

    [] Source code in maplibre/basemaps.py
    def construct_basemap_style(\n    name: str = \"nice-style\", sources: dict = {}, layers: list = []\n) -> dict:\n    \"\"\"Construct a basemap style\n\n    Args:\n        name (str): The name of the basemap style.\n        sources (dict): The sources to be used for the basemap style.\n        layers (list): The layers to be used for the basemap style.\n    \"\"\"\n    layers = [\n        layer.to_dict() if isinstance(layer, Layer) else layer for layer in layers\n    ]\n    return {\"name\": name, \"version\": 8, \"sources\": sources, \"layers\": layers}\n
    "},{"location":"api/controls/","title":"Markers and controls","text":""},{"location":"api/controls/#maplibre.controls.Popup","title":"maplibre.controls.Popup","text":"

    Bases: MapLibreBaseModel

    Popup

    Attributes:

    Name Type Description text str

    The Text of the popup.

    options PopupOptions | dict

    Popup options.

    Source code in maplibre/controls.py
    class Popup(MapLibreBaseModel):\n    \"\"\"Popup\n\n    Attributes:\n        text: The Text of the popup.\n        options (PopupOptions | dict): Popup options.\n    \"\"\"\n\n    text: str\n    options: Union[PopupOptions, dict] = {}\n
    "},{"location":"api/controls/#maplibre.controls.PopupOptions","title":"maplibre.controls.PopupOptions","text":"

    Bases: MapLibreBaseModel

    Popup options

    Source code in maplibre/controls.py
    class PopupOptions(MapLibreBaseModel):\n    \"\"\"Popup options\"\"\"\n\n    anchor: str = None\n    close_button: bool = Field(False, serialization_alias=\"closeButton\")\n    close_on_click: bool = Field(None, serialization_alias=\"closeOnClick\")\n    close_on_move: bool = Field(None, serialization_alias=\"closeOnMove\")\n    max_width: int = Field(None, serialization_alias=\"maxWidth\")\n    offset: Union[int, list, dict] = None\n
    "},{"location":"api/controls/#maplibre.controls.Marker","title":"maplibre.controls.Marker","text":"

    Bases: MapLibreBaseModel

    Marker

    Attributes:

    Name Type Description lng_lat tuple | list

    Required. The longitude and latitude of the marker.

    popup Popup | dict

    The Popup that is displayed when a user clicks on the marker.

    options MarkerOptions | dict

    Marker options.

    Source code in maplibre/controls.py
    class Marker(MapLibreBaseModel):\n    \"\"\"Marker\n\n    Attributes:\n        lng_lat (tuple |list): **Required.** The longitude and latitude of the marker.\n        popup (Popup | dict): The Popup that is displayed when a user clicks on the marker.\n        options (MarkerOptions | dict): Marker options.\n    \"\"\"\n\n    lng_lat: Union[tuple, list] = Field(None, serialization_alias=\"lngLat\")\n    popup: Union[Popup, dict] = None\n    options: Union[MarkerOptions, dict] = {}\n
    "},{"location":"api/controls/#maplibre.controls.MarkerOptions","title":"maplibre.controls.MarkerOptions","text":"

    Bases: MapLibreBaseModel

    Marker options

    Source code in maplibre/controls.py
    class MarkerOptions(MapLibreBaseModel):\n    \"\"\"Marker options\"\"\"\n\n    anchor: str = None\n    color: str = None\n    draggable: bool = None\n    offset: Union[tuple, list] = None\n    pitch_alignment: str = Field(None, serialization_alias=\"pitchAlignment\")\n    rotation: int = None\n    rotation_alignment: str = Field(None, serialization_alias=\"rotationAlignment\")\n    scale: int = None\n
    "},{"location":"api/controls/#maplibre.controls.ControlPosition","title":"maplibre.controls.ControlPosition","text":"

    Bases: Enum

    Control position

    Attributes:

    • TOP_LEFT \u2013

      top-left

    • TOP_RIGHT \u2013

      top-right

    • BOTTOM_LEFT \u2013

      bottom-left

    • BOTTOM_RIGHT \u2013

      bottom-right

    Source code in maplibre/controls.py
    class ControlPosition(Enum):\n    \"\"\"Control position\n\n    Attributes:\n        TOP_LEFT: top-left\n        TOP_RIGHT: top-right\n        BOTTOM_LEFT: bottom-left\n        BOTTOM_RIGHT: bottom-right\n    \"\"\"\n\n    TOP_LEFT = \"top-left\"\n    TOP_RIGHT = \"top-right\"\n    BOTTOM_LEFT = \"bottom-left\"\n    BOTTOM_RIGHT = \"bottom-right\"\n
    "},{"location":"api/controls/#maplibre.controls.FullscreenControl","title":"maplibre.controls.FullscreenControl","text":"

    Bases: Control

    Fullscreen control

    Examples:

    >>> from maplibre import Map\n>>> from maplibre.controls import FullscreenControl, ControlPosition\n
    >>> m = Map()\n>>> m.add_control(FullscreenControl(), ControlPosition.BOTTOM_LEFT)\n
    Source code in maplibre/controls.py
    class FullscreenControl(Control):\n    \"\"\"Fullscreen control\n\n    Examples:\n        >>> from maplibre import Map\n        >>> from maplibre.controls import FullscreenControl, ControlPosition\n\n        >>> m = Map()\n        >>> m.add_control(FullscreenControl(), ControlPosition.BOTTOM_LEFT)\n    \"\"\"\n\n    # _name: str = ControlType.FULLSCREEN.value\n    pass\n
    "},{"location":"api/controls/#maplibre.controls.ScaleControl","title":"maplibre.controls.ScaleControl","text":"

    Bases: Control

    Scale control

    Source code in maplibre/controls.py
    class ScaleControl(Control):\n    \"\"\"Scale control\"\"\"\n\n    max_width: int = Field(None, serialization_alias=\"maxWidth\")\n    unit: Literal[\"imperial\", \"metric\", \"nautical\"] = \"metric\"\n
    "},{"location":"api/controls/#maplibre.controls.NavigationControl","title":"maplibre.controls.NavigationControl","text":"

    Bases: Control

    Navigation control

    Source code in maplibre/controls.py
    class NavigationControl(Control):\n    \"\"\"Navigation control\"\"\"\n\n    # _name: str = ControlType.NAVIGATION.value\n    show_compass: bool = Field(True, serialization_alias=\"showCompass\")\n    show_zoom: bool = Field(True, serialization_alias=\"showZoom\")\n    visualize_pitch: bool = Field(False, serialization_alias=\"visualizePitch\")\n
    "},{"location":"api/controls/#maplibre.controls.GeolocateControl","title":"maplibre.controls.GeolocateControl","text":"

    Bases: Control

    Geolocate control

    Source code in maplibre/controls.py
    class GeolocateControl(Control):\n    \"\"\"Geolocate control\"\"\"\n\n    # _name: str = ControlType.GEOLOCATE.value\n    position_options: dict = Field(None, serialization_alias=\"positionOptions\")\n    show_accuracy_circle: bool = Field(True, serialization_alias=\"showAccuracyCircle\")\n    show_user_heading: bool = Field(False, serialization_alias=\"showUserHeading\")\n    show_user_location: bool = Field(True, serialization_alias=\"showUserLocation\")\n    track_user_location: bool = Field(False, serialization_alias=\"trackUserLocation\")\n
    "},{"location":"api/controls/#maplibre.controls.InfoBoxControl","title":"maplibre.controls.InfoBoxControl","text":"

    Bases: Control

    InfoBox control

    Attributes:

    Name Type Description content str

    Content (HTML or plain text) to be displayed in the info box.

    css_text str

    Optional inline style declaration of the control.

    Source code in maplibre/controls.py
    class InfoBoxControl(Control):\n    \"\"\"InfoBox control\n\n    Attributes:\n        content (str): Content (HTML or plain text) to be displayed in the info box.\n        css_text (str): Optional inline style declaration of the control.\n    \"\"\"\n\n    content: str\n    css_text: str = Field(None, serialization_alias=\"cssText\")\n
    "},{"location":"api/controls/#maplibre.controls.LayerSwitcherControl","title":"maplibre.controls.LayerSwitcherControl","text":"

    Bases: Control

    Layer switcher control

    Attributes:

    Name Type Description layer_ids list

    A list of layer ids to be shown in the layer switcher control.

    theme Literal['default', 'simple']

    The theme of the layer switcher control.

    css_text str

    Optional inline style declaration of the control.

    Source code in maplibre/controls.py
    class LayerSwitcherControl(Control):\n    \"\"\"Layer switcher control\n\n    Attributes:\n        layer_ids (list): A list of layer ids to be shown in the layer switcher control.\n        theme (Literal[\"default\", \"simple\"]): The theme of the layer switcher control.\n        css_text (str): Optional inline style declaration of the control.\n    \"\"\"\n\n    layer_ids: list = Field([], serialization_alias=\"layerIds\")\n    theme: Literal[\"default\", \"simple\"] = \"default\"\n    css_text: str = Field(None, serialization_alias=\"cssText\")\n
    "},{"location":"api/layer/","title":"Layer","text":""},{"location":"api/layer/#maplibre.Layer","title":"maplibre.Layer","text":"

    Bases: MapLibreBaseModel

    Layer properties

    Notes

    See layers for more details on the paint and layout properties of the layers.

    Attributes:

    Name Type Description id str

    Required. The unique ID of the layer. Defaults to str(uuid4()).

    type str | LayerType

    Required. The type of the layer.

    filter list

    A filter expression that is applied to the source of the layer.

    layout dict

    The layout properties for the layer.

    max_zoom int

    The maximum zoom level for the layer.

    min_zoom int

    The minimum zoom level for the layer.

    paint dict

    The paint properties for the layer.

    source str | Source | GeoDataFrame

    The name (unique ID) of a source, a source object or a GeoDataFrame to be used for the layer.

    source_layer str

    The layer to use from a vector tile source.

    Examples:

    >>> from maplibre.layer import Layer, LayerType\n
    >>> layer = Layer(id=\"test-layer\", type=LayerType.CIRCLE, source=\"test-source\")\n
    Source code in maplibre/layer.py
    class Layer(MapLibreBaseModel):\n    \"\"\"Layer properties\n\n    Notes:\n        See [layers](https://maplibre.org/maplibre-style-spec/layers/) for more details on the\n        `paint` and `layout` properties of the layers.\n\n    Attributes:\n        id (str): **Required.** The unique ID of the layer. Defaults to `str(uuid4())`.\n        type (str | LayerType): **Required.** The type of the layer.\n        filter (list): A filter expression that is applied to the source of the layer.\n        layout (dict): The layout properties for the layer.\n        max_zoom (int): The maximum zoom level for the layer.\n        min_zoom (int): The minimum zoom level for the layer.\n        paint (dict): The paint properties for the layer.\n        source (str | Source | GeoDataFrame): The name (unique ID) of a source, a source object or a GeoDataFrame\n            to be used for the layer.\n        source_layer (str): The layer to use from a vector tile source.\n\n    Examples:\n        >>> from maplibre.layer import Layer, LayerType\n\n        >>> layer = Layer(id=\"test-layer\", type=LayerType.CIRCLE, source=\"test-source\")\n    \"\"\"\n\n    id: Optional[str] = Field(default_factory=lambda: str(uuid4()))\n    type: LayerType\n    filter: Optional[list] = None\n    layout: Optional[dict] = None\n    max_zoom: Optional[int] = Field(None, serialization_alias=\"maxzoom\")\n    metadata: Optional[dict] = None\n    min_zoom: Optional[int] = Field(None, serialization_alias=\"minzoom\")\n    paint: Optional[dict] = None\n    source: Union[str, Source, dict, GeoDataFrame, None] = None\n    source_layer: Optional[str] = Field(None, serialization_alias=\"source-layer\")\n\n    # TODO: Use model_post_init and set bounds if source is a GeoDataFrame\n    @field_validator(\"source\")\n    def validate_source(cls, v):\n        if GeoDataFrame is not None and isinstance(v, GeoDataFrame):\n            return SimpleFeatures(v).to_source().to_dict()\n\n        if isinstance(v, Source):\n            return v.to_dict()\n\n        return v\n\n    @field_validator(\"paint\", \"layout\")\n    def fix_paint(cls, v):\n        if isinstance(v, dict):\n            return fix_keys(v)\n\n        return v\n\n    @property\n    def bounds(self) -> tuple | None:\n        try:\n            bounds = get_bounds(self.source[\"data\"])\n        except Exception as e:\n            # print(e)\n            bounds = None\n\n        return bounds\n\n    def set_paint_props(self, **props) -> Layer:\n        if self.paint is None:\n            self.paint = dict()\n\n        self.paint = self.paint | fix_keys(props)\n        return self\n\n    def set_layout_props(self, **props) -> Layer:\n        if self.layout is None:\n            self.layout = dict()\n\n        self.paint = self.paint | fix_keys(props)\n        return self\n
    "},{"location":"api/layer/#maplibre.LayerType","title":"maplibre.LayerType","text":"

    Bases: Enum

    Rendering type of layer

    Attributes:

    • CIRCLE \u2013

      A filled circle.

    • FILL \u2013

      A filled polygon with an optional stroked border.

    • FILL_EXTRUSION \u2013

      An extruded polygon.

    • LINE \u2013

      A stroked line.

    • SYMBOL \u2013

      An icon or a text label.

    • RASTER \u2013

      Raster map textures such as satellite imagery.

    • HEATMAP \u2013

      A heatmap.

    • HILLSHADE \u2013

      A Client-side hillshading visualization based on DEM data.

    • BACKGROUND \u2013

      A background color or pattern.

    Source code in maplibre/layer.py
    class LayerType(Enum):\n    \"\"\"Rendering type of layer\n\n    Attributes:\n        CIRCLE: A filled circle.\n        FILL: A filled polygon with an optional stroked border.\n        FILL_EXTRUSION: An extruded polygon.\n        LINE: A stroked line.\n        SYMBOL: An icon or a text label.\n        RASTER: Raster map textures such as satellite imagery.\n        HEATMAP: A heatmap.\n        HILLSHADE: A Client-side hillshading visualization based on DEM data.\n        BACKGROUND: A background color or pattern.\n    \"\"\"\n\n    CIRCLE = \"circle\"\n    FILL = \"fill\"\n    FILL_EXTRUSION = \"fill-extrusion\"\n    LINE = \"line\"\n    SYMBOL = \"symbol\"\n    RASTER = \"raster\"\n    HEATMAP = \"heatmap\"\n    HILLSHADE = \"hillshade\"\n    BACKGROUND = \"background\"\n
    "},{"location":"api/map/","title":"Map","text":""},{"location":"api/map/#maplibre.Map","title":"maplibre.Map","text":"

    Bases: object

    Map

    Parameters:

    Name Type Description Default map_options MapOptions

    Map options.

    MapOptions() sources dict

    Sources to be added to the map. Keys are source IDs.

    None layers list

    Layers to be added to the map.

    None controls list

    Controls to be added to the map.

    None **kwargs

    Keyword arguments that are appended to the MapOptions object.

    {}

    Examples:

    >>> from maplibre.map import Map, MapOptions\n>>> map_options = MapOptions(center=(9.5, 51.31667), zoom=8)\n>>> m = Map(map_options)\n>>> dict(m)\n{'mapOptions': {'center': (9.5, 51.31667), 'style': 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json', 'zoom': 8}, 'calls': []}\n
    Source code in maplibre/map.py
    class Map(object):\n    \"\"\"Map\n\n    Args:\n        map_options (MapOptions): Map options.\n        sources (dict): Sources to be added to the map. Keys are source IDs.\n        layers (list): Layers to be added to the map.\n        controls (list): Controls to be added to the map.\n        **kwargs: Keyword arguments that are appended to the `MapOptions` object.\n\n    Examples:\n        >>> from maplibre.map import Map, MapOptions\n        >>> map_options = MapOptions(center=(9.5, 51.31667), zoom=8)\n        >>> m = Map(map_options)\n        >>> dict(m)\n        {'mapOptions': {'center': (9.5, 51.31667), 'style': 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json', 'zoom': 8}, 'calls': []}\n    \"\"\"\n\n    MESSAGE = \"not implemented yet\"\n\n    def __init__(\n        self,\n        map_options: MapOptions = MapOptions(),\n        sources: dict = None,\n        layers: list = None,\n        controls: list = None,\n        **kwargs,\n    ):\n        self.map_options = (\n            map_options.to_dict() | kwargs\n        )  # MapOptions(**kwargs).to_dict() # need to fix MapWidget, because height is passed as kwarg\n        self._message_queue = []\n        self.add_layers(layers, sources)\n        if controls:\n            for control in controls:\n                self.add_control(control)\n\n    def __iter__(self):\n        for k, v in self.to_dict().items():\n            yield k, v\n\n    def to_dict(self) -> dict:\n        return {\"mapOptions\": self.map_options, \"calls\": self._message_queue}\n\n    \"\"\"\n    @property\n    def sources(self) -> list:\n        return [item[\"data\"] for item in self._calls if item[\"name\"] == \"addSource\"]\n\n    @property\n    def layers(self) -> list:\n        return [item[\"data\"] for item in self._calls if item[\"name\"] == \"addLayer\"]\n    \"\"\"\n\n    # TODO: Rename to add_map_call\n    \"\"\"\n    def add_call_(self, func_name: str, params: list) -> None:\n        self._message_queue.append(\n            {\"name\": \"applyFunc\", \"data\": {\"funcName\": func_name, \"params\": params}}\n        )\n    \"\"\"\n\n    def add_call(self, method_name: str, *args) -> None:\n        \"\"\"Add a method call that is executed on the map instance\n\n        Args:\n            method_name (str): The name of the map method to be executed.\n            *args (any): The arguments to be passed to the map method.\n        \"\"\"\n        # TODO: Pass as dict? {\"name\": method_name, \"args\": args}\n        call = [method_name, args]\n        self._message_queue.append(call)\n\n    def add_control(\n        self,\n        control: Control,\n        position: [str | ControlPosition] = None,\n    ) -> None:\n        \"\"\"Add a control to the map\n\n        Args:\n            control (Control): The control to be added to the map.\n            position (str | ControlPosition): The position of the control.\n        \"\"\"\n        position = position or control.position\n        self.add_call(\n            \"addControl\",\n            control.type,\n            control.to_dict(),\n            ControlPosition(position).value,\n        )\n\n    def add_source(self, id: str, source: [Source | dict | GeoDataFrame]) -> None:\n        \"\"\"Add a source to the map\n\n        Args:\n            id (str): The unique ID of the source.\n            source (Source | dict | GeoDataFrame): The source to be added to the map.\n        \"\"\"\n        if GeoDataFrame is not None and isinstance(source, GeoDataFrame):\n            source = SimpleFeatures(source).to_source()\n\n        if isinstance(source, Source):\n            source = source.to_dict()\n\n        self.add_call(\"addSource\", id, source)\n\n    def add_layer(self, layer: [Layer | dict], before_id: str = None) -> None:\n        \"\"\"Add a layer to the map\n\n        Args:\n            layer (Layer | dict): The Layer to be added to the map.\n            before_id (str): The ID of an existing layer to insert the new layer before,\n                resulting in the new layer appearing visually beneath the existing layer.\n                If `None`, the new layer will appear above all other layers.\n        \"\"\"\n        if isinstance(layer, Layer):\n            layer = layer.to_dict()\n\n        self.add_call(\"addLayer\", layer, before_id)\n\n    def add_layers(self, layers: list = None, sources: dict = None):\n        layers = layers or []\n        sources = sources or dict()\n        for source_id, source in sources.items():\n            self.add_source(source_id, source)\n\n        for layer in layers:\n            self.add_layer(layer)\n\n    def add_marker(self, marker: Marker) -> None:\n        \"\"\"Add a marker to the map\n\n        Args:\n            marker (Marker): The marker to be added to the map.\n        \"\"\"\n        self.add_call(\"addMarker\", marker.to_dict())\n\n    def add_popup(self, layer_id: str, prop: str = None, template: str = None) -> None:\n        \"\"\"Add a popup to the map\n\n        Args:\n            layer_id (str): The layer to which the popup is added.\n            prop (str): The property of the source to be displayed. If `None`, all properties are displayed.\n            template (str): A mustache template. If supplied, `prop` is ignored.\n        \"\"\"\n        self.add_call(\"addPopup\", layer_id, prop, template)\n\n    def add_tooltip(\n        self, layer_id: str, prop: str = None, template: str = None\n    ) -> None:\n        \"\"\"Add a tooltip to the map\n\n        Args:\n            layer_id (str): The layer to which the tooltip is added.\n            prop (str): The property of the source to be displayed. If `None`, all properties are displayed.\n            template (str): A mustache template. If supplied, `prop` is ignored.\n\n        Examples:\n            >>> m = Map()\n            >>> # ...\n            >>> m.add_tooltip(\"test-layer\", template=\"Name: {{ name }}\")\n        \"\"\"\n        self.add_call(\"addTooltip\", layer_id, prop, template)\n\n    def set_filter(self, layer_id: str, filter_: list):\n        \"\"\"Update the filter of a layer\n\n        Args:\n            layer_id (str): The name of the layer to be updated.\n            filter_ (list): The filter expression that is applied to the source of the layer.\n        \"\"\"\n        self.add_call(\"setFilter\", layer_id, filter_)\n\n    def set_paint_property(self, layer_id: str, prop: str, value: any) -> None:\n        \"\"\"Update the paint property of a layer\n\n        Args:\n            layer_id (str): The name of the layer to be updated.\n            prop (str): The name of the paint property to be updated.\n            value (any): The new value of the paint property.\n        \"\"\"\n        self.add_call(\"setPaintProperty\", layer_id, prop, value)\n\n    def set_layout_property(self, layer_id: str, prop: str, value: any) -> None:\n        \"\"\"Update a layout property of a layer\n\n        Args:\n            layer_id (str): The name of the layer to be updated.\n            prop (str): The name of the layout property to be updated.\n            value (any): The new value of the layout property.\n        \"\"\"\n        self.add_call(\"setLayoutProperty\", layer_id, prop, value)\n\n    def set_data(self, source_id: str, data: dict | GeoDataFrame) -> None:\n        \"\"\"Update the data of a GeoJSON source\n\n        Args:\n            source_id (str): The name of the source to be updated.\n            data (dict): The data of the source.\n        \"\"\"\n        if isinstance(data, GeoDataFrame):\n            data = SimpleFeatures(data).to_source().data\n\n        self.add_call(\"setSourceData\", source_id, data)\n\n    def set_visibility(self, layer_id: str, visible: bool = True) -> None:\n        \"\"\"Update the visibility of a layer\n\n        Args:\n            layer_id (str): The name of the layer to be updated.\n            visible (bool): Whether the layer is visible or not.\n        \"\"\"\n        value = \"visible\" if visible else \"none\"\n        self.add_call(\"setLayoutProperty\", layer_id, \"visibility\", value)\n\n    def fit_bounds(\n        self,\n        bounds: tuple | list = None,\n        data: GeoDataFrame = None,\n        animate=False,\n        **kwargs,\n    ) -> None:\n        \"\"\"Pan and zoom the map to contain its visible area within the specified geographical bounds\"\"\"\n        kwargs[\"animate\"] = animate\n        if data is not None:\n            bounds = tuple(data.total_bounds)\n\n        self.add_call(\"fitBounds\", bounds, kwargs)\n\n    def to_html(self, title: str = \"My Awesome Map\", **kwargs) -> str:\n        \"\"\"Render to html\n\n        Args:\n            title (str): The Title of the HTML document.\n            **kwargs (Any): Additional keyword arguments that are passed to the template.\n                Currently, `style` is the only supported keyword argument.\n\n        Examples:\n            >>> from maplibre import Map\n            >>> m = Map()\n            >>> with open(\"/tmp/map.html\", \"w\") as f:\n            ...     f.write(m.to_html(style=\"height: 800px;\") # doctest: +SKIP\n        \"\"\"\n        js_lib = read_internal_file(\"srcjs\", \"pywidget.js\")\n        js_snippet = Template(js_template).render(data=json.dumps(self.to_dict()))\n        css = read_internal_file(\"srcjs\", \"pywidget.css\")\n        headers = [f\"<style>{css}</style>\"]\n\n        # Deck.GL headers\n        add_deckgl_headers = \"addDeckOverlay\" in [\n            item[0] for item in self._message_queue\n        ]\n        # TODO: Set version in constants\n        deckgl_headers = (\n            [\n                # '<script src=\"https://unpkg.com/h3-js\"></script>',\n                '<script src=\"https://unpkg.com/h3-js@4.1.0/dist/h3-js.umd.js\"></script>',\n                '<script src=\"https://unpkg.com/deck.gl@9.0.16/dist.min.js\"></script>',\n                '<script src=\"https://unpkg.com/@deck.gl/json@9.0.16/dist.min.js\"></script>',\n            ]\n            if add_deckgl_headers\n            else []\n        )\n\n        # Mapbox Draw headers\n        add_mapbox_draw_headers = \"addMapboxDraw\" in [\n            item[0] for item in self._message_queue\n        ]\n        # TODO: Set version in constants\n        mapbox_draw_headers = (\n            [\n                # \"<script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.3/mapbox-gl-draw.js'></script>\",\n                # \"<link rel='stylesheet' href='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.3/mapbox-gl-draw.css' type='text/css' />\",\n                \"<script src='https://www.unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.js'></script>\",\n                \"<link rel='stylesheet' href='https://www.unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.css' type='text/css' />\",\n            ]\n            if add_mapbox_draw_headers\n            else []\n        )\n\n        output = Template(html_template).render(\n            js=\"\\n\".join([js_lib, js_snippet]),\n            title=title,\n            headers=headers + deckgl_headers + mapbox_draw_headers,\n            **kwargs,\n        )\n        return output\n\n    def save(self, filename: str = None, preview=True, **kwargs):\n        \"\"\"Save the map to an HTML file\"\"\"\n        return save_map(self, filename, preview, **kwargs)\n\n    # -------------------------\n    # Plugins\n    # -------------------------\n    def add_deck_layers(\n        self, layers: list[dict | \"pydeck.Layer\"], tooltip: str | dict = None\n    ) -> None:\n        \"\"\"Add Deck.GL layers to the layer stack\n\n        Args:\n            layers (list[dict | \"pydeck.Layer\"]): A list of dictionaries containing the Deck.GL layers to be added.\n            tooltip (str | dict): Either a single mustache template string applied to all layers\n                or a dictionary where keys are layer ids and values are mustache template strings.\n        \"\"\"\n        layers = parse_deck_layers(layers)\n        self.add_call(\"addDeckOverlay\", layers, tooltip)\n\n    def set_deck_layers(\n        self, layers: list[dict | \"pydeck.Layer\"], tooltip: str | dict = None\n    ) -> None:\n        \"\"\"Update Deck.GL layers\n\n        Args:\n            layers (list[dict | \"pydeck.Layer\"]): A list of dictionaries containing the Deck.GL layers to be updated.\n                New layers will be added. Missing layers will be removed.\n            tooltip (str | dict): Must be set to keep tooltip even if it did not change.\n        \"\"\"\n        layers = parse_deck_layers(layers)\n        self.add_call(\"setDeckLayers\", layers, tooltip)\n\n    def add_mapbox_draw(\n        self,\n        options: dict | MapboxDrawOptions = None,\n        position: str | ControlPosition = ControlPosition.TOP_LEFT,\n        geojson: dict = None,\n    ) -> None:\n        \"\"\"Add MapboxDraw controls to the map\n\n        Note:\n            See [MapboxDraw API Reference](https://github.com/mapbox/mapbox-gl-draw/blob/main/docs/API.md)\n                for available options.\n\n        Args:\n            options (dict | MapboxDrawOptions): MapboxDraw options.\n            position (str | ControlPosition): The position of the MapboxDraw controls.\n            geojson (dict): A GeoJSON Feature, FeatureCollection or Geometry to be added to the draw layer.\n        \"\"\"\n        if isinstance(options, MapboxDrawOptions):\n            options = options.to_dict()\n\n        self.add_call(\n            \"addMapboxDraw\", options or {}, ControlPosition(position).value, geojson\n        )\n
    "},{"location":"api/map/#maplibre.Map.add_call","title":"add_call(method_name, *args)","text":"

    Add a method call that is executed on the map instance

    Parameters:

    Name Type Description Default method_name str

    The name of the map method to be executed.

    required *args any

    The arguments to be passed to the map method.

    () Source code in maplibre/map.py
    def add_call(self, method_name: str, *args) -> None:\n    \"\"\"Add a method call that is executed on the map instance\n\n    Args:\n        method_name (str): The name of the map method to be executed.\n        *args (any): The arguments to be passed to the map method.\n    \"\"\"\n    # TODO: Pass as dict? {\"name\": method_name, \"args\": args}\n    call = [method_name, args]\n    self._message_queue.append(call)\n
    "},{"location":"api/map/#maplibre.Map.add_control","title":"add_control(control, position=None)","text":"

    Add a control to the map

    Parameters:

    Name Type Description Default control Control

    The control to be added to the map.

    required position str | ControlPosition

    The position of the control.

    None Source code in maplibre/map.py
    def add_control(\n    self,\n    control: Control,\n    position: [str | ControlPosition] = None,\n) -> None:\n    \"\"\"Add a control to the map\n\n    Args:\n        control (Control): The control to be added to the map.\n        position (str | ControlPosition): The position of the control.\n    \"\"\"\n    position = position or control.position\n    self.add_call(\n        \"addControl\",\n        control.type,\n        control.to_dict(),\n        ControlPosition(position).value,\n    )\n
    "},{"location":"api/map/#maplibre.Map.add_deck_layers","title":"add_deck_layers(layers, tooltip=None)","text":"

    Add Deck.GL layers to the layer stack

    Parameters:

    Name Type Description Default layers list[dict | 'pydeck.Layer']

    A list of dictionaries containing the Deck.GL layers to be added.

    required tooltip str | dict

    Either a single mustache template string applied to all layers or a dictionary where keys are layer ids and values are mustache template strings.

    None Source code in maplibre/map.py
    def add_deck_layers(\n    self, layers: list[dict | \"pydeck.Layer\"], tooltip: str | dict = None\n) -> None:\n    \"\"\"Add Deck.GL layers to the layer stack\n\n    Args:\n        layers (list[dict | \"pydeck.Layer\"]): A list of dictionaries containing the Deck.GL layers to be added.\n        tooltip (str | dict): Either a single mustache template string applied to all layers\n            or a dictionary where keys are layer ids and values are mustache template strings.\n    \"\"\"\n    layers = parse_deck_layers(layers)\n    self.add_call(\"addDeckOverlay\", layers, tooltip)\n
    "},{"location":"api/map/#maplibre.Map.add_layer","title":"add_layer(layer, before_id=None)","text":"

    Add a layer to the map

    Parameters:

    Name Type Description Default layer Layer | dict

    The Layer to be added to the map.

    required before_id str

    The ID of an existing layer to insert the new layer before, resulting in the new layer appearing visually beneath the existing layer. If None, the new layer will appear above all other layers.

    None Source code in maplibre/map.py
    def add_layer(self, layer: [Layer | dict], before_id: str = None) -> None:\n    \"\"\"Add a layer to the map\n\n    Args:\n        layer (Layer | dict): The Layer to be added to the map.\n        before_id (str): The ID of an existing layer to insert the new layer before,\n            resulting in the new layer appearing visually beneath the existing layer.\n            If `None`, the new layer will appear above all other layers.\n    \"\"\"\n    if isinstance(layer, Layer):\n        layer = layer.to_dict()\n\n    self.add_call(\"addLayer\", layer, before_id)\n
    "},{"location":"api/map/#maplibre.Map.add_mapbox_draw","title":"add_mapbox_draw(options=None, position=ControlPosition.TOP_LEFT, geojson=None)","text":"

    Add MapboxDraw controls to the map

    Note

    See MapboxDraw API Reference for available options.

    Parameters:

    Name Type Description Default options dict | MapboxDrawOptions

    MapboxDraw options.

    None position str | ControlPosition

    The position of the MapboxDraw controls.

    TOP_LEFT geojson dict

    A GeoJSON Feature, FeatureCollection or Geometry to be added to the draw layer.

    None Source code in maplibre/map.py
    def add_mapbox_draw(\n    self,\n    options: dict | MapboxDrawOptions = None,\n    position: str | ControlPosition = ControlPosition.TOP_LEFT,\n    geojson: dict = None,\n) -> None:\n    \"\"\"Add MapboxDraw controls to the map\n\n    Note:\n        See [MapboxDraw API Reference](https://github.com/mapbox/mapbox-gl-draw/blob/main/docs/API.md)\n            for available options.\n\n    Args:\n        options (dict | MapboxDrawOptions): MapboxDraw options.\n        position (str | ControlPosition): The position of the MapboxDraw controls.\n        geojson (dict): A GeoJSON Feature, FeatureCollection or Geometry to be added to the draw layer.\n    \"\"\"\n    if isinstance(options, MapboxDrawOptions):\n        options = options.to_dict()\n\n    self.add_call(\n        \"addMapboxDraw\", options or {}, ControlPosition(position).value, geojson\n    )\n
    "},{"location":"api/map/#maplibre.Map.add_marker","title":"add_marker(marker)","text":"

    Add a marker to the map

    Parameters:

    Name Type Description Default marker Marker

    The marker to be added to the map.

    required Source code in maplibre/map.py
    def add_marker(self, marker: Marker) -> None:\n    \"\"\"Add a marker to the map\n\n    Args:\n        marker (Marker): The marker to be added to the map.\n    \"\"\"\n    self.add_call(\"addMarker\", marker.to_dict())\n
    "},{"location":"api/map/#maplibre.Map.add_popup","title":"add_popup(layer_id, prop=None, template=None)","text":"

    Add a popup to the map

    Parameters:

    Name Type Description Default layer_id str

    The layer to which the popup is added.

    required prop str

    The property of the source to be displayed. If None, all properties are displayed.

    None template str

    A mustache template. If supplied, prop is ignored.

    None Source code in maplibre/map.py
    def add_popup(self, layer_id: str, prop: str = None, template: str = None) -> None:\n    \"\"\"Add a popup to the map\n\n    Args:\n        layer_id (str): The layer to which the popup is added.\n        prop (str): The property of the source to be displayed. If `None`, all properties are displayed.\n        template (str): A mustache template. If supplied, `prop` is ignored.\n    \"\"\"\n    self.add_call(\"addPopup\", layer_id, prop, template)\n
    "},{"location":"api/map/#maplibre.Map.add_source","title":"add_source(id, source)","text":"

    Add a source to the map

    Parameters:

    Name Type Description Default id str

    The unique ID of the source.

    required source Source | dict | GeoDataFrame

    The source to be added to the map.

    required Source code in maplibre/map.py
    def add_source(self, id: str, source: [Source | dict | GeoDataFrame]) -> None:\n    \"\"\"Add a source to the map\n\n    Args:\n        id (str): The unique ID of the source.\n        source (Source | dict | GeoDataFrame): The source to be added to the map.\n    \"\"\"\n    if GeoDataFrame is not None and isinstance(source, GeoDataFrame):\n        source = SimpleFeatures(source).to_source()\n\n    if isinstance(source, Source):\n        source = source.to_dict()\n\n    self.add_call(\"addSource\", id, source)\n
    "},{"location":"api/map/#maplibre.Map.add_tooltip","title":"add_tooltip(layer_id, prop=None, template=None)","text":"

    Add a tooltip to the map

    Parameters:

    Name Type Description Default layer_id str

    The layer to which the tooltip is added.

    required prop str

    The property of the source to be displayed. If None, all properties are displayed.

    None template str

    A mustache template. If supplied, prop is ignored.

    None

    Examples:

    >>> m = Map()\n>>> # ...\n>>> m.add_tooltip(\"test-layer\", template=\"Name: {{ name }}\")\n
    Source code in maplibre/map.py
    def add_tooltip(\n    self, layer_id: str, prop: str = None, template: str = None\n) -> None:\n    \"\"\"Add a tooltip to the map\n\n    Args:\n        layer_id (str): The layer to which the tooltip is added.\n        prop (str): The property of the source to be displayed. If `None`, all properties are displayed.\n        template (str): A mustache template. If supplied, `prop` is ignored.\n\n    Examples:\n        >>> m = Map()\n        >>> # ...\n        >>> m.add_tooltip(\"test-layer\", template=\"Name: {{ name }}\")\n    \"\"\"\n    self.add_call(\"addTooltip\", layer_id, prop, template)\n
    "},{"location":"api/map/#maplibre.Map.fit_bounds","title":"fit_bounds(bounds=None, data=None, animate=False, **kwargs)","text":"

    Pan and zoom the map to contain its visible area within the specified geographical bounds

    Source code in maplibre/map.py
    def fit_bounds(\n    self,\n    bounds: tuple | list = None,\n    data: GeoDataFrame = None,\n    animate=False,\n    **kwargs,\n) -> None:\n    \"\"\"Pan and zoom the map to contain its visible area within the specified geographical bounds\"\"\"\n    kwargs[\"animate\"] = animate\n    if data is not None:\n        bounds = tuple(data.total_bounds)\n\n    self.add_call(\"fitBounds\", bounds, kwargs)\n
    "},{"location":"api/map/#maplibre.Map.save","title":"save(filename=None, preview=True, **kwargs)","text":"

    Save the map to an HTML file

    Source code in maplibre/map.py
    def save(self, filename: str = None, preview=True, **kwargs):\n    \"\"\"Save the map to an HTML file\"\"\"\n    return save_map(self, filename, preview, **kwargs)\n
    "},{"location":"api/map/#maplibre.Map.set_data","title":"set_data(source_id, data)","text":"

    Update the data of a GeoJSON source

    Parameters:

    Name Type Description Default source_id str

    The name of the source to be updated.

    required data dict

    The data of the source.

    required Source code in maplibre/map.py
    def set_data(self, source_id: str, data: dict | GeoDataFrame) -> None:\n    \"\"\"Update the data of a GeoJSON source\n\n    Args:\n        source_id (str): The name of the source to be updated.\n        data (dict): The data of the source.\n    \"\"\"\n    if isinstance(data, GeoDataFrame):\n        data = SimpleFeatures(data).to_source().data\n\n    self.add_call(\"setSourceData\", source_id, data)\n
    "},{"location":"api/map/#maplibre.Map.set_deck_layers","title":"set_deck_layers(layers, tooltip=None)","text":"

    Update Deck.GL layers

    Parameters:

    Name Type Description Default layers list[dict | 'pydeck.Layer']

    A list of dictionaries containing the Deck.GL layers to be updated. New layers will be added. Missing layers will be removed.

    required tooltip str | dict

    Must be set to keep tooltip even if it did not change.

    None Source code in maplibre/map.py
    def set_deck_layers(\n    self, layers: list[dict | \"pydeck.Layer\"], tooltip: str | dict = None\n) -> None:\n    \"\"\"Update Deck.GL layers\n\n    Args:\n        layers (list[dict | \"pydeck.Layer\"]): A list of dictionaries containing the Deck.GL layers to be updated.\n            New layers will be added. Missing layers will be removed.\n        tooltip (str | dict): Must be set to keep tooltip even if it did not change.\n    \"\"\"\n    layers = parse_deck_layers(layers)\n    self.add_call(\"setDeckLayers\", layers, tooltip)\n
    "},{"location":"api/map/#maplibre.Map.set_filter","title":"set_filter(layer_id, filter_)","text":"

    Update the filter of a layer

    Parameters:

    Name Type Description Default layer_id str

    The name of the layer to be updated.

    required filter_ list

    The filter expression that is applied to the source of the layer.

    required Source code in maplibre/map.py
    def set_filter(self, layer_id: str, filter_: list):\n    \"\"\"Update the filter of a layer\n\n    Args:\n        layer_id (str): The name of the layer to be updated.\n        filter_ (list): The filter expression that is applied to the source of the layer.\n    \"\"\"\n    self.add_call(\"setFilter\", layer_id, filter_)\n
    "},{"location":"api/map/#maplibre.Map.set_layout_property","title":"set_layout_property(layer_id, prop, value)","text":"

    Update a layout property of a layer

    Parameters:

    Name Type Description Default layer_id str

    The name of the layer to be updated.

    required prop str

    The name of the layout property to be updated.

    required value any

    The new value of the layout property.

    required Source code in maplibre/map.py
    def set_layout_property(self, layer_id: str, prop: str, value: any) -> None:\n    \"\"\"Update a layout property of a layer\n\n    Args:\n        layer_id (str): The name of the layer to be updated.\n        prop (str): The name of the layout property to be updated.\n        value (any): The new value of the layout property.\n    \"\"\"\n    self.add_call(\"setLayoutProperty\", layer_id, prop, value)\n
    "},{"location":"api/map/#maplibre.Map.set_paint_property","title":"set_paint_property(layer_id, prop, value)","text":"

    Update the paint property of a layer

    Parameters:

    Name Type Description Default layer_id str

    The name of the layer to be updated.

    required prop str

    The name of the paint property to be updated.

    required value any

    The new value of the paint property.

    required Source code in maplibre/map.py
    def set_paint_property(self, layer_id: str, prop: str, value: any) -> None:\n    \"\"\"Update the paint property of a layer\n\n    Args:\n        layer_id (str): The name of the layer to be updated.\n        prop (str): The name of the paint property to be updated.\n        value (any): The new value of the paint property.\n    \"\"\"\n    self.add_call(\"setPaintProperty\", layer_id, prop, value)\n
    "},{"location":"api/map/#maplibre.Map.set_visibility","title":"set_visibility(layer_id, visible=True)","text":"

    Update the visibility of a layer

    Parameters:

    Name Type Description Default layer_id str

    The name of the layer to be updated.

    required visible bool

    Whether the layer is visible or not.

    True Source code in maplibre/map.py
    def set_visibility(self, layer_id: str, visible: bool = True) -> None:\n    \"\"\"Update the visibility of a layer\n\n    Args:\n        layer_id (str): The name of the layer to be updated.\n        visible (bool): Whether the layer is visible or not.\n    \"\"\"\n    value = \"visible\" if visible else \"none\"\n    self.add_call(\"setLayoutProperty\", layer_id, \"visibility\", value)\n
    "},{"location":"api/map/#maplibre.Map.to_html","title":"to_html(title='My Awesome Map', **kwargs)","text":"

    Render to html

    Parameters:

    Name Type Description Default title str

    The Title of the HTML document.

    'My Awesome Map' **kwargs Any

    Additional keyword arguments that are passed to the template. Currently, style is the only supported keyword argument.

    {}

    Examples:

    >>> from maplibre import Map\n>>> m = Map()\n>>> with open(\"/tmp/map.html\", \"w\") as f:\n...     f.write(m.to_html(style=\"height: 800px;\")\n
    Source code in maplibre/map.py
    def to_html(self, title: str = \"My Awesome Map\", **kwargs) -> str:\n    \"\"\"Render to html\n\n    Args:\n        title (str): The Title of the HTML document.\n        **kwargs (Any): Additional keyword arguments that are passed to the template.\n            Currently, `style` is the only supported keyword argument.\n\n    Examples:\n        >>> from maplibre import Map\n        >>> m = Map()\n        >>> with open(\"/tmp/map.html\", \"w\") as f:\n        ...     f.write(m.to_html(style=\"height: 800px;\") # doctest: +SKIP\n    \"\"\"\n    js_lib = read_internal_file(\"srcjs\", \"pywidget.js\")\n    js_snippet = Template(js_template).render(data=json.dumps(self.to_dict()))\n    css = read_internal_file(\"srcjs\", \"pywidget.css\")\n    headers = [f\"<style>{css}</style>\"]\n\n    # Deck.GL headers\n    add_deckgl_headers = \"addDeckOverlay\" in [\n        item[0] for item in self._message_queue\n    ]\n    # TODO: Set version in constants\n    deckgl_headers = (\n        [\n            # '<script src=\"https://unpkg.com/h3-js\"></script>',\n            '<script src=\"https://unpkg.com/h3-js@4.1.0/dist/h3-js.umd.js\"></script>',\n            '<script src=\"https://unpkg.com/deck.gl@9.0.16/dist.min.js\"></script>',\n            '<script src=\"https://unpkg.com/@deck.gl/json@9.0.16/dist.min.js\"></script>',\n        ]\n        if add_deckgl_headers\n        else []\n    )\n\n    # Mapbox Draw headers\n    add_mapbox_draw_headers = \"addMapboxDraw\" in [\n        item[0] for item in self._message_queue\n    ]\n    # TODO: Set version in constants\n    mapbox_draw_headers = (\n        [\n            # \"<script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.3/mapbox-gl-draw.js'></script>\",\n            # \"<link rel='stylesheet' href='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.3/mapbox-gl-draw.css' type='text/css' />\",\n            \"<script src='https://www.unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.js'></script>\",\n            \"<link rel='stylesheet' href='https://www.unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.css' type='text/css' />\",\n        ]\n        if add_mapbox_draw_headers\n        else []\n    )\n\n    output = Template(html_template).render(\n        js=\"\\n\".join([js_lib, js_snippet]),\n        title=title,\n        headers=headers + deckgl_headers + mapbox_draw_headers,\n        **kwargs,\n    )\n    return output\n
    "},{"location":"api/map/#maplibre.MapOptions","title":"maplibre.MapOptions","text":"

    Bases: MapLibreBaseModel

    Map options

    Note

    See MapOptions for more details.

    Source code in maplibre/map.py
    class MapOptions(MapLibreBaseModel):\n    \"\"\"Map options\n\n    Note:\n        See [MapOptions](https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/MapOptions) for more details.\n    \"\"\"\n\n    model_config = ConfigDict(\n        validate_assignment=True, extra=\"forbid\", use_enum_values=False\n    )\n    antialias: bool = None\n    attribution_control: bool = Field(None, serialization_alias=\"attributionControl\")\n    bearing: Union[int, float] = None\n    bearing_snap: int = Field(None, serialization_alias=\"bearingSnap\")\n    bounds: tuple = None\n    box_zoom: bool = Field(None, serialization_alias=\"boxZoom\")\n    center: tuple = None\n    click_tolerance: int = Field(None, serialization_alias=\"clickTolerance\")\n    custom_attribution: bool = Field(None, serialization_alias=\"customAttribution\")\n    double_click_zoom: bool = Field(None, serialization_alias=\"doubleClickZoom\")\n    fade_duration: int = Field(None, serialization_alias=\"fadeDuration\")\n    fit_bounds_options: dict = Field(None, serialization_alias=\"fitBoundsOptions\")\n    hash: Union[bool, str] = None\n    interactive: bool = None\n    keyword: bool = None\n    max_bounds: tuple = Field(None, serialization_alias=\"maxBounds\")\n    max_pitch: int = Field(None, serialization_alias=\"maxPitch\")\n    max_zoom: int = Field(None, serialization_alias=\"maxZoom\")\n    min_pitch: int = Field(None, serialization_alias=\"minPitch\")\n    min_zoom: int = Field(None, serialization_alias=\"minZoom\")\n    pitch: Union[int, float] = None\n    scroll_zoom: bool = Field(None, serialization_alias=\"scrollZoom\")\n    style: Union[str, Carto, MapTiler, dict] = construct_carto_basemap_url(\n        Carto.DARK_MATTER\n    )\n    zoom: Union[int, float] = None\n\n    @field_validator(\"style\")\n    def validate_style(cls, v):\n        if isinstance(v, Carto):\n            return construct_carto_basemap_url(v)\n\n        if isinstance(v, MapTiler):\n            return construct_maptiler_basemap_url(v)\n\n        return v\n
    "},{"location":"api/map/#maplibre.MapContext","title":"maplibre.MapContext","text":"

    Bases: Map

    MapContext

    Use this class to update a Map instance in a Shiny app. Must be used inside an async function.

    See maplibre.Map for available methods.

    Parameters:

    Name Type Description Default id string

    The id of the map to be updated.

    required session Session

    A Shiny session. If None, the active session is used.

    None Source code in maplibre/mapcontext.py
    class MapContext(Map):\n    \"\"\"MapContext\n\n    Use this class to update a `Map` instance in a Shiny app.\n    Must be used inside an async function.\n\n    See `maplibre.Map` for available methods.\n\n    Args:\n        id (string): The id of the map to be updated.\n        session (Session): A Shiny session.\n            If `None`, the active session is used.\n    \"\"\"\n\n    def __init__(self, id: str, session: Session = None) -> None:\n        self.id = id\n        self._session = require_active_session(session)\n        self.map_options = {}\n        self._message_queue = []\n\n    async def __aenter__(self):\n        return self\n\n    async def __aexit__(self, exc_type, exc_val, exc_tb):\n        await self.render()\n\n    async def render(self):\n        await self._session.send_custom_message(\n            f\"pymaplibregl-{self.id}\", {\"id\": self.id, \"calls\": self._message_queue}\n        )\n
    "},{"location":"api/map/#maplibre.ipywidget.MapWidget","title":"maplibre.ipywidget.MapWidget","text":"

    Bases: AnyWidget, Map

    MapWidget

    Use this class to display and update maps in Jupyter Notebooks.

    See maplibre.Map for available methods.

    Examples:

    >>> from maplibre import MapOptions\n>>> from maplibre.ipywidget import MapWidget as Map\n>>> m = Map(MapOptions(center=(-123.13, 49.254), zoom=11, pitch=45))\n>>> m\n
    Source code in maplibre/ipywidget.py
    class MapWidget(AnyWidget, Map):\n    \"\"\"MapWidget\n\n    Use this class to display and update maps in Jupyter Notebooks.\n\n    See `maplibre.Map` for available methods.\n\n    Examples:\n        >>> from maplibre import MapOptions\n        >>> from maplibre.ipywidget import MapWidget as Map\n        >>> m = Map(MapOptions(center=(-123.13, 49.254), zoom=11, pitch=45))\n        >>> m # doctest: +SKIP\n    \"\"\"\n\n    _esm = join(Path(__file__).parent, \"srcjs\", \"ipywidget.js\")\n    # _css = join(Path(__file__).parent, \"srcjs\", \"maplibre-gl.css\")\n    _css = join(Path(__file__).parent, \"srcjs\", \"ipywidget.css\")\n    _use_message_queue = True\n    # _rendered = traitlets.Bool(False, config=True).tag(sync=True)\n    _rendered = traitlets.Bool(False).tag(config=True).tag(sync=True)\n    map_options = traitlets.Dict().tag(sync=True)\n    calls = traitlets.List().tag(sync=True)\n    height = traitlets.Union([traitlets.Int(), traitlets.Unicode()]).tag(sync=True)\n\n    # Interactions Map\n    clicked = traitlets.Dict().tag(sync=True)\n    view_state = traitlets.Dict().tag(sync=True)\n\n    # Interactions MapboxDraw plugin\n    draw_features_selected = traitlets.List().tag(sync=True)\n    draw_feature_collection_all = traitlets.Dict().tag(sync=True)\n\n    def __init__(\n        self,\n        map_options=MapOptions(),\n        sources: dict = None,\n        layers: list = None,\n        controls: list = None,\n        height: int | str = 400,\n        **kwargs,\n    ) -> None:\n        self.calls = []\n        AnyWidget.__init__(self, height=height, **kwargs)\n        Map.__init__(self, map_options, sources, layers, controls, **kwargs)\n\n    \"\"\"\n    @traitlets.default(\"height\")\n    def _default_height(self):\n        return \"400px\"\n    \"\"\"\n\n    @traitlets.validate(\"height\")\n    def _validate_height(self, proposal):\n        height = proposal[\"value\"]\n        if isinstance(height, int):\n            return f\"{height}px\"\n\n        return height\n\n    @traitlets.observe(\"_rendered\")\n    def _on_rendered(self, change):\n        self.send({\"calls\": self._message_queue, \"msg\": \"init\"})\n        self._message_queue = []\n\n    def use_message_queue(self, value: bool = True) -> None:\n        self._use_message_queue = value\n\n    def add_call(self, method_name: str, *args) -> None:\n        call = [method_name, args]\n        if not self._rendered:\n            if not self._use_message_queue:\n                self.calls = self.calls + [call]\n                return\n\n            self._message_queue.append(call)\n            return\n\n        self.send({\"calls\": [call], \"msg\": \"custom call\"})\n
    "},{"location":"api/map/#maplibre.plugins","title":"maplibre.plugins","text":""},{"location":"api/map/#maplibre.plugins.MapboxDrawControls","title":"MapboxDrawControls","text":"

    Bases: MapLibreBaseModel

    MapboxDraw controls

    Source code in maplibre/plugins.py
    class MapboxDrawControls(MapLibreBaseModel):\n    \"\"\"MapboxDraw controls\"\"\"\n\n    point: bool = False\n    line_string: bool = False\n    polygon: bool = False\n    trash: bool = False\n    combine_features: bool = False\n    uncombine_features: bool = False\n
    "},{"location":"api/map/#maplibre.plugins.MapboxDrawOptions","title":"MapboxDrawOptions","text":"

    Bases: MapLibreBaseModel

    MapboxDraw Options

    Source code in maplibre/plugins.py
    class MapboxDrawOptions(MapLibreBaseModel):\n    \"\"\"MapboxDraw Options\"\"\"\n\n    display_controls_default: bool = Field(\n        True, serialization_alias=\"displayControlsDefault\"\n    )\n    controls: MapboxDrawControls = None\n    box_select: bool = Field(True, serialization_alias=\"boxSelect\")\n
    "},{"location":"api/sources/","title":"Sources","text":""},{"location":"api/sources/#maplibre.sources","title":"maplibre.sources","text":""},{"location":"api/sources/#maplibre.sources.GeoJSONSource","title":"GeoJSONSource","text":"

    Bases: Source

    GeoJSON Source

    Examples:

    >>> from maplibre.sources import GeoJSONSource\n
    >>> geojson = \"https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson\"\n>>> source = GeoJSONSource(data=geojson)\n
    Source code in maplibre/sources.py
    class GeoJSONSource(Source):\n    \"\"\"GeoJSON Source\n\n    Examples:\n        >>> from maplibre.sources import GeoJSONSource\n\n        >>> geojson = \"https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson\"\n        >>> source = GeoJSONSource(data=geojson)\n    \"\"\"\n\n    data: Union[str, dict]\n    attribution: Optional[str] = None\n    buffer: Optional[int] = None\n    cluster: Optional[bool] = None\n    cluster_max_zoom: Optional[int] = Field(None, serialization_alias=\"clusterMaxZoom\")\n    cluster_min_points: Optional[int] = Field(\n        None, serialization_alias=\"clusterMinPoints\"\n    )\n    cluster_properties: Optional[dict] = Field(\n        None, serialization_alias=\"clusterProperties\"\n    )\n    cluster_radius: Optional[int] = Field(None, serialization_alias=\"clusterRadius\")\n    filter: Optional[list] = None\n    generate_id: Optional[bool] = Field(None, serialization_alias=\"generateId\")\n    line_metrics: Optional[bool] = Field(None, serialization_alias=\"lineMetrics\")\n    min_zoom: Optional[int] = Field(None, serialization_alias=\"minzoom\")\n    max_zoom: Optional[int] = Field(None, serialization_alias=\"maxzoom\")\n    promote_id: Union[str, dict, None] = Field(None, serialization_alias=\"promoteId\")\n    tolerance: Optional[float] = None\n\n    @computed_field\n    @property\n    def type(self) -> str:\n        return SourceType.GEOJSON.value\n
    "},{"location":"api/sources/#maplibre.sources.RasterTileSource","title":"RasterTileSource","text":"

    Bases: Source

    Raster tile source

    Examples:

    >>> from maplibre.sources import RasterTileSource\n
    >>> raster_tile_source = RasterTileSource(\n...     tiles=[\"https://tile.openstreetmap.org/{z}/{x}/{y}.png\"],\n...     tile_size=256,\n...     min_zoom=0,\n...     max_zoom=19,\n... )\n
    Source code in maplibre/sources.py
    class RasterTileSource(Source):\n    \"\"\"Raster tile source\n\n    Examples:\n        >>> from maplibre.sources import RasterTileSource\n\n        >>> raster_tile_source = RasterTileSource(\n        ...     tiles=[\"https://tile.openstreetmap.org/{z}/{x}/{y}.png\"],\n        ...     tile_size=256,\n        ...     min_zoom=0,\n        ...     max_zoom=19,\n        ... )\n    \"\"\"\n\n    attribution: str = None\n    bounds: tuple = None\n    max_zoom: int = Field(None, serialization_alias=\"maxzoom\")\n    min_zoom: int = Field(None, serialization_alias=\"minzoom\")\n    scheme: str = None\n    tile_size: int = Field(None, serialization_alias=\"tileSize\")\n    tiles: Union[tuple, list] = None\n    url: str = None\n    volatile: bool = None\n\n    @computed_field\n    @property\n    def type(self) -> str:\n        return SourceType.RASTER.value\n
    "},{"location":"api/sources/#maplibre.sources.SourceType","title":"SourceType","text":"

    Bases: Enum

    Source types

    Source code in maplibre/sources.py
    class SourceType(Enum):\n    \"\"\"Source types\"\"\"\n\n    RASTER = \"raster\"\n    VECTOR = \"vector\"\n    RASTER_DEM = \"raster-dem\"\n    GEOJSON = \"geojson\"\n    IMAGE = \"image\"\n    VIDEO = \"video\"\n
    "},{"location":"api/sources/#maplibre.sources.VectorTileSource","title":"VectorTileSource","text":"

    Bases: Source

    Vector tile source

    Examples:

    >>> from maplibre.sources import VectorTileSource\n>>> from maplibre import LayerType, Layer\n
    >>> vector_tile_source = VectorTileSource(\n...     tiles=[\"https://demotiles.maplibre.org/tiles/{z}/{x}/{y}.pbf\"],\n...     min_zoom=0,\n...     max_zoom=6,\n... )\n
    >>> layer = Layer(\n...     type=LayerType.LINE,\n...     id=\"countries\",\n...     source=vector_tile_source,\n...     source_layer=\"countries\",\n...     paint={\"fill-color\": \"lightgreen\", \"fill-outline-color\": \"black\"},\n... )\n
    Source code in maplibre/sources.py
    class VectorTileSource(Source):\n    \"\"\"Vector tile source\n\n    Examples:\n        >>> from maplibre.sources import VectorTileSource\n        >>> from maplibre import LayerType, Layer\n\n        >>> vector_tile_source = VectorTileSource(\n        ...     tiles=[\"https://demotiles.maplibre.org/tiles/{z}/{x}/{y}.pbf\"],\n        ...     min_zoom=0,\n        ...     max_zoom=6,\n        ... )\n\n        >>> layer = Layer(\n        ...     type=LayerType.LINE,\n        ...     id=\"countries\",\n        ...     source=vector_tile_source,\n        ...     source_layer=\"countries\",\n        ...     paint={\"fill-color\": \"lightgreen\", \"fill-outline-color\": \"black\"},\n        ... )\n    \"\"\"\n\n    attribution: str = None\n    bounds: tuple = None\n    max_zoom: int = Field(None, serialization_alias=\"maxzoom\")\n    min_zoom: int = Field(None, serialization_alias=\"minzoom\")\n    scheme: str = None\n    tiles: Union[tuple, list] = None\n    url: str = None\n    volatile: bool = None\n\n    @computed_field\n    @property\n    def type(self) -> str:\n        return SourceType.VECTOR.value\n
    "},{"location":"examples/3d_indoor_mapping/","title":"3D Indoor Mapping","text":"
    import sys\nimport webbrowser\n\nfrom maplibre import Layer, LayerType, Map, MapOptions\nfrom maplibre.basemaps import background\nfrom maplibre.sources import GeoJSONSource, RasterTileSource\n\nfile_name = \"/tmp/pymaplibregl_temp.html\"\n\nFLOORPLAN_SOURCE_ID = \"floorplan\"\n\nraster_source = RasterTileSource(\n    tiles=[\"https://tile.openstreetmap.org/{z}/{x}/{y}.png\"],\n    tile_size=256,\n    min_zoom=0,\n    max_zoom=19,\n)\n\nraster_layer = Layer(type=LayerType.RASTER, source=raster_source)\n\nfloorplan_source = GeoJSONSource(\n    data=\"https://maplibre.org/maplibre-gl-js/docs/assets/indoor-3d-map.geojson\"\n)\n\nfloorplan_layer = Layer(\n    type=LayerType.FILL_EXTRUSION,\n    id=\"floorplan\",\n    source=FLOORPLAN_SOURCE_ID,\n    paint={\n        \"fill-extrusion-color\": [\"get\", \"color\"],\n        \"fill-extrusion-height\": [\"get\", \"height\"],\n        \"fill-extrusion-base\": [\"get\", \"base_height\"],\n        \"fill-extrusion-opacity\": 0.5,\n    },\n)\n\nmap_options = MapOptions(\n    style=background(\"yellow\"),\n    center=(-87.61694, 41.86625),\n    zoom=17,\n    pitch=40,\n    bearing=20,\n    antialias=True,\n)\n\n\ndef create_map():\n    m = Map(map_options)\n    m.add_layer(raster_layer)\n    m.add_source(FLOORPLAN_SOURCE_ID, floorplan_source)\n    m.add_layer(floorplan_layer)\n    return m\n\n\nif __name__ == \"__main__\":\n    m = create_map()\n    if len(sys.argv) == 2:\n        file_name = sys.argv[1]\n\n    with open(file_name, \"w\") as f:\n        f.write(m.to_html())\n\n    webbrowser.open(file_name)\n

    Run example:

    poetry run python docs/examples/3d_indoor_mapping/app.py\n
    "},{"location":"examples/airports/","title":"Airport Markers","text":"
    import sys\n\nimport pandas as pd\nfrom maplibre import (\n    Layer,\n    LayerType,\n    Map,\n    MapOptions,\n    output_maplibregl,\n    render_maplibregl,\n)\nfrom maplibre.basemaps import Carto\nfrom maplibre.controls import (\n    Marker,\n    MarkerOptions,\n    NavigationControl,\n    Popup,\n    PopupOptions,\n)\nfrom maplibre.sources import GeoJSONSource\nfrom maplibre.utils import GeometryType, df_to_geojson\nfrom shiny import App, ui\n\nBOUNDS = (-8.92242886, 43.30508298, 13.76496714, 59.87668996)\n\nairports_data = pd.read_json(\n    \"https://github.com/visgl/deck.gl-data/raw/master/examples/line/airports.json\"\n)\n\n\ndef get_color(airport_type: str) -> str:\n    color = \"darkblue\"\n    if airport_type == \"mid\":\n        color = \"darkred\"\n    elif airport_type == \"major\":\n        color = \"darkgreen\"\n\n    return color\n\n\ngeojson = df_to_geojson(\n    airports_data,\n    \"coordinates\",\n    GeometryType.POINT,\n    properties=[\"type\", \"name\", \"abbrev\"],\n)\n\nairport_circles = Layer(\n    type=LayerType.CIRCLE,\n    source=GeoJSONSource(data=geojson),\n    paint={\n        \"circle-color\": [\n            \"match\",\n            [\"get\", \"type\"],\n            \"mid\",\n            \"darkred\",\n            \"major\",\n            \"darkgreen\",\n            \"darkblue\",\n        ],\n        \"circle_radius\": 10,\n        \"circle-opacity\": 0.3,\n    },\n)\n\nmap_options = MapOptions(\n    style=Carto.POSITRON,\n    bounds=BOUNDS,\n    fit_bounds_options={\"padding\": 20},\n    hash=True,\n)\n\npopup_options = PopupOptions(close_button=False)\n\n\ndef create_map() -> Map:\n    m = Map(map_options)\n    m.add_control(NavigationControl())\n    m.add_layer(airport_circles)\n    for _, r in airports_data.iterrows():\n        marker = Marker(\n            lng_lat=r[\"coordinates\"],\n            options=MarkerOptions(color=get_color(r[\"type\"])),\n            popup=Popup(\n                text=r[\"name\"],\n                options=popup_options,\n            ),\n        )\n        m.add_marker(marker)\n    return m\n\n\napp_ui = ui.page_fluid(\n    ui.panel_title(\"Airports\"),\n    output_maplibregl(\"maplibre\", height=600),\n)\n\n\ndef server(input, output, session):\n    @render_maplibregl\n    def maplibre():\n        return create_map()\n\n\napp = App(app_ui, server)\n\nif __name__ == \"__main__\":\n    if len(sys.argv) == 2:\n        with open(sys.argv[1], \"w\") as f:\n            f.write(create_map().to_html())\n    else:\n        app.run()\n

    Run example:

    poetry run uvicorn docs.examples.airports.app:app --reload\n
    "},{"location":"examples/custom_basemap/","title":"Custom basemap","text":"
    import webbrowser\n\nfrom maplibre import Layer, LayerType, Map, MapOptions\nfrom maplibre.basemaps import construct_basemap_style\nfrom maplibre.controls import NavigationControl\nfrom maplibre.sources import GeoJSONSource\n\nfile_name = \"docs/examples/custom_basemap/app.html\"\n\nbg_layer = Layer(\n    type=LayerType.BACKGROUND,\n    id=\"background\",\n    source=None,\n    paint={\"background-color\": \"darkblue\", \"background-opacity\": 0.8},\n)\n\ncountries_source = GeoJSONSource(\n    data=\"https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_110m_admin_0_countries.geojson\"\n)\n\nlines_layer = Layer(\n    type=LayerType.LINE,\n    source=\"countries\",\n    paint={\"line-color\": \"white\", \"line-width\": 1.5},\n)\n\npolygons_layer = Layer(\n    type=LayerType.FILL,\n    source=\"countries\",\n    paint={\"fill-color\": \"darkred\", \"fill-opacity\": 0.8},\n)\n\ncustom_basemap = construct_basemap_style(\n    layers=[bg_layer, polygons_layer, lines_layer],\n    sources={\"countries\": countries_source},\n)\n\n\nmap_options = MapOptions(\n    style=custom_basemap,\n    center=(0, 0),\n    zoom=2,\n)\n\n\ndef create_map():\n    m = Map(map_options)\n    m.add_control(NavigationControl())\n    m.add_layer(\n        Layer(\n            type=LayerType.CIRCLE,\n            id=\"earthquakes\",\n            source=GeoJSONSource(\n                data=\"https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson\"\n            ),\n            paint={\"circle-color\": \"yellow\", \"circle-radius\": 5},\n        )\n    )\n    m.add_popup(\"earthquakes\", \"mag\")\n    return m\n\n\nif __name__ == \"__main__\":\n    m = create_map()\n    with open(file_name, \"w\") as f:\n        f.write(m.to_html())\n\n    webbrowser.open(file_name)\n

    Run example:

    poetry run python docs/examples/custom_basemap/app.py\n
    "},{"location":"examples/deckgl_layer/","title":"Deck.GL Layer","text":"
    # Shiny Express App\n\nimport json\n\nfrom maplibre import Map, MapOptions, render_maplibregl\nfrom maplibre.basemaps import Carto\nfrom maplibre.controls import NavigationControl\nfrom maplibre.ui import use_deckgl\n\n# from shiny import reactive\nfrom shiny.express import input, render, ui\n\nm = Map(\n    MapOptions(\n        style=Carto.POSITRON,\n        center=(-122.4, 37.74),\n        zoom=12,\n        hash=True,\n        pitch=40,\n    )\n)\nm.add_control(NavigationControl())\n\ndeck_grid_layer = {\n    \"@@type\": \"GridLayer\",\n    \"id\": \"GridLayer\",\n    \"data\": \"https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json\",\n    \"extruded\": True,\n    \"getPosition\": \"@@=COORDINATES\",\n    \"getColorWeight\": \"@@=SPACES\",\n    \"getElevationWeight\": \"@@=SPACES\",\n    \"elevationScale\": 4,\n    \"cellSize\": 200,\n    \"pickable\": True,\n}\n\nm.add_deck_layers([deck_grid_layer], tooltip=\"Number of points: {{ count }}\")\n\n# Shiny Express\nuse_deckgl()\n\n\n@render_maplibregl\ndef render_map():\n    return m\n\n\n@render.code\ndef picking_object():\n    obj = input.render_map_layer_GridLayer()\n    print(obj)\n    return json.dumps(obj[\"points\"], indent=2) if obj else \"Pick a feature!\"\n\n\nif __name__ == \"__main__\":\n    with open(\"docs/examples/deckgl_layer/app.html\", \"w\") as f:\n        f.write(m.to_html())\n

    Run example:

    # Run Shiny app\nshiny run docs/examples/deckgl_layer/app.py\n
    "},{"location":"examples/deckgl_multiple_layers/","title":"Multiple Deck.GL Layers","text":"
    # Shiny Express App\n\nimport requests as req\nfrom maplibre import Map, MapOptions, render_maplibregl\nfrom maplibre.basemaps import Carto\nfrom maplibre.controls import NavigationControl\nfrom maplibre.ui import use_deckgl\nfrom shiny.express import input, render, ui\n\ndata = req.get(\n    \"https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_airports.geojson\"\n).json()\n\nm = Map(\n    MapOptions(\n        style=Carto.POSITRON,\n        center=(0.45, 51.47),\n        zoom=4,\n        hash=True,\n        pitch=30,\n    )\n)\nm.add_control(NavigationControl())\n\ndeck_geojson_layer = {\n    \"@@type\": \"GeoJsonLayer\",\n    \"id\": \"airports\",\n    \"data\": data,\n    \"filled\": True,\n    \"pointRadiusMinPixels\": 2,\n    \"pointRadiusScale\": 2000,\n    \"getPointRadius\": \"@@=11 - properties.scalerank\",\n    \"getFillColor\": [200, 0, 80, 180],\n    \"autoHighlight\": True,\n    \"pickable\": True,\n}\n\ndeck_arc_layer = {\n    \"@@type\": \"ArcLayer\",\n    \"id\": \"arcs\",\n    \"data\": [\n        feature\n        for feature in data[\"features\"]\n        if feature[\"properties\"][\"scalerank\"] < 4\n    ],\n    \"getSourcePosition\": [-0.4531566, 51.4709959],  # London\n    \"getTargetPosition\": \"@@=geometry.coordinates\",\n    \"getSourceColor\": [0, 128, 200],\n    \"getTargetColor\": [200, 0, 80],\n    \"getWidth\": 2,\n    \"pickable\": True,\n}\n\nm.add_deck_layers(\n    [deck_geojson_layer, deck_arc_layer],\n    tooltip={\n        \"airports\": \"{{ &properties.name }}\",\n        \"arcs\": \"gps_code: {{ properties.gps_code }}\",\n    },\n)\n\n# Shiny Express\nuse_deckgl()\n\n\n@render_maplibregl\ndef render_map():\n    return m\n\n\nif __name__ == \"__main__\":\n    with open(\"docs/examples/deckgl_multiple_layers/app.html\", \"w\") as f:\n        f.write(m.to_html())\n

    Run example:

    # Run Shiny app\npoetry run shiny run docs/examples/deckgl_multiple_layers/app.py\n
    "},{"location":"examples/earthquake_clusters/","title":"Earthquake Clusters","text":"
    import sys\n\nfrom maplibre import (\n    Layer,\n    LayerType,\n    Map,\n    MapOptions,\n    output_maplibregl,\n    render_maplibregl,\n)\nfrom maplibre.basemaps import Carto\nfrom maplibre.controls import NavigationControl\nfrom maplibre.sources import GeoJSONSource\nfrom shiny import App, reactive, ui\n\nEARTHQUAKE_SOURCE = \"earthquakes\"\nEARTHQUAKE_CIRCLES = \"earthquake-circles\"\nEARTHQUAKE_CLUSTERS = \"earthquake-clusters\"\nEARTHQUAKE_LABELS = \"earthquake-labels\"\n\nCENTER = (-118.0931, 33.78615)\n\nearthquakes_source = GeoJSONSource(\n    data=\"https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson\",\n    cluster=True,\n    cluster_radius=50,\n    cluster_min_points=2,\n    cluster_max_zoom=14,\n    cluster_properties={\n        \"maxMag\": [\"max\", [\"get\", \"mag\"]],\n        \"minMag\": [\"min\", [\"get\", \"mag\"]],\n    },\n)\n\nearthquake_circles = Layer(\n    type=LayerType.CIRCLE,\n    id=EARTHQUAKE_CIRCLES,\n    source=EARTHQUAKE_SOURCE,\n    paint={\"circle-color\": \"darkblue\"},\n    filter=[\"!\", [\"has\", \"point_count\"]],\n)\n\nearthquake_clusters = Layer(\n    type=LayerType.CIRCLE,\n    id=EARTHQUAKE_CLUSTERS,\n    source=EARTHQUAKE_SOURCE,\n    filter=[\"has\", \"point_count\"],\n    paint={\n        \"circle-color\": [\n            \"step\",\n            [\"get\", \"point_count\"],\n            \"#51bbd6\",\n            100,\n            \"#f1f075\",\n            750,\n            \"#f28cb1\",\n        ],\n        \"circle-radius\": [\"step\", [\"get\", \"point_count\"], 20, 100, 30, 750, 40],\n    },\n)\n\nearthquake_labels = Layer(\n    type=LayerType.SYMBOL,\n    id=\"text\",\n    source=EARTHQUAKE_SOURCE,\n    filter=[\"has\", \"point_count\"],\n    layout={\n        \"text-field\": [\"get\", \"point_count_abbreviated\"],\n        \"text-size\": 12,\n    },\n)\n\nmap_options = MapOptions(style=Carto.POSITRON, center=CENTER, zoom=3, hash=True)\n\n\ndef create_map() -> Map:\n    m = Map(map_options)\n    m.add_control(NavigationControl())\n    m.add_source(EARTHQUAKE_SOURCE, earthquakes_source)\n    m.add_layer(earthquake_clusters)\n    m.add_layer(earthquake_circles)\n    m.add_tooltip(EARTHQUAKE_CLUSTERS, \"maxMag\")\n    m.add_layer(earthquake_labels)\n    return m\n\n\napp_ui = ui.page_fluid(\n    ui.panel_title(\"Earthquakes Cluster\"),\n    output_maplibregl(\"maplibre\", height=500),\n)\n\n\ndef server(input, output, session):\n    @render_maplibregl\n    def maplibre():\n        return create_map()\n\n    @reactive.Effect\n    @reactive.event(input.maplibre)\n    async def result():\n        print(f\"result: {input.maplibre()}\")\n\n\napp = App(app_ui, server)\n\nif __name__ == \"__main__\":\n    if len(sys.argv) == 2:\n        file_name = sys.argv[1]\n        with open(file_name, \"w\") as f:\n            f.write(create_map().to_html())\n    else:\n        app.run()\n

    Run example:

    poetry run uvicorn docs.examples.earthquake_clusters.app:app --reload\n
    "},{"location":"examples/every_person_in_manhattan/","title":"Every Person in Manhattan","text":"
    import json\nimport sys\n\nimport pandas as pd\nimport shapely\nfrom maplibre import (\n    Layer,\n    LayerType,\n    Map,\n    MapContext,\n    MapOptions,\n    output_maplibregl,\n    render_maplibregl,\n)\nfrom maplibre.basemaps import Carto\nfrom maplibre.controls import NavigationControl, ScaleControl\nfrom maplibre.sources import GeoJSONSource\nfrom maplibre.utils import df_to_geojson\nfrom shiny import App, reactive, ui\n\nMALE_COLOR = \"rgb(0, 128, 255)\"\nFEMALE_COLOR = \"rgb(255, 0, 128)\"\nLAYER_ID = \"every-person-in-manhattan-circles\"\nCIRCLE_RADIUS = 2\n\npoint_data = pd.read_json(\n    \"https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/scatterplot/manhattan.json\"\n)\n\npoint_data.columns = [\"lng\", \"lat\", \"sex\"]\n\nevery_person_in_manhattan_source = GeoJSONSource(\n    data=df_to_geojson(point_data, properties=[\"sex\"]),\n)\n\nbbox = shapely.bounds(\n    shapely.from_geojson(json.dumps(every_person_in_manhattan_source.data))\n)\n\nevery_person_in_manhattan_circles = Layer(\n    type=LayerType.CIRCLE,\n    id=LAYER_ID,\n    source=every_person_in_manhattan_source,\n    paint={\n        \"circle-color\": [\"match\", [\"get\", \"sex\"], 1, MALE_COLOR, FEMALE_COLOR],\n        \"circle-radius\": CIRCLE_RADIUS,\n    },\n)\n\nmap_options = MapOptions(\n    style=Carto.POSITRON,\n    bounds=tuple(bbox),\n    fit_bounds_options={\"padding\": 20},\n    hash=True,\n)\n\n\ndef create_map() -> Map:\n    m = Map(map_options)\n    m.add_control(NavigationControl())\n    m.add_control(ScaleControl(), position=\"bottom-left\")\n    m.add_layer(every_person_in_manhattan_circles)\n    return m\n\n\napp_ui = ui.page_fluid(\n    ui.panel_title(\"Every Person in Manhattan\"),\n    output_maplibregl(\"maplibre\", height=600),\n    ui.input_slider(\"radius\", \"Radius\", value=CIRCLE_RADIUS, min=1, max=5),\n)\n\n\ndef server(input, output, session):\n    @render_maplibregl\n    def maplibre():\n        return create_map()\n\n    @reactive.Effect\n    @reactive.event(input.radius, ignore_init=True)\n    async def radius():\n        async with MapContext(\"maplibre\") as m:\n            m.set_paint_property(LAYER_ID, \"circle-radius\", input.radius())\n\n\napp = App(app_ui, server)\n\nif __name__ == \"__main__\":\n    if len(sys.argv) == 2:\n        file_name = sys.argv[1]\n        with open(file_name, \"w\") as f:\n            f.write(create_map().to_html())\n    else:\n        app.run()\n

    Run example:

    poetry run uvicorn docs.examples.every_person_in_manhattan.app:app --reload\n
    "},{"location":"examples/geopandas/","title":"GeoPandas","text":"
    import sys\nimport webbrowser\n\nfrom maplibre import Layer, LayerType, Map, MapOptions\nfrom maplibre.controls import NavigationControl\nfrom maplibre.sources import GeoJSONSource\nfrom maplibre.utils import geopandas_to_geojson\n\nimport geopandas as gpd\n\nfile_name = \"/tmp/pymaplibregl_temp.html\"\nLAYER_ID = \"wilderness\"\n\ndf_geo = gpd.read_file(\n    \"zip+https://github.com/Toblerity/Fiona/files/11151652/coutwildrnp.zip\"\n)\n\nwilderness_source = GeoJSONSource(data=geopandas_to_geojson(df_geo))\n\nwilderness_layer = Layer(\n    type=LayerType.FILL,\n    id=LAYER_ID,\n    source=wilderness_source,\n    paint={\"fill-color\": \"darkred\", \"fill-opacity\": 0.5},\n)\n\nmap_options = MapOptions(bounds=df_geo.total_bounds)\n\n\ndef create_map():\n    m = Map(map_options)\n    m.add_control(NavigationControl())\n    m.add_layer(wilderness_layer)\n    m.add_tooltip(LAYER_ID, \"NAME\")\n    return m\n\n\nif __name__ == \"__main__\":\n    m = create_map()\n    if len(sys.argv) == 2:\n        file_name = sys.argv[1]\n\n    with open(file_name, \"w\") as f:\n        f.write(m.to_html())\n\n    webbrowser.open(file_name)\n

    Run example:

    poetry run python docs/examples/geopandas/app.py\n
    "},{"location":"examples/layer_order/","title":"Layer Order","text":"

    Add a data layer below the labels of the basemap.

    # Shiny Express App\n\nimport requests as req\nfrom maplibre import Layer, LayerType, Map, MapOptions, render_maplibregl\nfrom maplibre.basemaps import Carto, construct_carto_basemap_url\nfrom maplibre.controls import NavigationControl\nfrom maplibre.sources import GeoJSONSource\nfrom shiny.express import input, render, ui\n\nstyle = req.get(construct_carto_basemap_url(Carto.VOYAGER)).json()\n\n\nsymbol_ids = [layer[\"id\"] for layer in style[\"layers\"] if layer[\"type\"] == \"symbol\"]\n# print(symbol_ids)\n\nurban_areas = GeoJSONSource(\n    data=\"https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_urban_areas.geojson\"\n)\n\nm = Map(\n    MapOptions(\n        style=style,\n        center=(-89.928, 35.204),\n        # center=(-88.13734351262877, 35.137451890638886),\n        zoom=9,\n        hash=True,\n    )\n)\nm.add_control(NavigationControl())\nm.add_layer(\n    Layer(\n        id=\"urban-areas-fill\",\n        type=LayerType.FILL,\n        source=urban_areas,\n        paint={\"fill-color\": \"pink\", \"fill-opacity\": 1.0},\n    ),\n    before_id=symbol_ids[0],\n)\nfor symbol_id in symbol_ids:\n    m.set_paint_property(symbol_id, \"text-color\", \"purple\")\n\n\n@render_maplibregl\ndef render_map():\n    return m\n\n\nif __name__ == \"__main__\":\n    with open(\"docs/examples/layer_order/app.html\", \"w\") as f:\n        f.write(m.to_html())\n

    Run example:

    poetry run shiny run docs/examples/layer_order/app.py\n
    "},{"location":"examples/layer_switcher/","title":"Layer Switcher","text":"
    # Example taken from here: https://maplibre.org/maplibre-gl-js/docs/examples/pmtiles/\n\nimport webbrowser\n\nfrom maplibre import Layer, LayerType, Map, MapOptions, render_maplibregl\nfrom maplibre.basemaps import construct_basemap_style\nfrom maplibre.controls import (\n    ControlPosition,\n    InfoBoxControl,\n    LayerSwitcherControl,\n    NavigationControl,\n)\nfrom shiny.express import input, render, ui\n\nPMTILES_URL = \"https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles\"\n\npmtiles_source = {\n    \"type\": \"vector\",\n    \"url\": f\"pmtiles://{PMTILES_URL}\",\n    \"attribution\": '\u00a9 <a href=\"https://openstreetmap.org\">OpenStreetMap</a>',\n}\n\ncustom_basemap = construct_basemap_style(\n    sources={\"pmtiles\": pmtiles_source},\n    layers=[\n        Layer(\n            id=\"buildings\",\n            source=\"pmtiles\",\n            source_layer=\"landuse\",\n            type=LayerType.FILL,\n            paint={\"fill-color\": \"red\"},\n            layout={\"visibility\": \"none\"},\n        ),\n        Layer(\n            id=\"roads\",\n            source=\"pmtiles\",\n            source_layer=\"roads\",\n            type=LayerType.LINE,\n            paint={\"line-color\": \"black\"},\n        ),\n        Layer(\n            id=\"mask\",\n            source=\"pmtiles\",\n            source_layer=\"mask\",\n            type=LayerType.FILL,\n            paint={\"fill-color\": \"yellow\"},\n            layout={\"visibility\": \"none\"},\n        ),\n    ],\n)\n\n\nmap_options = MapOptions(\n    style=custom_basemap,\n    # hash=True,\n    # bounds=(11.154026, 43.7270125, 11.3289395, 43.8325455),\n    center=(11.2415, 43.7798),\n    zoom=12.5,\n)\n\n\ndef create_map():\n    m = Map(map_options)\n    m.add_control(NavigationControl())\n    m.add_control(\n        InfoBoxControl(\n            content=\"Toggle layers of PMTiles source\",\n            css_text=\"padding: 10px; background-color: yellow; color: steelblue; font-weight: bold;\",\n        ),\n        ControlPosition.TOP_LEFT,\n    )\n    m.add_control(\n        LayerSwitcherControl(\n            layer_ids=[\"buildings\", \"roads\", \"mask\"],\n            css_text=\"padding: 5px; border: 1px solid darkgrey; border-radius: 4px;\",\n        ),\n        position=ControlPosition.TOP_LEFT,\n    )\n    return m\n\n\n@render_maplibregl\ndef render_map():\n    return create_map()\n\n\nif __name__ == \"__main__\":\n    file_name = \"docs/examples/layer_switcher/app.html\"\n\n    m = create_map()\n    with open(file_name, \"w\") as f:\n        f.write(m.to_html())\n\n    webbrowser.open(file_name)\n

    Run example:

    shiny run docs/examples/layer_switcher/app.py\n
    "},{"location":"examples/mapbox_draw_plugin/","title":"Mapbox Draw Plugin","text":"
    # Shiny Express App\n\nimport json\nimport webbrowser\n\nfrom maplibre import Map, MapOptions, render_maplibregl\nfrom maplibre.basemaps import Carto\nfrom maplibre.controls import ControlPosition, NavigationControl, ScaleControl\nfrom maplibre.plugins import MapboxDrawControls, MapboxDrawOptions\nfrom maplibre.ui import use_mapboxgl_draw\n\n# from shiny import reactive\nfrom shiny.express import input, render, ui\n\ngeojson_feature = {\n    \"id\": \"xyz\",\n    \"type\": \"Feature\",\n    \"properties\": {},\n    \"geometry\": {\n        \"coordinates\": [\n            [\n                [-122.4523683552298, 37.775540942000546],\n                [-122.41910082339776, 37.75932501909665],\n                [-122.43487191413453, 37.72543546737114],\n                [-122.46053073611722, 37.729612763886834],\n                [-122.4523683552298, 37.775540942000546],\n            ]\n        ],\n        \"type\": \"Polygon\",\n    },\n}\n\nm = Map(\n    MapOptions(\n        style=Carto.POSITRON,\n        center=(-122.4, 37.74),\n        zoom=12,\n        hash=True,\n        pitch=40,\n    )\n)\nm.add_control(NavigationControl())\nm.add_control(ScaleControl(), ControlPosition.BOTTOM_LEFT)\n\n# Optional: only activate a given set of controls\ndraw_options = MapboxDrawOptions(\n    display_controls_default=False,\n    controls=MapboxDrawControls(polygon=True, line_string=True, trash=True),\n)\n# Use options from above\n# m.add_mapbox_draw(draw_options, geojson=geojson_feature)\n\n# If no options are passed, all controls are activated by default\nm.add_mapbox_draw(geojson=geojson_feature)\n\n# Shiny Express\nuse_mapboxgl_draw()\n\n\n@render_maplibregl\ndef maplibre():\n    return m\n\n\n@render.code\ndef selected_features():\n    obj = input.maplibre_draw_features_selected()\n    print(obj)\n    return json.dumps(obj[\"features\"], indent=2) if obj else \"Pick some features!\"\n\n\nif __name__ == \"__main__\":\n    filename = \"docs/examples/mapbox_draw_plugin/app.html\"\n    with open(filename, \"w\") as f:\n        f.write(m.to_html())\n\n    webbrowser.open(filename)\n

    Run example:

    shiny run docs/examples/mapbox_draw_plugin/app.py\n
    "},{"location":"examples/pmtiles/","title":"PMTiles","text":"
    # Example taken from here: https://maplibre.org/maplibre-gl-js/docs/examples/pmtiles/\n\nimport webbrowser\n\nfrom maplibre import Layer, LayerType, Map, MapOptions, render_maplibregl\nfrom maplibre.basemaps import construct_basemap_style\nfrom maplibre.controls import NavigationControl\nfrom shiny.express import input, render, ui\n\nPMTILES_URL = \"https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles\"\n\npmtiles_source = {\n    \"type\": \"vector\",\n    \"url\": f\"pmtiles://{PMTILES_URL}\",\n    \"attribution\": '\u00a9 <a href=\"https://openstreetmap.org\">OpenStreetMap</a>',\n}\n\ncustom_basemap = construct_basemap_style(\n    sources={\"pmtiles\": pmtiles_source},\n    layers=[\n        Layer(\n            id=\"buildings\",\n            source=\"pmtiles\",\n            source_layer=\"landuse\",\n            type=LayerType.FILL,\n            paint={\"fill-color\": \"steelblue\"},\n        ),\n        Layer(\n            id=\"roads\",\n            source=\"pmtiles\",\n            source_layer=\"roads\",\n            type=LayerType.LINE,\n            paint={\"line-color\": \"black\"},\n        ),\n        Layer(\n            id=\"mask\",\n            source=\"pmtiles\",\n            source_layer=\"mask\",\n            type=LayerType.FILL,\n            paint={\"fill-color\": \"white\"},\n        ),\n    ],\n)\n\n\nmap_options = MapOptions(\n    style=custom_basemap,\n    bounds=(11.154026, 43.7270125, 11.3289395, 43.8325455),\n)\n\n\ndef create_map():\n    m = Map(map_options)\n    m.add_control(NavigationControl())\n    return m\n\n\n@render_maplibregl\ndef render_map():\n    return create_map()\n\n\nif __name__ == \"__main__\":\n    file_name = \"docs/examples/pmtiles/app.html\"\n\n    m = create_map()\n    with open(file_name, \"w\") as f:\n        f.write(m.to_html())\n\n    webbrowser.open(file_name)\n

    Run example:

    shiny run docs/examples/pmtiles/app.py\n
    "},{"location":"examples/road_safety/","title":"H3 Grid UK Road Safety","text":"
    import sys\nimport webbrowser\n\nimport h3\nimport pandas as pd\nfrom maplibre import (\n    Layer,\n    LayerType,\n    Map,\n    MapContext,\n    MapOptions,\n    output_maplibregl,\n    render_maplibregl,\n)\nfrom maplibre.controls import NavigationControl\nfrom maplibre.sources import GeoJSONSource\nfrom maplibre.utils import df_to_geojson\nfrom shiny import App, reactive, ui\n\nRESOLUTION = 7\nCOLORS = (\n    \"lightblue\",\n    \"turquoise\",\n    \"lightgreen\",\n    \"yellow\",\n    \"orange\",\n    \"darkred\",\n)\n\nroad_safety = pd.read_csv(\n    \"https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv\"\n).dropna()\n\n\ndef create_h3_grid(res=RESOLUTION) -> dict:\n    road_safety[\"h3\"] = road_safety.apply(\n        lambda x: h3.geo_to_h3(x[\"lat\"], x[\"lng\"], resolution=res), axis=1\n    )\n    df = road_safety.groupby(\"h3\").h3.agg(\"count\").to_frame(\"count\").reset_index()\n    df[\"hexagon\"] = df.apply(\n        lambda x: [h3.h3_to_geo_boundary(x[\"h3\"], geo_json=True)], axis=1\n    )\n    df[\"color\"] = pd.cut(\n        df[\"count\"],\n        bins=len(COLORS),\n        labels=COLORS,\n    )\n    return df_to_geojson(\n        df, \"hexagon\", geometry_type=\"Polygon\", properties=[\"count\", \"color\"]\n    )\n\n\nsource = GeoJSONSource(data=create_h3_grid())\n\nmap_options = MapOptions(\n    center=(-1.415727, 52.232395),\n    zoom=7,\n    pitch=40,\n    bearing=-27,\n)\n\nh3_layer = Layer(\n    id=\"road-safety\",\n    type=LayerType.FILL_EXTRUSION,\n    source=source,\n    paint={\n        \"fill-extrusion-color\": [\"get\", \"color\"],\n        \"fill-extrusion-opacity\": 0.7,\n        \"fill-extrusion-height\": [\"*\", 100, [\"get\", \"count\"]],\n    },\n)\n\n\ndef create_map() -> Map:\n    m = Map(map_options)\n    m.add_control(NavigationControl())\n    m.add_layer(h3_layer)\n    m.add_tooltip(\"road-safety\", \"count\")\n    return m\n\n\napp_ui = ui.page_fluid(\n    ui.panel_title(\"Road safety in UK\"),\n    output_maplibregl(\"mapylibre\", height=700),\n    ui.input_slider(\"res\", \"Resolution\", min=4, max=8, step=1, value=RESOLUTION),\n)\n\n\ndef server(input, output, session):\n    @render_maplibregl\n    def mapylibre():\n        return create_map()\n\n    @reactive.Effect\n    @reactive.event(input.res, ignore_init=True)\n    async def resolution():\n        async with MapContext(\"mapylibre\") as m:\n            with ui.Progress() as p:\n                p.set(message=\"H3 calculation in progress\")\n                m.set_data(\"road-safety\", create_h3_grid(input.res()))\n                p.set(1, message=\"Calculation finished\")\n\n\napp = App(app_ui, server)\n\nif __name__ == \"__main__\":\n    filename = sys.argv[1] if len(sys.argv) == 2 else \"/tmp/road_safety.html\"\n    with open(filename, \"w\") as f:\n        m = create_map()\n        f.write(m.to_html(style=\"height: 700px;\"))\n\n    webbrowser.open(filename)\n

    Run example:

    poetry run uvicorn docs.examples.road_safety.app:app --reload\n
    "},{"location":"examples/vancouver_blocks/","title":"Vancouver Property Value","text":"
    import sys\n\nfrom maplibre import (\n    Layer,\n    LayerType,\n    Map,\n    MapContext,\n    MapOptions,\n    output_maplibregl,\n    render_maplibregl,\n)\nfrom maplibre.basemaps import Carto\nfrom maplibre.controls import NavigationControl, ScaleControl\nfrom maplibre.sources import GeoJSONSource\nfrom shiny import App, reactive, ui\n\nSOURCE_ID = \"vancouver-blocks\"\nLAYER_ID_LINES = \"vancouver-blocks-lines\"\nLAYER_ID_FILL = \"vancouver-blocks-fill-extrusion\"\nMAX_FILTER_VALUE = 1000000\n\nvancouver_blocks_source = GeoJSONSource(\n    data=\"https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/geojson/vancouver-blocks.json\"\n)\n\nvancouver_blocks_lines = Layer(\n    type=LayerType.LINE,\n    id=LAYER_ID_LINES,\n    source=SOURCE_ID,\n    paint={\n        \"line-color\": \"white\",\n        \"line-width\": 2,\n    },\n)\n\nvancouver_blocks_fill = Layer(\n    type=LayerType.FILL_EXTRUSION,\n    id=LAYER_ID_FILL,\n    source=SOURCE_ID,\n    paint={\n        \"fill-extrusion-color\": {\n            \"property\": \"valuePerSqm\",\n            \"stops\": [\n                [0, \"grey\"],\n                [1000, \"yellow\"],\n                [5000, \"orange\"],\n                [10000, \"darkred\"],\n                [50000, \"lightblue\"],\n            ],\n        },\n        \"fill-extrusion-height\": [\"*\", 10, [\"sqrt\", [\"get\", \"valuePerSqm\"]]],\n        \"fill-extrusion-opacity\": 0.9,\n    },\n)\n\nmap_options = MapOptions(\n    style=Carto.DARK_MATTER,\n    center=(-123.13, 49.254),\n    zoom=11,\n    pitch=45,\n    bearing=0,\n)\n\n\ndef create_map() -> Map:\n    m = Map(map_options)\n    m.add_control(NavigationControl())\n    m.add_control(ScaleControl(), position=\"bottom-left\")\n    m.add_source(SOURCE_ID, vancouver_blocks_source)\n    m.add_layer(vancouver_blocks_lines)\n    m.add_layer(vancouver_blocks_fill)\n    m.add_tooltip(LAYER_ID_FILL, \"valuePerSqm\")\n    return m\n\n\napp_ui = ui.page_fluid(\n    ui.panel_title(\"Vancouver Property Value\"),\n    ui.div(\n        \"Height of polygons - average property value per square meter of lot\",\n        style=\"padding: 10px;\",\n    ),\n    output_maplibregl(\"maplibre\", height=600),\n    ui.input_select(\n        \"filter\",\n        \"max property value per square meter\",\n        choices=[0, 1000, 5000, 10000, 50000, 100000, MAX_FILTER_VALUE],\n        selected=MAX_FILTER_VALUE,\n    ),\n    ui.input_checkbox_group(\n        \"layers\",\n        \"Layers\",\n        choices=[LAYER_ID_FILL, LAYER_ID_LINES],\n        selected=[LAYER_ID_FILL, LAYER_ID_LINES],\n    ),\n)\n\n\ndef server(input, output, session):\n    @render_maplibregl\n    def maplibre():\n        m = create_map()\n        return m\n\n    @reactive.Effect\n    @reactive.event(input.filter)\n    async def filter():\n        async with MapContext(\"maplibre\") as m:\n            filter_ = [\"<=\", [\"get\", \"valuePerSqm\"], int(input.filter())]\n            m.set_filter(LAYER_ID_FILL, filter_)\n\n    @reactive.Effect\n    @reactive.event(input.layers)\n    async def layers():\n        visible_layers = input.layers()\n        async with MapContext(\"maplibre\") as m:\n            for layer in [LAYER_ID_FILL, LAYER_ID_LINES]:\n                m.set_visibility(layer, layer in visible_layers)\n\n\napp = App(app_ui, server)\n\nif __name__ == \"__main__\":\n    if len(sys.argv) == 2:\n        file_name = sys.argv[1]\n        m = create_map()\n        with open(file_name, \"w\") as f:\n            f.write(m.to_html())\n    else:\n        app.run()\n

    Run example:

    poetry run uvicorn docs.examples.vancouver_blocks.app:app --reload\n
    "},{"location":"examples/vector_tiles/","title":"Vector Tiles","text":"
    # Example taken from here:\n# https://maplibre.org/maplibre-gl-js/docs/API/classes/VectorTileSource/\n# https://maplibre.org/maplibre-style-spec/sources/\n\nimport webbrowser\n\nfrom maplibre import Layer, LayerType, Map, MapOptions, render_maplibregl\nfrom maplibre.basemaps import Carto\nfrom maplibre.controls import NavigationControl\nfrom maplibre.sources import VectorTileSource\nfrom shiny.express import input, render, ui\n\n# Get layer ids and pbf url from here\nVECTOR_TILES_URL = \"https://demotiles.maplibre.org/tiles/tiles.json\"\nLAYER_ID = \"countries\"\n\nvector_source = VectorTileSource(\n    url=VECTOR_TILES_URL,\n    # tiles=[\"https://demotiles.maplibre.org/tiles/{z}/{x}/{y}.pbf\"],\n    min_zoom=0,\n    max_zoom=6,\n)\n\nvector_layer = Layer(\n    type=LayerType.FILL,\n    id=LAYER_ID,\n    source=vector_source,\n    paint={\"fill-color\": \"lightgreen\", \"fill-outline-color\": \"black\"},\n    source_layer=\"countries\",\n)\n\n\ndef create_map():\n    m = Map(MapOptions(style=Carto.POSITRON, center=(11, 42), zoom=3, hash=True))\n    m.add_control(NavigationControl())\n    m.add_layer(vector_layer)\n    m.add_tooltip(LAYER_ID)\n    return m\n\n\n@render_maplibregl\ndef render_map():\n    return create_map()\n\n\nif __name__ == \"__main__\":\n    file_name = \"docs/examples/vector_tiles/app.html\"\n\n    m = create_map()\n    with open(file_name, \"w\") as f:\n        f.write(m.to_html())\n\n    webbrowser.open(file_name)\n

    Run example:

    shiny run docs/examples/vector_tiles/app.py\n
    "},{"location":"examples/where_is_the_iss/","title":"Where is the ISS","text":"
    import requests\nfrom maplibre import (\n    Layer,\n    LayerType,\n    Map,\n    MapContext,\n    MapOptions,\n    output_maplibregl,\n    render_maplibregl,\n)\nfrom maplibre.controls import NavigationControl\nfrom maplibre.sources import GeoJSONSource\nfrom shiny import App, reactive, ui\n\nMAX_FEATURES = 30\nSOURCE_ID_ISS_POSITION = \"iss_position\"\nSOURCE_ID_ISS_LAST_POSITIONS = \"iss-last-positions\"\n\n\ndef where_is_the_iss() -> tuple:\n    r = requests.get(\"https://api.wheretheiss.at/v1/satellites/25544\").json()\n    return (r[\"longitude\"], r[\"latitude\"])\n\n\ndef create_feature(lng_lat: tuple) -> dict:\n    return {\n        \"type\": \"Feature\",\n        \"geometry\": {\n            \"type\": \"Point\",\n            \"coordinates\": lng_lat,\n        },\n        \"properties\": {\"coords\": \", \".join(map(str, lng_lat))},\n    }\n\n\nlng_lat = where_is_the_iss()\nfeature = create_feature(where_is_the_iss())\nfeature_collection = {\"type\": \"FeatureCollection\", \"features\": [feature]}\n\napp_ui = ui.page_fluid(\n    ui.panel_title(\"Where is the ISS\"),\n    ui.div(\"Click on Update to get the current position of the ISS.\"),\n    ui.div(\n        \"The yellow dots show the positions before. Hover the blue dot to display the coordinates.\"\n    ),\n    output_maplibregl(\"mapylibre\", height=600),\n    ui.input_action_button(\"update\", \"Update\"),\n)\n\n\ndef server(input, output, session):\n    @render_maplibregl\n    def mapylibre():\n        m = Map(MapOptions(center=lng_lat, zoom=3))\n        m.add_control(NavigationControl())\n        m.set_paint_property(\"water\", \"fill-color\", \"darkblue\")\n        m.add_source(\n            SOURCE_ID_ISS_LAST_POSITIONS, GeoJSONSource(data=feature_collection)\n        )\n        m.add_layer(\n            Layer(\n                type=LayerType.CIRCLE,\n                source=SOURCE_ID_ISS_LAST_POSITIONS,\n                paint={\"circle-color\": \"yellow\", \"circle-radius\": 5},\n            ),\n        )\n        m.add_source(SOURCE_ID_ISS_POSITION, GeoJSONSource(data=feature))\n        m.add_layer(\n            Layer(\n                type=LayerType.CIRCLE,\n                id=\"iss-position\",\n                source=SOURCE_ID_ISS_POSITION,\n                paint={\"circle-color\": \"lightblue\", \"circle-radius\": 7},\n            )\n        )\n        m.add_tooltip(\"iss-position\", \"coords\")\n        return m\n\n    @reactive.Effect\n    @reactive.event(input.update)\n    async def update():\n        print(\"Fetching new position\")\n        lng_lat = where_is_the_iss()\n        print(lng_lat)\n        if len(feature_collection[\"features\"]) == MAX_FEATURES:\n            feature_collection[\"features\"] = []\n\n        async with MapContext(\"mapylibre\") as m:\n            feature = create_feature(lng_lat)\n            m.set_data(SOURCE_ID_ISS_POSITION, feature)\n            feature_collection[\"features\"].append(feature)\n            m.set_data(SOURCE_ID_ISS_LAST_POSITIONS, feature_collection)\n            m.add_call(\"flyTo\", {\"center\": lng_lat, \"speed\": 0.5})\n\n\napp = App(app_ui, server)\n

    Run example:

    poetry run uvicorn docs.examples.where_is_the_iss.app:app --reload\n
    "},{"location":"examples/wms/","title":"WMS","text":"
    import webbrowser\n\nfrom maplibre import Layer, LayerType, Map, MapOptions\nfrom maplibre.controls import NavigationControl\nfrom maplibre.sources import RasterTileSource\n\nSOURCE_ID = \"wms-test-source\"\nLAYER_ID = \"wms-test-layer\"\n\nwms_source = RasterTileSource(\n    tiles=[\n        \"https://img.nj.gov/imagerywms/Natural2015?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.1&request=GetMap&srs=EPSG:3857&transparent=true&width=256&height=256&layers=Natural2015\"\n    ],\n    tile_size=256,\n)\nwms_layer = Layer(type=LayerType.RASTER, source=SOURCE_ID, id=LAYER_ID)\n\nmap_options = MapOptions(zoom=8, center=(-74.5447, 40.6892))\n\nm = Map(map_options=map_options)\nm.add_control(NavigationControl())\nm.add_source(SOURCE_ID, wms_source)\nm.add_layer(wms_layer)\n\nfilename = \"docs/examples/wms/app.html\"\n\nwith open(filename, \"w\") as f:\n    f.write(m.to_html(style=\"height: 800px;\"))\n\nwebbrowser.open(filename)\n
    "}]} \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz index 34b9ca3..8b52e61 100644 Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ