Quickstart#

Elevate your Folium maps with professional-grade interactive plugins. foliplus provides advanced Layer Management, Dynamic H3 Heatmaps, Spatial Measurement tools, and unified search capabilities in a single, easy-to-use toolkit.

What you will learn#

  • Building a multi-theme base map.

  • Preparing spatially-valid datasets using GeoPandas.

  • Integrating the full suite of foliplus plugins.

  • Exporting interactive maps for production use.

1. Installation#

Install the core package via pip:

$ pip install -U foliplus

For the spatial analysis used in this guide, we recommend:

$ pip install -U my-data-toolkit geopandas

Note: Please restart your notebook kernel after installing new packages.

2. Data Preparation & Base Map#

To fully demonstrate the power of foliplus, weโ€™ll prepare a multi-layered urban dataset:

  1. Administrative Context: Loading district boundaries (Polygons) to provide spatial grounding.

  2. Point Density: Generating a synthetic grid of facility points with numeric score weights.

  3. Symbolic Landmarks: Identifying district centers with custom icons to showcase how LayerControl automatically identifies geometry types.

  4. Base Themes: Setting up both Light and Dark modes for different visualization needs.

import dtoolkit.geoaccessor  # noqa: F401
import folium
import geopandas as gpd
import numpy as np
import pandas as pd

# ---------------------------------------------------------------------------
# 1. Load & Refine Administrative Boundaries
# ---------------------------------------------------------------------------
# Load the Shanghai district boundaries
boundary = gpd.read_file("data/gadm41_CHN_3_Shanghai.geojson")

# Calculate District Centers (Layer 1)
# Re-project to EPSG:3857 for accurate geometry centers, then back to EPSG:4326 for Folium
districts = boundary.copy()
districts["geometry"] = boundary.to_crs(3857).centroid.to_crs(4326)

# Inject simulated demographic data for visualization
np.random.seed(42)
districts["population_density"] = np.random.randint(5000, 30000, len(districts))

# ---------------------------------------------------------------------------
# 2. Vectorized Point Sampling (Layer 2)
# ---------------------------------------------------------------------------
bounds = boundary.total_bounds
facilities = (
    # Generate a dense candidate pool across the city
    pd.DataFrame(
        {
            "x": np.random.uniform(bounds[0], bounds[2], 5000),
            "y": np.random.uniform(bounds[1], bounds[3], 5000),
            "score": np.random.randint(1, 100, 5000),
        }
    )
    .from_xy("x", "y", crs=4326)
    # Perform spatial join to keep only points inside the land area
    .sjoin(boundary, how="inner")
    .groupby("NAME_3")
    .sample(n=50, random_state=42, replace=True)
    .reset_index(drop=True)
)

# ---------------------------------------------------------------------------
# 3. Initialize Base Map
# ---------------------------------------------------------------------------
center = districts.geocentroid()
m = folium.Map(location=[center.y, center.x], zoom_start=10, tiles=None)

# Add professional base themes
folium.TileLayer("CartoDB positron", name="Light Canvas").add_to(m)
folium.TileLayer("CartoDB dark_matter", name="Dark Mode", show=False).add_to(m)

# ---------------------------------------------------------------------------
# 4. Layer Orchestration
# ---------------------------------------------------------------------------
# Step 1: Add District Landmarks (Top markers)
districts.explore(
    m=m,
    name="District Landmarks",
    marker_type="marker",
    marker_kwds={"icon": folium.Icon(color="cadetblue", icon="map-pin", prefix="fa")},
    tooltip="NAME_3",
    popup=["NAME_3", "population_density"],
    legend=False,
)

# Step 2: Add Facility Points (Heatmap source)
facilities.explore(
    m=m,
    name="Facility Points",
    column="score",
    cmap="plasma",
    marker_type="circle_marker",
    style_kwds={"radius": 3, "fillOpacity": 0.8, "stroke": False},
    legend=True,
)

# Step 3: Add Administrative Polygons as the base overlay
boundary.explore(
    m=m,
    name="Municipal Boundaries",
    color="gray",
    style_kwds={"fillOpacity": 0.05, "weight": 1.5, "dashArray": "5, 5"},
    tooltip="NAME_3",
    popup=True,
    legend=False,
)

print(
    f"๐Ÿ“ฆ Geo-Engine Ready: Orchestrating {len(facilities)} points across {len(districts)} districts."
)
ERROR 1: PROJ: proj_create_from_database: Open of /home/docs/checkouts/readthedocs.org/user_builds/foliplus/conda/latest/share/proj failed
Matplotlib is building the font cache; this may take a moment.
๐Ÿ“ฆ Geo-Engine Ready: Orchestrating 600 points across 12 districts.

3. Enable foliplus Plugins#

Now, we activate the professional toolkit. Observe how the plugins interact with our data:

  • LayerControl: Allows real-time reordering of layers and quick adjustments to transparency or base themes.

  • HeatmapControl: Aggregates facility points into H3 hexagons. Weโ€™ve configured it to use the sum of the score field for a weighted density analysis.

  • MapSearch: Provides a unified interface for coordinate jumping and keyword search (e.g., search for โ€œPudongโ€).

  • MeasureControl: Precision tools for distance and area measurements directly on the map.

  • Fullscreen: A distraction-free mode for presentations and data exploration.

from foliplus import (
    Fullscreen,
    HeatmapControl,
    LayerControl,
    MapSearch,
    MeasureControl,
    ScaleControl,
)

# Add all plugins with default settings to keep it simple but powerful
MapSearch().add_to(m)
LayerControl().add_to(m)
HeatmapControl(agg="sum").add_to(m)
ScaleControl().add_to(m)
MeasureControl().add_to(m)
Fullscreen().add_to(m)

print("๐Ÿš€ foliplus engine active: Ready for exploration.")
๐Ÿš€ foliplus engine active: Ready for exploration.

4. Display Map#

Run the next cell to render the interactive map in Notebook.

If you want to share the result, use the optional HTML export section below.

m
Make this Notebook Trusted to load map: File -> Trust Notebook

If you want to view the map in a browser or host it as a static file, export it to HTML.

file = "map.html"
m.save(file)
print(f"โœ… Saved: {file}")
โœ… Saved: map.html

5. Troubleshooting & Tips#

  • Map Blank or Not Rendering: Ensure the notebook is trusted and that the map display cell (Cell 7) was the last one executed.

  • Dependency Issues: If dtoolkit or geopandas are missing, install them via the suggested command in Section 1 and restart the kernel.

  • Custom Localization: While plugins auto-detect your browser language, you can force a locale globally: LayerControl(locale="en").

6. Next Steps#

  • API Exploration: Refer to the API Reference for a deep dive into available properties.

  • Dynamic Theming: Experiment with HeatmapControl color scales like "Viridis" or "Plasma".

  • Real-World Integration: Swap the simulated data for your own local datasets to start generating professional spatial insights immediately.

Happy Mapping! You are now ready to build scalable, interactive maps with foliplus.