Component Apps: Mapping with GeoJSON

Important

These recipes only apply to Component App development and will not work for Standard Apps.

Last Updated: December 2025

GeoJSON from a URL

@App.page
def geojson_from_url(lib):
    return lib.tethys.Display(
        lib.tethys.Map(
            lib.ol.layer.Vector(
                lib.ol.source.Vector(
                    options=lib.Props(
                        url="https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_urban_areas.geojson",
                        format_="GeoJSON"
                    )
                )
            )
        )
    )

GeoJSON from explicit inline

@App.page
def geojson_from_inline(lib):
    features = {
        'type': 'FeatureCollection',
        'crs': {
            'type': 'name',
            'properties': {
            'name': 'EPSG:3857',
            },
        },
        'features': [
            {
                'type': 'Feature',
                'geometry': {
                    'type': 'Point',
                    'coordinates': [0, 0],
                },
            },
            {
                'type': 'Feature',
                'geometry': {
                    'type': 'LineString',
                    'coordinates': [
                        [4e6, -2e6],
                        [8e6, 2e6],
                    ],
                },
            },
            {
                'type': 'Feature',
                'geometry': {
                    'type': 'LineString',
                    'coordinates': [
                        [4e6, 2e6],
                        [8e6, -2e6],
                    ],
                },
            },
            {
                'type': 'Feature',
                'geometry': {
                    'type': 'Polygon',
                    'coordinates': [
                        [
                            [-5e6, -1e6],
                            [-3e6, -1e6],
                            [-4e6, 1e6],
                            [-5e6, -1e6],
                        ],
                    ],
                },
            },
            {
                'type': 'Feature',
                'geometry': {
                    'type': 'MultiLineString',
                    'coordinates': [
                        [
                            [-1e6, -7.5e5],
                            [-1e6, 7.5e5],
                        ],
                        [
                            [1e6, -7.5e5],
                            [1e6, 7.5e5],
                        ],
                        [
                            [-7.5e5, -1e6],
                            [7.5e5, -1e6],
                        ],
                        [
                            [-7.5e5, 1e6],
                            [7.5e5, 1e6],
                        ],
                    ],
                },
            },
            {
                'type': 'Feature',
                'geometry': {
                    'type': 'MultiPolygon',
                    'coordinates': [
                        [
                            [
                                [-5e6, 6e6],
                                [-3e6, 6e6],
                                [-3e6, 8e6],
                                [-5e6, 8e6],
                                [-5e6, 6e6],
                            ],
                        ],
                        [
                            [
                                [-2e6, 6e6],
                                [0, 6e6],
                                [0, 8e6],
                                [-2e6, 8e6],
                                [-2e6, 6e6],
                            ],
                        ],
                        [
                            [
                                [1e6, 6e6],
                                [3e6, 6e6],
                                [3e6, 8e6],
                                [1e6, 8e6],
                                [1e6, 6e6],
                            ],
                        ],
                    ],
                },
            },
            {
                'type': 'Feature',
                'geometry': {
                    'type': 'GeometryCollection',
                    'geometries': [
                        {
                            'type': 'LineString',
                            'coordinates': [
                                [-5e6, -5e6],
                                [0, -5e6],
                            ],
                        },
                        {
                            'type': 'Point',
                            'coordinates': [4e6, -5e6],
                        },
                        {
                            'type': 'Polygon',
                            'coordinates': [
                                [
                                    [1e6, -6e6],
                                    [3e6, -6e6],
                                    [2e6, -4e6],
                                    [1e6, -6e6],
                                ],
                            ],
                        },
                    ],
                },
            },
        ],
    }
    return lib.tethys.Display(
        lib.tethys.Map(
            lib.ol.layer.Vector(
                lib.ol.source.Vector(
                    options=lib.Props(
                        features=features,
                        format_="GeoJSON"
                    )
                )
            )
        )
    )

GeoJSON from File / Pandas Dataframe

import pandas as pd
import geopandas as gpd
from shapely.geometry import Point
from tethys_sdk.components.utils import transform_coordinate

def csv_to_geojson(csv_path):
    df = pd.read_csv(csv_path)
    geometry = [Point(transform_coordinate(xy, "EPSG:4326", "EPSG:3857")) for xy in zip(df['lat'], df['lon'])]
    gdf = gpd.GeoDataFrame(df, geometry=geometry, crs="EPSG:3857")
    return gdf.to_json()

@App.page
def geojson_from_csv(lib):
    resources = lib.hooks.use_resources()
    geojson = csv_to_geojson(resources.path / "points.csv")
    return lib.tethys.Display(
        lib.tethys.Map(
            lib.ol.layer.Vector(
                lib.ol.source.Vector(
                    options=lib.Props(
                        features=geojson,
                        format_="GeoJSON"
                    )
                )
            )
        )
    )

GeoJSON Features with Dynamic Style

@App.page
def geojson_with_dynamic_style(lib):
    fill_color, set_fill_color = lib.hooks.use_state("#ff0000")
    stroke_color, set_stroke_color = lib.hooks.use_state("#000000")
    return lib.tethys.Display(
        lib.html.div(
            style=lib.Style(
                position="absolute",
                top=10,
                right=20,
                zIndex=1
            )
        )(
            lib.bs.FormLabel(htmlFor="fill-color")("Fill Color"),
            lib.bs.FormControl(
                id_="fill-color",
                type_="color",
                defaultValue=fill_color,
                onChange=lambda e: set_fill_color(e.target.value)
            ),
            lib.bs.FormLabel(htmlFor="stroke-color")("Stroke Color"),
            lib.bs.FormControl(
                id_="stroke-color",
                type_="color",
                defaultValue=stroke_color,
                onChange=lambda e: set_stroke_color(e.target.value),
            ),
        ),
        lib.tethys.Map(
            lib.ol.layer.Vector(
                style=lib.ol.style.Style(
                    stroke=lib.ol.style.Stroke(
                        color=stroke_color,
                        width=1
                    ),
                    fill=lib.ol.style.Fill(
                        color=fill_color
                    )
                )
            )(
                lib.ol.source.Vector(
                    options=lib.Props(
                        url="https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_urban_areas.geojson",
                        format_="GeoJSON"
                    )
                )
            )
        )
    )

Style GeoJSON with Unique Values Styler

@App.page
def geojson_unique_styler(lib):
    return lib.tethys.Display(
        lib.tethys.Map(
            lib.ol.layer.Vector(
                style=lib.ol.style.Styler(
                    method="unique_values",
                    fields=["scalerank"],
                    values=[
                        (
                            1,
                            lib.ol.style.Style(
                                stroke=lib.ol.style.Stroke(
                                    color="purple",
                                    width=1
                                ),
                                fill=lib.ol.style.Fill(
                                    color="white"
                                ),
                                text=lib.ol.style.Text(
                                    text="%{$feature.scalerank}",
                                    fill=lib.ol.style.Fill(color="orange"),
                                    stroke=lib.ol.style.Stroke(color="#f1234567", width=2),
                                ),
                            )
                        ),
                        (
                            2,
                            lib.ol.style.Style(
                                stroke=lib.ol.style.Stroke(
                                    color="#459302",
                                    width=1
                                ),
                                fill=lib.ol.style.Fill(
                                    color="#18181818"
                                ),
                                text=lib.ol.style.Text(
                                    text="%{$feature.scalerank}",
                                    fill=lib.ol.style.Fill(color="orange"),
                                    stroke=lib.ol.style.Stroke(color="#f1234567", width=2),
                                ),
                            )
                        ),
                        (
                            3,
                            lib.ol.style.Style(
                                stroke=lib.ol.style.Stroke(
                                    color="#93102849",
                                    width=1
                                ),
                                fill=lib.ol.style.Fill(
                                    color="#f930efd3"
                                ),
                                text=lib.ol.style.Text(
                                    text="%{$feature.scalerank}",
                                    fill=lib.ol.style.Fill(color="orange"),
                                    stroke=lib.ol.style.Stroke(color="#f1234567", width=2),
                                ),
                            )
                        )
                    ],
                    default=lib.ol.style.Style(
                        stroke=lib.ol.style.Stroke(
                            color="#000000",
                            width=1
                        ),
                        fill=lib.ol.style.Fill(
                            color="#ff0000"
                        ),
                        text=lib.ol.style.Text(
                            text="%{$feature.scalerank}",
                            fill=lib.ol.style.Fill(color="blue"),
                            stroke=lib.ol.style.Stroke(color="white", width=2),
                        ),
                    )
                )
            )(
                lib.ol.source.Vector(
                    options=lib.Props(
                        url="https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_urban_areas.geojson",
                        format_="GeoJSON"
                    )
                )
            )
        )
    )

Display Feature Props On Click

@App.page
def display_feature_props_on_click(lib):
    props, set_props = lib.hooks.use_state({})
    return lib.tethys.Display(
        lib.tethys.Panel(title="Properties", show=len(props) > 0, on_close=lambda e: set_props({}), extent="300px")(
            *[
                lib.html.div(
                    lib.html.span(
                        lib.html.b(f"{k}: ")
                    ),
                    lib.html.span(v)
                ) for k, v in props.items()
            ]
        ),
        lib.tethys.Map(
            lib.ol.layer.Vector(
                onClick=lambda e: set_props(e.features[0] if e.features else lib.Props(Message="No features found"))
            )(
                lib.ol.source.Vector(
                    options=lib.Props(
                        url="https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_urban_areas.geojson",
                        format_="GeoJSON"
                    )
                )
            )
        )
    )