How to download GeoJSON

Hello,

Looking to understand a method for downloading data from a geo dataframe? Want to download data as geojson.

Have not found any help articles that work with geojson for file downloads. Any suggestions are appreciated.

Hi, so you have a geodataframe already and you want to save it as a geojson?

You can use geopandas for that
https://geopandas.org/en/stable/docs/reference/api/geopandas.GeoDataFrame.to_json.html

Correct - I already have a geodataframe setup and can save it as a shapefile or geojson.

For the purposes of Streamlit, I am looking to allow a user to download using st.download_button. I have been unable to find any help articles describing how to allows a user to download as geojson.

1 Like

Ah that makes sense, sorry I was a bit slow there.

Does the code below work for you?

import io
import streamlit as st
import geopandas as gpd

def save_geojson_with_bytesio(dataframe):
    #Function to return bytesIO of the geojson
    shp = io.BytesIO()
    dataframe.to_file(shp,  driver='GeoJSON')
    return shp

#########################################################
#get a geodataframe, this one is inbuilt in geopandas
path_to_data = gpd.datasets.get_path("nybb")
gdf = gpd.read_file(path_to_data)
#########################################################


#########################################################
#download the geodataframe
st.download_button(
    label="Download data",
    data=save_geojson_with_bytesio(gdf),
    file_name='my_geojson.geojson',
    mime='application/geo+json',
)
#########################################################

Hey Luke, Do you know of a way to convert a gdf to a shapefile using the download_button method?

See in the docs how to save a gdf in different formats:

https://geopandas.org/en/stable/docs/reference/api/geopandas.GeoDataFrame.to_file.html#geopandas.GeoDataFrame.to_file

using the default driver to save as a shapfile results in an empty file

Sorry, I’m on holiday for two weeks so I’m not near my computer.

I have done this before. You have to zip the shapefile files (for a shapefile there are multiple files) and then download the zip file.

If you can’t get it to work please DM me and I’ll get back to you with some code, but unfortunately it won’t be for two weeks

I don’t know how you ended up with an empty shapefile or that RuntimeError but indeed making this work was harder that I expected. I was unable to write the shapefile to a buffer. First I tried using the default driver, which is supposed to write a shapefile.

buf = io.BytesIO()
gdf.to_file(buf)

Raised TypeError: expected str, bytes or os.PathLike object, not BytesIO. So apparently in only works with paths. Then I tried with an explicit driver.

gdf.to_file(buf, driver="ESRI Shapefile")

This didn’t raise any errors and actually wrote something to the buffer, but it was just the .shp file, I couldn’t find a way to get the other files.

So the only way I found was writing the shapefile to the file system, then read from there and pass the data to st.download_button(). I wrote a simple streamlit application demonstrating the idea.

import tempfile
from pathlib import Path

import geopandas as gpd
import pandas as pd
import streamlit as st


def gdf_to_shz(gdf, name):
    with tempfile.TemporaryDirectory() as tmpdir:
        path = Path(tmpdir, f"{name}.shz")
        gdf.to_file(path, driver="ESRI Shapefile")
        return path.read_bytes()


def main():
    ds_name = st.selectbox(label="Select a dataset", options=gpd.datasets.available)
    gdf = gpd.read_file(gpd.datasets.get_path(ds_name))
    st.dataframe(pd.DataFrame(gdf.to_numpy(), columns=gdf.columns))
    st.pyplot(gdf.plot().figure)

    st.download_button(
        label="Download shapefile",
        data=gdf_to_shz(gdf, name=ds_name),
        file_name=f"{ds_name}.shz",
    )


if __name__ == "__main__":
    main()
1 Like

Hi, apologies for the delay. Just got back from holiday.
Did you manage to get this working? If not, here is code that works for me. I tested and the created shapefile opens and displays in Arcpro.

import io
import streamlit as st
import geopandas as gpd
from zipfile import ZipFile
import tempfile

def save_shapefile_with_bytesio(dataframe,directory):
    dataframe.to_file(f"{directory}/user_shapefiles.shp",  driver='ESRI Shapefile')
    zipObj = ZipFile(f"{directory}/user_shapefiles_zip.zip", 'w')
    zipObj.write(f"{directory}/user_shapefiles.shp",arcname = 'user_shapefiles.shp')
    zipObj.write(f"{directory}/user_shapefiles.cpg",arcname = 'user_shapefiles.cpg')
    zipObj.write(f"{directory}/user_shapefiles.dbf",arcname = 'user_shapefiles.dbf')
    zipObj.write(f"{directory}/user_shapefiles.prj",arcname = 'user_shapefiles.prj')
    zipObj.write(f"{directory}/user_shapefiles.shx",arcname = 'user_shapefiles.shx')
    zipObj.close()

#########################################################
#get a geodataframe, this one is inbuilt in geopandas
path_to_data = gpd.datasets.get_path("naturalearth_cities")
gdf = gpd.read_file(path_to_data)
#########################################################


#########################################################
#download the geodataframe
#we first create a temporary directory
with tempfile.TemporaryDirectory() as tmp:
    #create the shape files in the temporary directory
    st.write('creating temporary shape file...')
    save_shapefile_with_bytesio(gdf,tmp)
    with open(f"{tmp}/user_shapefiles_zip.zip", "rb") as file:
        st.download_button(
            label="Download data",
            data=file,
            file_name='my_shapefile.zip',
            mime='application/zip',
        )
#########################################################


The key thing is that we have to save all of the necessary files to get a working shapefile (.shp, .cpg, .dbf, .prj, .shx). Possibly you don’t need all of these, I’m not 100% sure, but for now it works.

We save these files by converting the geodataframe to the files, but we need a place to store them. So for that we have to use the tempfile import to create a temporary directory. Note that I have only tested this on local computers, not on, for example, streamlit cloud.

Then we can add the files in the temp directory into a zip file, and download.

Please let me know if it works for you.
Thanks.

Ah nice! I only read this after I posted my most recent reply. I could have just said, “see above” :slight_smile:

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.