Adicionando o GeoPandas Dataframe à tabela PostGIS?

14

Eu tenho um Dataframe simples do GeoPandas:

insira a descrição da imagem aqui

Gostaria de fazer upload deste GeoDataframe para uma tabela PostGIS. Já tenho uma configuração de banco de dados com a extensão PostGIS, mas não consigo adicionar esse Dataframe como uma tabela.

Eu tentei o seguinte:

engine = <>
meta = MetaData(engine)
eld_test = Table('eld_test', meta, Column('id', Integer, primary_key=True), Column('key_comb_drvr', Text), 
                 Column('geometry', Geometry('Point', srid=4326))) 
eld_test.create(engine) 
conn = engine.connect() 
conn.execute(eld_test.insert(), df.to_dict('records'))
thecornman
fonte
Eu tentei o seguinte: engine = <> # create table meta = MetaData (engine) eld_test = Tabela ('eld_test', meta, Column ('id', Integer, primary_key = True), Column ('key_comb_drvr', Text) , Column ('geometry', Geometry ('Point', srid = 4326))) eld_test.create (engine) # Executa o DBAPI com lista de dict conn = engine.connect () conn.execute (eld_test.insert (), df .to_dict ('records'))
thecornman
1
Bem-vindo ao GIS SE, leia o nosso tour ! Você pode editar sua postagem para incluir seu código postado nos comentários?
GISKid

Respostas:

28

Usando o método to_sql do Panda e o SQLAlchemy, você pode armazenar um quadro de dados no Postgres. E como você está armazenando um Geodataframe, o GeoAlchemy manipulará a coluna geom para você. Aqui está um exemplo de código:

# Imports
from geoalchemy2 import Geometry, WKTElement
from sqlalchemy import *
import pandas as pd
import geopandas as gpd

# Creating SQLAlchemy's engine to use
engine = create_engine('postgresql://username:password@host:socket/database')


geodataframe = gpd.GeoDataFrame(pd.DataFrame.from_csv('<your dataframe source>'))
#... [do something with the geodataframe]

geodataframe['geom'] = geodataframe['geometry'].apply(lambda x: WKTElement(x.wkt, srid=<your_SRID>)

#drop the geometry column as it is now duplicative
geodataframe.drop('geometry', 1, inplace=True)

# Use 'dtype' to specify column's type
# For the geom column, we will use GeoAlchemy's type 'Geometry'
geodataframe.to_sql(table_name, engine, if_exists='append', index=False, 
                         dtype={'geom': Geometry('POINT', srid= <your_srid>)})

Vale ressaltar que o parâmetro 'if_exists' permite que você lide com a maneira como o dataframe será adicionado à sua tabela do postgres:

    if_exists = replace: If table exists, drop it, recreate it, and insert data.
    if_exists = fail: If table exists, do nothing.
    if_exists = append: If table exists, insert data. Create if does not exist.
Hamri Said
fonte
4

Também tive a mesma pergunta que você fez e passou muitos e muitos dias nela (mais do que gostaria de admitir) procurando uma solução. Assumindo a seguinte tabela postgreSQL com a extensão postGIS,

postgres=> \d cldmatchup.geo_points;
Table "cldmatchup.geo_points"
Column   |         Type         |                               Modifiers                                
-----------+----------------------+------------------------------------------------------------------------
gridid    | bigint               | not null default nextval('cldmatchup.geo_points_gridid_seq'::regclass)
lat       | real                 | 
lon       | real                 | 
the_point | geography(Point,4326) | 

Indexes:
"geo_points_pkey" PRIMARY KEY, btree (gridid)

isto é o que eu finalmente comecei a trabalhar:

import geopandas as gpd
from geoalchemy2 import Geography, Geometry
from sqlalchemy import create_engine, MetaData, Table
from sqlalchemy.orm import sessionmaker
from shapely.geometry import Point
from psycopg2.extensions import adapt, register_adapter, AsIs

# From http://initd.org/psycopg/docs/advanced.html#adapting-new-types but 
# modified to accomodate postGIS point type rather than a postgreSQL 
# point type format
def adapt_point(point):
    from psycopg2.extensions import adapt, AsIs
    x = adapt(point.x).getquoted()
    y = adapt(point.y).getquoted()
    return AsIs("'POINT (%s %s)'" % (x, y))

register_adapter(Point, adapt_point)

engine = create_engine('postgresql://<yourUserName>:postgres@localhost:5432/postgres', echo=False)
Session = sessionmaker(bind=engine)
session = Session()
meta = MetaData(engine, schema='cldmatchup')

# Create reference to pre-existing "geo_points" table in schema "cldmatchup"
geoPoints = Table('geo_points', meta, autoload=True, schema='cldmatchup', autoload_with=engine)

df = gpd.GeoDataFrame({'lat':[45.15, 35., 57.], 'lon':[-35, -150, -90.]})

# Create a shapely.geometry point 
the_point = [Point(xy) for xy in zip(df.lon, df.lat)]

# Create a GeoDataFrame specifying 'the_point' as the column with the 
# geometry data
crs = {'init': 'epsg:4326'}
geo_df = gpd.GeoDataFrame(df.copy(), crs=crs, geometry=the_point)

# Rename the geometry column to match the database table's column name.
# From https://media.readthedocs.org/pdf/geopandas/latest/geopandas.pdf,
# Section 1.2.2 p 7
geo_df = geo_df.rename(columns{'geometry':'the_point'}).set_geometry('the_point')

# Write to sql table 'geo_points'
geo_df.to_sql(geoPoints.name, engine, if_exists='append', schema='cldmatchup', index=False)

session.close()

Não sei dizer se minha lógica de conexão com o banco de dados é a melhor, pois basicamente copiei isso de outro link e fiquei feliz por poder automatizar (ou refletir) com êxito minha tabela existente com a definição de geometria reconhecida. Eu tenho escrito python em código espacial sql por apenas alguns meses, então eu sei que há muito a aprender.

user1745564
fonte