Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Adding and styling GeoJSON, GeoDataFrames, and point markers on top of raster imagery

Why vector visualization matters

Vector data carries the labels, boundaries, and outputs that give raster imagery meaning: building footprints, glacier outlines, detected objects. Overlaying vector features on their source imagery is the standard way to check whether annotations actually line up with what is on the ground, which matters both for your own labels and for anything you download from elsewhere.


Core idea

leafmap accepts vector data from files, URLs, or in-memory GeoDataFrames, and gives you direct control over how each feature is styled.


Workflow

A. Prepare sample data

This page uses the same Willisau building sample as the raster page.

import geoai
import geopandas as gpd
import leafmap

swissimage_url = "https://source.coop/giuz/sds320/L03/data/willisau_2024_swissimage_rgb_subset.tif"
buildings_overture_url = ("https://data.source.coop/giuz/sds320/L03/data/willisau_overture_buildings_subset.geojson")
buildings_OSM_url = ("https://data.source.coop/giuz/sds320/L03/data/willisau_OSM_buildings_subset_epsg4326.geojson")

swissimage_path = geoai.download_file(swissimage_url)
buildings_path = geoai.download_file(buildings_overture_url)

B. Load vector data as a GeoDataFrame

This keeps you in familiar territory from SDS210: read a vector file with geopandas, inspect it, then hand it to leafmap.

gdf = gpd.read_file(buildings_path)

print(f"Features: {len(gdf)}")
gdf.head()

Before mapping, check whether the layer has a CRS and expected geometry type.

print("CRS:", gdf.crs)
print("Geometry types:")
print(gdf.geom_type.value_counts())

C. Overlay it on the source imagery

Adding vector labels directly on top of the raster they came from is the fastest way to catch a misaligned or incomplete annotation.

m = leafmap.Map()
m.add_raster(swissimage_path, layer_name="SWISSIMAGE")
m.add_gdf(gdf, layer_name="Building Footprints", zoom_to_layer=True)
m

D. Add vector data directly from a URL

You do not always need to load a file into a GeoDataFrame first; add_geojson() works directly from a URL, if the dataset is projected in geographic coordinates EPSG:4326.

m2 = leafmap.Map()
m2.add_raster(swissimage_path, zoom_to_layer=True)
m2.add_geojson(buildings_OSM_url, layer_name="Buildings", zoom_to_layer=True)
m2

E. Style vector layers deliberately

A style dictionary follows the Leaflet path-options convention: color (outline), weight (outline width), fillColor, and fillOpacity.

style = {
    "color": "red",
    "weight": 2,
    "fillColor": "yellow",
    "fillOpacity": 0.3,
}

m3 = leafmap.Map()
m3.add_raster(swissimage_path)
m3.add_gdf(gdf, layer_name="Styled Buildings", style=style, zoom_to_layer=True)
m3

F. Add point markers

For point data, such as sample locations or points of interest, use add_marker(). For datasets with many points, consider marker clustering so the map stays readable at broader zoom levels.

m4 = leafmap.Map(center=[47.11677, 7.99028], zoom=11)
m4.add_marker(location=[47.11677, 7.99028])     # Kantonsschule Willisau
m4

Python reactivation

The style dictionary here is an ordinary Python dictionary with string keys, the same structure you used for project parameters in SDS210. If a style is not applying, check for a typo in a key name (fillColor, not fillcolor) before assuming something is broken with the map itself.


Common pitfalls


Mini task

Load a vector dataset relevant to your project (or the Willisau buildings example above), overlay it on its source imagery, and apply a custom style that makes the features stand out clearly against the basemap.


Key takeaways