python 将shp导入postgressql数据库

本文讲述了如何处理shp文件中的数据类型转换,特别是Polygon和MultiPolygon之间的转换,以及如何将转换后的数据成功导入到PostGIS数据库中,包括注意事项如`if_exists`参数的选择和可能遇到的SRS问题。
摘要由CSDN通过智能技术生成

简介

最近公司让把shp文件修改数据后导入数据库中,研究了许久,网上的资料全是一个to_sql就搞定了,但是由于shp文件的多样性会出现很多问题。
在此总结一下遇到的问题

shp数据类型分类

shp数据共有6种类型MultiPolygon, MultiPoint, MultiLineString, Point, Polygon, LineString,分别是点、线、面、多点、多线、多面
点线面可以转换为多点、多线、多面,多点、多线、多面也可以转为、点、线、面。两者可以互相转换

将Polygon转为MultiPolygon

例:

from shapely import Polygon, MultiPolygon

polygon = Polygon([(0, 0), (1, 1), (1, 0)])
multi = MultiPolygon([polygon])
print(multi)

在这里插入图片描述

将 MultiPolygon转为Polygon

polygon = Polygon([(0, 0), (1, 1), (1, 0)])
multi = MultiPolygon([polygon])

for i in MultiPolygon([polygon]).geoms:
    print(i)

在这里插入图片描述

总结

点、线、面与多点、多线、多面可以相互转换,多点可以理解为一个点的列表,同理其他的也是一样的

POLYGONMultiPolygon不相符

在将shp中,一般的文件中只有一个类型,但是有特殊情况,例如:一个shp中有两个类型POLYGONMultiPolygon,shp文件本身是支持的,但是数据库不支持,如果矢量数据包含这两种类型,geopandas本身是添加不进去的,如果强行添加会报错。
因此就需要手动转换类型,如果你用的是qgis或者arcgis也会发现,他会把POLYGON转为MultiPolygon再添加到数据中。
如果使用MultiPolygon转为POLYGON,它会产生多条数据,不符合需求,因此一般使用POLYGON转为MultiPolygon

需要注意的是if_exists必须选择replace,否则会出现各种各样的问题,比如没有指定srid等错误,但是使用replace会先删除原本的表,表的约束会清空,pandas在重新创建表时,没办法指定主键等约束,目前没有找到解决方法

完整代码:

import re
from urllib import parse

from geoalchemy2 import Geometry
from shapely import MultiPoint, MultiLineString, MultiPolygon
from sqlalchemy import create_engine
from geopandas import GeoDataFrame, GeoSeries


def translation(url):
	# 对用户名密码进行转码,如果账号密码有特殊字符,sqlalchemy 会连接失败
    return parse.quote_plus(url)


url = f"postgresql+psycopg2://{translation(user)}:{translation(pwd)}@{host}:{port}/{dbname}"
engine = create_engine(url)
file = r"JBHQ418.shp"
shp_data = GeoDataFrame.from_file(file)  # type: gpd.GeoDataFrame
shp_data.columns = [i.lower() for i in shp_data.columns]
shp_len = len(shp_data)
# 获取srid, 坐标系
srid = int(re.search('\d+', str(shp_data.crs).split(',')[-1]).group(0))
# 获取每一行矢量数据的数据类型
geom_type = shp_data.geom_type  # type: core.series.Series
# 给类型去重,并获取总类型
types = list(set(i for i in geom_type))
print(types)
# ['Polygon', 'MultiPolygon']
geometry_type = None
if 'Point' in types:
    geometry_type = 'MultiPoint'
if 'LineString' in types:
    geometry_type = 'MultiLineString'
if 'Polygon' in types:
    geometry_type = 'MultiPolygon'
# 重新创建表,并给表赋值
geo_data = GeoDataFrame()
geo_data['bsm'] = shp_data['bsm']
geo_data['ysdm'] = shp_data['ysdm']
geo_data['bhqbh'] = shp_data['bhqbh']
geo_data['jbntmj'] = shp_data['jbntmj']
geo_data['jbntmjm'] = shp_data['jbntmjm']
geo_data['qxdm'] = ['15' for _ in range(shp_len)]

# `POLYGON`转为`MultiPolygon`
def one_to_multi1(x):
    _t = type(x).__name__
    if _t == 'Point':
        w = MultiPoint([x])
        return w
    elif _t == 'LineString':
        w = MultiLineString([x])
        return w
    elif _t == 'Polygon':
        w = MultiPolygon([x])
        return w
    else:
        return x


geo_data['shape'] = shp_data['geometry'].apply(lambda x: one_to_multi1(x))
# 重写矢量数据字段
geo_data.set_geometry('shape', inplace=True)
# 写入坐标系
geo_data.set_crs(f'EPSG:{srid}', allow_override=True)

geo_data.to_postgis(
    'table_name',
    engine,
    if_exists='replace',
    index=False,
    chunksize=1000,
    dtype={'geometry': Geometry(geometry_type=geometry_type, srid=srid)},
)

to_postgis

在这里插入图片描述

postgresql数据导出

from shapely import wkb
from geopandas import GeoDataFrame


def translation(url):
	# 对用户名密码进行转码,如果账号密码有特殊字符,sqlalchemy 会连接失败
    return parse.quote_plus(url)


url = f"postgresql+psycopg2://{translation(user)}:{translation(pwd)}@{host}:{port}/{dbname}"
engine = create_engine(url)
conn = engine.connect()
def select_to_geo(table_name):
    sql = f'SELECT * FROM  "{table_name}"'
    return GeoDataFrame(read_sql(sql, conn))
geo['shape'] = geo['shape'].apply(lambda x: wkb.loads(x))
geo.set_geometry('shape', inplace=True)
geo.to_file(
    r'F:\test\shp\dlxx_xzdy.shp',
    index=False,
    encoding='utf-8'
)

geopandas中文文档

最后分享一个文档https://www.osgeo.cn/geopandas/docs/reference/api/geopandas.GeoDataFrame.to_postgis.html?highlight=to_postgis

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值