developmentseed/geojson-pydantic

Generic Feature's title is too long in schema definition

Closed this issue · 3 comments

Hello, thanks again for this amazing package.

Issue

The title of a generic Feature model is too long if combined with a Geometry geometry, e.g. for a Feature[Geometry, MyProperties] we get the following title in the json schema:

"Feature[Annotated[Union[geojson_pydantic.geometries.Point, geojson_pydantic.geometries.MultiPoint, geojson_pydantic.geometries.LineString, geojson_pydantic.geometries.MultiLineString, geojson_pydantic.geometries.Polygon, geojson_pydantic.geometries.MultiPolygon], FieldInfo(annotation=NoneType, required=True, discriminator='type')], MyProperties]"

This can be an issue for code documentation when the title is too long and breaks the page. For example, we get the following swagger documentation:

Screenshot from 2023-07-12 13-32-11

Reproduce

To reproduce, create a main.py file with the below code, and run it with python main.py (requires fastapi and uvicorn to be installed).

from geojson_pydantic import FeatureCollection, Feature
from geojson_pydantic.geometries import Geometry
from pydantic import BaseModel
from fastapi import FastAPI
import uvicorn


class MyProperties(BaseModel):
    a: int


class MyFeature(Feature[Geometry, MyProperties]):
    ...


class MyFeatureCollection(FeatureCollection[Geometry, MyProperties]):
    ...


app = FastAPI()


@app.get("/api/geojson", response_model=MyFeatureCollection)
def get_geojson() -> MyFeatureCollection:
    return MyFeatureCollection.model_validate({"type": "FeatureCollection", "features": []})


if __name__ == "__main__":
    uvicorn.run("main:app", host="localhost", port=8888, reload=True)

Suggested Change

I suggest to update the FeatureCollection class to only depends on one generic type bounded to the Feature model (instead on depending on the generic types bounded to the geometry and properties respectively).

The modification will look like:

# features.py

Feat = TypeVar("Feat", bound=Feature)


class FeatureCollection(BaseModel, Generic[Feat], GeoInterfaceMixin):
    """FeatureCollection Model"""

    type: Literal["FeatureCollection"]
    features: List[Feat]
    bbox: Optional[BBox] = None

    def __iter__(self) -> Iterator[Feat]:  # type: ignore [override]
        """iterate over features"""
        return iter(self.features)

    def __len__(self) -> int:
        """return features length"""
        return len(self.features)

    def __getitem__(self, index: int) -> Feat:
        """get feature at a given index"""
        return self.features[index]

    _validate_bbox = field_validator("bbox")(validate_bbox)

Then, the instantiation a generic feature collection can be done by providing a feature:

from geojson_pydantic import FeatureCollection, Feature
from geojson_pydantic.geometries import Geometry
from pydantic import BaseModel
from fastapi import FastAPI
import uvicorn


class MyProperties(BaseModel):
    a: int


class MyFeature(Feature[Geometry, MyProperties]):
    ...


class MyFeatureCollection(FeatureCollection[MyFeature]):
    ...


app = FastAPI()


@app.get("/api/geojson", response_model=MyFeatureCollection)
def get_geojson() -> MyFeatureCollection:
    return MyFeatureCollection.model_validate({"type": "FeatureCollection", "features": []})


if __name__ == "__main__":
    uvicorn.run("main:app", host="localhost", port=8888, reload=True)

The associated title of the generic feature is then much shorted and easier to read as it is associated to the user defined feature MyFeature. It results in the below swagger documentation:

Screenshot from 2023-07-12 13-30-48

Versions

  • pydantic v2.0.2
  • geojson-pydantic v0.6.3 (from main branch of the github repo)

If you want, I can propose a PR for this specific update.

Looks reasonable to me. Please feel free to start a PR 🙏

Thanks for the feedback, I love the repo and will work on that ! 🚀

closed in #138