1

I have this simple code which should write an empty GeoJSON.

import geopandas as gpd

gpd.GeoDataFrame(geometry=[]).to_file("test", driver="GeoJSON")

However, the library thinks it's smart and throws the following exception.

Traceback (most recent call last):

  File "/tmp/ipykernel_2102262/1921918865.py", line 1, in <module>
    gpd.GeoDataFrame(geometry=[]).to_file("test", driver="GeoJSON")

  File "/home/lukas/anaconda3/envs/tf_gpu/lib/python3.9/site-packages/geopandas/geodataframe.py", line 1086, in to_file
    _to_file(self, filename, driver, schema, index, **kwargs)

  File "/home/lukas/anaconda3/envs/tf_gpu/lib/python3.9/site-packages/geopandas/io/file.py", line 304, in _to_file
    schema = infer_schema(df)

  File "/home/lukas/anaconda3/envs/tf_gpu/lib/python3.9/site-packages/geopandas/io/file.py", line 362, in infer_schema
    raise ValueError("Cannot write empty DataFrame to file.")

ValueError: Cannot write empty DataFrame to file.

What's the best way to prevent this and have Geopandas write the file regardless?

One thing I can do is comment out the offending code in geopandas.io.file.py/infer_schema:

    # if df.empty:
        # raise ValueError("Cannot write empty DataFrame to file.")
    geom_types = _geometry_types(df)
    schema = {"geometry": geom_types, "properties": properties}
    return schema

That solves the problem but I'm not keen on forking Geopandas because of a tiny issue like this and then missing out on all new features.

My ideal solution would be to get this changed to a warning in Geopandas upstream but that requires consensus and time.

I'm aware of contextlib.suppress but that only suppresses the error at the level outside the context manager, after all the calls to GeoPandas have exited, so the file is not written.

I'm not interested in any workarounds for writing the GeoJSON any other way as that would be more complicated than commenting out the two lines or monkey-patching the infer_schema method. This question is just asking if it is at all possible to just tell Python to ignore the exception completely and continue execution at geom_types = _geometry_types(df).

Joooeey
  • 3,394
  • 1
  • 35
  • 49
  • 1
    no - you can't force python to ignore errors and continue execution in the same context without modifying the source code – Michael Delgado Nov 24 '21 at 04:40
  • Does this answer your question? [Python: How to ignore an exception and proceed?](https://stackoverflow.com/questions/574730/python-how-to-ignore-an-exception-and-proceed) – Michael Delgado Nov 24 '21 at 04:49
  • No, your first comment *answers* my question. Can you please post it as an answer and perhaps cite a source if you can find one quickly enough? – Joooeey Nov 24 '21 at 13:02
  • actually, this is a better duplicate question: https://stackoverflow.com/questions/22886920/bypass-error-and-continue-code – Michael Delgado Nov 24 '21 at 16:19
  • if you really want to patch the code, you could do it at the module level with something like this: https://stackoverflow.com/a/2887888/3888719. But best is probably just to fork it, rely on the fork for as long as you need, and then contribute the change to geopandas. You're right that there's no reason they can't write a zero-length geojson file, and this seems (to me) to fit with their goal of more closely aligning with the pandas api. – Michael Delgado Nov 24 '21 at 16:27
  • Yap that's basically the same question I asked here but in a different context. And because of the different context (i.e. the OP there has more control over the source code), the answers there don't address the pertinent point. – Joooeey Nov 24 '21 at 17:04

1 Answers1

0
import geopandas as gpd
import shapely.wkt
import json
import wrapt

# wrapper function to patch behaviour
def gpd_to_file_wrapper(wrapped, instance, args, kwargs):
    if len(instance) == 0 and kwargs.get("driver", "") == "GeoJSON":
        with open(args[0], "w") as f:
            json.dump({"type": {"FeatureCollection": []}}, f)
    else:
        return wrapped(*args, **kwargs)

# register monkey patch
wrapt.wrap_function_wrapper(gpd.GeoDataFrame, "to_file", gpd_to_file_wrapper)

gpd.GeoDataFrame(geometry=[]).to_file("test.geojson", driver="GeoJSON")
gpd.GeoDataFrame(
    geometry=[shapely.wkt.loads("POINT (15.43828764139886 46.30211698572747)")]
).to_file("test2.geojson", driver="GeoJSON")
Rob Raymond
  • 29,118
  • 3
  • 14
  • 30