¿Agregar GeoPandas Dataframe a la tabla PostGIS?

15

Tengo un marco de datos GeoPandas simple:

ingrese la descripción de la imagen aquí

Me gustaría subir este GeoDataframe a una tabla PostGIS. Ya tengo una configuración de base de datos con la extensión PostGIS pero parece que no puedo agregar este Dataframe como una tabla.

He probado lo siguiente:

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
fuente
He intentado lo siguiente: engine = <> # create table 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) # DBAPI's executemany with list of dicts conn = engine.connect () conn.execute (eld_test.insert (), df .to_dict ('records'))
thecornman
1
¡Bienvenido a GIS SE, por favor, lea nuestro recorrido ! ¿Podría editar su publicación para incluir su código publicado en los comentarios?
GISKid

Respuestas:

30

Usando el método to_sql de Panda y SQLAlchemy puede almacenar un marco de datos en Postgres. Y como está almacenando un Geodataframe, GeoAlchemy se encargará de la columna de geom por usted. Aquí hay una muestra 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 la pena señalar que el parámetro 'if_exists' le permite manejar la forma en que se agregará el marco de datos a su tabla de 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.
Dijo Hamri
fuente
¿Hay alguna oportunidad de reproyectar aquí especificando un SRID diferente al de la columna de geometría, o se debe usar el SRID actual? Además, ¿cuál es la mejor manera de obtener el SRID entero de la columna de geometría ?
rovyko
4

También he tenido la misma pregunta que me has hecho y he pasado muchos, muchos días en ella (más de lo que me gustaría admitir) buscando una solución. Suponiendo la siguiente tabla postgreSQL con la extensión 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)

Esto es lo que finalmente conseguí trabajar:

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()

No puedo decir si la lógica de conexión de mi base de datos es la mejor, ya que básicamente copié eso de otro enlace y me alegré de haber podido automatizar (o reflejar) con éxito mi tabla existente con la definición de geometría reconocida. He estado escribiendo código espacial de Python a SQL durante solo unos meses, así que sé que hay mucho que aprender.

usuario1745564
fuente