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
24 changes: 11 additions & 13 deletions folium/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -1047,19 +1047,22 @@ def __init__(
def style_data(self) -> None:
"""Applies self.style_function to each feature of self.data."""

for feature in self._get_geometries():
feature.setdefault("properties", {}).setdefault("style", {}).update(
self.style_function(feature)
) # noqa

def _get_geometries(self) -> list:
"""Return the selected TopoJSON object as a list of geometries."""

def recursive_get(data, keys):
if len(keys):
return recursive_get(data.get(keys[0]), keys[1:])
else:
return data

geometries = recursive_get(self.data, self.object_path.split("."))[
"geometries"
] # noqa
for feature in geometries:
feature.setdefault("properties", {}).setdefault("style", {}).update(
self.style_function(feature)
) # noqa
geometry = recursive_get(self.data, self.object_path.split("."))
return geometry["geometries"] if "geometries" in geometry else [geometry]

def render(self, **kwargs):
"""Renders the HTML representation of the element."""
Expand Down Expand Up @@ -1200,12 +1203,7 @@ def render(self, **kwargs):
)
self.warn_for_geometry_collections()
elif isinstance(self._parent, TopoJson):
obj_name = self._parent.object_path.split(".")[-1]
keys = tuple(
self._parent.data["objects"][obj_name]["geometries"][0][
"properties"
].keys()
)
keys = tuple(self._parent._get_geometries()[0]["properties"].keys())
else:
raise TypeError(
f"You cannot add a {self._name} to anything other than a "
Expand Down
69 changes: 69 additions & 0 deletions tests/test_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,75 @@
from folium.utilities import JsCode


@pytest.mark.parametrize("geometry_type", ["Polygon", "MultiPolygon"])
def test_topojson_non_collection_geometry(geometry_type):
"""TopoJson supports objects that are not GeometryCollections."""
style = {"color": "red"}
topology = {
"type": "Topology",
"objects": {
"shape": {
"type": geometry_type,
"arcs": [[0]] if geometry_type == "Polygon" else [[[0]]],
}
},
"arcs": [[[0, 0], [1, 0], [0, 1], [-1, -1]]],
"transform": {"scale": [1, 1], "translate": [0, 0]},
}

map_ = folium.Map()
folium.TopoJson(
topology, "objects.shape", style_function=lambda feature: style
).add_to(map_)

map_.get_root().render()

assert topology["objects"]["shape"]["properties"]["style"] == style


def test_topojson_non_collection_geometry_without_type():
"""TopoJson does not require a type key to apply styles."""
topology = {
"type": "Topology",
"objects": {"shape": {"arcs": [[0]]}},
"arcs": [[[0, 0], [1, 0], [0, 1], [-1, -1]]],
"transform": {"scale": [1, 1], "translate": [0, 0]},
}

topojson = folium.TopoJson(
topology,
"objects.shape",
style_function=lambda feature: {"color": "red"},
)

topojson.style_data()

assert topology["objects"]["shape"]["properties"]["style"] == {"color": "red"}


@pytest.mark.parametrize("detail_type", [folium.GeoJsonPopup, folium.GeoJsonTooltip])
def test_topojson_non_collection_geometry_detail(detail_type):
"""TopoJson popup and tooltip fields support non-collection geometries."""
topology = {
"type": "Topology",
"objects": {
"shape": {
"type": "Polygon",
"arcs": [[0]],
"properties": {"name": "A"},
}
},
"arcs": [[[0, 0], [1, 0], [0, 1], [-1, -1]]],
"transform": {"scale": [1, 1], "translate": [0, 0]},
}

map_ = folium.Map()
topojson = folium.TopoJson(topology, "objects.shape").add_to(map_)
detail_type(fields=["name"]).add_to(topojson)

assert "name" in map_.get_root().render()


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