Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions folium/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -1191,12 +1191,24 @@ def __init__(
# noqa outside of type checking.
self.style = style

def _geojson_features(self) -> list:
"""Return the parent GeoJson's features, whatever form its data has."""
assert isinstance(self._parent, GeoJson)
data = self._parent.data
if data.get("type") == "FeatureCollection":
return data["features"]
if "geometry" in data:
# A single Feature.
return [data]
# A bare geometry has no properties.
return []

def warn_for_geometry_collections(self) -> None:
"""Checks for GeoJson GeometryCollection features to warn user about incompatibility."""
assert isinstance(self._parent, GeoJson)
geom_collections = [
feature.get("properties") if feature.get("properties") is not None else key
for key, feature in enumerate(self._parent.data["features"])
for key, feature in enumerate(self._geojson_features())
if feature["geometry"]
and feature["geometry"]["type"] == "GeometryCollection"
]
Expand All @@ -1212,10 +1224,9 @@ def render(self, **kwargs):
"""Renders the HTML representation of the element."""
figure = self.get_root()
if isinstance(self._parent, GeoJson):
features = self._geojson_features()
keys = tuple(
self._parent.data["features"][0]["properties"].keys()
if self._parent.data["features"]
else []
(features[0].get("properties") or {}).keys() if features else []
)
self.warn_for_geometry_collections()
elif isinstance(self._parent, TopoJson):
Expand Down
33 changes: 33 additions & 0 deletions tests/test_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,39 @@ def test_topojson_non_collection_geometry_detail(detail_type):
assert "name" in map_.get_root().render()


@pytest.mark.parametrize("detail_type", [folium.GeoJsonPopup, folium.GeoJsonTooltip])
@pytest.mark.parametrize(
"properties, fields",
[({"name": "A"}, ["name"]), (None, [])],
ids=["properties", "null-properties"],
)
def test_geojson_single_feature_detail(detail_type, properties, fields):
"""GeoJson popup and tooltip support a single Feature, not only collections."""
feature = {
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [0, 0]},
"properties": properties,
}

map_ = folium.Map()
geojson = folium.GeoJson(feature).add_to(map_)
detail_type(fields=fields).add_to(geojson)

rendered = map_.get_root().render()
assert all(field in rendered for field in fields)


@pytest.mark.parametrize("detail_type", [folium.GeoJsonPopup, folium.GeoJsonTooltip])
def test_geojson_geometry_detail_unknown_field(detail_type):
"""A bare geometry has no properties, so any field is reported as missing."""
map_ = folium.Map()
geojson = folium.GeoJson({"type": "Point", "coordinates": [0, 0]}).add_to(map_)
detail_type(fields=["name"]).add_to(geojson)

with pytest.raises(AssertionError, match="The field name is not available"):
map_.get_root().render()


@pytest.fixture
def tmpl():
yield ("""
Expand Down