Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions maplibre/__future__/controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

from enum import Enum

from ..controls import Control

from pydantic import Field

from ..controls import Control


class GeocoderType(Enum):
MAPTILTER = "maptiler"
Expand Down
2 changes: 1 addition & 1 deletion maplibre/__future__/datasets.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from typing import Optional, Union

import geopandas as gpd
from pydantic import BaseModel

# from geopandas import read_file
from maplibre.sources import GeoJSONSource
from pydantic import BaseModel


class DataSet(BaseModel):
Expand Down
2 changes: 1 addition & 1 deletion maplibre/__future__/ipywidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

from pathlib import Path

from ..ipywidget import MapWidget as BaseWidget
from .controls import GeocoderType

from ..ipywidget import MapWidget as BaseWidget

class MapWidget(BaseWidget):
_geocoder_type = GeocoderType.MAPTILTER
Expand Down
3 changes: 2 additions & 1 deletion maplibre/__future__/pandas_ext.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import geopandas as gpd
import maplibre.express as mx
import pandas as pd

import maplibre.express as mx

from ..sources import GeoJSONSource, SimpleFeatures


Expand Down
14 changes: 12 additions & 2 deletions maplibre/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import importlib.metadata
import logging

__version__ = importlib.metadata.version(__package__)

logging.basicConfig()
logger = logging.getLogger(__name__)

Expand All @@ -25,3 +23,15 @@
except ImportError as e:
logger.warning(e)
logger.warning(WARNING_MESSAGE)

# New imports
try:
from .anywidget import MapWidget
except ImportError as e:
print(e)

from .sources import *

__version__ = importlib.metadata.version(__package__)

__all__ = ["Map", "MapOptions", "Layer", "LayerType", "ControlPosition"]
8 changes: 2 additions & 6 deletions maplibre/_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@
_points = "https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/scatterplot/manhattan.json"
_geojson = "https://docs.mapbox.com/mapbox-gl-js/assets/indoor-3d-map.geojson"
_urban_areas = "https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_urban_areas.geojson"
_highway_lines = (
"https://github.com/visgl/deck.gl-data/raw/master/examples/highway/roads.json"
)
_highway_lines = "https://github.com/visgl/deck.gl-data/raw/master/examples/highway/roads.json"

_airports = (
"https://github.com/visgl/deck.gl-data/raw/master/examples/line/airports.json"
)
_airports = "https://github.com/visgl/deck.gl-data/raw/master/examples/line/airports.json"
_flights = "https://github.com/visgl/deck.gl-data/raw/master/examples/line/heathrow-flights.json"
122 changes: 122 additions & 0 deletions maplibre/anywidget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
from __future__ import annotations

import logging
from pathlib import Path

logging.basicConfig()
logger = logging.getLogger(__name__)


try:
import traitlets
from anywidget import AnyWidget
except ImportError as e:
logger.error(e)
logger.error("Please install 'maplibre[anywidget]' if you want to use maplibre in 'marimo' or 'jupyter' notebooks.")

from maplibre.map import Map, MapOptions


class MapWidget(AnyWidget, Map):
"""MapWidget

Use this class to display and update maps in Marimo or 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 = Path(__file__).parent / "srcjs" / "ipywidget.js"
# _css = Path(__file__).parent / "srcjs" / "ipywidget.css"

_esm = Path(__file__).parent / "srcjs" / "maplibre.anywidget.js"
_css = Path(__file__).parent / "srcjs" / "maplibre.anywidget.css"

_use_message_queue = True
# _rendered = traitlets.Bool(False).tag(config=True).tag(sync=True)
load = traitlets.Bool(False).tag(sync=True)
map_options = traitlets.Dict().tag(sync=True)
calls = traitlets.List().tag(sync=True)
msg = 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)
draw_features_created = traitlets.List().tag(sync=True)
draw_features_updated = traitlets.List().tag(sync=True)
draw_features_deleted = traitlets.List().tag(sync=True)

def __init__(
self,
map_options: MapOptions = MapOptions(),
sources: dict | None = None,
layers: list | None = None,
controls: list | None = None,
height: int | str = 400,
**kwargs,
) -> None:
# self._rendered = False
# self.calls = []
AnyWidget.__init__(self, height=height, **kwargs)
Map.__init__(self, map_options, sources, layers, controls, **kwargs)

@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 = []
"""

"""
def use_message_queue(self, value: bool = True) -> None:
self._use_message_queue = value
"""

# @traitlets.observe("load")
# def _(self, change) -> None:
# self.send({"calls": self.calls, "msg": "observe load"})

def add_call(self, method_name: str, *args) -> None:
call = [method_name, args]
if self.load:
# self.send({"calls": [call], "msg": "custom call"})
self.msg = call
return

# Initial calls
self.calls = self.calls + [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"})
"""

def add_tooltip(self, layer_id, template = None) -> None:
return self.add_call("addTooltip", layer_id, template)
4 changes: 1 addition & 3 deletions maplibre/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ class Config(BaseModel):

maptiler_api_key_env_var: Optional[str] = "MAPTILER_API_KEY"

layer_types: Optional[dict] = dict(
Polygon="fill", LineString="line", Point="circle"
)
layer_types: Optional[dict] = dict(Polygon="fill", LineString="line", Point="circle")

@property
def maptiler_api_key(self) -> str:
Expand Down
9 changes: 1 addition & 8 deletions maplibre/experimental.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,7 @@
from typing import Literal

from geopandas import GeoDataFrame
from pydantic import (
BaseModel,
ConfigDict,
Field,
computed_field,
field_validator,
model_serializer,
)
from pydantic import BaseModel, ConfigDict, Field, computed_field, field_validator, model_serializer

from maplibre import Layer, LayerType
from maplibre.utils import geopandas_to_geojson
Expand Down
Empty file added maplibre/export.py
Empty file.
20 changes: 5 additions & 15 deletions maplibre/express.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,25 +77,19 @@ def color_quantile(
probs: list = [0.1, 0.25, 0.5, 0.75],
cmap: str = config.cmap,
) -> Self:
expr = color_quantile_step_expr(
column, probs, values=self.sf.data[column], cmap=cmap
)
expr = color_quantile_step_expr(column, probs, values=self.sf.data[column], cmap=cmap)
self._set_paint_property("color", expr)
return self

def color_bin(
self, column: str, stops: list = None, n: int = None, cmap=config.cmap
) -> Self:
def color_bin(self, column: str, stops: list = None, n: int = None, cmap=config.cmap) -> Self:
if stops is None and n is None:
pass

expr = color_step_expr(column, stops, cmap)
self._set_paint_property("color", expr)
return self

def interpolate_color(
self, column: str, stops=None, colors=("yellow", "red")
) -> Self:
def interpolate_color(self, column: str, stops=None, colors=("yellow", "red")) -> Self:
stops = stops or [f(self.sf.data[column]) for f in [min, max]]
expr = interpolate(column, stops, colors)
self._set_paint_property("color", expr)
Expand Down Expand Up @@ -164,9 +158,7 @@ def fill_extrusion(
**kwargs,
) -> SimpleLayer:
if "paint" not in kwargs:
kwargs["paint"] = config.paint_props[
LayerType.FILL_EXTRUSION.value.replace("-", "_")
]
kwargs["paint"] = config.paint_props[LayerType.FILL_EXTRUSION.value.replace("-", "_")]

if fill_extrusion_base is not None:
kwargs["paint"]["fill-extrusion-base"] = fill_extrusion_base
Expand All @@ -190,9 +182,7 @@ def fill_line_circle(source_id: str, colors: list = None) -> list:
type=LayerType.FILL,
source=source_id,
filter=geometry_type_filter(GeometryType.POLYGON),
).set_paint_props(
fill_color=fill_color, fill_outline_color=config.fill_outline_color
)
).set_paint_props(fill_color=fill_color, fill_outline_color=config.fill_outline_color)

line_layer = Layer(
type=LayerType.LINE,
Expand Down
38 changes: 8 additions & 30 deletions maplibre/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,30 +27,20 @@ def geometry_type_filter(geom_type: GeometryType | str) -> list:
return ["==", get_geometry_type(), GeometryType(geom_type).value]


def interpolate(
property: str | list, stops: list, outputs: list, type: list = ["linear"]
) -> list:
def interpolate(property: str | list, stops: list, outputs: list, type: list = ["linear"]) -> list:
assert len(stops) == len(outputs)
return [
"interpolate",
type,
get_column(property) if isinstance(property, str) else property,
] + list(
itertools.chain.from_iterable(
[[stop, output] for stop, output in zip(stops, outputs)]
)
)
] + list(itertools.chain.from_iterable([[stop, output] for stop, output in zip(stops, outputs)]))


def match_expr(column: str, categories: list, outputs: list[T], fallback: T) -> list:
assert len(categories) == len(outputs)
return (
["match", get_column(column)]
+ list(
itertools.chain.from_iterable(
[[category, output] for category, output in zip(categories, outputs)]
)
)
+ list(itertools.chain.from_iterable([[category, output] for category, output in zip(categories, outputs)]))
+ [fallback]
)

Expand All @@ -70,18 +60,12 @@ def step_expr(
"step",
get_column(property) if isinstance(property, str) else property,
]
+ list(
itertools.chain.from_iterable(
[[output, stop] for output, stop in zip(outputs, stops)]
)
)
+ list(itertools.chain.from_iterable([[output, stop] for output, stop in zip(outputs, stops)]))
+ [fallback]
)


def quantile_step_expr(
column: str, probs: list, outputs: list[T], fallback: T, values: list
) -> list:
def quantile_step_expr(column: str, probs: list, outputs: list[T], fallback: T, values: list) -> list:
assert len(probs) == len(outputs)
try:
import numpy as np
Expand All @@ -98,14 +82,10 @@ def quantile_step_expr(
# -----


def color_quantile_step_expr(
column: str, probs: list, values: Any, cmap="viridis"
) -> list:
def color_quantile_step_expr(column: str, probs: list, values: Any, cmap="viridis") -> list:
n = len(probs)
colors = color_brewer(cmap, n + 1)
return quantile_step_expr(
column, probs, outputs=colors[0:n], fallback=colors[-1], values=values
)
return quantile_step_expr(column, probs, outputs=colors[0:n], fallback=colors[-1], values=values)


def color_step_expr(column: str, stops: list, cmap="viridis") -> list:
Expand Down Expand Up @@ -143,8 +123,6 @@ def filter_expr(column: str, operator: str, value: Any) -> list:
return [operator, get_column(column), value]


def range_filter(
column, values: tuple | list, operators: tuple | list = (">=", "<=")
) -> list:
def range_filter(column, values: tuple | list, operators: tuple | list = (">=", "<=")) -> list:
assert len(values) == len(operators) == 2
return ["all"] + [[operators[i], get_column(column), values[i]] for i in range(2)]
Loading