2016-07-13 102 views
6

Spero che la mia domanda non sia ridicola dato che, sorprendentemente, questa domanda non è apparentemente ancora stata realmente posta (per quanto a mia conoscenza) sui siti web popolari.Scrivi GeoDataFrame nel database SQL

La situazione è che ho diversi file CSV contenenti più di 1 milione di osservazioni in totale. Ogni osservazione contiene, tra gli altri, un indirizzo postale. Sto pianificando di leggere tutti i file in un singolo GeoDataFrame, geocodificare gli indirizzi, eseguire un join spaziale dato uno shapefile e salvare alcune informazioni dal poligono per ogni riga. Abbastanza standard, suppongo. Questa è una parte di un processo di pulizia dei dati una tantum.

Il mio obiettivo è impostare un database con questo dataset finale. Questo perché mi permette di condividere e cercare i dati abbastanza facilmente così come per es. traccia alcune osservazioni su un sito web. Inoltre, rende abbastanza facile selezionare le osservazioni in base ad alcuni criteri e quindi eseguire alcune analisi.

Il mio problema è che la funzione di inserimento di un GeoDataFrame in un database sembra non essere ancora implementato - a quanto pare perché GeoPandas si suppone che sia un subsitute per i database ("GeoPandas consente di eseguire facilmente operazioni in python che altrimenti richiederebbero un database spaziale come PostGIS ").

Naturalmente, potrei scorrere ogni riga e inserire ogni punto di dati "manualmente", ma sto cercando la soluzione migliore qui. Per qualsiasi soluzione, temerei anche che il tipo di dati possa entrare in conflitto con quello del database. C'è "un modo migliore" per portare qui?

Grazie per il vostro aiuto.

risposta

2

Quindi, l'ho appena implementato per un database PostGIS e posso incollare qui il mio metodo. Per MySQL, dovrai adattare il codice.

primo passo è stato per convertire le colonne geocodificati in WKB stringa esadecimale, perché utilizzo SQLAlchemy, con un motore basato su pyscopg, ed entrambi quei pacchetti non comprendo geo-tipo nativo. Il passo successivo è quello di scrivere tali dati in un DB SQL, come al solito (si noti che tutte le colonne geometriche devono essere convertite in colonne di testo contenenti la stringa esadecimale WKB) e infine modificare il tipo delle colonne in Geometria eseguendo una query. Fare riferimento al seguente pseudocodice:

# Imports 
import sqlalchemy as sal 
import geopandas as gpd 

# Function to generate WKB hex 
def wkb_hexer(line): 
    return line.wkb_hex 

# Convert `'geom'` column in GeoDataFrame `gdf` to hex 
    # Note that following this step, the GeoDataFrame is just a regular DataFrame 
    # because it does not have a geometry column anymore. Also note that 
    # it is assumed the `'geom'` column is correctly datatyped. 
gdf['geom'] = gdf['geom'].apply(wkb_hexer) 

# Create SQL connection engine 
engine = sal.create_engine('postgresql://username:[email protected]:socket/database') 

# Connect to database using a context manager 
with engine.connect() as conn, conn.begin(): 
    # Note use of regular Pandas `to_sql()` method. 
    gdf.to_sql(table_name, con=conn, schema=schema_name, 
       if_exists='append', index=False) 
    # Convert the `'geom'` column back to Geometry datatype, from text 
    sql = """ALTER TABLE schema_name.table_name 
       ALTER COLUMN geom TYPE Geometry(LINESTRING, <SRID>) 
       USING ST_SetSRID(geom::Geometry, <SRID>)""" 
    conn.execute(sql) 
+0

Cool! Funziona alla prima chiamata quando la tabella non esiste. Ma le chiamate consecutive mi danno un 'DataError: (psycopg2.DataError) Geometry SRID (0) non corrisponde alla colonna SRID (4326)'. – j08lue

+0

'psycopg-postgis' potrebbe avere qualche ispirazione: https://github.com/yohanboniface/psycopg-postgis. Tuttavia, implementano i propri tipi piuttosto che quelli in 'formosa' /' geopandas'. – j08lue

+0

Questo ** può ** lavorare per una singola chiamata, non per aggiungere dati, poiché la colonna 'geom' quindi si aspetta che la geometria abbia un SRID. – j08lue

5

Come accennato prima, @ risposta di Kartik funziona solo per una singola chiamata, ad apporre i dati solleva una DataError dal momento che la colonna di geom poi si aspetta la geometria di avere uno SRID. È possibile utilizzare GeoAlchemy per gestire tutti i casi:

# Imports 
from geoalchemy2 import Geometry, WKTElement 
from sqlalchemy import * 

# Use GeoAlchemy's WKTElement to create a geom with SRID 
def create_wkt_element(geom): 
    return WKTElement(geom.wkt, srid = <your_SRID>) 

geodataframe['geom'] = geodataframe['geom'].apply(create_wkt_element) 

db_url = create_engine('postgresql://username:[email protected]:socket/database') 
engine = create_engine(db_url, echo=False) 

# Use 'dtype' to specify column's type 
# For the geom column, we will use GeoAlchemy's type 'Geometry' 
your_geodataframe.to_sql(table_name, engine, if_exists='append', index=False, 
         dtype={geom: Geometry('POINT', srid= <your_srid>)})