Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v4 Python Bindings: h3.Polygon has no "len()" #284

Open
KcTheMapper opened this issue Sep 13, 2022 · 12 comments
Open

v4 Python Bindings: h3.Polygon has no "len()" #284

KcTheMapper opened this issue Sep 13, 2022 · 12 comments

Comments

@KcTheMapper
Copy link

h3.polygon_to_cells requires an h3 polygon input.

I get the following error when running h3.polygon_to_cells on an polygon, however:
TypeError: object of type 'Polygon' has no len()

I see that there is a no length variable tied to the h3.Polygon object, but there is a value for lengh under Polygon.outer.length

This prevents me from doing any type of polyfill-ing (er, i mean polygon_to_cells-ifying)

Screen Shot 2022-09-13 at 2 54 19 PM

@ajfriend
Copy link
Contributor

Thanks for the report! It is definitely an experimental object, so I'm happy to hear any and all feedback you have about your experience using it!

What version of Python are you using? Is this a runtime error or a type-checking error?

@kylebarron
Copy link
Contributor

kylebarron commented Sep 21, 2022

@KcTheMapper Are you trying to pass in a shapely polygon? I assume h3 only supports geojson-like coordinates

@KcTheMapper
Copy link
Author

Hey @ajfriend, I appreciate your attention to this!

I am using Python Version 3.9.6, h3 version 4
The error I am receiving is a Type-Error: "TypeError: object of type 'Polygon' has no len()"

@kylebarron I am trying to pass in an h3 polygon, which appears to be the only acceptable poly to pass into in v4.

@kylebarron
Copy link
Contributor

Apologies; I haven't followed the API changes in v4 and didn't know there was a new Polygon class

@KcTheMapper
Copy link
Author

@kylebarron no worries!
All these changes have thrown me for quite the loop as well

@ajfriend
Copy link
Contributor

ajfriend commented Sep 21, 2022

Can you share a code snippet that gives you this error? The following works for me:

import h3
poly = h3.Polygon(
    [(37.68, -122.54), (37.68, -122.34), (37.82, -122.34), (37.82, -122.54)],
    [(37.76, -122.51), (37.76, -122.44), (37.81, -122.51)],
)
h3.polygon_to_cells(poly, 6)

I can reproduce your error if I call len(poly), but I'm not sure if len() even makes sense for an h3.Polygon object. How would we define it? Number of holes? Total number of lat/lng points?

I'm curious what bit of your code might be trying to call len().

And it is true that Polygon is currently the only acceptable input for polygon_to_cells. I hope to add GeoJSON functionality to the next beta release to get feature parity with v3 (which will basically just convert the GeoJSON to an h3.Polygon or list of them and pass that off to polygon_to_cells).

Also, the h3.Polygon class was an experiment to clean up the API. It definitely isn't set in stone, and I would love any feedback or alternative proposals you might have.

@Filimoa
Copy link

Filimoa commented Feb 22, 2023

I'm encountering the same issue on h3==4.0.0b2 running in Python 3.10.8. Here's the code to reproduce

import geopandas as gpd
import h3

path = gpd.datasets.get_path('nybb')
df = gpd.read_file(path).to_crs(epsg=4326)

# finding largest polygon in multipolygon
polygon = max(df.loc[0, "geometry"].geoms, key=lambda a: a.area)
assert isinstance(polygon, shapely.geometry.polygon.Polygon)

h3_poly = h3.Polygon(polygon)
assert isinstance(h3_poly, h3.Polygon)

# raises "TypeError: object of type 'Polygon' has no len()"
h3.polygon_to_cells(h3_poly, 9) 

Stack Trace:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[58], line 10
      8 h3_poly = h3.Polygon(polygon)
      9 assert isinstance(h3_poly, h3.Polygon)
---> 10 h3.polygon_to_cells(h3_poly, 9) 

File [~/.pyenv/versions/3.10.8/envs/crash-data-extraction-3.10/lib/python3.10/site-packages/h3/api/_api_template.py:429](https://file+.vscode-resource.vscode-cdn.net/Users/sergey/Coding/business/three-sigma-etl/src/risk_score/notebooks/~/.pyenv/versions/3.10.8/envs/crash-data-extraction-3.10/lib/python3.10/site-packages/h3/api/_api_template.py:429), in _API_FUNCTIONS.polygon_to_cells(self, polygon, res)
    400 def polygon_to_cells(self, polygon, res):
    401     """
    402     Return the set of H3 cells at a given resolution whose center points
    403     are contained within a `h3.Polygon`
   (...)
    427      '86283095fffffff'}
    428     """
--> 429     mv = _cy.polygon_to_cells(polygon.outer, res, holes=polygon.holes)
    431     return self._out_unordered(mv)

File geo.pyx:160, in h3._cy.geo.polygon_to_cells()

File geo.pyx:108, in h3._cy.geo.GeoPolygon.__cinit__()

File geo.pyx:71, in h3._cy.geo.make_geoloop()

TypeError: object of type 'Polygon' has no len()

I was able to fix it by changing

# h3_poly = h3.Polygon(polygon)
h3_poly = h3.Polygon(polygon.exterior.coords)

My first thought is maybe this has something do with holes? When I print the working code I see

>>> h3_poly
<h3.Polygon |outer|=8877, |holes|=()>

Attempting to print the non working one returns an error (although it initializes with no issues).

>>> h3_poly
File [~/.pyenv/versions/3.10.8/envs/crash-data-extraction-3.10/lib/python3.10/site-packages/h3/_polygon.py:52](https://file+.vscode-resource.vscode-cdn.net/Users/sergey/Coding/business/three-sigma-etl/src/risk_score/notebooks/~/.pyenv/versions/3.10.8/envs/crash-data-extraction-3.10/lib/python3.10/site-packages/h3/_polygon.py:52), in Polygon.__repr__(self)
     50 def __repr__(self):
     51     s = '<h3.Polygon |outer|={}, |holes|={}>'.format(
---> 52         len(self.outer),
     53         tuple(map(len, self.holes)),
     54     )
     56     return s

TypeError: object of type 'Polygon' has no len()

I don't totally understand this behavior but it seems like a foot gun.

@spawn-guy
Copy link

@KcTheMapper Are you trying to pass in a shapely polygon? I assume h3 only supports geojson-like coordinates

it is quite the opposite. shapely IS "geojson"-compliant, but h3.Polygon is the opposite.

the whole difference is lon,lat vs lat,lon. the X,Y vs Y,X.

@ajfriend may i ask you to speed up that "geojson" support? i started with 4.0.0b2 last week and now i have to write a bunch of xy-yx conversion code. this is a bit inconvenient

@KcTheMapper
Copy link
Author

Ah, thanks for the info @spawn-guy! Indeed the old xy vs yx order seems to be at the root of my trouble.

I'm really not liking working with the h3.Polygon (with inners and outters) class.
But i bet I could find some examples with which to better acquaint myself.

Thanks @Filimoa! That's getting me in a closer direction so it seems.

@ajfriend
Copy link
Contributor

@spawn-guy @KcTheMapper Thanks for the feedback on this! Apologies that I've let this languish in beta for so long. I'm hoping to get back into active development.

That being said, please do keep the feedback coming around the h3.Polygon class! It is very much an experiment with the interface, and we can change it now while we're in still beta. But we'll be stuck with whatever's in the final release. @KcTheMapper, what would feel more ergonomic to you?

@spawn-guy
Copy link

@ajfriend well, you asked for it 😅

Why not just implement the __geo_interface__?! In the way python describes it. I see this link in my Google https://gist.github.com/sgillies/2217756

I think, most of the current py geo tools support this. What do you think?

@ajfriend
Copy link
Contributor

@spawn-guy, I'm glad I did ask for it! This is great! 😄

I wasn't aware of the interface, but the fact that it seems pretty much standard makes things easier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants