shp文件在arcgis打开失败及其修复

引言

image-20240207110903501

打开不了shp文件

同学们在平常使用arcgis的时候,是否遇到了下面这样的情况。

image-20240103152004322

这是由于shp文件丢失了部分数据,导致在arcgis打不开。

1个 Shape文件结构Shape文件的文件构成Shape文件由3 个文件构成: 主文件、索引文件、数据文件。它们分别是“.shp” , “.shx””.dbf”文件。

如果数据文件dbf,丢失了部分数据,就会出现上面说的打不开的错误。

修复Shapefile的Python源代码

Shapefile实际上是一组几个相关文件的集合,其中最重要的是.shp(包含几何信息)、.dbf(包含属性信息)和.shx(包含索引信息)文件。有时,.shp文件和.dbf文件中的记录数量可能会不一致,这可能会导致GIS软件无法正确读取Shapefile。这个脚本的目的就是修复这种不一致。

代码详解

接下来,我将逐段解释这个脚本的代码:

  1. 导入所需的模块:脚本开始时,导入了需要的Python模块。这包括os、shutil(用于文件操作)、struct(用于处理二进制数据)和dbf(用于处理.dbf文件)。
  2. 定义RestoreShp类:这个类是脚本的核心,它包含了修复Shapefile的所有功能。
    • __init__方法:这个方法是类的初始化方法。它接收两个参数:要修复的Shapefile的路径(file)和输出文件的路径(outpath)。如果outpath没有提供,那么输出文件将被保存在file所在的目录。如果file不存在,那么会抛出一个异常。方法最后设置了self.outfile,这是一个列表,包含了输出的.shp、.dbf和.shx文件的路径。
    • copyfile方法:这个方法将原始的Shapefile(.shp、.dbf和.shx文件)复制到输出目录。
    • get_shp_shape_records方法:这个方法读取.shp文件中的几何数据,并返回几何数据的数量。
    • get_dbf_shape_records方法:这个方法读取.dbf文件中的属性数据,并返回一个dbf.Table对象。
    • restore_shp方法:这个方法是修复Shapefile的主要方法。它首先获取.shp文件和.dbf文件中的记录数量,然后比较这两个数量。如果.shp文件的记录数量多,那么会在.dbf文件中添加记录;如果.dbf文件的记录数量多,那么会在.dbf文件中删除记录。
  3. 主程序:如果这个脚本被作为主程序运行(而不是被导入到其他脚本中),那么会执行这部分代码。它首先提示用户输入Shapefile的路径和输出文件的路径,然后创建一个RestoreShp对象,并调用其restore_shp方法来修复Shapefile。如果修复成功,那么会输出“complete”;如果文件不存在,那么会输出“File does not exist”。

这就是这个脚本的全部内容。它提供了一种自动修复Shapefile记录数量不一致问题的方法

代码概述

脚本首先定义了一个名为RestoreShp的类,这个类有三个主要的方法:

  1. copyfile:将原始的Shapefile(.shp、.dbf和.shx文件)复制到输出目录。
  2. get_shp_shape_records:读取.shp文件中的几何数据,并返回几何数据的数量。
  3. get_dbf_shape_records:读取.dbf文件中的属性数据,并返回属性数据的数量。

最后,restore_shp方法使用上述方法,比较.shp文件和.dbf文件中的记录数量。如果数量不一致,它会相应地添加或删除记录,以使数量一致。

使用方法

要使用此脚本,首先需要在命令行中运行它,并按照提示输入Shapefile的路径和输出文件的路径。然后,脚本会自动修复Shapefile,并将修复后的文件保存到指定的输出路径。

注意事项

这个脚本使用了dbf库来处理.dbf文件。在运行脚本之前,需要确保已经安装了这个库。可以使用以下命令进行安装:

pip install dbfread

完整代码

#!/usr/bin/env python
# -*- coding: utf-8 -*- 
# @Time : 2024/1/3 20:32
# @File : RestoreShp.py

# 修复shp文件, shp文件中的几何数据和属性数据不一致
import os
import shutil
from struct import unpack
import dbf

class RestoreShp(object):
    def __init__(self, file, outpath=None):
        self.file = file
        self.outpath = outpath
        if self.outpath is None:
            self.outpath = os.path.dirname(self.file)
        if not os.path.exists(self.outpath):
            os.makedirs(self.outpath)
        if os.path.exists(self.file) is None:
            raise Exception('File does not exist')
        else:
            self.dbffile = os.path.splitext(self.file)[0] + '.dbf'

        self.outfile = [os.path.join(self.outpath, os.path.splitext(os.path.basename(self.file))[0] + '_restore.shp'),
                        os.path.join(self.outpath, os.path.splitext(os.path.basename(self.file))[0] + '_restore.dbf'),
                        os.path.join(self.outpath, os.path.splitext(os.path.basename(self.file))[0] + '_restore.shx')]
        self.copyfile
    @property
    def copyfile(self):
        # 读取shp文件
        shutil.copyfile(self.file, self.outfile[0])
        shutil.copyfile(self.dbffile, self.outfile[1])
        shutil.copyfile(os.path.splitext(self.file)[0] + '.shx', self.outfile[2])
        print('Copy the initial file to the output directory')

    def get_shp_shape_records(self):
        try:
            # First read the geometry data and attribute data of the original file, and return the number of geometric data
                self.shx = open("%s" % (self.outfile[2]), "rb")
                self.shx.seek(24)
                shxRecordLength = (unpack(">i", self.shx.read(4))[0] * 2) - 100
                self.numShapes = shxRecordLength // 8
                return self.numShapes
        except Exception as e:
            print(e)
    def get_dbf_shape_records(self):
        # db = dbf.Dbf(self.dbffile)
        with dbf.Table(self.outfile[1], codepage='utf8', default_data_types='enhanced') as table:
            pass
        return table
    def restore_shp(self):
        #  Number of records read from shp file

        shp_numrecords = self.get_shp_shape_records()
        #  Number of records read from dbf file
        table = self.get_dbf_shape_records()
        dbf_numrecords = len(table)
        titles = dbf.get_fields(self.outfile[1])[0]

        # Check whether the number of shp and dbf records is equal
        if shp_numrecords != dbf_numrecords:
            num = shp_numrecords - dbf_numrecords
            if num > 0:
                print('The shp file has {} more records than the dbf file'.format(num))
                for i in range(num):
                    table.open(mode=dbf.READ_WRITE)
                    table.append()
                table.pack()

            else:
                print('The dbf file has {} more records than the shp file'.format(abs(num)))
                table.open(mode=dbf.READ_WRITE)
                for record in table[shp_numrecords:]:
                    dbf.delete(record)
                table.pack()

if __name__ == '__main__':
    file = input('Please enter the path of the shp file: ')
    outpath = input('Please enter the path of the output file: ')

    if os.path.exists(file) :
        record_num = RestoreShp(file, outpath).restore_shp()
        print('complete')
    else:
        print('File does not exist')
    input()

此外,这个脚本只处理了记录数量不一致的问题。如果Shapefile有其他类型的错误,例如几何数据或属性数据的格式错误,这个脚本可能无法修复。

开源代码地址在:

https://github.com/ytkz11/RestoreShp

如果对你有帮助,请点一个star!

快捷修复

我把以上的代码打包为一个命令行的exe,如果你对代码不熟练,可以直接运行这个exe,输入对应的文件路径,完成对shape文件的修复。

在公众号回复:shp修复

获得exe文件。

image-20240103161521093