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

Converting a GeoJSON that has a list-of-strings for a property fails with AttributeError: 'str' object has no attribute 'items' #125

Closed
jpotter opened this issue Aug 29, 2021 · 5 comments · Fixed by #129

Comments

@jpotter
Copy link

jpotter commented Aug 29, 2021

We've been playing with this topojson library for automating topojson conversion from our geojsons (which we need to do to get our maps to work in Looker), and wanted to first say thanks for creating such a useful library!

Background of issue

One of our GeoJSON fields is a list of strings, e.g.:

      "properties": {
        "id": "bi_ssu_14",
        "label": "14",
        "geo.neighbors": [
          "bi_ssu_2",
          "bi_ssu_3",
          "bi_ssu_5",
          "bi_ssu_9",
          "bi_ssu_11",
          "bi_ssu_12",
          "bi_ssu_13"
        ],
...

This is valid, and has the nice benefit that tools like QGIS provide an editor around these lists like so:
Screen Shot 2021-08-29 at 12 36 28 PM

Issue in topojson library

When running the topojson library on one of these files, an exception is generated:

  File "/home/jeff/codebase/geospatial/__init__.py", line 286, in convert_geojson_to_topojson
    topojson_data = topojson.Topology(
  File "/home/jeff/codebase/build/venv/lib/python3.8/site-packages/topojson/core/topology.py", line 106, in __init__
    super().__init__(data, options)
  File "/home/jeff/codebase/build/venv/lib/python3.8/site-packages/topojson/core/hashmap.py", line 21, in __init__
    self.output = self._hashmapper(self.output)
  File "/home/jeff/codebase/build/venv/lib/python3.8/site-packages/topojson/core/hashmap.py", line 92, in _hashmapper
    list(self._resolve_objects(["arcs", "coordinates"], self._data["objects"]))
  File "/home/jeff/codebase/build/venv/lib/python3.8/site-packages/topojson/core/hashmap.py", line 350, in _resolve_objects
    for result in self._resolve_objects(keys, v):
  File "/home/jeff/codebase/build/venv/lib/python3.8/site-packages/topojson/core/hashmap.py", line 350, in _resolve_objects
    for result in self._resolve_objects(keys, v):
  File "/home/jeff/codebase/build/venv/lib/python3.8/site-packages/topojson/core/hashmap.py", line 354, in _resolve_objects
    for result in self._resolve_objects(keys, d):
  File "/home/jeff/codebase/build/venv/lib/python3.8/site-packages/topojson/core/hashmap.py", line 344, in _resolve_objects
    for k, v in dictionary.items():
AttributeError: 'str' object has no attribute 'items'

Looking at the _resolve_objects function in hashmap.py, it has this chunk of code:

        for k, v in dictionary.items():
            # resolve when key equals 'arcs' and v contains arc indici
            if str(k) in keys and v is not None:
                dictionary[k] = self._resolve_bookkeeping(v, k)
                yield v
            elif isinstance(v, dict):
                for result in self._resolve_objects(keys, v):
                    yield result
            elif isinstance(v, list):
                for d in v:
                    for result in self._resolve_objects(keys, d):
                        yield result

The last branch of the if/else here that checks if the instance is of type list calls self._resolve_objects with each item from the list, which in the case of a string-set for properties, means the individual string values are being passed into _resolve_objects(). This then fails because k, v in dictionary.items() attempts to call items() on the string value as though it were a dict.

Proposed Solution

I'm not sure what the expected behavior of _resolve_objects should be here to be more fully compliant with the GeoJSON spec. Having debugged this and traced down the source of the issue, I wanted to open the issue and provide the details of what we learned. For now, we're stepping around this issue by converting the list-of-strings to a basic comma-delimited string.

@mattijn
Copy link
Owner

mattijn commented Aug 29, 2021

Thanks for raising the issue! Clear description. I'll have a look how to support this enhancement

@jpotter
Copy link
Author

jpotter commented Sep 5, 2021

Thanks, @mattijn -- much appreciated!

Note that things like bounding boxes also currently fail:

  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "MultiPolygon",
        "coordinates": [
          [
            [
              [
                -27.343,
                18.366
              ],
              [
                -27.442,
                18.406
              ],
              [
                -27.453,
                18.430
              ],
              [
                -27.309,
                18.475
              ],
              [
                -27.283,
                18.463
              ],
              [
                -27.343,
                18.366
              ]
            ]
          ]
        ]
      },
      "bbox": [-7.45337, 8.36618, -7.2835, 8.47532],
      "properties": {
        "id": "example_id",
        "label": "Example ID",
      }
      ...

@mattijn
Copy link
Owner

mattijn commented Sep 7, 2021

Thanks @jpotter for the follow up, this is resolved in PR #129

@jpotter
Copy link
Author

jpotter commented Sep 7, 2021

Thanks, @mattijn -- I've confirmed it works on our system internally too.

Thank you so much for helping us with this, as a non-profit, these types of packages make all the difference in the work we're able to do. :-)

@mattijn
Copy link
Owner

mattijn commented Sep 7, 2021

Great! I'm glad its useful for you and others!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants