Pyshp 将新字段附加到现有 shapefile 但不记录值

Pyshp appends new fields but not record values to an existing shapefile

问题在于将 .csv 文件中显示的列作为新字段附加到现有 shapefile。因此,我使用 Python 和模块 pyshp 和 csv 来首先复制原始 shapefile 的内容(几何和记录),其次,在此副本中创建新字段并在相应的 .csv 中迭代行以便在其上插入:

import os, sys
import shapefile, csv

from os.path import basename

filename_full = sys.argv[1]
output_full = sys.argv[2]

name, file_extension = os.path.splitext(filename_full)
output_name, file_extension = os.path.splitext(output_full)

filename_dbf =  name + ".dbf"
filename_classified =  name + "_classified.csv"
output_dbf =  output_name + ".dbf"

# reader
myshp = open(filename_full, "rb")
mydbf = open(filename_dbf, "rb")
r = shapefile.Reader(shp=myshp, dbf=mydbf)

# writer
w = shapefile.Writer(r.shapeType)

# copy shapefiles content
w._shapes.extend(r.shapes())
w.records.extend(r.records())
w.fields = list(r.fields)
w.save(output_full)

# add new records from the csv
with open(filename_classified, 'rt', encoding='utf-8') as csvfile:
     reader = csv.DictReader(csvfile, delimiter=',')    
     headers = reader.fieldnames
     [w.field(field) for field in headers]     
     for row in reader:            
        w.record(*tuple([row[f] for f in headers])) # <-- insertion in specific fields

w.save(output_full)

pyshp page 中,有几个示例。其中之一特定于将行插入特定字段。如下:

>>> w = shapefile.Writer()
>>> w.field('FIRST_FLD','C','40')
>>> w.field('SECOND_FLD','C','40')
>>> w.record('First', 'Line')
>>> w.record(FIRST_FLD='First', SECOND_FLD='Line')

但是,即使指示字段,我得到:

Traceback (most recent call last):
  File "assigning-shapefile.py", line 68, in <module>
    w.record(*tuple([row[f] for f in headers]))
  File "/usr/local/lib/python3.5/dist-packages/shapefile.py", line 1040, in record
    record = [recordList[i] for i in range(fieldCount)]
  File "/usr/local/lib/python3.5/dist-packages/shapefile.py", line 1040, in <listcomp>
    record = [recordList[i] for i in range(fieldCount)]
IndexError: tuple index out of range 

而且,如果我们查看 shapefile 内部,我们会看到类似这样的内容:

QGIS attribute table before and after the code execution

我得出的结论是字段已成功添加,但行(w.record 指定了字段名称)没有。

使用 osgeo 库使用非常简单的方法解决了问题:

# --
# USAGE: 
#       python3 assinging-shapefile.py [input-shapefile] [output-shapefile]
# --
# REQUISITE: 
#   The classification csv file should be edited as a header of classifiers and its labels. The file name is mandatory to be IMAGE_NAME-classified.csv
#   Ex: 
#       Filename: IMAGE_NAME-classified.csv
#       Content: 
#               Random forest, Multilayer-Perc, CRF, SVM
#               vegetation, vegetation, building, vegetation
#               wall, window, window, window 
#               ...
# --

import os, sys
import shapefile, csv

from os.path import basename
from osgeo import ogr

filename_full = sys.argv[1]
output_full = sys.argv[2]

name, file_extension = os.path.splitext(filename_full)
output_name, file_extension = os.path.splitext(output_full)

filename_dbf =  name + ".dbf"
filename_classified =  name + "_classified.csv"
output_dbf =  output_name + ".dbf"

myshp = open(filename_full, "rb")
mydbf = open(filename_dbf, "rb")
r = shapefile.Reader(shp=myshp, dbf=mydbf)
w = shapefile.Writer(r.shapeType)

# copy shapefile
w._shapes.extend(r.shapes())
w.records.extend(r.records())
w.fields = list(r.fields)
w.save(output_full)

# read the csv records
csvRecords = []
csvHeaders = []
with open(filename_classified, 'rt', encoding='utf-8') as csvfile:
    reader = csv.DictReader(csvfile, delimiter=',')
    csvHeaders = reader.fieldnames  
    for line in reader:
        csvRecords.append(line)

driver = ogr.GetDriverByName('ESRI Shapefile')
infile = driver.Open(output_full, 1)

for classifier in csvHeaders:
    field = ogr.FieldDefn(classifier, ogr.OFTString)
    field.SetWidth(16)

    layer = infile.GetLayer()
    layer.CreateField(field)

cont = 0
for feature in layer:   
    for classifier in csvHeaders:       
        if(feature.GetField(0)!=cont):
            cont += 1

        feature.SetField(classifier, csvRecords[cont][classifier]) 
        layer.SetFeature(feature)           
infile=None

它能够 (i) 读取 csv 文件(包含要添加的列),(ii) 读取 shapefile 并复制它,(iii) 通过用对应的行编辑每一行来修改 .shp 副本csv 记录。