Skip to content

Commit 9be4bda

Browse files
author
Nathaniel Henry
committed
Add background_fill, a points overlay, and black outlines on few-polygon fills to close_map().
1 parent 33c9855 commit 9be4bda

1 file changed

Lines changed: 40 additions & 10 deletions

File tree

src/closecity/map.py

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ def close_map(
109109
background=None,
110110
background_color: Any = "#3b6fb0",
111111
background_opacity: float = 0.3,
112+
background_fill: bool = True,
113+
points=None,
114+
points_color: str = "#e8590c",
112115
mark=None,
113116
buffer: float = 0.15,
114117
):
@@ -131,7 +134,9 @@ def close_map(
131134
(e.g. a city boundary from ``place_boundary``). ``background`` is one polygon
132135
GeoDataFrame, or a list of them, drawn as semi-transparent fills underneath
133136
(e.g. commute isochrones or a walkshed); ``background_color`` recycles across
134-
them. ``mark`` draws an X on top at a ``(lon, lat)`` pair or a point
137+
them, and ``background_fill=False`` draws them as outlines only. ``points`` is a
138+
point GeoDataFrame drawn as markers on top of the main layer (e.g. POI locations
139+
over a block map). ``mark`` draws an X on top at a ``(lon, lat)`` pair or a point
135140
GeoDataFrame (e.g. a starting point). Returns a plotly ``Figure``.
136141
"""
137142
try:
@@ -156,13 +161,20 @@ def close_map(
156161
bounds.append(_bounds(lg))
157162
lons, lats = _polygon_lines(lg)
158163
col = colors[i % len(colors)]
159-
traces.append(go.Scattermapbox(
160-
lon=lons, lat=lats, mode="lines", fill="toself",
161-
fillcolor=_rgba(col, background_opacity),
162-
line={"color": _rgba(col, min(1.0, background_opacity + 0.3)),
163-
"width": 1},
164-
hoverinfo="skip", showlegend=False,
165-
))
164+
if background_fill:
165+
traces.append(go.Scattermapbox(
166+
lon=lons, lat=lats, mode="lines", fill="toself",
167+
fillcolor=_rgba(col, background_opacity),
168+
line={"color": _rgba(col, min(1.0, background_opacity + 0.3)),
169+
"width": 1},
170+
hoverinfo="skip", showlegend=False,
171+
))
172+
else:
173+
traces.append(go.Scattermapbox(
174+
lon=lons, lat=lats, mode="lines",
175+
line={"color": col, "width": 2},
176+
hoverinfo="skip", showlegend=False,
177+
))
166178

167179
# City-boundary outline (no fill), above the background fills.
168180
if boundary is not None:
@@ -213,6 +225,9 @@ def close_map(
213225

214226
g = g.reset_index(drop=True)
215227
g["_id"] = [str(i) for i in range(len(g))]
228+
# Outline the polygons in black when there are few of them (e.g. isochrone
229+
# contours); skip it on dense block maps, where every border would clutter.
230+
poly_line = {"color": "#000000", "width": 1.5 if len(g) <= _OVERLAP_MAX else 0}
216231

217232
if fv is not None and len(g) <= _OVERLAP_MAX:
218233
# Filled polygons that may overlap (nested isochrone contours): one
@@ -224,7 +239,7 @@ def close_map(
224239
traces.append(go.Choroplethmapbox(
225240
geojson=json.loads(sub.to_json()), locations=[g.at[i, "_id"]],
226241
featureidkey="properties._id", z=[fv[i]], coloraxis="coloraxis",
227-
marker={"opacity": opacity, "line": {"width": 0}},
242+
marker={"opacity": opacity, "line": poly_line},
228243
text=[hover[i]], hoverinfo="text", showlegend=False))
229244
coloraxis = {"colorscale": palette, "reversescale": reverse,
230245
"cmin": min(fv), "cmax": max(fv), "colorbar": {"title": fill}}
@@ -233,7 +248,7 @@ def close_map(
233248
common = {
234249
"geojson": geojson, "locations": g["_id"].tolist(),
235250
"featureidkey": "properties._id",
236-
"marker": {"opacity": opacity, "line": {"width": 0}},
251+
"marker": {"opacity": opacity, "line": poly_line},
237252
"text": hover, "hoverinfo": "text", "showlegend": False,
238253
}
239254
if fv is not None:
@@ -247,6 +262,21 @@ def close_map(
247262
traces.append(go.Choroplethmapbox(
248263
z=z, colorscale=colorscale, showscale=False, **common))
249264

265+
# Extra POI points drawn on top of the main layer (e.g. library locations),
266+
# with the same black hairline border as the main point markers.
267+
if points is not None:
268+
pg = points.to_crs(4326)
269+
bounds.append(_bounds(pg))
270+
plon, plat = pg.geometry.x.tolist(), pg.geometry.y.tolist()
271+
traces.append(go.Scattermapbox(
272+
lat=plat, lon=plon, mode="markers",
273+
marker={"size": size + 2, "color": "#000000"},
274+
hoverinfo="skip", showlegend=False))
275+
traces.append(go.Scattermapbox(
276+
lat=plat, lon=plon, mode="markers",
277+
marker={"size": size, "color": points_color},
278+
text=_hover(pg, label), hoverinfo="text", showlegend=False))
279+
250280
# A point marked with an X (two crossing line segments — mapbox text glyphs
251281
# do not render on the raster basemap), drawn last so it sits on top.
252282
if mark is not None:

0 commit comments

Comments
 (0)