import pandas as pd
import geopandas as gpd
= gpd.read_file('data/brisbane-park-locations.geojson') gdf
When importing data in a GeoJSON file to a PostGIS database, we will do so using Python and apply the following process:
- import the data into a
GeoDataFrame
usinggeopandas
, specifically theread_file
method. - write the
GeoDataFrame
to a PostGIS databse using theto_postgis
method ingeopandas
.
The example below illustrates in detail how this can be achieved.
Read data to a GeoDataFrame
The data is available here.
To work directly with geopandas
, we will download the data in GeoJSON format.
5) gdf.head(
Write to PostGIS
geopandas
can now write gdf
to a PostGIS with its to_postgis
method. As mentioned in the documentation, this method requires SQLAlchemy, GeoAlchemy2 and a PostgreSQL Python driver to be installed. If not already present, these can be installed as follows:
uv add sqlalchemy geoalchemy2 psycopg[binary]
Note that since we’re using psycopg (version 3), and sqlalchemy uses psycopg2 as default, we need to change the driver name from postgresql
to postgresql+psycopg
. See this StackOverflow answer.
from sqlalchemy import create_engine
= 'sandeep'
user = 'localhost'
host = 'brisbane'
database = 'postgresql+psycopg'
driver = f'{driver}://{user}@{host}/{database}'
connection_str
= create_engine(connection_str) engine
Now that we have the engine setup, we can write gdf
to the PostGIS
database.
='parks', con=engine, if_exists='fail') gdf.to_postgis(name
Verify that the table has been written to the database
Finally, we can use psycopg
to verify that the data has been copied to PostGIS.
import psycopg
with psycopg.connect("dbname=brisbane user=sandeep") as conn:
with conn.cursor() as cur:
"SELECT * FROM parks LIMIT 10")
cur.execute(print(cur.fetchone())
('1', 'D2496', "O'REILLY STREET PARK", 'B-RE-0337', '10', "O'REILLY STREET", 'WAKERLEY', '4154', 153.15384614879127, -27.477642702086637, 10020.313720703125, 439.2917820727244, '{ "lon": 153.15384614879127, "lat": -27.477642702086637 }', '10000+ sq metres', 1, 'W, WAKERLEY', "O, O'REILLY STREET PARK", '0103000020E61000000100000019000000CC7D63FFF124634048B12FFE547A3BC01A5AABB8F22463403859CF0E637A3BC034B5EB3FF02463409E345309607A3BC062992F63EE24634042A971C25D7A3BC092DA2F9AEC246340723AD0935B7A3BC0DD89B79AEB2463405F48735A5A7A3BC045D32BD1EA24634022391265597A3BC07A483308E9246340C1CA6D36577A3BC022FA233FE72463402FCF9107557A3BC0B1330BF2E5246340FD197170537A3BC01D82D743E624634097F8D011467A3BC00CDB5F7EE6246340363030803C7A3BC03093E3B5E624634014252E6D337A3BC0AEF0A3EEE62463403EBBEF252A7A3BC066879BB0E8246340755D51492C7A3BC03DCCA76DEA246340B498D0662E7A3BC08DABF3FDEA2463400BD76F6D2F7A3BC032CFC782EB246340239CB195307A3BC03798DB73EC2463401890D1E2327A3BC08092878BEC246340D0BB91FF327A3BC054937B24F22463402571D3CF397A3BC0BFFE5F60F2246340FBA32E943A7A3BC025C37F86F22463401FFB0F143C7A3BC022AC978CF2246340E0EA51E83D7A3BC0CC7D63FFF124634048B12FFE547A3BC0')
Complete the round-trip
Now, we’ll read the data from PostGIS into a GeoDataFrame
.
= 'SELECT park_number, park_name, geometry FROM parks'
sql = gpd.read_postgis(sql, engine, geom_col='geometry') parks