Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Geojson ids option #220

Merged
merged 8 commits into from
May 6, 2024
31 changes: 31 additions & 0 deletions tests/test_extract.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
import pytest
from topojson.core.extract import Extract
from shapely import geometry
import geopandas
Expand Down Expand Up @@ -469,6 +470,36 @@ def test_extract_keep_properties():
assert topo["objects"]["feature_1"]["properties"]["name"]["def"] == "ghi"


def test_extract_geojson_keep_index():
feat_1 = Feature(
id="custom_index",
geometry=Polygon([[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]]),
)
feat_2 = Feature(
geometry=Polygon([[[1, 0], [2, 0], [2, 1], [1, 1], [1, 0]]]),
)
data = FeatureCollection([feat_1, feat_2])
topo = Extract(data).to_dict()
objects = topo["objects"]

assert bool(objects.get("custom_index")) == True
assert bool(objects.get("feature_1")) == True


def test_extract_geojson_keep_index_duplicates():
feat_1 = Feature(
id="duplicate_id",
geometry=Polygon([[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]]),
)
feat_2 = Feature(
id="duplicate_id",
geometry=Polygon([[[1, 0], [2, 0], [2, 1], [1, 1], [1, 0]]]),
)
data = FeatureCollection([feat_1,feat_2])
with pytest.raises(IndexError):
Extract(data)


# why cannot load geojson file using json module?
def test_extract_read_geojson_from_json_dict():
with open("tests/files_geojson/naturalearth_lowres.geojson") as f:
Expand Down
24 changes: 22 additions & 2 deletions tests/test_topology.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def test_topology_winding_order_TopoOptions():
topo = topojson.Topology(data, winding_order="CW_CCW").to_dict(options=True)

assert len(topo["objects"]) == 1
assert len(topo["options"]) == 11
assert len(topo["options"]) == 12


# test winding order using kwarg variables
Expand All @@ -106,7 +106,7 @@ def test_topology_winding_order_kwarg_vars():
topo = topojson.Topology(data, winding_order="CW_CCW").to_dict(options=True)

assert len(topo["objects"]) == 1
assert len(topo["options"]) == 11
assert len(topo["options"]) == 12


def test_topology_computing_topology():
Expand Down Expand Up @@ -699,3 +699,23 @@ def test_topology_write_multiple_object_json_dict():
topo_dict = topo.to_dict()

assert len(topo_dict["objects"]) == 2

def test_topology_ignore_index_true_geojson():

from geojson import Feature, FeatureCollection, Polygon
feat_1 = Feature(
id="duplicate_id",
geometry=Polygon([[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]]),
)
feat_2 = Feature(
id="duplicate_id",
geometry=Polygon([[[1, 0], [2, 0], [2, 1], [1, 1], [1, 0]]]),
)
fc = FeatureCollection([feat_1,feat_2])

# Using ignore_index to use default feature ids.
topo = topojson.Topology(fc, ignore_index=True).to_dict(options=True)
geom = topo["objects"]["data"]["geometries"]

index = [obj["id"] for obj in geom]
assert index == ["feature_0","feature_1"]
16 changes: 13 additions & 3 deletions topojson/core/extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,9 +460,19 @@ def _extract_featurecollection(self, geom):

if feature["type"] == "GeometryCollection":
feature_dict["geometries"] = feature["geometry"]["geometries"]
data[
"feature_{}".format(str(idx).zfill(zfill_value))
] = feature_dict # feature

if self.options.ignore_index or not feature.get("id"):
data[
"feature_{}".format(str(idx).zfill(zfill_value))
] = feature_dict
else:
data[feature.get("id")] = feature_dict # feature

# check for overwritten duplicate keys
if len(data) < len(obj["features"]):
msg = "index in data duplicated, use `ignore_index=True` to overwrite index"
raise IndexError(msg)

# new data dictionary is created, throw the geometries back to main()
self._is_single = False
self._extractor(data)
Expand Down
6 changes: 6 additions & 0 deletions topojson/core/topology.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ class Topology(Hashmap):
case it is required to provide a list of the referenced `object_name` in
combination with an equal length list of `data` objects.
Default is a single object named `data`.
ignore_index : bool
If set to true existing ids/indexes of geojson FeatureCollections will be
ignored and overwritten. Otherwise features with ids will use their existing one.
If indexes are not ignored and a duplicate id exists an exception will be raised.
Default is false.
"""

def __init__(
Expand All @@ -114,6 +119,7 @@ def __init__(
simplify_algorithm="dp",
winding_order="CW_CCW",
object_name="data",
ignore_index=False,
):
options = TopoOptions(locals())

Expand Down
6 changes: 6 additions & 0 deletions topojson/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def __init__(
simplify_algorithm="dp",
winding_order=None,
object_name="data",
ignore_index=False,
):
# get all arguments
arguments = locals()
Expand Down Expand Up @@ -91,6 +92,11 @@ def __init__(
else:
self.object_name = ["data"]

if "ignore_index" in arguments:
self.ignore_index = arguments["ignore_index"]
else:
self.ignore_index = False

def __repr__(self):
return "TopoOptions(\n {}\n)".format(pprint.pformat(self.__dict__))

Expand Down
Loading