GeospatialPython / pyshp

This library reads and writes ESRI Shapefiles in pure Python.
MIT License
1.09k stars 259 forks source link

check if polygon is closed fails #261

Closed Nikolaus closed 5 months ago

Nikolaus commented 1 year ago

PyShp Version

2.3.1

Python Version

3.10.7

Your code

with shp.Writer(path[:-4]+'_offset.shp', shapefile.POLYGON) as writer:
        writer.field('name', 'C')
        writer.poly([points.T])
        writer.record('polygon1')

Full stacktrace

Traceback (most recent call last):
  File "/home/horn/develop/antelope/zamg_antelope/scripts/./shapeoffset.py", line 154, in <module>
    main()
  File "/home/horn/develop/antelope/zamg_antelope/scripts/./shapeoffset.py", line 150, in main
    save_offset(args.path, offseted)
  File "/home/horn/develop/antelope/zamg_antelope/scripts/./shapeoffset.py", line 118, in save_offset
    writer.poly([points.T])
  File "/opt/antelope/python3.10.7/lib/python3.10/site-packages/shapefile.py", line 2464, in poly
    self._shapeparts(parts=polys, shapeType=shapeType)
  File "/opt/antelope/python3.10.7/lib/python3.10/site-packages/shapefile.py", line 2524, in _shapeparts
    if part[0] != part[-1]:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Other notes

Obviously, the problem here is comparing two array with multiple elements. A dumb would be to compare all elements like here:

for part in parts:

if part[0] != part[-1]:

            append = False
            for ndx in range(len/part[0])):
               if part[0][ndx] != part[-1][ndx]:
                  append = True
               if append:
                  part.append(part[0])
JamesParrott commented 11 months ago

Nikolaus, does PyShp claim to support numpy arrays?

The only examples I can find in the PyShp docs, show that a point passed to a shapefile.Writer method, must be a native Python list of numbers, not a numpy array.

Make a feature request issue for numpy.array support if you like, to see if there's any support or enthusiasm for adding that. In my opinion, as PyShp still supports Python 2 as well as Python 3, the old Python 2 versions of numpy ought to be looked at as well. That could uncover further 'bug's, and fixing them (adding support for numpy arrays) could easily be a lot more work than generalising a few if statements as in your fix.

Anyway, I have reproduced this (Python 3.11.4, Windows 11, x86, numpy==1.26.0, pyshp==2.3.1), and show the cause of the error below:

import numpy as np
import shapefile as shp

def f(convert = None, file_name = 'test_polygon_bug_261.shp'):

    points = [[0,0], [1,0],[1,1]]

    if convert is not None:
        points = [convert(point) for point in points]

    with shp.Writer(file_name, shp.POLYGON) as w:
        w.field('name', 'C')
        w.poly([points])
        w.record('triangle')

f()

f(convert = np.array)

Equality and inequality on numpy arrays is evaluated element wise. The error in _shapeparts in:

if part[0] != part[-1]:

comes when numpy.array.__bool__ is called on an array of length 2 or more, e.g.:

>>> l = [0,0]

>>> np.array(l) == np.array(l)
array([ True,  True])

>>> assert np.array(l) == np.array(l)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()