@@ -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