Aggiunta di GeoPandas Dataframe alla tabella PostGIS?


15

Ho un semplice Dataframe GeoPandas:

inserisci qui la descrizione dell'immagine

Vorrei caricare questo GeoDataframe su una tabella PostGIS. Ho già una configurazione del database con l'estensione PostGIS ma non riesco ad aggiungere questo Dataframe come tabella.

Ho provato quanto segue:

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

Ho provato quanto segue: 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) # Esecutivo di DBAPI con elenco di dic conn = engine.connect () conn.execute (eld_test.insert (), df .to_dict ('records'))
thornornman

1
Benvenuto in GIS SE, ti preghiamo di leggere il nostro tour ! Potresti modificare il tuo post per includere il tuo codice inserito nei commenti?
GISKid

Risposte:


30

Utilizzando il metodo to_sql di Panda e SQLAlchemy è possibile memorizzare un frame di dati in Postgres. E poiché stai memorizzando un geodataframe, GeoAlchemy gestirà la colonna geom per te. Ecco un esempio di codice:

# 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 notare che il parametro 'if_exists' consente di gestire il modo in cui il frame di dati verrà aggiunto alla tabella 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.

C'è un'opportunità per riproiettare qui specificando un SRID diverso da quello nella colonna della geometria o deve essere utilizzato l'attuale SRID? Inoltre, qual è il modo migliore per ottenere l'intero SRID dalla colonna della geometria ?
Rovyko

4

Ho anche avuto la stessa domanda che mi hai posto e ci ho passato molti, molti giorni (più di quanto mi dispiaccia ammettere) alla ricerca di una soluzione. Supponendo che la seguente tabella postgreSQL con l'estensione 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)

questo è quello che ho finalmente funzionato:

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

Non posso dire se la mia logica di connessione al database sia la migliore, dato che l'ho praticamente copiata da un altro collegamento ed ero felice di essere riuscita a automatizzare (o riflettere) la mia tabella esistente con la definizione della geometria riconosciuta. Sto scrivendo Python in sql codice spaziale solo per pochi mesi, quindi so che c'è molto da imparare.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.