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

Enforce type geometry properties #94

Merged
merged 6 commits into from
Jan 31, 2023
Merged

Conversation

vincentsarago
Copy link
Member

closes #92

@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
python-version: ['3.8', '3.9', '3.10', '3.11']
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's deprecate python 3.7:

  • because it's old
  • because it doesn't support Literal by default

properties: Optional[Props] = None
type: Literal["Feature"]
geometry: Union[Geom, None] = Field(...)
properties: Props = Field(...)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TO BE DISCUSSED

Copy link
Collaborator

@eseglem eseglem Jan 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A Feature object has a member with the name "properties". The value of the properties member is an object (any JSON object or a JSON null value).

Probably should be: properties: Union[Props, None] = Field(...)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right so we should accept properties=None

@@ -44,23 +46,22 @@ def __geo_interface__(self) -> Dict[str, Any]:
"geometry": self.geometry.__geo_interface__
if self.geometry is not None
else None,
"properties": self.properties,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

properties should always be set to at least {}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically GeoJSON can have null properties. But geo_interface says

properties (optional) - A mapping of feature properties ...

So, I would say leave this how it was.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the issue then is that you wouldn't be able to do Feature(**Feature(type="Feature", geometry=None, properties=None).__geo_interface__)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: we already allow geometry to be None in the geo_interface while the spec says it should be a mapping 🤷

Copy link
Collaborator

@eseglem eseglem Jan 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is an interesting one. Not being able to put it back into itself is problematic. And None becomes {} in __geo_interface__ that would mean properties would change when fed back in. The least bad thing might be a pre validator that converts None to dict()?

Note: we already allow geometry to be None in the geo_interface while the spec says it should be a mapping 🤷

The current way won't add the geometry key to the dict unless it is truthy. So it would never be None, it just wouldn't exist at all. Edit: Mixed up properties and geometry in my head at some point.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current way won't add the geometry key to the dict unless it is truthy. So it would never be None, it just wouldn't exist at all.

Feature(type="Feature", geometry=None, properties=None).__geo_interface__
>> {'type': 'Feature', 'geometry': None, 'properties': None}

we do add geometry: None in the current implementation

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vincentsarago Sorry, I had started to edit that comment and apparently never saved it. Got lost in the 12 tabs open looking at too many parts at the same time.

Based on a strict reading geometry may be wrong. However jazzband/geojson is just returning self as the __geo_interface__ so geometry could be None there as well.

Which actually seems like a good idea, its supposed to be modeled after geojson, so just return self.dict(). Don't worry about picking fields like it currently does.

properties: Optional[Props] = None
type: Literal["Feature"]
geometry: Union[Geom, None] = Field(...)
properties: Props = Field(...)
Copy link
Collaborator

@eseglem eseglem Jan 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A Feature object has a member with the name "properties". The value of the properties member is an object (any JSON object or a JSON null value).

Probably should be: properties: Union[Props, None] = Field(...)

tests/test_features.py Outdated Show resolved Hide resolved
@@ -44,23 +46,22 @@ def __geo_interface__(self) -> Dict[str, Any]:
"geometry": self.geometry.__geo_interface__
if self.geometry is not None
else None,
"properties": self.properties,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically GeoJSON can have null properties. But geo_interface says

properties (optional) - A mapping of feature properties ...

So, I would say leave this how it was.

@vincentsarago vincentsarago requested review from eseglem and removed request for eseglem January 30, 2023 10:01
@vincentsarago vincentsarago marked this pull request as ready for review January 30, 2023 10:02
@vincentsarago
Copy link
Member Author

@eseglem are we happy with the changes proposed here?

Copy link
Collaborator

@eseglem eseglem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think __geo_interface__ may be worth additional discussion but that is outside the scope of this PR. Looks good to me.

@vincentsarago
Copy link
Member Author

@geospatial-jeff do you have opinions? the TLDR of this pr is:

  • makes type=Feature, geometry and properties mandatory (but they can be None) for Feature
  • makes type=FeatureCollection and features mandatory for FeatureCollection
  • makes type mandatory for geometries

Those are quite breaking change but better match the specification. We could argue that type do not have to be passed when constructing a Feature or Geometry but if we think as geojson-pydantic more as the validation library then it makes more sense...

Another change that will be discussed in another Issue is the fact that we don't follow the geo_interface_ specifiation to the letter because we set values to None for geometry and properties. The spec for Feature is kinda not clear about what we should do but there were a lot of discussion about geometries in the shapely repo which I'm going link in the new issue.

@geospatial-jeff
Copy link
Contributor

I agree with these changes, it better matches the spec which is what I care about. Keeping in mind that this library was spun out of stac-pydantic which itself didn't attempt to implement the full geojson spec, just enough to make it work for STAC. So I think these changes are good and much needed.

@vincentsarago vincentsarago merged commit d65cdc0 into main Jan 31, 2023
@vincentsarago vincentsarago deleted the EnforceTypeGeometryProperties branch January 31, 2023 07:45
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

Successfully merging this pull request may close these issues.

Feature validates arbitrary dictionaries
3 participants