【问题标题】:使用 pysftp 从 SFTP 读取 SHP 文件
【发布时间】:2022-01-23 14:37:37
【问题描述】:

我正在尝试使用 pysftp 的 getfo() 来读取 shapefile(无需下载)。但是,我得到的输出似乎不可行,我不确定它是否可以用 shapefile 做到这一点。

理想情况下,我想读入文件并将其转换为 Geopandas GeoDataFrame。

import pysftp
import io

with pysftp.Connection(host=myHostname, username=myUsername, password=myPassword, cnopts=cnopts) as sftp:
    print("Connection established ... ")

    
   
    # check files in directory
    directory_structure = sftp.listdir('sites')
    

    # Print data
    for attr in directory_structure:
        print(attr)
    
    
    flo = io.BytesIO()
    sites = sftp.getfo('sites/Sites.shp', flo)
    value=flo.getvalue()
   

从这里我无法解码该值并且不确定如何继续。

【问题讨论】:

  • 什么意思“似乎不可行”?您的代码甚至没有尝试对value 做任何事情。什么版本的 Python?

标签: python shapefile pysftp


【解决方案1】:

应该这样做:

flo.seek(0)
df = geopandas.read_file(shp=flo)

虽然使用Connection.getfo 不必要地将整个原始文件保留在内存中。更有效的是:

with sftp.open('sites/Sites.shp', bufsize=32768) as f:
    df = geopandas.read_file(f)

(关于bufsize=32768,见Reading file opened with Python Paramiko SFTPClient.open method is slow


如果我理解正确,您需要多个文件。当您通过类似文件的对象提供“shp”时,geopandas 无法神奇地访问远程服务器上的其他相关文件。 Geopandas 不知道“shp”从何而来,甚至不知道它的物理名称是什么。您需要为所有单个文件提供类似文件的对象。见Using pyshp to read a file-like object from a zipped archive——他们不使用 Geopandas,但原理是一样的。

对于 Geopandas,似乎底层 fiona 库可以处理这个问题,我没有找到任何相关参数的文档。

我猜这样的事情可能会做,但这只是一个疯狂的猜测:

with sftp.open('sites/Sites.shp', bufsize=32768) as shp,
     sftp.open('sites/Sites.shx', bufsize=32768) as shx:
     sftp.open('sites/Sites.dbf', bufsize=32768) as dbf:
     ...
    df = geopandas.read_file(shp, shx=shx, dbf=dbf, ...)

另一个技巧是将所有文件打包到一个 zip 存档中:
Read shapefile from HDFS with geopandas


顺便说一句,请注意代码下载文件无论如何。如果没有实际下载该文件内容,您将无法解析远程文件内容。该代码只是避免将下载的文件内容存储到(临时)本地文件中。

【讨论】:

  • 感谢您的详细回答,运行这两条代码都会引发错误DriverError: '/vsimem/f53fc6b530ed43a79671947eb31c315b' not recognized as a supported file format. 我知道有问题的文件是有效的 shp 文件。您的回答可能已经解决了最初的问题,不确定这是否相关。
  • 如果文件是本地文件,你将如何解析它?
  • 所以我使用get_r()下载了文件,然后我可以使用sites = geopandas.read_file(local_path)读入
  • 可以sites = geopandas.read_file(open(local_path))吗?
  • 使用 sites = geopandas.read_file(open(local_path)) 会出现以下错误:UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 624: character maps to <undefined>
【解决方案2】:

使用马丁的回答,我能够得到我需要的结果。但是需要将数据压缩到 SFTP 中,因此需要更改数据源。

这里的输出是一个数据框,但很容易适应为 GeoDataFrame。

with pysftp.Connection(host=myHostname, username=myUsername, password=myPassword, cnopts=cnopts) as sftp:
    print("Connection succesfully stablished ... ")  


    zipshape = zipfile.ZipFile(sftp.open('sites/sites_test.zip', bufsize=32768))
    r = shapefile.Reader(
            shp=zipshape.open('sites/Sites.shp'),
            shx=zipshape.open('sites/Sites.SHX'),
            dbf=zipshape.open('sites/Sites.DBF')
        )
    
    #check we have actually got the file
    print(r.bbox)
    print(r.numRecords)
    
    #get field names
    fields = [x[0] for x in r.fields][1:]
    records = r.records()
    
    #get coords and tidy up
    shps = [s.points for s in r.shapes()]
    
    #write the records into a dataframe 
    gdf = pd.DataFrame(columns=fields, data=records)

    #add the coordinate data to a column called "coords" 
    gdf = gdf.assign(coords=coords)

【讨论】:

  • 如果可行,您确定使用三个sftp.open 的更简单方法不可行吗? shapefile 应该是相同的。 Imo,解决方案的实际核心是解析库的更改(从 geopandas 到 shapefile),而不是 zip 文件,不是吗?
猜你喜欢
  • 2019-03-14
  • 2020-02-08
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-15
相关资源
最近更新 更多