Vancouver Property Value

import sys

from maplibre import (
    Layer,
    LayerType,
    Map,
    MapContext,
    MapOptions,
    output_maplibregl,
    render_maplibregl,
)
from maplibre.basemaps import Carto
from maplibre.controls import NavigationControl, ScaleControl
from maplibre.sources import GeoJSONSource
from shiny import App, reactive, ui

SOURCE_ID = "vancouver-blocks"
LAYER_ID_LINES = "vancouver-blocks-lines"
LAYER_ID_FILL = "vancouver-blocks-fill-extrusion"
MAX_FILTER_VALUE = 1000000

vancouver_blocks_source = GeoJSONSource(
    data="https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/geojson/vancouver-blocks.json"
)

vancouver_blocks_lines = Layer(
    type=LayerType.LINE,
    id=LAYER_ID_LINES,
    source=SOURCE_ID,
    paint={
        "line-color": "white",
        "line-width": 2,
    },
)

vancouver_blocks_fill = Layer(
    type=LayerType.FILL_EXTRUSION,
    id=LAYER_ID_FILL,
    source=SOURCE_ID,
    paint={
        "fill-extrusion-color": {
            "property": "valuePerSqm",
            "stops": [
                [0, "grey"],
                [1000, "yellow"],
                [5000, "orange"],
                [10000, "darkred"],
                [50000, "lightblue"],
            ],
        },
        "fill-extrusion-height": ["*", 10, ["sqrt", ["get", "valuePerSqm"]]],
        "fill-extrusion-opacity": 0.9,
    },
)

map_options = MapOptions(
    style=Carto.DARK_MATTER,
    center=(-123.13, 49.254),
    zoom=11,
    pitch=45,
    bearing=0,
)


def create_map() -> Map:
    m = Map(map_options)
    m.add_control(NavigationControl())
    m.add_control(ScaleControl(), position="bottom-left")
    m.add_source(SOURCE_ID, vancouver_blocks_source)
    m.add_layer(vancouver_blocks_lines)
    m.add_layer(vancouver_blocks_fill)
    m.add_tooltip(LAYER_ID_FILL, "valuePerSqm")
    return m


app_ui = ui.page_fluid(
    ui.panel_title("Vancouver Property Value"),
    ui.div(
        "Height of polygons - average property value per square meter of lot",
        style="padding: 10px;",
    ),
    output_maplibregl("maplibre", height=600),
    ui.input_select(
        "filter",
        "max property value per square meter",
        choices=[0, 1000, 5000, 10000, 50000, 100000, MAX_FILTER_VALUE],
        selected=MAX_FILTER_VALUE,
    ),
    ui.input_checkbox_group(
        "layers",
        "Layers",
        choices=[LAYER_ID_FILL, LAYER_ID_LINES],
        selected=[LAYER_ID_FILL, LAYER_ID_LINES],
    ),
)


def server(input, output, session):
    @render_maplibregl
    def maplibre():
        m = create_map()
        return m

    @reactive.Effect
    @reactive.event(input.filter)
    async def filter():
        async with MapContext("maplibre") as m:
            filter_ = ["<=", ["get", "valuePerSqm"], int(input.filter())]
            m.set_filter(LAYER_ID_FILL, filter_)

    @reactive.Effect
    @reactive.event(input.layers)
    async def layers():
        visible_layers = input.layers()
        async with MapContext("maplibre") as m:
            for layer in [LAYER_ID_FILL, LAYER_ID_LINES]:
                m.set_visibility(layer, layer in visible_layers)


app = App(app_ui, server)

if __name__ == "__main__":
    if len(sys.argv) == 2:
        file_name = sys.argv[1]
        m = create_map()
        with open(file_name, "w") as f:
            f.write(m.to_html())
    else:
        app.run()

Run example:

poetry run uvicorn docs.examples.vancouver_blocks.app:app --reload