Hinzufügen eines GeoPandas-Datenrahmens zur PostGIS-Tabelle?

14

Ich habe einen einfachen GeoPandas-Datenrahmen:

Bildbeschreibung hier eingeben

Ich möchte diesen GeoDataFrame in eine PostGIS-Tabelle hochladen. Ich habe bereits ein Datenbank-Setup mit der PostGIS-Erweiterung, kann diesen DataFrame jedoch nicht als Tabelle hinzufügen.

Ich habe Folgendes versucht:

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
quelle
Ich habe Folgendes ausprobiert: engine = <> # create table meta = Metadaten (engine) eld_test = Tabelle ('eld_test', meta, Spalte ('id', Ganzzahl, Primärschlüssel = Wahr), Spalte ('key_comb_drvr', Text) , Column ('geometry', Geometry ('Point', srid = 4326))) eld_test.create (engine) # DBAPIs Executemany mit Diktatliste conn = engine.connect () conn.execute (eld_test.insert (), df .to_dict ('records')
thecornman
1
Willkommen bei der GIS SE, bitte lesen Sie unsere Tour ! Könnten Sie Ihren Beitrag so bearbeiten , dass Ihr Code in den Kommentaren enthalten ist?
GISKid

Antworten:

27

Mit der to_sql- Methode von Panda und SQLAlchemy können Sie einen Datenrahmen in Postgres speichern. Und da Sie einen Geodatenrahmen speichern, übernimmt GeoAlchemy die Geom-Spalte für Sie. Hier ist ein Codebeispiel:

# 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>)})

Beachten Sie, dass Sie mit dem Parameter 'if_exists' festlegen können, wie der Datenrahmen zu Ihrer postgres-Tabelle hinzugefügt wird:

    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
quelle
4

Ich hatte auch die gleiche Frage, die Sie gestellt haben, und habe viele, viele Tage damit verbracht (mehr als ich zugeben möchte), nach einer Lösung zu suchen. Angenommen die folgende postgreSQL-Tabelle mit der postGIS-Erweiterung,

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)

Das ist es, woran ich endlich arbeiten konnte:

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

Ich kann nicht sagen, ob meine Datenbankverbindungslogik die beste ist, da ich sie im Grunde genommen von einem anderen Link kopiert habe und einfach froh war, dass ich meine vorhandene Tabelle mit der erkannten Geometriedefinition erfolgreich automatisieren (oder widerspiegeln) konnte. Ich habe nur ein paar Monate lang Python in SQL-Raumcode geschrieben, daher weiß ich, dass es viel zu lernen gibt.

user1745564
quelle