Skip to content

feat(osm-equity): interactive MapLibre choropleth heat map#24

Merged
rlemke merged 1 commit into
mainfrom
feat/osm-equity-maplibre-map
Jul 14, 2026
Merged

feat(osm-equity): interactive MapLibre choropleth heat map#24
rlemke merged 1 commit into
mainfrom
feat/osm-equity-maplibre-map

Conversation

@rlemke

@rlemke rlemke commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Adds a self-contained MapLibre GL heat map to the osm-equity example's Phase-5 reporting, in the facetwork-maps gallery house style (inline GeoJSON, CARTO Voyager basemap, popups, legend, findings banner, data-desert outlines). Prominently labelled SYNTHETIC DEMONSTRATION DATA. map_path now returns this interactive map. 13 tests pass; ruff clean.

🤖 Generated with Claude Code

BuildEquityReport now emits a self-contained MapLibre GL "heat map" of
per-tract OSM attribute completeness (green=well-mapped, red=under-mapped),
in the facetwork-maps gallery house style: inline GeoJSON, CARTO Voyager
raster basemap, click popups (income / completeness / POI diversity /
footprint completeness / data-desert flag), a legend, a findings banner
(Spearman rho, Moran's I, temporal trend), and red outlines on data-desert
tracts. Prominently labelled SYNTHETIC DEMONSTRATION DATA.

map_path now points at this interactive map (equity_map.html); the raw
choropleth GeoJSON is still written alongside. Test asserts the map is a
MapLibre document. ruff clean; 13 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rlemke rlemke merged commit 4d0a99d into main Jul 14, 2026
17 of 20 checks passed
@rlemke rlemke deleted the feat/osm-equity-maplibre-map branch July 14, 2026 00:13

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request replaces the static GeoJSON choropleth output with an interactive MapLibre GL heat map HTML report. Feedback focuses on improving robustness and cross-platform compatibility: specifically, handling potential MultiPolygon geometries when calculating map bounds to avoid unpacking errors, explicitly specifying UTF-8 encoding when writing the HTML map, and using a context manager with explicit encoding when reading files in tests to prevent resource leaks.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +660 to +665
xs, ys = [], []
for f in feats:
for ring in f["geometry"]["coordinates"]:
for x, y in ring:
xs.append(x)
ys.append(y)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current coordinate extraction logic assumes all geometries are single Polygons. If any tract geometry is a MultiPolygon (which is common for real-world census tracts), f["geometry"]["coordinates"] will have an extra level of nesting, causing for x, y in ring: to raise a ValueError: too many values to unpack.

Since shapely.geometry.shape is already imported, we can compute the bounding box robustly for any geometry type (including Polygon and MultiPolygon) by using the .bounds property of the shapely shape.

    xs, ys = [], []
    for f in feats:
        geom = f.get("geometry")
        if geom:
            try:
                sh = shape(geom)
                if not sh.is_empty:
                    minx, miny, maxx, maxy = sh.bounds
                    xs.extend([minx, maxx])
                    ys.extend([miny, maxy])
            except Exception:
                pass


# the interactive heat map (this is the returned map_path)
map_path = out_dir() / "equity_map.html"
map_path.write_text(render_maplibre_map(feats, title, stats_res, deserts))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When writing text files in Python, it is a best practice to explicitly specify encoding="utf-8". Otherwise, Python will fall back to the platform's default encoding (e.g., cp1252 on Windows), which can lead to UnicodeEncodeError if the HTML content contains non-ASCII characters.

Suggested change
map_path.write_text(render_maplibre_map(feats, title, stats_res, deserts))
map_path.write_text(render_maplibre_map(feats, title, stats_res, deserts), encoding="utf-8")

assert len(records) >= 9
assert os.path.exists(report["report_html"])
assert os.path.exists(report["map_path"])
assert "maplibre" in open(report["map_path"]).read().lower() # interactive heat map

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using open().read() without a with statement leaves the file handle open until garbage collection runs, which can cause resource leaks or file locking issues (especially on Windows). Additionally, specifying encoding="utf-8" ensures the test runs consistently across different platforms.

Suggested change
assert "maplibre" in open(report["map_path"]).read().lower() # interactive heat map
with open(report["map_path"], encoding="utf-8") as f:
assert "maplibre" in f.read().lower() # interactive heat map

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant