PostGIS 테이블에 GeoPandas 데이터 프레임을 추가 하시겠습니까?


17

간단한 GeoPandas 데이터 프레임이 있습니다.

여기에 이미지 설명을 입력하십시오

이 GeoDataframe을 PostGIS 테이블에 업로드하고 싶습니다. PostGIS 확장자를 가진 데이터베이스 설정이 이미 있지만이 데이터 프레임을 테이블로 추가 할 수 없습니다.

나는 다음을 시도했다.

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

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) # dicts 목록이있는 DBAPI의 실행 횟수 conn = engine.connect () conn.execute (eld_test.insert (), df .to_dict ( 'records'))
thecornman

1
GIS SE에 오신 것을 환영합니다. 둘러보기를 읽으십시오 ! 댓글에 게시 된 코드를 포함하도록 게시물을 편집 할 수 있습니까?
GISKid

답변:


31

Panda의 to_sql 메소드와 SQLAlchemy를 사용 하면 Postgres에 데이터 프레임을 저장할 수 있습니다. 또한 Geodataframe을 저장하기 때문에 GeoAlchemy 가 geom 열을 처리합니다. 코드 샘플은 다음과 같습니다.

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

'if_exists'매개 변수를 사용하면 데이터 프레임이 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.

geometry 열에있는 것과 다른 SRID를 지정하여 여기에서 다시 투영 할 수 있습니까? 아니면 현재 SRID를 사용해야합니까? 또한 기하학 열에서 정수 SRID를 얻는 가장 좋은 방법은 무엇 입니까?
rovyko

이 방법을 사용하는 이유는 무엇입니까? sqlalchemy.exc.InvalidRequestError : 반영 할 수 없습니다 : 요청 된 테이블을 엔진 오류에서 사용할 수 없습니까?
Vilq

4

또한 귀하가 요청한 것과 동일한 질문이 있었고 해결책을 찾기 위해 많은 시간을 며칠 동안 보냈습니다 (승인하는 것 이상). postGIS 확장자를 가진 다음 postgreSQL 테이블을 가정하면,

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)

이것이 내가 마침내 일한 것입니다.

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

기본적으로 다른 링크에서 복사하여 데이터베이스 연결 논리가 최고인지 말할 수 없으며 기하학 정의를 인식하여 기존 테이블을 성공적으로 자동 매핑 (또는 반영) 할 수있어서 기뻤습니다. 몇 달 동안 SQL 공간 코드에 파이썬을 작성해 왔으므로 배울 점이 많다는 것을 알고 있습니다.


0

나는 psycopg2 만 필요로하는 해결책을 가지고 있습니다 (물론 geopandas도 추가로). 일반적으로 (Geo)DataFrame속도가 느리기 때문에 개체 를 반복 하는 것은 좋지 않지만 작은 개체 나 일회성 작업의 경우 여전히 작업을 수행하게됩니다.

기본적으로 다른 열에서 지오메트리를 WKB 형식으로 덤프 한 다음 GEOMETRY삽입 할 때 유형 을 다시 캐스팅합니다 .

올바른 열을 사용하여 미리 테이블을 작성해야합니다.

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()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.