PostGIS tablosuna GeoPandas Veri Çerçevesi eklensin mi?


17

Basit bir GeoPandas veri çerçevesi var:

resim açıklamasını buraya girin

Bu GeoDataframe bir PostGIS tabloya yüklemek istiyorum. Zaten PostGIS uzantısı ile bir Veritabanı kurulumu var ama bu Dataframe tablo olarak eklemek gibi görünüyor.

Aşağıdakileri denedim:

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

Aşağıdakileri denedim: engine = <> # create table meta = MetaData (motor) eld_test = Tablo ('eld_test', meta, Sütun ('id', Tamsayı, birincil_anahtar = Doğru), Sütun ('key_comb_drvr', Metin) , Sütun ('geometri', Geometri ('Nokta', srid = 4326))) eld_test.create (motor) # DBAPI'nin dicts listesiyle yürüttüğü conn = engine.connect () conn.execute (eld_test.insert (), df .to_dict ('kayıtlar'))
thecornman

1
GIS SE'ye hoş geldiniz, lütfen turumuzu okuyun ! Yorumlarda yayınlanan kodunuzu içerecek şekilde yayınınızı düzenleyebilir misiniz ?
GISKid

Yanıtlar:


32

Panda'nın to_sql yöntemini ve SQLAlchemy'yi kullanarak bir veri çerçevesini Postgres'te saklayabilirsiniz. Ve bir Geodataframe sakladığınız için, GeoAlchemy sizin için geom sütununu yönetecektir . İşte bir kod örneği:

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

'İf_exists' parametresinin, veri çerçevesinin postgres tablonuza nasıl ekleneceğini yönetmenize izin verdiğini belirtmek gerekir:

    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.

Burada geometri sütunundakinden farklı bir SRID belirterek yeniden projeksiyon yapma imkanı var mı, yoksa şu andaki SRID kullanılmalı mı? Ayrıca geometri sütunundan tamsayı SRID'sini almanın en iyi yolu nedir?
rovyko

Neden bu yöntemi kullanarak sqlalchemy.exc.InvalidRequestError: Yansıtma: Motor hatası içinde istenen tablo (lar) mevcut değil?
Vilq

4

Ayrıca, sorduğunuz aynı soruyu aldım ve bir çözüm aramak için günlerce (itiraf ettiğimden daha fazla) harcadım. PostGIS uzantılı aşağıdaki postgreSQL tablosunu varsayarsak,

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)

nihayet çalıştığım şey bu :

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

Temelde başka bir bağlantıdan kopyaladım ve sadece tanınan geometri tanımı ile mevcut tablomu otomatik olarak (veya yansıtmak) yapabildiğim için veritabanı bağlantı mantığımın en iyisi olup olmadığını söyleyemem. Birkaç aydır sql uzamsal kod için python yazıyorum, bu yüzden öğrenecek çok şey olduğunu biliyorum.


0

Ben sadece psycopg2 ve düzgün (tabii ek olarak geopandas) gerektiren bir çözüm var. (Geo)DataFrameNesneler arasında yineleme yapmak genellikle kötü bir uygulamadır, çünkü yavaştır, ancak küçük olanlar veya bir defalık görevler için, yine de işi halledecektir.

Temel olarak geometriyi başka bir sütunda WKB formatına dökerek çalışır ve daha sonra yerleştirirken yazmak için yeniden yayınlar GEOMETRY.

Doğru sütunlarla tabloyu önceden oluşturmanız gerekeceğini unutmayın.

import psycopg2 as pg2
from shapely.wkb import dumps as wkb_dumps
import geopandas as gpd


# Assuming you already have a GeoDataFrame called "gdf"...

# Copy the gdf if you want to keep the original intact
insert_gdf = gdf.copy()

# Make a new field containing the WKB dumped from the geometry column, then turn it into a regular 
insert_gdf["geom_wkb"] = insert_gdf["geometry"].apply(lambda x: wkb_dumps(x))

# Define an insert query which will read the WKB geometry and cast it to GEOMETRY type accordingly
insert_query = """
    INSERT INTO my_table (id, geom)
    VALUES (%(id)s, ST_GeomFromWKB(%(geom_wkb)s));
"""

# Build a list of execution parameters by iterating through the GeoDataFrame
# This is considered bad practice by the pandas community because it is slow.
params_list = [
    {
        "id": i,
        "geom_wkb": row["geom_wkb"]
    } for i, row in insert_gdf.iterrows()
]

# Connect to the database and make a cursor
conn = pg2.connect(host=<your host>, port=<your port>, dbname=<your dbname>, user=<your username>, password=<your password>)
cur = conn.cursor()

# Iterate through the list of execution parameters and apply them to an execution of the insert query
for params in params_list:
    cur.execute(insert_query, params)
conn.commit()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.