cityjson / cjio

CityJSON/io: Python CLI to process and manipulate CityJSON files
MIT License
118 stars 30 forks source link

IndexError out of range when converting to STL #176

Open mdjong1 opened 7 months ago

mdjong1 commented 7 months ago

Describe the bug When trying to convert a CityJSON to STL I get a list index out of range. I'm running the command on a CityJSON I created by downloading BAG3D tiles and merging them. I then also upgraded the merged file to CityJSON 2.0 using cjio delft.city.json upgrade save delft-upgraded.city.json.

cjio validate on the file indicates that everything is valid.

Stacktrace ```shell āžœ cjio delft-upgraded.city.json export stl delft.stl Parsing delft-upgraded.city.json Exporting CityJSON to STL (/home/maarten/Documents/3DDelft/CityJSON/delft.stl) Traceback (most recent call last): File "/home/maarten/Repositories/cityjson-related/venv/bin/cjio", line 33, in sys.exit(load_entry_point('cjio==0.9.0', 'console_scripts', 'cjio')()) File "/home/maarten/Repositories/cityjson-related/venv/lib/python3.10/site-packages/click/core.py", line 1157, in __call__ return self.main(*args, **kwargs) File "/home/maarten/Repositories/cityjson-related/venv/lib/python3.10/site-packages/click/core.py", line 1078, in main rv = self.invoke(ctx) File "/home/maarten/Repositories/cityjson-related/venv/lib/python3.10/site-packages/click/core.py", line 1720, in invoke return _process_result(rv) File "/home/maarten/Repositories/cityjson-related/venv/lib/python3.10/site-packages/click/core.py", line 1657, in _process_result value = ctx.invoke(self._result_callback, value, **ctx.params) File "/home/maarten/Repositories/cityjson-related/venv/lib/python3.10/site-packages/click/core.py", line 783, in invoke return __callback(*args, **kwargs) File "/home/maarten/Repositories/cityjson-related/venv/lib/python3.10/site-packages/cjio/cjio.py", line 94, in process_pipeline cm = processor(cm) File "/home/maarten/Repositories/cityjson-related/venv/lib/python3.10/site-packages/cjio/cjio.py", line 240, in processor exporter(cm, sloppy) File "/home/maarten/Repositories/cityjson-related/venv/lib/python3.10/site-packages/cjio/cjio.py", line 179, in exporter re = cm.export2stl(sloppy) File "/home/maarten/Repositories/cityjson-related/venv/lib/python3.10/site-packages/cjio/cityjson.py", line 1752, in export2stl n, bb = geom_help.get_normal_newell(face) File "/home/maarten/Repositories/cityjson-related/venv/lib/python3.10/site-packages/cjio/geom_help.py", line 42, in get_normal_newell n[0] += ( (poly[i][1] - poly[ne][1]) * (poly[i][2] + poly[ne][2]) ) IndexError: list index out of range ```

cjio version I installed from the git repo, but used tag v0.9.0.

āžœ cjio --version                        
cjio v0.9.0; supports CityJSON v2.0

To Reproduce It's awkward, because if I convert a single CityJSON to STL it works fine, but converting the merged file results in the IndexError, so I'm not sure which file is introducing it at this point. The merged file is kind of large to attach; I guess it might help if I try to convert each file separately and see which one crashes it?

Expected behavior No IndexError upon conversion to STL šŸ˜…

Desktop (please complete the following information): Ubuntu 22.04 Python 3.10.12

cjio installed to a fresh venv (with latest main of cjvalpy from git)

pip freeze ```shell certifi==2023.11.17 cjio==0.9.0 cjvalpy @ file:///home/maarten/Repositories/cityjson-related/cjvalpy/target/wheels/cjvalpy-0.4.1-cp310-cp310-manylinux_2_34_x86_64.whl click==8.1.7 mapbox-earcut==1.0.1 numpy==1.26.2 pandas==2.1.3 pyproj==3.6.1 python-dateutil==2.8.2 pytz==2023.3.post1 six==1.16.0 triangle==20230923 tzdata==2023.3 ```
mdjong1 commented 7 months ago

So it looks like it's trying to get the normal using Newell's method for a poly that is only 2 elements long ([[1099, 1098]]) and therefore fails.

Happy to create a quick PR for it if I understand what would need to be changed here; should len(poly) >= 3 for this, since otherwise calculating a normal is kind of pointless anyways? And does this have further consequences, such as needing to drop the polygon in it's entirety further down (i.e. maybe returning False from the function I guess)?

Relevant function

def get_normal_newell(poly):
    # find normal with Newell's method
    # print (poly)
    n = np.array([0.0, 0.0, 0.0], dtype=np.float64)
    # if len(poly) == 0:
    #     print ("NOPOINTS")
    for i,p in enumerate(poly):
        ne = i + 1
        if (ne == len(poly)):
            ne = 0
        n[0] += ( (poly[i][1] - poly[ne][1]) * (poly[i][2] + poly[ne][2]) )
        n[1] += ( (poly[i][2] - poly[ne][2]) * (poly[i][0] + poly[ne][0]) )
        n[2] += ( (poly[i][0] - poly[ne][0]) * (poly[i][1] + poly[ne][1]) )

    if (n==np.array([0.0, 0.0, 0.0])).all():
        # print("one wrong")
        return (n, False)
    n = n / math.sqrt(n[0]*n[0] + n[1]*n[1] + n[2]*n[2])    
    return (n, True)
hugoledoux commented 7 months ago

PR welcome.

Yes just drop the invalid/collapsed face, nothing more you can do here I'm afraid.

sugizo commented 1 month ago

stil got the same error env google colab

steps

pip install -U cjio triangle cjvalpy meshio
!wget -c https://www.cityjson.org/tutorials/files/twobuildings.city.json
!cjio twobuildings.city.json export stl two_buildings.stl

result

Parsing twobuildings.city.json
Exporting CityJSON to STL (/content/two_buildings.stl)
Traceback (most recent call last):
  File "/usr/local/bin/cjio", line 8, in <module>
    sys.exit(cli())
  File "/usr/local/lib/python3.10/dist-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
  File "/usr/local/lib/python3.10/dist-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
  File "/usr/local/lib/python3.10/dist-packages/click/core.py", line 1720, in invoke
    return _process_result(rv)
  File "/usr/local/lib/python3.10/dist-packages/click/core.py", line 1657, in _process_result
    value = ctx.invoke(self._result_callback, value, **ctx.params)
  File "/usr/local/lib/python3.10/dist-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
  File "/usr/local/lib/python3.10/dist-packages/cjio/cjio.py", line 94, in process_pipeline
    cm = processor(cm)
  File "/usr/local/lib/python3.10/dist-packages/cjio/cjio.py", line 240, in processor
    exporter(cm, sloppy)
  File "/usr/local/lib/python3.10/dist-packages/cjio/cjio.py", line 179, in exporter
    re = cm.export2stl(sloppy)
  File "/usr/local/lib/python3.10/dist-packages/cjio/cityjson.py", line 1740, in export2stl
    n, bb = geom_help.get_normal_newell(face)
  File "/usr/local/lib/python3.10/dist-packages/cjio/geom_help.py", line 42, in get_normal_newell
    n[0] += ( (poly[i][1] - poly[ne][1]) * (poly[i][2] + poly[ne][2]) )
IndexError: list index out of range

expected result can produce *.stl file with cjio

known work generate .obj file using cjio then use meshio to generate .stl file e.g.

!cjio twobuildings.city.json export obj two_buildings.obj
!meshio convert two_buildings.obj two_buildings.stl

best regards