From 131fa71f91b772beafb7206761a119a0041dba80 Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Thu, 1 Jun 2023 09:35:21 -0400 Subject: [PATCH 01/28] Initial commit --- README.md | 43 ++++++- docs/example.md | 95 +++++++++++++++ docs/index.md | 87 ++++++++++++++ docs/validation.md | 3 + examples/example.py | 27 +++++ examples/json/sample_geojson.json | 50 ++++++++ examples/json/sample_geometrycollection.json | 13 +++ examples/json/sample_microjson.json | 67 +++++++++++ microjson/__init__.py | 1 + microjson/model.py | 117 +++++++++++++++++++ mkdocs.yml | 7 ++ noxfile.py | 18 +++ pyproject.toml | 18 +++ requirements-dev.txt | 4 + tests/test_microjson.py | 50 ++++++++ 15 files changed, 599 insertions(+), 1 deletion(-) create mode 100644 docs/example.md create mode 100644 docs/index.md create mode 100644 docs/validation.md create mode 100644 examples/example.py create mode 100644 examples/json/sample_geojson.json create mode 100644 examples/json/sample_geometrycollection.json create mode 100644 examples/json/sample_microjson.json create mode 100644 microjson/__init__.py create mode 100644 microjson/model.py create mode 100644 mkdocs.yml create mode 100644 noxfile.py create mode 100644 pyproject.toml create mode 100644 requirements-dev.txt create mode 100644 tests/test_microjson.py diff --git a/README.md b/README.md index c7cac3b..b161b00 100644 --- a/README.md +++ b/README.md @@ -1 +1,42 @@ -# microjson \ No newline at end of file +# MicroJSON + +MicroJSON is a JSON-based format inspired by [GeoJSON](https://geojson.org), designed to encode a variety of data structures related to microscopy images. It can handle representations of reference points, regions of interest, and other annotations, making it an ideal solution for conveying complex microscopy data in a straightforward, easy-to-use format. + +## Features + +MicroJSON offers a range of features designed to meet the needs of microscopy data representation: + +- **Flexible Data Structures:** MicroJSON can represent diverse data structures, including geometries (such as points, multipoints, linestrings, polygons), features (individual entities with specific properties), and feature collections (groups of features). + +- **Standardized Format:** MicroJSON uses the widely adopted JSON format, ensuring compatibility with a wide range of programming languages and tools. + +- **Extensibility:** MicroJSON can handle additional properties associated with specific features, such as metadata relating to microscopy images. + +## Usage + +MicroJSON can be used with any application or tool that can process JSON data. Due to its design, it is particularly suited to applications related to the analysis, visualization, and manipulation of microscopy images. + +## Examples + +Refer to the examples foler to see samples of MicroJSON files as well as a simple parsing example, or the [example in the documentation](docs/example.md) + +## Specification + +For more detailed information about the MicroJSON structure and its components, please refer to the [Specification](docs/index.md) file. + + +## Validation + +We provide validation through Pydantic data classes. [Validation documentation](docs/validation.md). + +## Contribution + +We welcome contributions to the development and enhancement of MicroJSON. Whether you're reporting bugs, suggesting enhancements, or contributing to the code, your input is highly appreciated. + +## License + +MicroJSON is licensed under [MIT License](./LICENSE). + +--- + +This project is maintained by Polus AI. For any queries or further discussion, please contact [contact details]. diff --git a/docs/example.md b/docs/example.md new file mode 100644 index 0000000..e302175 --- /dev/null +++ b/docs/example.md @@ -0,0 +1,95 @@ +# Sample MicroJSON File +This JSON file demonstrates how MicroJSON can be used to define and describe complex structures related to microscopy, such as cells and their nuclei, including their spatial relationships, identifiers, labels, and color representations. + +```json +{ + "coordinatesystem": { + "axes": [ + "x", + "y", + "z" + ], + "units": [ + "micrometer", + "micrometer", + "micrometer" + ], + "pixelsPerUnit": [ + 0.5, + 0.5, + 2 + ], + }, + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "id": "cell-1", + "type": "cell", + "label": "Cell A", + "color": "red" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [10, 10], + [10, 50], + [50, 50], + [50, 10], + [10, 10] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "id": "cell-2", + "type": "cell", + "label": "Cell B", + "color": "blue" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [60, 60], + [60, 100], + [100, 100], + [100, 60], + [60, 60] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "id": "nucleus-1", + "type": "nucleus", + "label": "Nucleus A", + "parentCell": "cell-1" + }, + "geometry": { + "type": "Point", + "coordinates": [30, 30] + } + }, + { + "type": "Feature", + "properties": { + "id": "nucleus-2", + "type": "nucleus", + "label": "Nucleus B", + "parentCell": "cell-2" + }, + "geometry": { + "type": "Point", + "coordinates": [80, 80] + } + } + ] +} +``` \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..ba6f66b --- /dev/null +++ b/docs/index.md @@ -0,0 +1,87 @@ +# MicroJSON Specification + +## Introduction + +MicroJSON is a format, inspired by GeoJSON, for encoding a variety of data structures related to microscopy images, including reference points, regions of interest, and other annotations. These data structures are represented using the widely adopted JSON format, making it easy to work with in various programming languages and applications. + +## Objects + +### MicroJSON Object + +A MicroJSON object is a JSON object that represents a geometry, feature, or collection of features. + +### Geometry Object + +A geometry object is a JSON object where the `type` member's value is one of the following strings: `"Point"`, `"MultiPoint"`, `"LineString"`, `"MultiLineString"`, `"Polygon"`, `"Rectangle"`, or `"MultiPolygon"`. + +Each geometry object must have a `"coordinates"` member with an array value. The structure of the coordinates array depends on the geometry type: + +- **Point**: The coordinates array must contain two or three (if 3D) numbers representing the X and Y (and Z) coordinates of the point in the image. A “Point” Geometry MAY have a radius, if indicating a circular object, with the value in pixels, specified as a member `“radius”` of the Geometry object. + +- **MultiPoint**: The coordinates array must be an array of point coordinates. + +- **LineString**: The coordinates array must be an array of two or more point coordinates forming a continuous line. A “LineString” Geometry MAY have a radius, with the value in pixels, specified as a member “radius” of the Geometry object. + +- **MultiLineString**: The coordinates array must be an array of LineString coordinate arrays. + +- **Polygon**: The coordinates array must be an array of linear ring coordinate arrays, where the first linear ring represents the outer boundary and any additional rings represent holes within the polygon. + + - A subtype of “Polygon” is the “Rectangle” geometry: A polygon with an array of four point coordinates representing the corners of the rectangle in a clockwise order. + + - A subtype of “Polygon” is the “Cuboid”: A polygon with an array of eight point coordinates representing the corners of the rectangle in a clockwise order in the X-Y plane, starting with the plane with the lowest Z value. + +- **MultiPolygon**: The coordinates array must be an array of Polygon coordinate arrays. + +### GeometryCollection + +A GeometryCollection is an array of geometries (Point, multipoint, LinesString, MultiLineString, Polygon, MultiPolygon). It is possible for this array to be empty. + +### Feature Object + +A feature object represents a spatially bounded entity associated with properties specific to that entity. A feature object is a JSON object with the following members: + +- `"type"`: A string with the value `"Feature"`. + +- `"geometry"`: A geometry object as defined in the section above or a JSON null value. + +- `"properties"`: A JSON object containing properties specific to the feature, or a JSON null value. + +#### Special Feature Objects + +- **Image**: An image MUST have the following key-value pairs in its “properties” object: + + - `"type"`: A string with the value “Image” + + - `"URI"`: A string with the image URI, e.g. “./image_1.tif" + + An Image MUST also have a geometry object (as its “geometry” member) of type “Rectangle”, indicating the shape of the image. + +### FeatureCollection Object + +A FeatureCollection object is a JSON object representing a collection of feature objects. A FeatureCollection object has a member with the name `"features"`. The value of `"features"` is a JSON array. Each element of the array is + + a Feature object as defined above. It is possible for this array to be empty. + +It MUST have the top-level property `"coordinates"`. The value of this property is a Coordinates object. + +#### Special FeatureCollection Objects + +- **StitchingVector**: Represents a stitching vector, and MUST have the following key-value pairs in its “properties” object: + + - `"type"`: A string with the value “StitchingVector” + + Any object of a StitchingVector “features” array MUST be an “Image” special type of features object. + +### Coordinates Object + +A coordinates object represents the choice of axes (2D or 3D) and potentially their scale. It MUST have the following properties: + +- `"axes"`: Representing the choice of axes. It MUST contain an array of length two from either of the following: `[“x“, ”y”, “z“]`, `[“r“, ”theta”, “z“]`, or `[“r“, ”theta”, “phi“]`. + +It MAY contain the following properties: + +- `"units"`: Representing the units of the corresponding axis in the axes property. It MUST be an array with the elements having any of the following values: `[“pixel“, “meter”, ”decimeter”, “centimeter“, “millimeter”, “micrometer”, “nanometer”, “picometer“, “radian“, “degree“]` + +- `"pixelsPerUnit"`: A decimal value, except for angles, where it SHOULD have the value “0”. + + diff --git a/docs/validation.md b/docs/validation.md new file mode 100644 index 0000000..46f7b5c --- /dev/null +++ b/docs/validation.md @@ -0,0 +1,3 @@ +# MicroJSON Pydantic validation + +To ensure the correctness and validity of MicroJSON files, we provide a tool that uses the Python Pydantic library to create a model of the MicroJSON schema, which forms the basis of the validation process. The validation works by comparing your MicroJSON files against the defined Pydantic model. It checks for adherence to the schema, ensuring that required fields are present and that the data types for all fields are as expected. The pydantic model is in the `microjson.py` file. It contains both a pydantic model for GeoJSON, `microjson.GeoJSON`, as well as MicroJSON, `microjson.MicroJSON` \ No newline at end of file diff --git a/examples/example.py b/examples/example.py new file mode 100644 index 0000000..1a8daf0 --- /dev/null +++ b/examples/example.py @@ -0,0 +1,27 @@ +from microjson import MicroJSON, GeoJSON +import json + +# load the microjson file +with open('examples/json/sample_microjson.json') as f: + data = json.load(f) + +# validate the microjson file +microjsonobj = MicroJSON.parse_obj(data) +print("done validating: {}".format(microjsonobj)) + +# load the geojson file +with open('examples/json/sample_geometrycollection.json') as f: + data = json.load(f) + +# validate the geojson file +geojsonobj = GeoJSON.parse_obj(data) + +print("done validating: {}".format(geojsonobj)) + +# load the microjson file +with open('examples/json/sample_geojson.json') as f: + data = json.load(f) + +# validate the microjson file +geojsonobj = GeoJSON.parse_obj(data) +print("done validating: {}".format(geojsonobj)) diff --git a/examples/json/sample_geojson.json b/examples/json/sample_geojson.json new file mode 100644 index 0000000..bb8cd6a --- /dev/null +++ b/examples/json/sample_geojson.json @@ -0,0 +1,50 @@ +{ + "type": "FeatureCollection", + "features": [ + + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + 20.522875, + 51.070292 + ] + }, + "properties": { + "id": 1, + "marker-size": "small", + "marker-color": "#f5a91a", + "marker-symbol": "b" + } + }, + { + "type": "Feature", + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [ + 20.522875, + 51.070292 + ], + [ + 20.522875, + 51.070292 + ], + [ + 20.522875, + 51.070292 + ], + [ + 20.522875, + 51.070292 + ] + ] + ] + ] + } + } + ] +} \ No newline at end of file diff --git a/examples/json/sample_geometrycollection.json b/examples/json/sample_geometrycollection.json new file mode 100644 index 0000000..e6d57fd --- /dev/null +++ b/examples/json/sample_geometrycollection.json @@ -0,0 +1,13 @@ +{ + "type": "GeometryCollection", + "geometries": [{ + "type": "Point", + "coordinates": [100.0, 0.0] + }, { + "type": "LineString", + "coordinates": [ + [101.0, 0.0], + [102.0, 1.0] + ] + }] +} \ No newline at end of file diff --git a/examples/json/sample_microjson.json b/examples/json/sample_microjson.json new file mode 100644 index 0000000..5dc19f6 --- /dev/null +++ b/examples/json/sample_microjson.json @@ -0,0 +1,67 @@ +{ + "coordinatesystem": { + "axes": [ + "x", + "y", + "z" + ], + "units": [ + "micrometer", + "micrometer", + "micrometer" + ], + "pixelsPerUnit": [ + 0.5, + 0.5, + 2 + ] + }, + "type": "FeatureCollection", + "features": [ + + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + 20.522875, + 51.070292 + ] + }, + "properties": { + "id": 1, + "marker-size": "small", + "marker-color": "#f5a91a", + "marker-symbol": "b" + } + }, + { + "type": "Feature", + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [ + 20.522875, + 51.070292 + ], + [ + 20.522875, + 51.070292 + ], + [ + 20.522875, + 51.070292 + ], + [ + 20.522875, + 51.070292 + ] + ] + ] + ] + } + } + ] +} \ No newline at end of file diff --git a/microjson/__init__.py b/microjson/__init__.py new file mode 100644 index 0000000..61ebb0c --- /dev/null +++ b/microjson/__init__.py @@ -0,0 +1 @@ +from .model import MicroJSON, GeoJSON \ No newline at end of file diff --git a/microjson/model.py b/microjson/model.py new file mode 100644 index 0000000..bcdb8b4 --- /dev/null +++ b/microjson/model.py @@ -0,0 +1,117 @@ + +from typing import List, Optional, Union, Dict, Literal +from enum import Enum +from pydantic import BaseModel, Field, conlist + + +Coordinates = conlist(float, min_items=2, max_items=3) + + +class Point(BaseModel): + type: str = Field("Point", description="Type, must be Point") + coordinates: Coordinates = Field(..., description="The coordinates of the point") + + +class MultiPoint(BaseModel): + type: str = Field("MultiPoint", description="Type, must be MultiPoint") + coordinates: List[Coordinates] = Field(..., description="The coordinates of the MultiPoint") + + +class LineString(BaseModel): + type: str = Field("LineString", description="Type, must be LineString") + coordinates: List[Coordinates] = Field(..., description="The coordinates of the LineString") + + +class MultiLineString(BaseModel): + type: str = Field("MultiLineString", description="Type, must be MultiLineString") + coordinates: List[List[Coordinates]] = Field(..., description="The coordinates of the MultiLineString") + + +class Polygon(BaseModel): + type: str = Field("Polygon", description="Type, must be Polygon") + coordinates: List[List[Coordinates]] = Field(..., description="The coordinates of the Polygon") + + +class MultiPolygon(BaseModel): + type: str = Field("MultiPolygon", description="Type, must be MultiPolygon") + coordinates: List[List[List[Coordinates]]] = Field(..., description="The coordinates of the MultiPolygon") + + +Geometry = Union[Point, + MultiPoint, + LineString, + MultiLineString, + Polygon, + MultiPolygon] + + +class GeometryCollection(BaseModel): + type: str = Field("GeometryCollection", description="The type of the object, must be GeometryCollection") + geometries: List[Geometry] = Field(..., description="The list of geometries in the collection") + + +class Feature(BaseModel): + type: str = Field("Feature", description="The type of the object, must be Feature") + geometry: Geometry = Field(..., description="The geometric data of the feature") + properties: Optional[Dict] = Field(None, description="The properties associated with the collection") + + +class FeatureCollection(BaseModel): + type: str = Field("FeatureCollection", description="The type of the object, must be FeatureCollection") + features: List[Feature] = Field(..., description="The list of features in the collection") + properties: Optional[Dict] = Field(None, description="The properties associated with the collection") + + +class GeoJSON(BaseModel): + """The root object of a GeoJSON file""" + __root__: Union[Feature, FeatureCollection, Geometry, GeometryCollection] + + +class Unit(Enum): + PIXEL = 'pixel' + METER = 'meter' + DECIMETER = 'decimeter' + CENTIMETER = 'centimeter' + MILLIMETER = 'millimeter' + MICROMETER = 'micrometer' + NANOMETER = 'nanometer' + PICOMETER = 'picometer' + RADIAN = 'radian' + DEGREE = 'degree' + + +class Coordinatesystem(BaseModel): + axes: List[Literal["x", "y", "z", "r", "theta", "phi"]] = Field(..., description="The coordinate system of the coordinates") + units: List[Unit] = Field(..., description="The units of the coordinates") + pixelsPerUnit: List[float] = Field(..., description="The number of pixels per unit") + + +class MicroFeature(BaseModel): + type: str + properties: Optional[Dict] + geometry: Geometry + coordinatesystem: Coordinatesystem + + +class MicroFeatureCollection(BaseModel): + type: str + features: List[Feature] + coordinatesystem: Coordinatesystem + + +class MicroGeometry(BaseModel): + geometry: Geometry + coordinatesystem: Coordinatesystem + + +class MicroGeometryCollection(BaseModel): + geometries: GeometryCollection + coordinatesystem: Coordinatesystem + + +class MicroJSON(BaseModel): + """The root object of a MicroJSON file""" + __root__: Union[MicroFeature, + MicroFeatureCollection, + MicroGeometry, + MicroGeometryCollection] diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..725ce53 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,7 @@ +site_name: MicroJSON specification +site_author: Bengt Ljungquist +copyright: (c) 2023 PolusAI +nav: + - Specification: index.md + - Examples: example.md + - Validation Tool: validation.md diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000..a419fe3 --- /dev/null +++ b/noxfile.py @@ -0,0 +1,18 @@ +"""Nox automation file.""" + +from nox import Session, session + +python_versions = ["3.9"] + + +@session(python=["3.9"]) +def export_ts(session: Session) -> None: + """Export Pydantic model as TypeScript object.""" + session.install("-r", "requirements-dev.txt") + session.run( + "pydantic2ts", + "--module", + "microjson.py", + "--output", + "microjsonschema.ts", + ) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..d213876 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,18 @@ +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry] +name = "microjson" +version = "0.1.0" +description = "MicroJSON is a library for validating, parsing, and manipulating MicroJSON data." +authors = ["Bengt Ljungquist "] +license = "MIT" + +[tool.poetry.dependencies] +python = "^3.9" +pydantic = "^1.8.2" + +[tool.poetry.dev-dependencies] +pytest = "^6.2.4" +nox = "^2021.6.12" diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..793f7e6 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,4 @@ +nox==2022.1.7 +pre-commit==2.15.0 +pydantic-to-typescript==1.0.8 +datamodel-code-generator==0.13.1 diff --git a/tests/test_microjson.py b/tests/test_microjson.py new file mode 100644 index 0000000..6bbfdd7 --- /dev/null +++ b/tests/test_microjson.py @@ -0,0 +1,50 @@ +import json +import pytest +from pydantic import ValidationError +from microjson import MicroJSON # Import your MicroJSON Pydantic model + + +def test_valid_microjson(): + # This is an example of a valid MicroJSON document + valid_microjson = { + "type": "FeatureCollection", + "coordinatesystem": { + "axes": ["x", "y", "z"], + "units": ["pixel", "pixel", "pixel"], + "pixelsPerUnit": [1.0, 1.0, 1.0] + }, + "features": [ + { + "type": "Feature", + "properties": { + "id": "1", + "type": "type1" + }, + "geometry": { + "type": "Point", + "coordinates": [1.0, 2.0, 3.0] + } + } + ], + "geometries": [] + } + + try: + valid_microjson_str = json.dumps(valid_microjson) + MicroJSON.parse_raw(valid_microjson_str) + except ValidationError: + pytest.fail("Valid MicroJSON raised ValidationError") + + +def test_invalid_microjson(): + # This is an example of an invalid MicroJSON document + invalid_microjson = { + "type": "FeatureCollection", + "coordinates": {"axes": ["invalid_axis", "y", "z"]}, + "features": [], + # Add all the required fields + } + with pytest.raises(ValidationError): + invalid_microjson_str = json.dumps(invalid_microjson) + MicroJSON.parse_raw(invalid_microjson_str) + From 6eb987892c5a835bfb8a25041252e734c8da2123 Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Mon, 5 Jun 2023 20:32:55 -0400 Subject: [PATCH 02/28] Added geojson validation, updated model --- external/Feature.json | 505 +++++++++++ external/FeatureCollection.json | 531 +++++++++++ external/Geometry.json | 218 +++++ external/GeometryCollection.json | 243 +++++ external/LineString.json | 36 + external/MultiLineString.json | 39 + external/MultiPoint.json | 35 + external/MultiPolygon.json | 42 + external/Point.json | 32 + external/Polygon.json | 39 + geojson/Feature.py | 132 +++ geojson/FeatureCollection.py | 142 +++ geojson/Geometry.py | 85 ++ geojson/GeometryCollection.py | 86 ++ geojson/LineString.py | 24 + geojson/MultiLineString.py | 24 + geojson/MultiPoint.py | 20 + geojson/MultiPolygon.py | 24 + geojson/Point.py | 20 + geojson/Polygon.py | 24 + geojson/__init__.py | 3 + geojson_schema.json | 418 +++++++++ microjson/__init__.py | 3 +- microjson/automodel.py | 103 +++ microjson/model.py | 159 ++-- microjson_schema.json | 854 ++++++++++++++++++ microjsonschema.ts | 250 +++++ noxfile.py | 35 +- tests/json/invalid/feature/1d-point.json | 13 + tests/json/invalid/feature/boolean-id.json | 11 + tests/json/invalid/feature/no-geometry.json | 7 + tests/json/invalid/feature/no-properties.json | 8 + tests/json/invalid/feature/no-type.json | 10 + tests/json/invalid/feature/object-id.json | 13 + .../featurecollection/no-features.json | 3 + .../invalid/featurecollection/no-type.json | 15 + tests/json/invalid/geometrycollection/1d.json | 32 + .../geometrycollection/bad-geometry.json | 32 + .../geometrycollection/no-geometries.json | 32 + .../invalid/geometrycollection/no-type.json | 31 + tests/json/invalid/linestring/1d.json | 4 + tests/json/invalid/linestring/bad-bbox.json | 5 + .../invalid/linestring/no-coordinates.json | 4 + tests/json/invalid/linestring/no-type.json | 4 + tests/json/invalid/multilinestring/1d.json | 7 + .../invalid/multilinestring/bad-bbox.json | 8 + .../multilinestring/no-coordinates.json | 7 + .../json/invalid/multilinestring/no-type.json | 6 + tests/json/invalid/multipoint/1d.json | 7 + tests/json/invalid/multipoint/bad-bbox.json | 8 + .../invalid/multipoint/no-coordinates.json | 7 + tests/json/invalid/multipoint/no-type.json | 7 + tests/json/invalid/multipolygon/1d.json | 30 + tests/json/invalid/multipolygon/bad-bbox.json | 31 + .../invalid/multipolygon/no-coordinates.json | 3 + tests/json/invalid/multipolygon/no-type.json | 30 + tests/json/invalid/point/1d.json | 4 + tests/json/invalid/point/bad-bbox.json | 5 + tests/json/invalid/point/no-coordinates.json | 3 + tests/json/invalid/point/no-type.json | 3 + tests/json/invalid/polygon/1d.json | 19 + tests/json/invalid/polygon/bad-type.json | 19 + .../json/invalid/polygon/no-coordinates.json | 3 + tests/json/invalid/polygon/no-type.json | 18 + tests/json/valid/feature/basic.json | 11 + tests/json/valid/feature/empty-polygon.json | 9 + tests/json/valid/feature/null-geometry.json | 8 + tests/json/valid/feature/with-number-id.json | 11 + tests/json/valid/feature/with-string-id.json | 11 + tests/json/valid/featurecollection/basic.json | 16 + tests/json/valid/featurecollection/empty.json | 4 + tests/json/valid/geometrycollection/3d.json | 32 + .../json/valid/geometrycollection/basic.json | 32 + tests/json/valid/geometrycollection/bbox.json | 33 + tests/json/valid/linestring/3d.json | 4 + tests/json/valid/linestring/basic.json | 4 + tests/json/valid/linestring/bbox.json | 5 + tests/json/valid/multilinestring/3d.json | 7 + tests/json/valid/multilinestring/basic.json | 7 + tests/json/valid/multilinestring/bbox.json | 8 + tests/json/valid/multipoint/3d.json | 7 + tests/json/valid/multipoint/basic.json | 7 + tests/json/valid/multipoint/bbox.json | 8 + tests/json/valid/multipolygon/3d.json | 30 + tests/json/valid/multipolygon/basic.json | 30 + tests/json/valid/multipolygon/bbox.json | 31 + tests/json/valid/point/3d.json | 4 + tests/json/valid/point/basic.json | 4 + tests/json/valid/point/bbox.json | 5 + tests/json/valid/point/extended.json | 5 + tests/json/valid/polygon/3d.json | 19 + tests/json/valid/polygon/basic.json | 19 + tests/json/valid/polygon/bbox.json | 20 + tests/json/valid/polygon/extended.json | 20 + tests/test_microjson _auto.py | 95 ++ tests/test_microjson.py | 127 ++- 96 files changed, 5093 insertions(+), 115 deletions(-) create mode 100644 external/Feature.json create mode 100644 external/FeatureCollection.json create mode 100644 external/Geometry.json create mode 100644 external/GeometryCollection.json create mode 100644 external/LineString.json create mode 100644 external/MultiLineString.json create mode 100644 external/MultiPoint.json create mode 100644 external/MultiPolygon.json create mode 100644 external/Point.json create mode 100644 external/Polygon.json create mode 100644 geojson/Feature.py create mode 100644 geojson/FeatureCollection.py create mode 100644 geojson/Geometry.py create mode 100644 geojson/GeometryCollection.py create mode 100644 geojson/LineString.py create mode 100644 geojson/MultiLineString.py create mode 100644 geojson/MultiPoint.py create mode 100644 geojson/MultiPolygon.py create mode 100644 geojson/Point.py create mode 100644 geojson/Polygon.py create mode 100644 geojson/__init__.py create mode 100644 geojson_schema.json create mode 100644 microjson/automodel.py create mode 100644 microjson_schema.json create mode 100644 microjsonschema.ts create mode 100644 tests/json/invalid/feature/1d-point.json create mode 100644 tests/json/invalid/feature/boolean-id.json create mode 100644 tests/json/invalid/feature/no-geometry.json create mode 100644 tests/json/invalid/feature/no-properties.json create mode 100644 tests/json/invalid/feature/no-type.json create mode 100644 tests/json/invalid/feature/object-id.json create mode 100644 tests/json/invalid/featurecollection/no-features.json create mode 100644 tests/json/invalid/featurecollection/no-type.json create mode 100644 tests/json/invalid/geometrycollection/1d.json create mode 100644 tests/json/invalid/geometrycollection/bad-geometry.json create mode 100644 tests/json/invalid/geometrycollection/no-geometries.json create mode 100644 tests/json/invalid/geometrycollection/no-type.json create mode 100644 tests/json/invalid/linestring/1d.json create mode 100644 tests/json/invalid/linestring/bad-bbox.json create mode 100644 tests/json/invalid/linestring/no-coordinates.json create mode 100644 tests/json/invalid/linestring/no-type.json create mode 100644 tests/json/invalid/multilinestring/1d.json create mode 100644 tests/json/invalid/multilinestring/bad-bbox.json create mode 100644 tests/json/invalid/multilinestring/no-coordinates.json create mode 100644 tests/json/invalid/multilinestring/no-type.json create mode 100644 tests/json/invalid/multipoint/1d.json create mode 100644 tests/json/invalid/multipoint/bad-bbox.json create mode 100644 tests/json/invalid/multipoint/no-coordinates.json create mode 100644 tests/json/invalid/multipoint/no-type.json create mode 100644 tests/json/invalid/multipolygon/1d.json create mode 100644 tests/json/invalid/multipolygon/bad-bbox.json create mode 100644 tests/json/invalid/multipolygon/no-coordinates.json create mode 100644 tests/json/invalid/multipolygon/no-type.json create mode 100644 tests/json/invalid/point/1d.json create mode 100644 tests/json/invalid/point/bad-bbox.json create mode 100644 tests/json/invalid/point/no-coordinates.json create mode 100644 tests/json/invalid/point/no-type.json create mode 100644 tests/json/invalid/polygon/1d.json create mode 100644 tests/json/invalid/polygon/bad-type.json create mode 100644 tests/json/invalid/polygon/no-coordinates.json create mode 100644 tests/json/invalid/polygon/no-type.json create mode 100644 tests/json/valid/feature/basic.json create mode 100644 tests/json/valid/feature/empty-polygon.json create mode 100644 tests/json/valid/feature/null-geometry.json create mode 100644 tests/json/valid/feature/with-number-id.json create mode 100644 tests/json/valid/feature/with-string-id.json create mode 100644 tests/json/valid/featurecollection/basic.json create mode 100644 tests/json/valid/featurecollection/empty.json create mode 100644 tests/json/valid/geometrycollection/3d.json create mode 100644 tests/json/valid/geometrycollection/basic.json create mode 100644 tests/json/valid/geometrycollection/bbox.json create mode 100644 tests/json/valid/linestring/3d.json create mode 100644 tests/json/valid/linestring/basic.json create mode 100644 tests/json/valid/linestring/bbox.json create mode 100644 tests/json/valid/multilinestring/3d.json create mode 100644 tests/json/valid/multilinestring/basic.json create mode 100644 tests/json/valid/multilinestring/bbox.json create mode 100644 tests/json/valid/multipoint/3d.json create mode 100644 tests/json/valid/multipoint/basic.json create mode 100644 tests/json/valid/multipoint/bbox.json create mode 100644 tests/json/valid/multipolygon/3d.json create mode 100644 tests/json/valid/multipolygon/basic.json create mode 100644 tests/json/valid/multipolygon/bbox.json create mode 100644 tests/json/valid/point/3d.json create mode 100644 tests/json/valid/point/basic.json create mode 100644 tests/json/valid/point/bbox.json create mode 100644 tests/json/valid/point/extended.json create mode 100644 tests/json/valid/polygon/3d.json create mode 100644 tests/json/valid/polygon/basic.json create mode 100644 tests/json/valid/polygon/bbox.json create mode 100644 tests/json/valid/polygon/extended.json create mode 100644 tests/test_microjson _auto.py diff --git a/external/Feature.json b/external/Feature.json new file mode 100644 index 0000000..30151f5 --- /dev/null +++ b/external/Feature.json @@ -0,0 +1,505 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://geojson.org/schema/Feature.json", + "title": "GeoJSON Feature", + "type": "object", + "required": [ + "type", + "properties", + "geometry" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Feature" + ] + }, + "id": { + "oneOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "properties": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "object" + } + ] + }, + "geometry": { + "oneOf": [ + { + "type": "null" + }, + { + "title": "GeoJSON Point", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Point" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON LineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "LineString" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON Polygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Polygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPoint", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPoint" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiLineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiLineString" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPolygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPolygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON GeometryCollection", + "type": "object", + "required": [ + "type", + "geometries" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "GeometryCollection" + ] + }, + "geometries": { + "type": "array", + "items": { + "oneOf": [ + { + "title": "GeoJSON Point", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Point" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON LineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "LineString" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON Polygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Polygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPoint", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPoint" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiLineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiLineString" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPolygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPolygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + ] + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + ] + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } +} diff --git a/external/FeatureCollection.json b/external/FeatureCollection.json new file mode 100644 index 0000000..c8d4e1b --- /dev/null +++ b/external/FeatureCollection.json @@ -0,0 +1,531 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://geojson.org/schema/FeatureCollection.json", + "title": "GeoJSON FeatureCollection", + "type": "object", + "required": [ + "type", + "features" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "FeatureCollection" + ] + }, + "features": { + "type": "array", + "items": { + "title": "GeoJSON Feature", + "type": "object", + "required": [ + "type", + "properties", + "geometry" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Feature" + ] + }, + "id": { + "oneOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "properties": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "object" + } + ] + }, + "geometry": { + "oneOf": [ + { + "type": "null" + }, + { + "title": "GeoJSON Point", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Point" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON LineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "LineString" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON Polygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Polygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPoint", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPoint" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiLineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiLineString" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPolygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPolygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON GeometryCollection", + "type": "object", + "required": [ + "type", + "geometries" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "GeometryCollection" + ] + }, + "geometries": { + "type": "array", + "items": { + "oneOf": [ + { + "title": "GeoJSON Point", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Point" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON LineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "LineString" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON Polygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Polygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPoint", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPoint" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiLineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiLineString" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPolygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPolygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + ] + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + ] + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } +} diff --git a/external/Geometry.json b/external/Geometry.json new file mode 100644 index 0000000..de56716 --- /dev/null +++ b/external/Geometry.json @@ -0,0 +1,218 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://geojson.org/schema/Geometry.json", + "title": "GeoJSON Geometry", + "oneOf": [ + { + "title": "GeoJSON Point", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Point" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON LineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "LineString" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON Polygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Polygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPoint", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPoint" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiLineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiLineString" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPolygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPolygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + ] +} diff --git a/external/GeometryCollection.json b/external/GeometryCollection.json new file mode 100644 index 0000000..92aabc4 --- /dev/null +++ b/external/GeometryCollection.json @@ -0,0 +1,243 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://geojson.org/schema/GeometryCollection.json", + "title": "GeoJSON GeometryCollection", + "type": "object", + "required": [ + "type", + "geometries" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "GeometryCollection" + ] + }, + "geometries": { + "type": "array", + "items": { + "oneOf": [ + { + "title": "GeoJSON Point", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Point" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON LineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "LineString" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON Polygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Polygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPoint", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPoint" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiLineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiLineString" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPolygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPolygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + ] + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } +} diff --git a/external/LineString.json b/external/LineString.json new file mode 100644 index 0000000..3c0b3e6 --- /dev/null +++ b/external/LineString.json @@ -0,0 +1,36 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://geojson.org/schema/LineString.json", + "title": "GeoJSON LineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "LineString" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } +} diff --git a/external/MultiLineString.json b/external/MultiLineString.json new file mode 100644 index 0000000..2b68e45 --- /dev/null +++ b/external/MultiLineString.json @@ -0,0 +1,39 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://geojson.org/schema/MultiLineString.json", + "title": "GeoJSON MultiLineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiLineString" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } +} diff --git a/external/MultiPoint.json b/external/MultiPoint.json new file mode 100644 index 0000000..490fcdc --- /dev/null +++ b/external/MultiPoint.json @@ -0,0 +1,35 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://geojson.org/schema/MultiPoint.json", + "title": "GeoJSON MultiPoint", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPoint" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } +} diff --git a/external/MultiPolygon.json b/external/MultiPolygon.json new file mode 100644 index 0000000..ac581c7 --- /dev/null +++ b/external/MultiPolygon.json @@ -0,0 +1,42 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://geojson.org/schema/MultiPolygon.json", + "title": "GeoJSON MultiPolygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPolygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } +} diff --git a/external/Point.json b/external/Point.json new file mode 100644 index 0000000..09b45ea --- /dev/null +++ b/external/Point.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://geojson.org/schema/Point.json", + "title": "GeoJSON Point", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Point" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } +} diff --git a/external/Polygon.json b/external/Polygon.json new file mode 100644 index 0000000..1e3b49b --- /dev/null +++ b/external/Polygon.json @@ -0,0 +1,39 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://geojson.org/schema/Polygon.json", + "title": "GeoJSON Polygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Polygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } +} diff --git a/geojson/Feature.py b/geojson/Feature.py new file mode 100644 index 0000000..6b72476 --- /dev/null +++ b/geojson/Feature.py @@ -0,0 +1,132 @@ +# generated by datamodel-codegen: +# filename: Feature.json +# timestamp: 2023-06-01T19:31:21+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import Any, Dict, List, Optional, Union + +from pydantic import BaseModel, Field + + +class Type(Enum): + feature = 'Feature' + + +class Type34(Enum): + point = 'Point' + + +class GeometryItem(BaseModel): + type: Type34 + coordinates: List[float] = Field(..., min_items=2) + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type35(Enum): + line_string = 'LineString' + + +class Coordinate(BaseModel): + __root__: List[Any] + + +class GeometryItem8(BaseModel): + type: Type35 + coordinates: List[Coordinate] = Field(..., min_items=2) + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type36(Enum): + polygon = 'Polygon' + + +class GeometryItem9(BaseModel): + type: Type36 + coordinates: List[List[Coordinate]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type37(Enum): + multi_point = 'MultiPoint' + + +class GeometryItem10(BaseModel): + type: Type37 + coordinates: List[List[float]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type38(Enum): + multi_line_string = 'MultiLineString' + + +class GeometryItem11(BaseModel): + type: Type38 + coordinates: List[List[Coordinate]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type39(Enum): + multi_polygon = 'MultiPolygon' + + +class GeometryItem12(BaseModel): + type: Type39 + coordinates: List[List[List[Coordinate]]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type40(Enum): + geometry_collection = 'GeometryCollection' + + +class Geometry(GeometryItem): + pass + + +class Geometry13(GeometryItem8): + pass + + +class Geometry14(GeometryItem9): + pass + + +class Geometry15(GeometryItem10): + pass + + +class Geometry16(GeometryItem11): + pass + + +class Geometry17(GeometryItem12): + pass + + +class GeometryItem13(BaseModel): + type: Type40 + geometries: List[ + Union[Geometry, Geometry13, Geometry14, Geometry15, Geometry16, Geometry17] + ] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class GeojsonFeature(BaseModel): + type: Type + id: Optional[Union[float, str]] = None + properties: Optional[Dict[str, Any]] + geometry: Optional[ + Union[ + GeometryItem, + GeometryItem8, + GeometryItem9, + GeometryItem10, + GeometryItem11, + GeometryItem12, + GeometryItem13, + ] + ] + bbox: Optional[List[float]] = Field(None, min_items=4) diff --git a/geojson/FeatureCollection.py b/geojson/FeatureCollection.py new file mode 100644 index 0000000..9c0ee20 --- /dev/null +++ b/geojson/FeatureCollection.py @@ -0,0 +1,142 @@ +# generated by datamodel-codegen: +# filename: FeatureCollection.json +# timestamp: 2023-06-01T19:31:21+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import Any, Dict, List, Optional, Union + +from pydantic import BaseModel, Field + + +class Type(Enum): + feature_collection = 'FeatureCollection' + + +class Type12(Enum): + feature = 'Feature' + + +class Type13(Enum): + point = 'Point' + + +class GeometryItem(BaseModel): + type: Type13 + coordinates: List[float] = Field(..., min_items=2) + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type14(Enum): + line_string = 'LineString' + + +class Coordinate(BaseModel): + __root__: List[Any] + + +class GeometryItem1(BaseModel): + type: Type14 + coordinates: List[Coordinate] = Field(..., min_items=2) + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type15(Enum): + polygon = 'Polygon' + + +class GeometryItem2(BaseModel): + type: Type15 + coordinates: List[List[Coordinate]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type16(Enum): + multi_point = 'MultiPoint' + + +class GeometryItem3(BaseModel): + type: Type16 + coordinates: List[List[float]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type17(Enum): + multi_line_string = 'MultiLineString' + + +class GeometryItem4(BaseModel): + type: Type17 + coordinates: List[List[Coordinate]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type18(Enum): + multi_polygon = 'MultiPolygon' + + +class GeometryItem5(BaseModel): + type: Type18 + coordinates: List[List[List[Coordinate]]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type19(Enum): + geometry_collection = 'GeometryCollection' + + +class Geometry(GeometryItem): + pass + + +class Geometry7(GeometryItem1): + pass + + +class Geometry8(GeometryItem2): + pass + + +class Geometry9(GeometryItem3): + pass + + +class Geometry10(GeometryItem4): + pass + + +class Geometry11(GeometryItem5): + pass + + +class GeometryItem6(BaseModel): + type: Type19 + geometries: List[ + Union[Geometry, Geometry7, Geometry8, Geometry9, Geometry10, Geometry11] + ] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Feature(BaseModel): + type: Type12 + id: Optional[Union[float, str]] = None + properties: Optional[Dict[str, Any]] + geometry: Optional[ + Union[ + GeometryItem, + GeometryItem1, + GeometryItem2, + GeometryItem3, + GeometryItem4, + GeometryItem5, + GeometryItem6, + ] + ] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class GeojsonFeaturecollection(BaseModel): + type: Type + features: List[Feature] + bbox: Optional[List[float]] = Field(None, min_items=4) diff --git a/geojson/Geometry.py b/geojson/Geometry.py new file mode 100644 index 0000000..bbf429e --- /dev/null +++ b/geojson/Geometry.py @@ -0,0 +1,85 @@ +# generated by datamodel-codegen: +# filename: Geometry.json +# timestamp: 2023-06-01T19:31:21+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import Any, List, Optional, Union + +from pydantic import BaseModel, Field + + +class Type(Enum): + point = 'Point' + + +class GeojsonGeometryItem(BaseModel): + type: Type + coordinates: List[float] = Field(..., min_items=2) + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type27(Enum): + line_string = 'LineString' + + +class Coordinate(BaseModel): + __root__: List[Any] + + +class GeojsonGeometryItem1(BaseModel): + type: Type27 + coordinates: List[Coordinate] = Field(..., min_items=2) + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type28(Enum): + polygon = 'Polygon' + + +class GeojsonGeometryItem2(BaseModel): + type: Type28 + coordinates: List[List[Coordinate]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type29(Enum): + multi_point = 'MultiPoint' + + +class GeojsonGeometryItem3(BaseModel): + type: Type29 + coordinates: List[List[float]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type30(Enum): + multi_line_string = 'MultiLineString' + + +class GeojsonGeometryItem4(BaseModel): + type: Type30 + coordinates: List[List[Coordinate]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type31(Enum): + multi_polygon = 'MultiPolygon' + + +class GeojsonGeometryItem5(BaseModel): + type: Type31 + coordinates: List[List[List[Coordinate]]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class GeojsonGeometry(BaseModel): + __root__: Union[ + GeojsonGeometryItem, + GeojsonGeometryItem1, + GeojsonGeometryItem2, + GeojsonGeometryItem3, + GeojsonGeometryItem4, + GeojsonGeometryItem5, + ] = Field(..., title='GeoJSON Geometry') diff --git a/geojson/GeometryCollection.py b/geojson/GeometryCollection.py new file mode 100644 index 0000000..536d0c1 --- /dev/null +++ b/geojson/GeometryCollection.py @@ -0,0 +1,86 @@ +# generated by datamodel-codegen: +# filename: GeometryCollection.json +# timestamp: 2023-06-01T19:31:21+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import Any, List, Optional, Union + +from pydantic import BaseModel, Field + + +class Type(Enum): + geometry_collection = 'GeometryCollection' + + +class Type4(Enum): + point = 'Point' + + +class Geometry(BaseModel): + type: Type4 + coordinates: List[float] = Field(..., min_items=2) + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type5(Enum): + line_string = 'LineString' + + +class Coordinate(BaseModel): + __root__: List[Any] + + +class Geometry1(BaseModel): + type: Type5 + coordinates: List[Coordinate] = Field(..., min_items=2) + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type6(Enum): + polygon = 'Polygon' + + +class Geometry2(BaseModel): + type: Type6 + coordinates: List[List[Coordinate]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type7(Enum): + multi_point = 'MultiPoint' + + +class Geometry3(BaseModel): + type: Type7 + coordinates: List[List[float]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type8(Enum): + multi_line_string = 'MultiLineString' + + +class Geometry4(BaseModel): + type: Type8 + coordinates: List[List[Coordinate]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Type9(Enum): + multi_polygon = 'MultiPolygon' + + +class Geometry5(BaseModel): + type: Type9 + coordinates: List[List[List[Coordinate]]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class GeojsonGeometrycollection(BaseModel): + type: Type + geometries: List[ + Union[Geometry, Geometry1, Geometry2, Geometry3, Geometry4, Geometry5] + ] + bbox: Optional[List[float]] = Field(None, min_items=4) diff --git a/geojson/LineString.py b/geojson/LineString.py new file mode 100644 index 0000000..116059b --- /dev/null +++ b/geojson/LineString.py @@ -0,0 +1,24 @@ +# generated by datamodel-codegen: +# filename: LineString.json +# timestamp: 2023-06-01T19:31:21+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import Any, List, Optional + +from pydantic import BaseModel, Field + + +class Type(Enum): + line_string = 'LineString' + + +class Coordinate(BaseModel): + __root__: List[Any] + + +class GeojsonLinestring(BaseModel): + type: Type + coordinates: List[Coordinate] = Field(..., min_items=2) + bbox: Optional[List[float]] = Field(None, min_items=4) diff --git a/geojson/MultiLineString.py b/geojson/MultiLineString.py new file mode 100644 index 0000000..1e95ba2 --- /dev/null +++ b/geojson/MultiLineString.py @@ -0,0 +1,24 @@ +# generated by datamodel-codegen: +# filename: MultiLineString.json +# timestamp: 2023-06-01T19:31:21+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import Any, List, Optional + +from pydantic import BaseModel, Field + + +class Type(Enum): + multi_line_string = 'MultiLineString' + + +class Coordinate(BaseModel): + __root__: List[Any] + + +class GeojsonMultilinestring(BaseModel): + type: Type + coordinates: List[List[Coordinate]] + bbox: Optional[List[float]] = Field(None, min_items=4) diff --git a/geojson/MultiPoint.py b/geojson/MultiPoint.py new file mode 100644 index 0000000..77f2245 --- /dev/null +++ b/geojson/MultiPoint.py @@ -0,0 +1,20 @@ +# generated by datamodel-codegen: +# filename: MultiPoint.json +# timestamp: 2023-06-01T19:31:21+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import List, Optional + +from pydantic import BaseModel, Field + + +class Type(Enum): + multi_point = 'MultiPoint' + + +class GeojsonMultipoint(BaseModel): + type: Type + coordinates: List[List[float]] + bbox: Optional[List[float]] = Field(None, min_items=4) diff --git a/geojson/MultiPolygon.py b/geojson/MultiPolygon.py new file mode 100644 index 0000000..8aec150 --- /dev/null +++ b/geojson/MultiPolygon.py @@ -0,0 +1,24 @@ +# generated by datamodel-codegen: +# filename: MultiPolygon.json +# timestamp: 2023-06-01T19:31:21+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import Any, List, Optional + +from pydantic import BaseModel, Field + + +class Type(Enum): + multi_polygon = 'MultiPolygon' + + +class Coordinate(BaseModel): + __root__: List[Any] + + +class GeojsonMultipolygon(BaseModel): + type: Type + coordinates: List[List[List[Coordinate]]] + bbox: Optional[List[float]] = Field(None, min_items=4) diff --git a/geojson/Point.py b/geojson/Point.py new file mode 100644 index 0000000..0ce9115 --- /dev/null +++ b/geojson/Point.py @@ -0,0 +1,20 @@ +# generated by datamodel-codegen: +# filename: Point.json +# timestamp: 2023-06-01T19:31:21+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import List, Optional + +from pydantic import BaseModel, Field + + +class Type(Enum): + point = 'Point' + + +class GeojsonPoint(BaseModel): + type: Type + coordinates: List[float] = Field(..., min_items=2) + bbox: Optional[List[float]] = Field(None, min_items=4) diff --git a/geojson/Polygon.py b/geojson/Polygon.py new file mode 100644 index 0000000..faf2c53 --- /dev/null +++ b/geojson/Polygon.py @@ -0,0 +1,24 @@ +# generated by datamodel-codegen: +# filename: Polygon.json +# timestamp: 2023-06-01T19:31:21+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import Any, List, Optional + +from pydantic import BaseModel, Field + + +class Type(Enum): + polygon = 'Polygon' + + +class Coordinate(BaseModel): + __root__: List[Any] + + +class GeojsonPolygon(BaseModel): + type: Type + coordinates: List[List[Coordinate]] + bbox: Optional[List[float]] = Field(None, min_items=4) diff --git a/geojson/__init__.py b/geojson/__init__.py new file mode 100644 index 0000000..8c00cef --- /dev/null +++ b/geojson/__init__.py @@ -0,0 +1,3 @@ +# generated by datamodel-codegen: +# filename: external +# timestamp: 2023-06-01T19:31:21+00:00 diff --git a/geojson_schema.json b/geojson_schema.json new file mode 100644 index 0000000..075833f --- /dev/null +++ b/geojson_schema.json @@ -0,0 +1,418 @@ +{ + "title": "GeoJSON", + "description": "The root object of a GeoJSON file", + "anyOf": [ + { + "$ref": "#/definitions/Feature" + }, + { + "$ref": "#/definitions/FeatureCollection" + }, + { + "$ref": "#/definitions/Point" + }, + { + "$ref": "#/definitions/MultiPoint" + }, + { + "$ref": "#/definitions/LineString" + }, + { + "$ref": "#/definitions/MultiLineString" + }, + { + "$ref": "#/definitions/Polygon" + }, + { + "$ref": "#/definitions/MultiPolygon" + }, + { + "$ref": "#/definitions/GeometryCollection" + } + ], + "definitions": { + "Point": { + "title": "Point", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "Point" + ], + "type": "string" + }, + "coordinates": { + "title": "Coordinates", + "minItems": 2, + "maxItems": 3, + "type": "array", + "items": { + "type": "number" + } + } + }, + "required": [ + "type", + "coordinates" + ] + }, + "MultiPoint": { + "title": "MultiPoint", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "MultiPoint" + ], + "type": "string" + }, + "coordinates": { + "title": "Coordinates", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 3 + } + } + }, + "required": [ + "type", + "coordinates" + ] + }, + "LineString": { + "title": "LineString", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "LineString" + ], + "type": "string" + }, + "coordinates": { + "title": "Coordinates", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 3 + } + } + }, + "required": [ + "type", + "coordinates" + ] + }, + "MultiLineString": { + "title": "MultiLineString", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "MultiLineString" + ], + "type": "string" + }, + "coordinates": { + "title": "Coordinates", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 3 + } + } + } + }, + "required": [ + "type", + "coordinates" + ] + }, + "Polygon": { + "title": "Polygon", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "Polygon" + ], + "type": "string" + }, + "coordinates": { + "title": "Coordinates", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 3 + } + } + } + }, + "required": [ + "type", + "coordinates" + ] + }, + "MultiPolygon": { + "title": "MultiPolygon", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "MultiPolygon" + ], + "type": "string" + }, + "coordinates": { + "title": "Coordinates", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 3 + } + } + } + } + }, + "required": [ + "type", + "coordinates" + ] + }, + "GeometryCollection": { + "title": "GeometryCollection", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "GeometryCollection" + ], + "type": "string" + }, + "geometries": { + "title": "Geometries", + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/Point" + }, + { + "$ref": "#/definitions/MultiPoint" + }, + { + "$ref": "#/definitions/LineString" + }, + { + "$ref": "#/definitions/MultiLineString" + }, + { + "$ref": "#/definitions/Polygon" + }, + { + "$ref": "#/definitions/MultiPolygon" + } + ] + } + } + }, + "required": [ + "type", + "geometries" + ] + }, + "Feature": { + "title": "Feature", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "Feature" + ], + "type": "string" + }, + "geometry": { + "title": "Geometry", + "description": "The geometric data\n of the feature", + "anyOf": [ + { + "$ref": "#/definitions/Point" + }, + { + "$ref": "#/definitions/MultiPoint" + }, + { + "$ref": "#/definitions/LineString" + }, + { + "$ref": "#/definitions/MultiLineString" + }, + { + "$ref": "#/definitions/Polygon" + }, + { + "$ref": "#/definitions/MultiPolygon" + }, + { + "$ref": "#/definitions/GeometryCollection" + } + ] + }, + "properties": { + "title": "Properties", + "description": "Properties of the\n feature", + "type": "object" + }, + "id": { + "title": "Id", + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + } + }, + "required": [ + "type", + "geometry", + "properties" + ] + }, + "FeatureCollection": { + "title": "FeatureCollection", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "FeatureCollection" + ], + "type": "string" + }, + "features": { + "title": "Features", + "type": "array", + "items": { + "$ref": "#/definitions/Feature" + } + } + }, + "required": [ + "type", + "features" + ] + } + } +} \ No newline at end of file diff --git a/microjson/__init__.py b/microjson/__init__.py index 61ebb0c..c52b1cb 100644 --- a/microjson/__init__.py +++ b/microjson/__init__.py @@ -1 +1,2 @@ -from .model import MicroJSON, GeoJSON \ No newline at end of file +from .model import MicroJSON, GeoJSON +from .automodel import GeoJSONAuto, MicroJSONAuto \ No newline at end of file diff --git a/microjson/automodel.py b/microjson/automodel.py new file mode 100644 index 0000000..de56774 --- /dev/null +++ b/microjson/automodel.py @@ -0,0 +1,103 @@ +from typing import List, Optional, Union, Dict, Literal +from enum import Enum +from pydantic import BaseModel, Field, StrictInt, StrictStr +from geojson.Feature import GeojsonFeature +from geojson.FeatureCollection import GeojsonFeaturecollection +from geojson.Geometry import GeojsonGeometry +from geojson.GeometryCollection import GeojsonGeometrycollection +from geojson.LineString import GeojsonLinestring +from geojson.MultiLineString import GeojsonMultilinestring +from geojson.MultiPoint import GeojsonMultipoint +from geojson.MultiPolygon import GeojsonMultipolygon +from geojson.Point import GeojsonPoint +from geojson.Polygon import GeojsonPolygon + + +class GeoJSONAuto(BaseModel): + __root__: Union[ + GeojsonFeature, + GeojsonFeaturecollection, + GeojsonGeometry, + GeojsonGeometrycollection, + ] + + +class Unit(Enum): + PIXEL = "pixel" + METER = "meter" + DECIMETER = "decimeter" + CENTIMETER = "centimeter" + MILLIMETER = "millimeter" + MICROMETER = "micrometer" + NANOMETER = "nanometer" + PICOMETER = "picometer" + RADIAN = "radian" + DEGREE = "degree" + + +class Coordinatesystem(BaseModel): + axes: List[Literal["x", "y", "z", "r", "theta", "phi"]] = Field( + ..., description="The coordinate system of the coordinates" + ) + units: List[Unit] = Field(..., description="The units of the coordinates") + pixelsPerUnit: List[float] = Field( + ..., description="The number of pixels per unit" + ) + + +class MicroFeature(BaseModel): + type: Literal["Feature"] + properties: Optional[Dict] + geometry: Optional[GeojsonGeometry] + coordinatesystem: Optional[Coordinatesystem] + id: Optional[Union[StrictStr, StrictInt]] + + +class MicroFeatureCollection(BaseModel): + type: Literal["FeatureCollection"] + features: List[GeojsonFeature] + coordinatesystem: Optional[Coordinatesystem] + + +class MicroPoint(GeojsonPoint): + coordinatesystem: Optional[Coordinatesystem] + + +class MicroMultiPoint(GeojsonMultipoint): + coordinatesystem: Optional[Coordinatesystem] + + +class MicroLineString(GeojsonLinestring): + coordinatesystem: Optional[Coordinatesystem] + + +class MicroMultiLineString(GeojsonMultilinestring): + coordinatesystem: Optional[Coordinatesystem] + + +class MicroPolygon(GeojsonPolygon): + coordinatesystem: Optional[Coordinatesystem] + + +class MicroMultiPolygon(GeojsonMultipolygon): + coordinatesystem: Optional[Coordinatesystem] + + +class MicroGeometryCollection(GeojsonGeometrycollection): + coordinatesystem: Optional[Coordinatesystem] + + +MicroGeometryType = Union[MicroPoint, + MicroMultiPoint, + MicroLineString, + MicroMultiLineString, + MicroPolygon, + MicroMultiPolygon, + MicroGeometryCollection] + + +class MicroJSONAuto(BaseModel): + """The root object of a MicroJSON file""" + __root__: Union[MicroFeature, + MicroFeatureCollection, + MicroGeometryType] diff --git a/microjson/model.py b/microjson/model.py index bcdb8b4..12687d8 100644 --- a/microjson/model.py +++ b/microjson/model.py @@ -1,70 +1,86 @@ - from typing import List, Optional, Union, Dict, Literal from enum import Enum -from pydantic import BaseModel, Field, conlist +from pydantic import BaseModel, Field, StrictInt, StrictStr, conlist Coordinates = conlist(float, min_items=2, max_items=3) -class Point(BaseModel): - type: str = Field("Point", description="Type, must be Point") - coordinates: Coordinates = Field(..., description="The coordinates of the point") +class GeoAbstract(BaseModel): + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Point(GeoAbstract): + type: Literal["Point"] + coordinates: Coordinates + + +class MultiPoint(GeoAbstract): + type: Literal["MultiPoint"] + coordinates: List[Coordinates] -class MultiPoint(BaseModel): - type: str = Field("MultiPoint", description="Type, must be MultiPoint") - coordinates: List[Coordinates] = Field(..., description="The coordinates of the MultiPoint") +class LineString(GeoAbstract): + type: Literal["LineString"] + coordinates: List[Coordinates] -class LineString(BaseModel): - type: str = Field("LineString", description="Type, must be LineString") - coordinates: List[Coordinates] = Field(..., description="The coordinates of the LineString") +class MultiLineString(GeoAbstract): + type: Literal["MultiLineString"] + coordinates: List[List[Coordinates]] -class MultiLineString(BaseModel): - type: str = Field("MultiLineString", description="Type, must be MultiLineString") - coordinates: List[List[Coordinates]] = Field(..., description="The coordinates of the MultiLineString") +class Polygon(GeoAbstract): + type: Literal["Polygon"] + coordinates: List[List[Coordinates]] -class Polygon(BaseModel): - type: str = Field("Polygon", description="Type, must be Polygon") - coordinates: List[List[Coordinates]] = Field(..., description="The coordinates of the Polygon") +class MultiPolygon(GeoAbstract): + type: Literal["MultiPolygon"] + coordinates: List[List[List[Coordinates]]] -class MultiPolygon(BaseModel): - type: str = Field("MultiPolygon", description="Type, must be MultiPolygon") - coordinates: List[List[List[Coordinates]]] = Field(..., description="The coordinates of the MultiPolygon") +GeometryBaseType = Union[Point, + MultiPoint, + LineString, + MultiLineString, + Polygon, + MultiPolygon] -Geometry = Union[Point, - MultiPoint, - LineString, - MultiLineString, - Polygon, - MultiPolygon] +class GeometryCollection(GeoAbstract): + type: Literal["GeometryCollection"] + geometries: List[GeometryBaseType] -class GeometryCollection(BaseModel): - type: str = Field("GeometryCollection", description="The type of the object, must be GeometryCollection") - geometries: List[Geometry] = Field(..., description="The list of geometries in the collection") +GeometryType = Union[Point, + MultiPoint, + LineString, + MultiLineString, + Polygon, + MultiPolygon, + GeometryCollection] -class Feature(BaseModel): - type: str = Field("Feature", description="The type of the object, must be Feature") - geometry: Geometry = Field(..., description="The geometric data of the feature") - properties: Optional[Dict] = Field(None, description="The properties associated with the collection") +class Feature(GeoAbstract): + type: Literal["Feature"] + geometry: Optional[GeometryType] = Field(..., + description="""The geometric data + of the feature""") + properties: Optional[Dict] = Field(..., + description="""Properties of the + feature""") + id: Optional[Union[StrictStr, StrictInt]] -class FeatureCollection(BaseModel): - type: str = Field("FeatureCollection", description="The type of the object, must be FeatureCollection") - features: List[Feature] = Field(..., description="The list of features in the collection") - properties: Optional[Dict] = Field(None, description="The properties associated with the collection") +class FeatureCollection(GeoAbstract): + type: Literal["FeatureCollection"] + features: List[Feature] class GeoJSON(BaseModel): """The root object of a GeoJSON file""" - __root__: Union[Feature, FeatureCollection, Geometry, GeometryCollection] + __root__: Union[Feature, FeatureCollection, GeometryType] class Unit(Enum): @@ -81,37 +97,62 @@ class Unit(Enum): class Coordinatesystem(BaseModel): - axes: List[Literal["x", "y", "z", "r", "theta", "phi"]] = Field(..., description="The coordinate system of the coordinates") + axes: List[Literal["x", "y", "z", "r", "theta", "phi"]] = Field( + ..., description="The coordinate system of the coordinates" + ) units: List[Unit] = Field(..., description="The units of the coordinates") - pixelsPerUnit: List[float] = Field(..., description="The number of pixels per unit") + pixelsPerUnit: List[float] = Field( + ..., description="The number of pixels per unit" + ) -class MicroFeature(BaseModel): - type: str - properties: Optional[Dict] - geometry: Geometry - coordinatesystem: Coordinatesystem +class MicroPoint(Point): + coordinatesystem: Optional[Coordinatesystem] -class MicroFeatureCollection(BaseModel): - type: str - features: List[Feature] - coordinatesystem: Coordinatesystem +class MicroMultiPoint(MultiPoint): + coordinatesystem: Optional[Coordinatesystem] + + +class MicroLineString(LineString): + coordinatesystem: Optional[Coordinatesystem] + + +class MicroMultiLineString(MultiLineString): + coordinatesystem: Optional[Coordinatesystem] + + +class MicroPolygon(Polygon): + coordinatesystem: Optional[Coordinatesystem] + + +class MicroMultiPolygon(MultiPolygon): + coordinatesystem: Optional[Coordinatesystem] + + +class MicroGeometryCollection(GeometryCollection): + coordinatesystem: Optional[Coordinatesystem] + + +class MicroFeature(Feature): + coordinatesystem: Optional[Coordinatesystem] -class MicroGeometry(BaseModel): - geometry: Geometry - coordinatesystem: Coordinatesystem +class MicroFeatureCollection(FeatureCollection): + coordinatesystem: Optional[Coordinatesystem] -class MicroGeometryCollection(BaseModel): - geometries: GeometryCollection - coordinatesystem: Coordinatesystem +MicroGeometryType = Union[MicroPoint, + MicroMultiPoint, + MicroLineString, + MicroMultiLineString, + MicroPolygon, + MicroMultiPolygon, + MicroGeometryCollection] class MicroJSON(BaseModel): """The root object of a MicroJSON file""" - __root__: Union[MicroFeature, - MicroFeatureCollection, - MicroGeometry, - MicroGeometryCollection] + __root__: Union[MicroFeature, + MicroFeatureCollection, + MicroGeometryType] diff --git a/microjson_schema.json b/microjson_schema.json new file mode 100644 index 0000000..278ad42 --- /dev/null +++ b/microjson_schema.json @@ -0,0 +1,854 @@ +{ + "title": "MicroJSON", + "description": "The root object of a MicroJSON file", + "anyOf": [ + { + "$ref": "#/definitions/MicroFeature" + }, + { + "$ref": "#/definitions/MicroFeatureCollection" + }, + { + "$ref": "#/definitions/MicroPoint" + }, + { + "$ref": "#/definitions/MicroMultiPoint" + }, + { + "$ref": "#/definitions/MicroLineString" + }, + { + "$ref": "#/definitions/MicroMultiLineString" + }, + { + "$ref": "#/definitions/MicroPolygon" + }, + { + "$ref": "#/definitions/MicroMultiPolygon" + }, + { + "$ref": "#/definitions/MicroGeometryCollection" + } + ], + "definitions": { + "Point": { + "title": "Point", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "Point" + ], + "type": "string" + }, + "coordinates": { + "title": "Coordinates", + "minItems": 2, + "maxItems": 3, + "type": "array", + "items": { + "type": "number" + } + } + }, + "required": [ + "type", + "coordinates" + ] + }, + "MultiPoint": { + "title": "MultiPoint", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "MultiPoint" + ], + "type": "string" + }, + "coordinates": { + "title": "Coordinates", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 3 + } + } + }, + "required": [ + "type", + "coordinates" + ] + }, + "LineString": { + "title": "LineString", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "LineString" + ], + "type": "string" + }, + "coordinates": { + "title": "Coordinates", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 3 + } + } + }, + "required": [ + "type", + "coordinates" + ] + }, + "MultiLineString": { + "title": "MultiLineString", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "MultiLineString" + ], + "type": "string" + }, + "coordinates": { + "title": "Coordinates", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 3 + } + } + } + }, + "required": [ + "type", + "coordinates" + ] + }, + "Polygon": { + "title": "Polygon", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "Polygon" + ], + "type": "string" + }, + "coordinates": { + "title": "Coordinates", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 3 + } + } + } + }, + "required": [ + "type", + "coordinates" + ] + }, + "MultiPolygon": { + "title": "MultiPolygon", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "MultiPolygon" + ], + "type": "string" + }, + "coordinates": { + "title": "Coordinates", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 3 + } + } + } + } + }, + "required": [ + "type", + "coordinates" + ] + }, + "GeometryCollection": { + "title": "GeometryCollection", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "GeometryCollection" + ], + "type": "string" + }, + "geometries": { + "title": "Geometries", + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/Point" + }, + { + "$ref": "#/definitions/MultiPoint" + }, + { + "$ref": "#/definitions/LineString" + }, + { + "$ref": "#/definitions/MultiLineString" + }, + { + "$ref": "#/definitions/Polygon" + }, + { + "$ref": "#/definitions/MultiPolygon" + } + ] + } + } + }, + "required": [ + "type", + "geometries" + ] + }, + "Unit": { + "title": "Unit", + "description": "An enumeration.", + "enum": [ + "pixel", + "meter", + "decimeter", + "centimeter", + "millimeter", + "micrometer", + "nanometer", + "picometer", + "radian", + "degree" + ] + }, + "Coordinatesystem": { + "title": "Coordinatesystem", + "type": "object", + "properties": { + "axes": { + "title": "Axes", + "description": "The coordinate system of the coordinates", + "type": "array", + "items": { + "enum": [ + "x", + "y", + "z", + "r", + "theta", + "phi" + ], + "type": "string" + } + }, + "units": { + "description": "The units of the coordinates", + "type": "array", + "items": { + "$ref": "#/definitions/Unit" + } + }, + "pixelsPerUnit": { + "title": "Pixelsperunit", + "description": "The number of pixels per unit", + "type": "array", + "items": { + "type": "number" + } + } + }, + "required": [ + "axes", + "units", + "pixelsPerUnit" + ] + }, + "MicroFeature": { + "title": "MicroFeature", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "Feature" + ], + "type": "string" + }, + "geometry": { + "title": "Geometry", + "description": "The geometric data\n of the feature", + "anyOf": [ + { + "$ref": "#/definitions/Point" + }, + { + "$ref": "#/definitions/MultiPoint" + }, + { + "$ref": "#/definitions/LineString" + }, + { + "$ref": "#/definitions/MultiLineString" + }, + { + "$ref": "#/definitions/Polygon" + }, + { + "$ref": "#/definitions/MultiPolygon" + }, + { + "$ref": "#/definitions/GeometryCollection" + } + ] + }, + "properties": { + "title": "Properties", + "description": "Properties of the\n feature", + "type": "object" + }, + "id": { + "title": "Id", + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "coordinatesystem": { + "$ref": "#/definitions/Coordinatesystem" + } + }, + "required": [ + "type", + "geometry", + "properties" + ] + }, + "Feature": { + "title": "Feature", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "Feature" + ], + "type": "string" + }, + "geometry": { + "title": "Geometry", + "description": "The geometric data\n of the feature", + "anyOf": [ + { + "$ref": "#/definitions/Point" + }, + { + "$ref": "#/definitions/MultiPoint" + }, + { + "$ref": "#/definitions/LineString" + }, + { + "$ref": "#/definitions/MultiLineString" + }, + { + "$ref": "#/definitions/Polygon" + }, + { + "$ref": "#/definitions/MultiPolygon" + }, + { + "$ref": "#/definitions/GeometryCollection" + } + ] + }, + "properties": { + "title": "Properties", + "description": "Properties of the\n feature", + "type": "object" + }, + "id": { + "title": "Id", + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + } + }, + "required": [ + "type", + "geometry", + "properties" + ] + }, + "MicroFeatureCollection": { + "title": "MicroFeatureCollection", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "FeatureCollection" + ], + "type": "string" + }, + "features": { + "title": "Features", + "type": "array", + "items": { + "$ref": "#/definitions/Feature" + } + }, + "coordinatesystem": { + "$ref": "#/definitions/Coordinatesystem" + } + }, + "required": [ + "type", + "features" + ] + }, + "MicroPoint": { + "title": "MicroPoint", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "Point" + ], + "type": "string" + }, + "coordinates": { + "title": "Coordinates", + "minItems": 2, + "maxItems": 3, + "type": "array", + "items": { + "type": "number" + } + }, + "coordinatesystem": { + "$ref": "#/definitions/Coordinatesystem" + } + }, + "required": [ + "type", + "coordinates" + ] + }, + "MicroMultiPoint": { + "title": "MicroMultiPoint", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "MultiPoint" + ], + "type": "string" + }, + "coordinates": { + "title": "Coordinates", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 3 + } + }, + "coordinatesystem": { + "$ref": "#/definitions/Coordinatesystem" + } + }, + "required": [ + "type", + "coordinates" + ] + }, + "MicroLineString": { + "title": "MicroLineString", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "LineString" + ], + "type": "string" + }, + "coordinates": { + "title": "Coordinates", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 3 + } + }, + "coordinatesystem": { + "$ref": "#/definitions/Coordinatesystem" + } + }, + "required": [ + "type", + "coordinates" + ] + }, + "MicroMultiLineString": { + "title": "MicroMultiLineString", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "MultiLineString" + ], + "type": "string" + }, + "coordinates": { + "title": "Coordinates", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 3 + } + } + }, + "coordinatesystem": { + "$ref": "#/definitions/Coordinatesystem" + } + }, + "required": [ + "type", + "coordinates" + ] + }, + "MicroPolygon": { + "title": "MicroPolygon", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "Polygon" + ], + "type": "string" + }, + "coordinates": { + "title": "Coordinates", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 3 + } + } + }, + "coordinatesystem": { + "$ref": "#/definitions/Coordinatesystem" + } + }, + "required": [ + "type", + "coordinates" + ] + }, + "MicroMultiPolygon": { + "title": "MicroMultiPolygon", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "MultiPolygon" + ], + "type": "string" + }, + "coordinates": { + "title": "Coordinates", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 2, + "maxItems": 3 + } + } + } + }, + "coordinatesystem": { + "$ref": "#/definitions/Coordinatesystem" + } + }, + "required": [ + "type", + "coordinates" + ] + }, + "MicroGeometryCollection": { + "title": "MicroGeometryCollection", + "type": "object", + "properties": { + "bbox": { + "title": "Bbox", + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + }, + "type": { + "title": "Type", + "enum": [ + "GeometryCollection" + ], + "type": "string" + }, + "geometries": { + "title": "Geometries", + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/Point" + }, + { + "$ref": "#/definitions/MultiPoint" + }, + { + "$ref": "#/definitions/LineString" + }, + { + "$ref": "#/definitions/MultiLineString" + }, + { + "$ref": "#/definitions/Polygon" + }, + { + "$ref": "#/definitions/MultiPolygon" + } + ] + } + }, + "coordinatesystem": { + "$ref": "#/definitions/Coordinatesystem" + } + }, + "required": [ + "type", + "geometries" + ] + } + } +} \ No newline at end of file diff --git a/microjsonschema.ts b/microjsonschema.ts new file mode 100644 index 0000000..a4de4d6 --- /dev/null +++ b/microjsonschema.ts @@ -0,0 +1,250 @@ +/* tslint:disable */ +/* eslint-disable */ +/** +/* This file was automatically generated from pydantic models by running pydantic2ts. +/* Do not modify it by hand - just update the pydantic models and then re-run the script +*/ + +export type Unit = + | "pixel" + | "meter" + | "decimeter" + | "centimeter" + | "millimeter" + | "micrometer" + | "nanometer" + | "picometer" + | "radian" + | "degree"; +/** + * The root object of a GeoJSON file + */ +export type GeoJSON = + | Feature + | FeatureCollection + | Point + | MultiPoint + | LineString + | MultiLineString + | Polygon + | MultiPolygon + | GeometryCollection; +/** + * The root object of a MicroJSON file + */ +export type MicroJSON = + | MicroFeature + | MicroFeatureCollection + | MicroPoint + | MicroMultiPoint + | MicroLineString + | MicroMultiLineString + | MicroPolygon + | MicroMultiPolygon + | MicroGeometryCollection; + +export interface Coordinatesystem { + /** + * The coordinate system of the coordinates + */ + axes: ("x" | "y" | "z" | "r" | "theta" | "phi")[]; + /** + * The units of the coordinates + */ + units: Unit[]; + /** + * The number of pixels per unit + */ + pixelsPerUnit: number[]; +} +export interface Feature { + /** + * @minItems 4 + */ + bbox?: [number, number, number, number, ...number[]]; + type: "Feature"; + /** + * The geometric data + * of the feature + */ + geometry: Point | MultiPoint | LineString | MultiLineString | Polygon | MultiPolygon | GeometryCollection; + /** + * Properties of the + * feature + */ + properties: { + [k: string]: unknown; + }; + id?: string | number; +} +export interface Point { + /** + * @minItems 4 + */ + bbox?: [number, number, number, number, ...number[]]; + type: "Point"; + /** + * @minItems 2 + * @maxItems 3 + */ + coordinates: [number, number] | [number, number, number]; +} +export interface MultiPoint { + /** + * @minItems 4 + */ + bbox?: [number, number, number, number, ...number[]]; + type: "MultiPoint"; + coordinates: [number, number] | [number, number, number][]; +} +export interface LineString { + /** + * @minItems 4 + */ + bbox?: [number, number, number, number, ...number[]]; + type: "LineString"; + coordinates: [number, number] | [number, number, number][]; +} +export interface MultiLineString { + /** + * @minItems 4 + */ + bbox?: [number, number, number, number, ...number[]]; + type: "MultiLineString"; + coordinates: [number, number] | [number, number, number][][]; +} +export interface Polygon { + /** + * @minItems 4 + */ + bbox?: [number, number, number, number, ...number[]]; + type: "Polygon"; + coordinates: [number, number] | [number, number, number][][]; +} +export interface MultiPolygon { + /** + * @minItems 4 + */ + bbox?: [number, number, number, number, ...number[]]; + type: "MultiPolygon"; + coordinates: [number, number] | [number, number, number][][][]; +} +export interface GeometryCollection { + /** + * @minItems 4 + */ + bbox?: [number, number, number, number, ...number[]]; + type: "GeometryCollection"; + geometries: (Point | MultiPoint | LineString | MultiLineString | Polygon | MultiPolygon)[]; +} +export interface FeatureCollection { + /** + * @minItems 4 + */ + bbox?: [number, number, number, number, ...number[]]; + type: "FeatureCollection"; + features: Feature[]; +} +export interface GeoAbstract { + /** + * @minItems 4 + */ + bbox?: [number, number, number, number, ...number[]]; +} +export interface MicroFeature { + /** + * @minItems 4 + */ + bbox?: [number, number, number, number, ...number[]]; + type: "Feature"; + /** + * The geometric data + * of the feature + */ + geometry: Point | MultiPoint | LineString | MultiLineString | Polygon | MultiPolygon | GeometryCollection; + /** + * Properties of the + * feature + */ + properties: { + [k: string]: unknown; + }; + id?: string | number; + coordinatesystem?: Coordinatesystem; +} +export interface MicroFeatureCollection { + /** + * @minItems 4 + */ + bbox?: [number, number, number, number, ...number[]]; + type: "FeatureCollection"; + features: Feature[]; + coordinatesystem?: Coordinatesystem; +} +export interface MicroGeometryCollection { + /** + * @minItems 4 + */ + bbox?: [number, number, number, number, ...number[]]; + type: "GeometryCollection"; + geometries: (Point | MultiPoint | LineString | MultiLineString | Polygon | MultiPolygon)[]; + coordinatesystem?: Coordinatesystem; +} +export interface MicroPoint { + /** + * @minItems 4 + */ + bbox?: [number, number, number, number, ...number[]]; + type: "Point"; + /** + * @minItems 2 + * @maxItems 3 + */ + coordinates: [number, number] | [number, number, number]; + coordinatesystem?: Coordinatesystem; +} +export interface MicroMultiPoint { + /** + * @minItems 4 + */ + bbox?: [number, number, number, number, ...number[]]; + type: "MultiPoint"; + coordinates: [number, number] | [number, number, number][]; + coordinatesystem?: Coordinatesystem; +} +export interface MicroLineString { + /** + * @minItems 4 + */ + bbox?: [number, number, number, number, ...number[]]; + type: "LineString"; + coordinates: [number, number] | [number, number, number][]; + coordinatesystem?: Coordinatesystem; +} +export interface MicroMultiLineString { + /** + * @minItems 4 + */ + bbox?: [number, number, number, number, ...number[]]; + type: "MultiLineString"; + coordinates: [number, number] | [number, number, number][][]; + coordinatesystem?: Coordinatesystem; +} +export interface MicroPolygon { + /** + * @minItems 4 + */ + bbox?: [number, number, number, number, ...number[]]; + type: "Polygon"; + coordinates: [number, number] | [number, number, number][][]; + coordinatesystem?: Coordinatesystem; +} +export interface MicroMultiPolygon { + /** + * @minItems 4 + */ + bbox?: [number, number, number, number, ...number[]]; + type: "MultiPolygon"; + coordinates: [number, number] | [number, number, number][][][]; + coordinatesystem?: Coordinatesystem; +} diff --git a/noxfile.py b/noxfile.py index a419fe3..4a58bd0 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,18 +1,25 @@ -"""Nox automation file.""" +import nox -from nox import Session, session -python_versions = ["3.9"] +@nox.session(python="3.9") +def export_ts(session): + session.install("-r", "requirements-dev.txt") + session.install("-e", ".") + session.run("datamodel-codegen", + "--reuse-model", + "--snake-case-field", + "--input", "GeoJSON.json", + "--input-file-type", "json", + "--output", "microjson/geojson.py", + "--target", "3.9") + session.run("pydantic2ts", "--module", "microjson/model.py", + "--output", "microjsonschema.ts") + # generate GeoJSON schema + from microjson.model import GeoJSON + with open('geojson_schema.json', 'w') as f: + f.write(GeoJSON.schema_json(indent=2)) -@session(python=["3.9"]) -def export_ts(session: Session) -> None: - """Export Pydantic model as TypeScript object.""" - session.install("-r", "requirements-dev.txt") - session.run( - "pydantic2ts", - "--module", - "microjson.py", - "--output", - "microjsonschema.ts", - ) \ No newline at end of file + from microjson.model import MicroJSON + with open('microjson_schema.json', 'w') as f: + f.write(MicroJSON.schema_json(indent=2)) diff --git a/tests/json/invalid/feature/1d-point.json b/tests/json/invalid/feature/1d-point.json new file mode 100644 index 0000000..490fee7 --- /dev/null +++ b/tests/json/invalid/feature/1d-point.json @@ -0,0 +1,13 @@ +{ + "id": "ABC", + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + -122.308150179 + ] + }, + "properties": { + "name": "Dinagat Islands" + } +} \ No newline at end of file diff --git a/tests/json/invalid/feature/boolean-id.json b/tests/json/invalid/feature/boolean-id.json new file mode 100644 index 0000000..7dc4400 --- /dev/null +++ b/tests/json/invalid/feature/boolean-id.json @@ -0,0 +1,11 @@ +{ + "type": "Feature", + "id": false, + "geometry": { + "type": "Point", + "coordinates": [0, 0] + }, + "properties": { + "name": "basic" + } +} diff --git a/tests/json/invalid/feature/no-geometry.json b/tests/json/invalid/feature/no-geometry.json new file mode 100644 index 0000000..9916ead --- /dev/null +++ b/tests/json/invalid/feature/no-geometry.json @@ -0,0 +1,7 @@ +{ + "type": "Feature", + "id": "1", + "properties": { + "name": "no geometry" + } +} diff --git a/tests/json/invalid/feature/no-properties.json b/tests/json/invalid/feature/no-properties.json new file mode 100644 index 0000000..c5bbe68 --- /dev/null +++ b/tests/json/invalid/feature/no-properties.json @@ -0,0 +1,8 @@ +{ + "type": "Feature", + "id": "1", + "geometry": { + "type": "Point", + "coordinates": [0, 0] + } +} diff --git a/tests/json/invalid/feature/no-type.json b/tests/json/invalid/feature/no-type.json new file mode 100644 index 0000000..d833f55 --- /dev/null +++ b/tests/json/invalid/feature/no-type.json @@ -0,0 +1,10 @@ +{ + "id": "1", + "geometry": { + "type": "Point", + "coordinates": [0, 0] + }, + "properties": { + "name": "basic" + } +} diff --git a/tests/json/invalid/feature/object-id.json b/tests/json/invalid/feature/object-id.json new file mode 100644 index 0000000..fa283ff --- /dev/null +++ b/tests/json/invalid/feature/object-id.json @@ -0,0 +1,13 @@ +{ + "type": "Feature", + "id": { + "foo": "bar" + }, + "geometry": { + "type": "Point", + "coordinates": [0, 0] + }, + "properties": { + "name": "basic" + } +} diff --git a/tests/json/invalid/featurecollection/no-features.json b/tests/json/invalid/featurecollection/no-features.json new file mode 100644 index 0000000..bdfb8da --- /dev/null +++ b/tests/json/invalid/featurecollection/no-features.json @@ -0,0 +1,3 @@ +{ + "type": "FeatureCollection" +} \ No newline at end of file diff --git a/tests/json/invalid/featurecollection/no-type.json b/tests/json/invalid/featurecollection/no-type.json new file mode 100644 index 0000000..a751837 --- /dev/null +++ b/tests/json/invalid/featurecollection/no-type.json @@ -0,0 +1,15 @@ +{ + "features": [ + { + "type": "Feature", + "id": "1", + "geometry": { + "type": "Point", + "coordinates": [0, 0] + }, + "properties": { + "name": "basic" + } + } + ] +} \ No newline at end of file diff --git a/tests/json/invalid/geometrycollection/1d.json b/tests/json/invalid/geometrycollection/1d.json new file mode 100644 index 0000000..1d00e69 --- /dev/null +++ b/tests/json/invalid/geometrycollection/1d.json @@ -0,0 +1,32 @@ +{ + "type": "GeometryCollection", + "geometries": [ + { + "type": "Point", + "coordinates": [0, 0] + }, + { + "type": "LineString", + "coordinates": [[-110, 45], [110]] + }, + { + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ] + } + ] +} diff --git a/tests/json/invalid/geometrycollection/bad-geometry.json b/tests/json/invalid/geometrycollection/bad-geometry.json new file mode 100644 index 0000000..e007c28 --- /dev/null +++ b/tests/json/invalid/geometrycollection/bad-geometry.json @@ -0,0 +1,32 @@ +{ + "type": "GeometryCollection", + "geometries": [ + { + "type": "Point", + "coordinates": [0, 0] + }, + { + "type": "Line", + "coordinates": [[-110, 45], [110, -45]] + }, + { + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ] + } + ] +} diff --git a/tests/json/invalid/geometrycollection/no-geometries.json b/tests/json/invalid/geometrycollection/no-geometries.json new file mode 100644 index 0000000..17a3f78 --- /dev/null +++ b/tests/json/invalid/geometrycollection/no-geometries.json @@ -0,0 +1,32 @@ +{ + "type": "GeometryCollection", + "coordinates": [ + { + "type": "Point", + "coordinates": [0, 0] + }, + { + "type": "LineString", + "coordinates": [[-110, 45], [110, -45]] + }, + { + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ] + } + ] +} diff --git a/tests/json/invalid/geometrycollection/no-type.json b/tests/json/invalid/geometrycollection/no-type.json new file mode 100644 index 0000000..a7b8783 --- /dev/null +++ b/tests/json/invalid/geometrycollection/no-type.json @@ -0,0 +1,31 @@ +{ + "geometries": [ + { + "type": "Point", + "coordinates": [0, 0] + }, + { + "type": "LineString", + "coordinates": [[-110, 45], [110, -45]] + }, + { + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ] + } + ] +} diff --git a/tests/json/invalid/linestring/1d.json b/tests/json/invalid/linestring/1d.json new file mode 100644 index 0000000..7564ec8 --- /dev/null +++ b/tests/json/invalid/linestring/1d.json @@ -0,0 +1,4 @@ +{ + "type": "LineString", + "coordinates": [[-110], [110]] +} diff --git a/tests/json/invalid/linestring/bad-bbox.json b/tests/json/invalid/linestring/bad-bbox.json new file mode 100644 index 0000000..01236d3 --- /dev/null +++ b/tests/json/invalid/linestring/bad-bbox.json @@ -0,0 +1,5 @@ +{ + "type": "LineString", + "coordinates": [[-110, 45], [110, -45]], + "bbox": [-110, -45, 110] +} diff --git a/tests/json/invalid/linestring/no-coordinates.json b/tests/json/invalid/linestring/no-coordinates.json new file mode 100644 index 0000000..25d3615 --- /dev/null +++ b/tests/json/invalid/linestring/no-coordinates.json @@ -0,0 +1,4 @@ +{ + "type": "LineString", + "coords": [[-110, 45], [110, -45]] +} diff --git a/tests/json/invalid/linestring/no-type.json b/tests/json/invalid/linestring/no-type.json new file mode 100644 index 0000000..f8f14c4 --- /dev/null +++ b/tests/json/invalid/linestring/no-type.json @@ -0,0 +1,4 @@ +{ + "kind": "LineString", + "coordinates": [[-110, 45], [110, -45]] +} diff --git a/tests/json/invalid/multilinestring/1d.json b/tests/json/invalid/multilinestring/1d.json new file mode 100644 index 0000000..d4667ae --- /dev/null +++ b/tests/json/invalid/multilinestring/1d.json @@ -0,0 +1,7 @@ +{ + "type": "MultiLineString", + "coordinates": [ + [[-111, 45], [111, -45]], + [[-111, -45], [111]] + ] +} diff --git a/tests/json/invalid/multilinestring/bad-bbox.json b/tests/json/invalid/multilinestring/bad-bbox.json new file mode 100644 index 0000000..5bd0387 --- /dev/null +++ b/tests/json/invalid/multilinestring/bad-bbox.json @@ -0,0 +1,8 @@ +{ + "type": "MultiLineString", + "bbox": [-111, -45, 111], + "coordinates": [ + [[-111, 45], [111, -45]], + [[-111, -45], [111, 45]] + ] +} diff --git a/tests/json/invalid/multilinestring/no-coordinates.json b/tests/json/invalid/multilinestring/no-coordinates.json new file mode 100644 index 0000000..d2a3736 --- /dev/null +++ b/tests/json/invalid/multilinestring/no-coordinates.json @@ -0,0 +1,7 @@ +{ + "type": "MultiLineString", + "cordinates": [ + [[-111, 45], [111, -45]], + [[-111, -45], [111, 45]] + ] +} diff --git a/tests/json/invalid/multilinestring/no-type.json b/tests/json/invalid/multilinestring/no-type.json new file mode 100644 index 0000000..0cb47c6 --- /dev/null +++ b/tests/json/invalid/multilinestring/no-type.json @@ -0,0 +1,6 @@ +{ + "coordinates": [ + [[-111, 45], [111, -45]], + [[-111, -45], [111, 45]] + ] +} diff --git a/tests/json/invalid/multipoint/1d.json b/tests/json/invalid/multipoint/1d.json new file mode 100644 index 0000000..c16cbcb --- /dev/null +++ b/tests/json/invalid/multipoint/1d.json @@ -0,0 +1,7 @@ +{ + "type": "MultiPoint", + "coordinates": [ + [100, 0], + [101] + ] +} diff --git a/tests/json/invalid/multipoint/bad-bbox.json b/tests/json/invalid/multipoint/bad-bbox.json new file mode 100644 index 0000000..13dbcfd --- /dev/null +++ b/tests/json/invalid/multipoint/bad-bbox.json @@ -0,0 +1,8 @@ +{ + "type": "MultiPoint", + "coordinates": [ + [100, 0], + [101, 1] + ], + "bbox": [100, 101, 1] +} diff --git a/tests/json/invalid/multipoint/no-coordinates.json b/tests/json/invalid/multipoint/no-coordinates.json new file mode 100644 index 0000000..780dbc2 --- /dev/null +++ b/tests/json/invalid/multipoint/no-coordinates.json @@ -0,0 +1,7 @@ +{ + "type": "MultiPoint", + "coordinate": [ + [100, 0], + [101, 1] + ] +} diff --git a/tests/json/invalid/multipoint/no-type.json b/tests/json/invalid/multipoint/no-type.json new file mode 100644 index 0000000..2fce731 --- /dev/null +++ b/tests/json/invalid/multipoint/no-type.json @@ -0,0 +1,7 @@ +{ + "kind": "MultiPoint", + "coordinates": [ + [100, 0], + [101, 1] + ] +} diff --git a/tests/json/invalid/multipolygon/1d.json b/tests/json/invalid/multipolygon/1d.json new file mode 100644 index 0000000..ebb864b --- /dev/null +++ b/tests/json/invalid/multipolygon/1d.json @@ -0,0 +1,30 @@ +{ + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [102.0, 2.0], + [103.0, 2.0], + [103.0, 3.0], + [102.0, 3.0], + [102.0, 2.0] + ] + ], + [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.2, 0.2], + [100.8, 0.2], + [100.8, 0.8], + [100.2, 0.8], + [100.2] + ] + ] + ] +} diff --git a/tests/json/invalid/multipolygon/bad-bbox.json b/tests/json/invalid/multipolygon/bad-bbox.json new file mode 100644 index 0000000..27e39e6 --- /dev/null +++ b/tests/json/invalid/multipolygon/bad-bbox.json @@ -0,0 +1,31 @@ +{ + "type": "MultiPolygon", + "bbox": 42, + "coordinates": [ + [ + [ + [102.0, 2.0], + [103.0, 2.0], + [103.0, 3.0], + [102.0, 3.0], + [102.0, 2.0] + ] + ], + [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.2, 0.2], + [100.8, 0.2], + [100.8, 0.8], + [100.2, 0.8], + [100.2, 0.2] + ] + ] + ] +} diff --git a/tests/json/invalid/multipolygon/no-coordinates.json b/tests/json/invalid/multipolygon/no-coordinates.json new file mode 100644 index 0000000..aa613e2 --- /dev/null +++ b/tests/json/invalid/multipolygon/no-coordinates.json @@ -0,0 +1,3 @@ +{ + "type": "MultiPolygon" +} diff --git a/tests/json/invalid/multipolygon/no-type.json b/tests/json/invalid/multipolygon/no-type.json new file mode 100644 index 0000000..9c3ce92 --- /dev/null +++ b/tests/json/invalid/multipolygon/no-type.json @@ -0,0 +1,30 @@ +{ + "typo": "MultiPolygon", + "coordinates": [ + [ + [ + [102.0, 2.0], + [103.0, 2.0], + [103.0, 3.0], + [102.0, 3.0], + [102.0, 2.0] + ] + ], + [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.2, 0.2], + [100.8, 0.2], + [100.8, 0.8], + [100.2, 0.8], + [100.2, 0.2] + ] + ] + ] +} diff --git a/tests/json/invalid/point/1d.json b/tests/json/invalid/point/1d.json new file mode 100644 index 0000000..3d32431 --- /dev/null +++ b/tests/json/invalid/point/1d.json @@ -0,0 +1,4 @@ +{ + "type": "Point", + "coordinates": [0] +} diff --git a/tests/json/invalid/point/bad-bbox.json b/tests/json/invalid/point/bad-bbox.json new file mode 100644 index 0000000..f6dde44 --- /dev/null +++ b/tests/json/invalid/point/bad-bbox.json @@ -0,0 +1,5 @@ +{ + "type": "Point", + "coordinates": [0, 0], + "bbox": [0, 0] +} diff --git a/tests/json/invalid/point/no-coordinates.json b/tests/json/invalid/point/no-coordinates.json new file mode 100644 index 0000000..7c345af --- /dev/null +++ b/tests/json/invalid/point/no-coordinates.json @@ -0,0 +1,3 @@ +{ + "type": "Point" +} diff --git a/tests/json/invalid/point/no-type.json b/tests/json/invalid/point/no-type.json new file mode 100644 index 0000000..3558deb --- /dev/null +++ b/tests/json/invalid/point/no-type.json @@ -0,0 +1,3 @@ +{ + "coordinates": [0, 0] +} diff --git a/tests/json/invalid/polygon/1d.json b/tests/json/invalid/polygon/1d.json new file mode 100644 index 0000000..97ccbcd --- /dev/null +++ b/tests/json/invalid/polygon/1d.json @@ -0,0 +1,19 @@ +{ + "type": "Polygon", + "coordinates": [ + [ + [100], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ] +} diff --git a/tests/json/invalid/polygon/bad-type.json b/tests/json/invalid/polygon/bad-type.json new file mode 100644 index 0000000..50c888d --- /dev/null +++ b/tests/json/invalid/polygon/bad-type.json @@ -0,0 +1,19 @@ +{ + "type": "Polgon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ] +} diff --git a/tests/json/invalid/polygon/no-coordinates.json b/tests/json/invalid/polygon/no-coordinates.json new file mode 100644 index 0000000..a7406d4 --- /dev/null +++ b/tests/json/invalid/polygon/no-coordinates.json @@ -0,0 +1,3 @@ +{ + "type": "Polygon" +} diff --git a/tests/json/invalid/polygon/no-type.json b/tests/json/invalid/polygon/no-type.json new file mode 100644 index 0000000..518712e --- /dev/null +++ b/tests/json/invalid/polygon/no-type.json @@ -0,0 +1,18 @@ +{ + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ] +} diff --git a/tests/json/valid/feature/basic.json b/tests/json/valid/feature/basic.json new file mode 100644 index 0000000..327eeb8 --- /dev/null +++ b/tests/json/valid/feature/basic.json @@ -0,0 +1,11 @@ +{ + "type": "Feature", + "id": "1", + "geometry": { + "type": "Point", + "coordinates": [0, 0] + }, + "properties": { + "name": "basic" + } +} diff --git a/tests/json/valid/feature/empty-polygon.json b/tests/json/valid/feature/empty-polygon.json new file mode 100644 index 0000000..7ac6e73 --- /dev/null +++ b/tests/json/valid/feature/empty-polygon.json @@ -0,0 +1,9 @@ +{ + "id": "ABC", + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [] + } +} \ No newline at end of file diff --git a/tests/json/valid/feature/null-geometry.json b/tests/json/valid/feature/null-geometry.json new file mode 100644 index 0000000..2bd8e9f --- /dev/null +++ b/tests/json/valid/feature/null-geometry.json @@ -0,0 +1,8 @@ +{ + "type": "Feature", + "id": "1", + "geometry": null, + "properties": { + "name": "null geometry" + } +} diff --git a/tests/json/valid/feature/with-number-id.json b/tests/json/valid/feature/with-number-id.json new file mode 100644 index 0000000..0e06e98 --- /dev/null +++ b/tests/json/valid/feature/with-number-id.json @@ -0,0 +1,11 @@ +{ + "type": "Feature", + "id": 42, + "geometry": { + "type": "Point", + "coordinates": [0, 0] + }, + "properties": { + "name": "basic" + } +} diff --git a/tests/json/valid/feature/with-string-id.json b/tests/json/valid/feature/with-string-id.json new file mode 100644 index 0000000..076a7a2 --- /dev/null +++ b/tests/json/valid/feature/with-string-id.json @@ -0,0 +1,11 @@ +{ + "type": "Feature", + "id": "feature.1", + "geometry": { + "type": "Point", + "coordinates": [0, 0] + }, + "properties": { + "name": "basic" + } +} diff --git a/tests/json/valid/featurecollection/basic.json b/tests/json/valid/featurecollection/basic.json new file mode 100644 index 0000000..4d3d3ba --- /dev/null +++ b/tests/json/valid/featurecollection/basic.json @@ -0,0 +1,16 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "id": "1", + "geometry": { + "type": "Point", + "coordinates": [0, 0] + }, + "properties": { + "name": "basic" + } + } + ] +} \ No newline at end of file diff --git a/tests/json/valid/featurecollection/empty.json b/tests/json/valid/featurecollection/empty.json new file mode 100644 index 0000000..8bc6daf --- /dev/null +++ b/tests/json/valid/featurecollection/empty.json @@ -0,0 +1,4 @@ +{ + "type": "FeatureCollection", + "features": [] +} \ No newline at end of file diff --git a/tests/json/valid/geometrycollection/3d.json b/tests/json/valid/geometrycollection/3d.json new file mode 100644 index 0000000..74045ab --- /dev/null +++ b/tests/json/valid/geometrycollection/3d.json @@ -0,0 +1,32 @@ +{ + "type": "GeometryCollection", + "geometries": [ + { + "type": "Point", + "coordinates": [0, 0, 10] + }, + { + "type": "LineString", + "coordinates": [[-110, 45], [110, -45]] + }, + { + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ] + } + ] +} diff --git a/tests/json/valid/geometrycollection/basic.json b/tests/json/valid/geometrycollection/basic.json new file mode 100644 index 0000000..b13f128 --- /dev/null +++ b/tests/json/valid/geometrycollection/basic.json @@ -0,0 +1,32 @@ +{ + "type": "GeometryCollection", + "geometries": [ + { + "type": "Point", + "coordinates": [0, 0] + }, + { + "type": "LineString", + "coordinates": [[-110, 45], [110, -45]] + }, + { + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ] + } + ] +} diff --git a/tests/json/valid/geometrycollection/bbox.json b/tests/json/valid/geometrycollection/bbox.json new file mode 100644 index 0000000..b5b9137 --- /dev/null +++ b/tests/json/valid/geometrycollection/bbox.json @@ -0,0 +1,33 @@ +{ + "type": "GeometryCollection", + "bbox": [-110, -45, 110, 45], + "geometries": [ + { + "type": "Point", + "coordinates": [0, 0] + }, + { + "type": "LineString", + "coordinates": [[-110, 45], [110, -45]] + }, + { + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ] + } + ] +} diff --git a/tests/json/valid/linestring/3d.json b/tests/json/valid/linestring/3d.json new file mode 100644 index 0000000..7441abf --- /dev/null +++ b/tests/json/valid/linestring/3d.json @@ -0,0 +1,4 @@ +{ + "type": "LineString", + "coordinates": [[-110, 45, 100], [110, -45, 200]] +} diff --git a/tests/json/valid/linestring/basic.json b/tests/json/valid/linestring/basic.json new file mode 100644 index 0000000..3f20a4f --- /dev/null +++ b/tests/json/valid/linestring/basic.json @@ -0,0 +1,4 @@ +{ + "type": "LineString", + "coordinates": [[-110, 45], [110, -45]] +} diff --git a/tests/json/valid/linestring/bbox.json b/tests/json/valid/linestring/bbox.json new file mode 100644 index 0000000..fecc9de --- /dev/null +++ b/tests/json/valid/linestring/bbox.json @@ -0,0 +1,5 @@ +{ + "type": "LineString", + "coordinates": [[-110, 45], [110, -45]], + "bbox": [-110, -45, 110, 45] +} diff --git a/tests/json/valid/multilinestring/3d.json b/tests/json/valid/multilinestring/3d.json new file mode 100644 index 0000000..329540a --- /dev/null +++ b/tests/json/valid/multilinestring/3d.json @@ -0,0 +1,7 @@ +{ + "type": "MultiLineString", + "coordinates": [ + [[-111, 45, 10], [111, -45, 20]], + [[-111, -45, 30], [111, 45, 40]] + ] +} diff --git a/tests/json/valid/multilinestring/basic.json b/tests/json/valid/multilinestring/basic.json new file mode 100644 index 0000000..951d879 --- /dev/null +++ b/tests/json/valid/multilinestring/basic.json @@ -0,0 +1,7 @@ +{ + "type": "MultiLineString", + "coordinates": [ + [[-111, 45], [111, -45]], + [[-111, -45], [111, 45]] + ] +} diff --git a/tests/json/valid/multilinestring/bbox.json b/tests/json/valid/multilinestring/bbox.json new file mode 100644 index 0000000..b3fdde5 --- /dev/null +++ b/tests/json/valid/multilinestring/bbox.json @@ -0,0 +1,8 @@ +{ + "type": "MultiLineString", + "bbox": [-111, -45, 111, 45], + "coordinates": [ + [[-111, 45], [111, -45]], + [[-111, -45], [111, 45]] + ] +} diff --git a/tests/json/valid/multipoint/3d.json b/tests/json/valid/multipoint/3d.json new file mode 100644 index 0000000..76506d1 --- /dev/null +++ b/tests/json/valid/multipoint/3d.json @@ -0,0 +1,7 @@ +{ + "type": "MultiPoint", + "coordinates": [ + [100, 0, 1], + [101, 1, -1] + ] +} diff --git a/tests/json/valid/multipoint/basic.json b/tests/json/valid/multipoint/basic.json new file mode 100644 index 0000000..965fec4 --- /dev/null +++ b/tests/json/valid/multipoint/basic.json @@ -0,0 +1,7 @@ +{ + "type": "MultiPoint", + "coordinates": [ + [100, 0], + [101, 1] + ] +} diff --git a/tests/json/valid/multipoint/bbox.json b/tests/json/valid/multipoint/bbox.json new file mode 100644 index 0000000..e3c9531 --- /dev/null +++ b/tests/json/valid/multipoint/bbox.json @@ -0,0 +1,8 @@ +{ + "type": "MultiPoint", + "coordinates": [ + [100, 0], + [101, 1] + ], + "bbox": [100, 0, 101, 1] +} diff --git a/tests/json/valid/multipolygon/3d.json b/tests/json/valid/multipolygon/3d.json new file mode 100644 index 0000000..d95b72f --- /dev/null +++ b/tests/json/valid/multipolygon/3d.json @@ -0,0 +1,30 @@ +{ + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [102.0, 2.0, 10], + [103.0, 2.0, 10], + [103.0, 3.0, 10], + [102.0, 3.0, 10], + [102.0, 2.0, 10] + ] + ], + [ + [ + [100.0, 0.0, 20], + [101.0, 0.0, 20], + [101.0, 1.0, 20], + [100.0, 1.0, 20], + [100.0, 0.0, 20] + ], + [ + [100.2, 0.2, 30], + [100.8, 0.2, 30], + [100.8, 0.8, 30], + [100.2, 0.8, 30], + [100.2, 0.2, 30] + ] + ] + ] +} diff --git a/tests/json/valid/multipolygon/basic.json b/tests/json/valid/multipolygon/basic.json new file mode 100644 index 0000000..928e4e0 --- /dev/null +++ b/tests/json/valid/multipolygon/basic.json @@ -0,0 +1,30 @@ +{ + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [102.0, 2.0], + [103.0, 2.0], + [103.0, 3.0], + [102.0, 3.0], + [102.0, 2.0] + ] + ], + [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.2, 0.2], + [100.8, 0.2], + [100.8, 0.8], + [100.2, 0.8], + [100.2, 0.2] + ] + ] + ] +} diff --git a/tests/json/valid/multipolygon/bbox.json b/tests/json/valid/multipolygon/bbox.json new file mode 100644 index 0000000..c73d50e --- /dev/null +++ b/tests/json/valid/multipolygon/bbox.json @@ -0,0 +1,31 @@ +{ + "type": "MultiPolygon", + "bbox": [100, 0, 103, 3], + "coordinates": [ + [ + [ + [102.0, 2.0], + [103.0, 2.0], + [103.0, 3.0], + [102.0, 3.0], + [102.0, 2.0] + ] + ], + [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.2, 0.2], + [100.8, 0.2], + [100.8, 0.8], + [100.2, 0.8], + [100.2, 0.2] + ] + ] + ] +} diff --git a/tests/json/valid/point/3d.json b/tests/json/valid/point/3d.json new file mode 100644 index 0000000..24ff422 --- /dev/null +++ b/tests/json/valid/point/3d.json @@ -0,0 +1,4 @@ +{ + "type": "Point", + "coordinates": [-110, 45, 100] +} diff --git a/tests/json/valid/point/basic.json b/tests/json/valid/point/basic.json new file mode 100644 index 0000000..3f087e1 --- /dev/null +++ b/tests/json/valid/point/basic.json @@ -0,0 +1,4 @@ +{ + "type": "Point", + "coordinates": [0, 0] +} diff --git a/tests/json/valid/point/bbox.json b/tests/json/valid/point/bbox.json new file mode 100644 index 0000000..dbb0a9e --- /dev/null +++ b/tests/json/valid/point/bbox.json @@ -0,0 +1,5 @@ +{ + "type": "Point", + "coordinates": [0, 0], + "bbox": [0, 0, 0, 0] +} diff --git a/tests/json/valid/point/extended.json b/tests/json/valid/point/extended.json new file mode 100644 index 0000000..c878c2f --- /dev/null +++ b/tests/json/valid/point/extended.json @@ -0,0 +1,5 @@ +{ + "type": "Point", + "coordinates": [0, 0], + "title": "Null Island" +} diff --git a/tests/json/valid/polygon/3d.json b/tests/json/valid/polygon/3d.json new file mode 100644 index 0000000..e28ad87 --- /dev/null +++ b/tests/json/valid/polygon/3d.json @@ -0,0 +1,19 @@ +{ + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0, 10], + [101.0, 0.0, 10], + [101.0, 1.0, 10], + [100.0, 1.0, 10], + [100.0, 0.0, 10] + ], + [ + [100.8, 0.8, 10], + [100.8, 0.2, 10], + [100.2, 0.2, 10], + [100.2, 0.8, 10], + [100.8, 0.8, 10] + ] + ] +} diff --git a/tests/json/valid/polygon/basic.json b/tests/json/valid/polygon/basic.json new file mode 100644 index 0000000..b3319a4 --- /dev/null +++ b/tests/json/valid/polygon/basic.json @@ -0,0 +1,19 @@ +{ + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ] +} diff --git a/tests/json/valid/polygon/bbox.json b/tests/json/valid/polygon/bbox.json new file mode 100644 index 0000000..3e79aa7 --- /dev/null +++ b/tests/json/valid/polygon/bbox.json @@ -0,0 +1,20 @@ +{ + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ], + "bbox": [100, 0, 101, 1] +} diff --git a/tests/json/valid/polygon/extended.json b/tests/json/valid/polygon/extended.json new file mode 100644 index 0000000..056e8c1 --- /dev/null +++ b/tests/json/valid/polygon/extended.json @@ -0,0 +1,20 @@ +{ + "title": "Titled Polygon", + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ] +} diff --git a/tests/test_microjson _auto.py b/tests/test_microjson _auto.py new file mode 100644 index 0000000..0fd1d4d --- /dev/null +++ b/tests/test_microjson _auto.py @@ -0,0 +1,95 @@ +import json +import pytest +from pydantic import ValidationError +from microjson import MicroJSONAuto, GeoJSONAuto +import os + +# Define the directories containing the example JSON files +VALID_EXAMPLES_DIR = 'tests/json/valid' +INVALID_EXAMPLES_DIR = 'tests/json/invalid' + + +def gather_example_files(directory): + files = [] + # Walk through the directory + for dirpath, dirnames, filenames in os.walk(directory): + # Filter to just the .json files + example_files = [os.path.join(dirpath, f) + for f in filenames if f.endswith('.json')] + files.extend(example_files) + return files + + +valid_examples = gather_example_files(VALID_EXAMPLES_DIR) +invalid_examples = gather_example_files(INVALID_EXAMPLES_DIR) + + +@pytest.mark.parametrize("filename", valid_examples) +def test_valid_geojsons(filename): + with open(filename, 'r') as f: + data = json.load(f, strict=True) + + # Try to parse the data as a GeoJSON object + try: + _ = GeoJSONAuto.parse_obj(data) + except ValidationError as e: + pytest.fail(f"""ValidationError occurred + during validation of {filename}: {str(e)}""") + except Exception as e: + pytest.fail(f"""Unexpected error occurred + during validation of {filename}: {str(e)}""") + + +@pytest.mark.parametrize("filename", invalid_examples) +def test_invalid_geojsons(filename): + with open(filename, 'r') as f: + data = json.load(f, strict=True) + + # This will raise a ValidationError if the data does not match the GeoJSON + try: + _ = GeoJSONAuto.parse_obj(data) + pytest.fail(f"""Parsing succeeded on {filename}, + but it should not have.""") + except ValidationError: + # The validation error is expected, so we just pass + pass + except Exception as e: + # An unexpected error occurred, so we fail the test + pytest.fail(f"""Unexpected error occurred + during validation of {filename}: {str(e)}""") + + +@pytest.mark.parametrize("filename", valid_examples) +def test_valid_microjsons(filename): + with open(filename, 'r') as f: + data = json.load(f, strict=True) + + # Try to parse the data as a GeoJSON object + try: + _ = MicroJSONAuto.parse_obj(data) + except ValidationError as e: + pytest.fail(f"""ValidationError occurred + during validation of {filename}: {str(e)}""") + except Exception as e: + pytest.fail(f"""Unexpected error occurred + during validation of {filename}: {str(e)}""") + + +@pytest.mark.parametrize("filename", invalid_examples) +def test_invalid_microjsons(filename): + with open(filename, 'r') as f: + data = json.load(f, strict=True) + + # This will raise a ValidationError if the data does not + # match the GeoJSON schema + try: + _ = MicroJSONAuto.parse_obj(data) + pytest.fail(f"""Parsing succeeded on {filename}, + but it should not have.""") + except ValidationError: + # The validation error is expected, so we just pass + pass + except Exception as e: + # An unexpected error occurred, so we fail the test + pytest.fail(f"""Unexpected error occurred + during validation of {filename}: {str(e)}""") diff --git a/tests/test_microjson.py b/tests/test_microjson.py index 6bbfdd7..14980c1 100644 --- a/tests/test_microjson.py +++ b/tests/test_microjson.py @@ -1,50 +1,95 @@ import json import pytest from pydantic import ValidationError -from microjson import MicroJSON # Import your MicroJSON Pydantic model - - -def test_valid_microjson(): - # This is an example of a valid MicroJSON document - valid_microjson = { - "type": "FeatureCollection", - "coordinatesystem": { - "axes": ["x", "y", "z"], - "units": ["pixel", "pixel", "pixel"], - "pixelsPerUnit": [1.0, 1.0, 1.0] - }, - "features": [ - { - "type": "Feature", - "properties": { - "id": "1", - "type": "type1" - }, - "geometry": { - "type": "Point", - "coordinates": [1.0, 2.0, 3.0] - } - } - ], - "geometries": [] - } +from microjson import MicroJSON, GeoJSON +import os +# Define the directories containing the example JSON files +VALID_EXAMPLES_DIR = 'tests/json/valid' +INVALID_EXAMPLES_DIR = 'tests/json/invalid' + + +def gather_example_files(directory): + files = [] + # Walk through the directory + for dirpath, dirnames, filenames in os.walk(directory): + # Filter to just the .json files + example_files = [os.path.join(dirpath, f) + for f in filenames if f.endswith('.json')] + files.extend(example_files) + return files + + +valid_examples = gather_example_files(VALID_EXAMPLES_DIR) +invalid_examples = gather_example_files(INVALID_EXAMPLES_DIR) + + +@pytest.mark.parametrize("filename", valid_examples) +def test_valid_geojsons(filename): + with open(filename, 'r') as f: + data = json.load(f, strict=True) + + # Try to parse the data as a GeoJSON object try: - valid_microjson_str = json.dumps(valid_microjson) - MicroJSON.parse_raw(valid_microjson_str) + _ = GeoJSON.parse_obj(data) + except ValidationError as e: + pytest.fail(f"""ValidationError occurred + during validation of {filename}: {str(e)}""") + except Exception as e: + pytest.fail(f"""Unexpected error occurred + during validation of {filename}: {str(e)}""") + + +@pytest.mark.parametrize("filename", invalid_examples) +def test_invalid_geojsons(filename): + with open(filename, 'r') as f: + data = json.load(f, strict=True) + + # This will raise a ValidationError if the data does not match the GeoJSON + try: + _ = GeoJSON.parse_obj(data) + pytest.fail(f"""Parsing succeeded on {filename}, + but it should not have.""") except ValidationError: - pytest.fail("Valid MicroJSON raised ValidationError") + # The validation error is expected, so we just pass + pass + except Exception as e: + # An unexpected error occurred, so we fail the test + pytest.fail(f"""Unexpected error occurred + during validation of {filename}: {str(e)}""") + + +@pytest.mark.parametrize("filename", valid_examples) +def test_valid_microjsons(filename): + with open(filename, 'r') as f: + data = json.load(f, strict=True) + + # Try to parse the data as a GeoJSON object + try: + _ = MicroJSON.parse_obj(data) + except ValidationError as e: + pytest.fail(f"""ValidationError occurred + during validation of {filename}: {str(e)}""") + except Exception as e: + pytest.fail(f"""Unexpected error occurred + during validation of {filename}: {str(e)}""") -def test_invalid_microjson(): - # This is an example of an invalid MicroJSON document - invalid_microjson = { - "type": "FeatureCollection", - "coordinates": {"axes": ["invalid_axis", "y", "z"]}, - "features": [], - # Add all the required fields - } - with pytest.raises(ValidationError): - invalid_microjson_str = json.dumps(invalid_microjson) - MicroJSON.parse_raw(invalid_microjson_str) +@pytest.mark.parametrize("filename", invalid_examples) +def test_invalid_microjsons(filename): + with open(filename, 'r') as f: + data = json.load(f, strict=True) + # This will raise a ValidationError if the data does not + # match the GeoJSON schema + try: + _ = MicroJSON.parse_obj(data) + pytest.fail(f"""Parsing succeeded on {filename}, + but it should not have.""") + except ValidationError: + # The validation error is expected, so we just pass + pass + except Exception as e: + # An unexpected error occurred, so we fail the test + pytest.fail(f"""Unexpected error occurred + during validation of {filename}: {str(e)}""") From 386982b61bcb4c743d4e01b62d923331304049a7 Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Mon, 5 Jun 2023 20:39:48 -0400 Subject: [PATCH 03/28] Noxfile input from folder --- noxfile.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/noxfile.py b/noxfile.py index 4a58bd0..23a4dd3 100644 --- a/noxfile.py +++ b/noxfile.py @@ -8,9 +8,8 @@ def export_ts(session): session.run("datamodel-codegen", "--reuse-model", "--snake-case-field", - "--input", "GeoJSON.json", - "--input-file-type", "json", - "--output", "microjson/geojson.py", + "--input", "external", + "--output", "geojson", "--target", "3.9") session.run("pydantic2ts", "--module", "microjson/model.py", "--output", "microjsonschema.ts") From 34310a000f0ba9635a61440091cc1f29db74c3ee Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Thu, 8 Jun 2023 10:53:50 -0400 Subject: [PATCH 04/28] Added git action, updated docs --- .github/workflows/ci.yml | 23 ++++++ docs/example.md | 170 +++++++++++++++++++-------------------- docs/index.md | 15 ++-- docs/init_highlight.js | 5 ++ docs/validation.md | 0 mkdocs.yml | 43 +++++++++- 6 files changed, 160 insertions(+), 96 deletions(-) create mode 100755 .github/workflows/ci.yml mode change 100644 => 100755 docs/example.md mode change 100644 => 100755 docs/index.md create mode 100755 docs/init_highlight.js mode change 100644 => 100755 docs/validation.md mode change 100644 => 100755 mkdocs.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100755 index 0000000..716620d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,23 @@ + name: ci + on: + push: + branches: + - master + - main + permissions: + contents: write + jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: 3.x + - uses: actions/cache@v2 + with: + key: ${{ github.ref }} + path: .cache + - run: pip install mkdocs-material + - run: pip install pillow cairosvg + - run: mkdocs gh-deploy --force \ No newline at end of file diff --git a/docs/example.md b/docs/example.md old mode 100644 new mode 100755 index e302175..b10f28a --- a/docs/example.md +++ b/docs/example.md @@ -3,93 +3,93 @@ This JSON file demonstrates how MicroJSON can be used to define and describe com ```json { - "coordinatesystem": { - "axes": [ - "x", - "y", - "z" + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + 200.0, + 150.0 + ], + "coordinatesystem": { + "axes": [ + "x", + "y" + ], + "units": [ + "micrometer", + "micrometer" + ], + "pixelsPerUnit": [ + 0.5, + 0.5 + ] + } + }, + "properties": { + "name": "Reference Point", + "description": "Specific point of interest", + "color": "red" + }, + "id": "1" + }, + { + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [ + 100.0, + 100.0 + ], + [ + 200.0, + 200.0 + ], + [ + 300.0, + 100.0 + ] + ], + "coordinatesystem": { + "axes": [ + "x", + "y" + ], + "units": [ + "micrometer", + "micrometer" + ], + "pixelsPerUnit": [ + 0.5, + 0.5 + ] + } + }, + "properties": { + "name": "Cell Path", + "description": "Path traced within a cell", + "color": "blue" + }, + "id": "2" + } ], - "units": [ - "micrometer", - "micrometer", - "micrometer" - ], - "pixelsPerUnit": [ - 0.5, - 0.5, - 2 - ], - }, - "type": "FeatureCollection", - "features": [ - { - "type": "Feature", - "properties": { - "id": "cell-1", - "type": "cell", - "label": "Cell A", - "color": "red" - }, - "geometry": { - "type": "Polygon", - "coordinates": [ - [ - [10, 10], - [10, 50], - [50, 50], - [50, 10], - [10, 10] - ] - ] - } - }, - { - "type": "Feature", - "properties": { - "id": "cell-2", - "type": "cell", - "label": "Cell B", - "color": "blue" - }, - "geometry": { - "type": "Polygon", - "coordinates": [ - [ - [60, 60], - [60, 100], - [100, 100], - [100, 60], - [60, 60] - ] + "coordinatesystem": { + "axes": [ + "x", + "y" + ], + "units": [ + "micrometer", + "micrometer" + ], + "pixelsPerUnit": [ + 0.5, + 0.5 ] - } - }, - { - "type": "Feature", - "properties": { - "id": "nucleus-1", - "type": "nucleus", - "label": "Nucleus A", - "parentCell": "cell-1" - }, - "geometry": { - "type": "Point", - "coordinates": [30, 30] - } - }, - { - "type": "Feature", - "properties": { - "id": "nucleus-2", - "type": "nucleus", - "label": "Nucleus B", - "parentCell": "cell-2" - }, - "geometry": { - "type": "Point", - "coordinates": [80, 80] - } } - ] } + ``` \ No newline at end of file diff --git a/docs/index.md b/docs/index.md old mode 100644 new mode 100755 index ba6f66b..ec857c6 --- a/docs/index.md +++ b/docs/index.md @@ -8,13 +8,15 @@ MicroJSON is a format, inspired by GeoJSON, for encoding a variety of data struc ### MicroJSON Object -A MicroJSON object is a JSON object that represents a geometry, feature, or collection of features. +A MicroJSON object is a JSON object that represents a geometry, feature, or collection of features. A MicroJSON object MAY have a `bbox` property": + +- `"bbox"`: (Optional) Bounding Box of the feature represented as an array of length 4 (2D) or length 6 (3D). ### Geometry Object -A geometry object is a JSON object where the `type` member's value is one of the following strings: `"Point"`, `"MultiPoint"`, `"LineString"`, `"MultiLineString"`, `"Polygon"`, `"Rectangle"`, or `"MultiPolygon"`. +A geometry object is a JSON object where the `type` member's value is one of the following strings: `"Point"`, `"MultiPoint"`, `"LineString"`, `"MultiLineString"`, `"Polygon"`, `"Rectangle"`, `"MultiPolygon"`, or `"GeometryCollection"`. -Each geometry object must have a `"coordinates"` member with an array value. The structure of the coordinates array depends on the geometry type: +Each geometry object must have a `"coordinates"` member with an array value. The structure of the coordinates array depends on the geometry type. - **Point**: The coordinates array must contain two or three (if 3D) numbers representing the X and Y (and Z) coordinates of the point in the image. A “Point” Geometry MAY have a radius, if indicating a circular object, with the value in pixels, specified as a member `“radius”` of the Geometry object. @@ -40,12 +42,12 @@ A GeometryCollection is an array of geometries (Point, multipoint, LinesString, A feature object represents a spatially bounded entity associated with properties specific to that entity. A feature object is a JSON object with the following members: +- `"id"`: (Optional) A unique identifier for this feature. - `"type"`: A string with the value `"Feature"`. - - `"geometry"`: A geometry object as defined in the section above or a JSON null value. - - `"properties"`: A JSON object containing properties specific to the feature, or a JSON null value. + #### Special Feature Objects - **Image**: An image MUST have the following key-value pairs in its “properties” object: @@ -58,7 +60,7 @@ A feature object represents a spatially bounded entity associated with propertie ### FeatureCollection Object -A FeatureCollection object is a JSON object representing a collection of feature objects. A FeatureCollection object has a member with the name `"features"`. The value of `"features"` is a JSON array. Each element of the array is +A FeatureCollection object is a JSON object representing a collection of feature objects. A FeatureCollection object has a member with the name `"features"`. The value of `"features"` is a JSON array. Each element of the array is a Feature object as defined above. It MUST have the top-level property `"coordinates"`. The value of this property is a Coordinates object. a Feature object as defined above. It is possible for this array to be empty. @@ -81,7 +83,6 @@ A coordinates object represents the choice of axes (2D or 3D) and potentially th It MAY contain the following properties: - `"units"`: Representing the units of the corresponding axis in the axes property. It MUST be an array with the elements having any of the following values: `[“pixel“, “meter”, ”decimeter”, “centimeter“, “millimeter”, “micrometer”, “nanometer”, “picometer“, “radian“, “degree“]` - - `"pixelsPerUnit"`: A decimal value, except for angles, where it SHOULD have the value “0”. diff --git a/docs/init_highlight.js b/docs/init_highlight.js new file mode 100755 index 0000000..9c4e0f7 --- /dev/null +++ b/docs/init_highlight.js @@ -0,0 +1,5 @@ +document.addEventListener('DOMContentLoaded', (event) => { + document.querySelectorAll('pre code').forEach((block) => { + hljs.highlightBlock(block); + }); + }); \ No newline at end of file diff --git a/docs/validation.md b/docs/validation.md old mode 100644 new mode 100755 diff --git a/mkdocs.yml b/mkdocs.yml old mode 100644 new mode 100755 index 725ce53..89c90d3 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,7 +1,42 @@ -site_name: MicroJSON specification +site_name: MicroJSON Documentation site_author: Bengt Ljungquist copyright: (c) 2023 PolusAI +extra_javascript: + - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.5.0/highlight.min.js + - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.5.0/languages/json.min.js + - init_highlight.js +extra_css: + - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.5.0/styles/default.min.css + +markdown_extensions: + - codehilite +theme: + name: material + features: + - navigation.tabs + - navigation.sections + - toc.integrate + - navigation.top + - search.suggest + - search.highlight + - content.tabs.link + - content.code.annotation + - content.code.copy + language: en + palette: + - scheme: default + toggle: + icon: material/toggle-switch-off-outline + name: Switch to dark mode + primary: teal + accent: purple + - scheme: slate + toggle: + icon: material/toggle-switch + name: Switch to light mode + primary: teal + accent: lime nav: - - Specification: index.md - - Examples: example.md - - Validation Tool: validation.md + - Home: 'index.md' + - Example: 'example.md' + - Validation: 'validation.md' From 67152121d3def709035d695e0b4c1dda51ffe09d Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Tue, 13 Jun 2023 08:28:24 -0400 Subject: [PATCH 05/28] Added docs, tests, roundtrip generation. --- .github/workflows/ci.yml | 23 + README.md | 9 +- docs/example.md | 170 +- docs/index.md | 33 +- docs/init_highlight.js | 5 + docs/ome_ngff.md | 115 ++ docs/validation.md | 3 - external/GeoJSON.json | 1491 +++++++++++++++++ geojson_schema.json | 5 +- .../test_microjson _auto.py | 4 +- inactive_tests/test_microjson_round.py | 84 + microjson/__init__.py | 4 +- microjson/geojson.py | 455 +++++ microjson/model.py | 22 +- microjson/roundtrip.py | 248 +++ microjson/utils.py | 12 + microjson_schema.json | 23 +- microjsonschema.ts | 23 +- mkdocs.yml | 43 +- noxfile.py | 23 + pyproject.toml | 6 +- .../invalid/feature/1d-point.json | 0 .../invalid/feature/boolean-id.json | 0 .../invalid/feature/no-geometry.json | 0 .../invalid/feature/no-properties.json | 0 .../invalid/feature/no-type.json | 0 .../invalid/feature/object-id.json | 0 .../featurecollection/no-features.json | 0 .../invalid/featurecollection/no-type.json | 0 .../invalid/geometrycollection/1d.json | 0 .../geometrycollection/bad-geometry.json | 0 .../geometrycollection/no-geometries.json | 0 .../invalid/geometrycollection/no-type.json | 0 .../{ => geojson}/invalid/linestring/1d.json | 0 .../invalid/linestring/bad-bbox.json | 0 .../invalid/linestring/no-coordinates.json | 0 .../invalid/linestring/no-type.json | 0 .../invalid/multilinestring/1d.json | 0 .../invalid/multilinestring/bad-bbox.json | 0 .../multilinestring/no-coordinates.json | 0 .../invalid/multilinestring/no-type.json | 0 .../{ => geojson}/invalid/multipoint/1d.json | 0 .../invalid/multipoint/bad-bbox.json | 0 .../invalid/multipoint/no-coordinates.json | 0 .../invalid/multipoint/no-type.json | 0 .../invalid/multipolygon/1d.json | 0 .../invalid/multipolygon/bad-bbox.json | 0 .../invalid/multipolygon/no-coordinates.json | 0 .../invalid/multipolygon/no-type.json | 0 .../json/{ => geojson}/invalid/point/1d.json | 0 .../{ => geojson}/invalid/point/bad-bbox.json | 0 .../invalid/point/no-coordinates.json | 0 .../{ => geojson}/invalid/point/no-type.json | 0 .../{ => geojson}/invalid/polygon/1d.json | 0 .../invalid/polygon/bad-type.json | 0 .../invalid/polygon/no-coordinates.json | 0 .../invalid/polygon/no-type.json | 0 .../{ => geojson}/valid/feature/basic.json | 0 .../valid/feature/empty-polygon.json | 0 .../valid/feature/null-geometry.json | 0 .../valid/feature/with-number-id.json | 0 .../valid/feature/with-string-id.json | 0 .../valid/featurecollection/basic.json | 0 .../valid/featurecollection/empty.json | 0 .../valid/geometrycollection/3d.json | 0 .../valid/geometrycollection/basic.json | 0 .../valid/geometrycollection/bbox.json | 0 .../{ => geojson}/valid/linestring/3d.json | 0 .../{ => geojson}/valid/linestring/basic.json | 0 .../{ => geojson}/valid/linestring/bbox.json | 0 .../valid/multilinestring/3d.json | 0 .../valid/multilinestring/basic.json | 0 .../valid/multilinestring/bbox.json | 0 .../{ => geojson}/valid/multipoint/3d.json | 0 .../{ => geojson}/valid/multipoint/basic.json | 0 .../{ => geojson}/valid/multipoint/bbox.json | 0 .../{ => geojson}/valid/multipolygon/3d.json | 0 .../valid/multipolygon/basic.json | 0 .../valid/multipolygon/bbox.json | 0 tests/json/{ => geojson}/valid/point/3d.json | 0 .../json/{ => geojson}/valid/point/basic.json | 0 .../json/{ => geojson}/valid/point/bbox.json | 0 .../{ => geojson}/valid/point/extended.json | 0 .../json/{ => geojson}/valid/polygon/3d.json | 0 .../{ => geojson}/valid/polygon/basic.json | 0 .../{ => geojson}/valid/polygon/bbox.json | 0 .../{ => geojson}/valid/polygon/extended.json | 0 .../invalid/empty-coordinatesystem.json | 21 + tests/json/microjson/invalid/noaxes.json | 25 + tests/json/microjson/valid/axesonly.json | 25 + tests/json/microjson/valid/fullexample.json | 89 + .../microjson/valid/no-coordinatesystem.json | 19 + .../microjson/valid/no-pixelsperunit.json | 29 + tests/test_geojson_schema.py | 51 + tests/test_microjson.py | 41 +- tests/test_original_schema.py | 51 + 96 files changed, 2977 insertions(+), 175 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 docs/init_highlight.js create mode 100644 docs/ome_ngff.md delete mode 100644 docs/validation.md create mode 100644 external/GeoJSON.json rename {tests => inactive_tests}/test_microjson _auto.py (96%) create mode 100644 inactive_tests/test_microjson_round.py create mode 100644 microjson/geojson.py create mode 100644 microjson/roundtrip.py create mode 100644 microjson/utils.py rename tests/json/{ => geojson}/invalid/feature/1d-point.json (100%) rename tests/json/{ => geojson}/invalid/feature/boolean-id.json (100%) rename tests/json/{ => geojson}/invalid/feature/no-geometry.json (100%) rename tests/json/{ => geojson}/invalid/feature/no-properties.json (100%) rename tests/json/{ => geojson}/invalid/feature/no-type.json (100%) rename tests/json/{ => geojson}/invalid/feature/object-id.json (100%) rename tests/json/{ => geojson}/invalid/featurecollection/no-features.json (100%) rename tests/json/{ => geojson}/invalid/featurecollection/no-type.json (100%) rename tests/json/{ => geojson}/invalid/geometrycollection/1d.json (100%) rename tests/json/{ => geojson}/invalid/geometrycollection/bad-geometry.json (100%) rename tests/json/{ => geojson}/invalid/geometrycollection/no-geometries.json (100%) rename tests/json/{ => geojson}/invalid/geometrycollection/no-type.json (100%) rename tests/json/{ => geojson}/invalid/linestring/1d.json (100%) rename tests/json/{ => geojson}/invalid/linestring/bad-bbox.json (100%) rename tests/json/{ => geojson}/invalid/linestring/no-coordinates.json (100%) rename tests/json/{ => geojson}/invalid/linestring/no-type.json (100%) rename tests/json/{ => geojson}/invalid/multilinestring/1d.json (100%) rename tests/json/{ => geojson}/invalid/multilinestring/bad-bbox.json (100%) rename tests/json/{ => geojson}/invalid/multilinestring/no-coordinates.json (100%) rename tests/json/{ => geojson}/invalid/multilinestring/no-type.json (100%) rename tests/json/{ => geojson}/invalid/multipoint/1d.json (100%) rename tests/json/{ => geojson}/invalid/multipoint/bad-bbox.json (100%) rename tests/json/{ => geojson}/invalid/multipoint/no-coordinates.json (100%) rename tests/json/{ => geojson}/invalid/multipoint/no-type.json (100%) rename tests/json/{ => geojson}/invalid/multipolygon/1d.json (100%) rename tests/json/{ => geojson}/invalid/multipolygon/bad-bbox.json (100%) rename tests/json/{ => geojson}/invalid/multipolygon/no-coordinates.json (100%) rename tests/json/{ => geojson}/invalid/multipolygon/no-type.json (100%) rename tests/json/{ => geojson}/invalid/point/1d.json (100%) rename tests/json/{ => geojson}/invalid/point/bad-bbox.json (100%) rename tests/json/{ => geojson}/invalid/point/no-coordinates.json (100%) rename tests/json/{ => geojson}/invalid/point/no-type.json (100%) rename tests/json/{ => geojson}/invalid/polygon/1d.json (100%) rename tests/json/{ => geojson}/invalid/polygon/bad-type.json (100%) rename tests/json/{ => geojson}/invalid/polygon/no-coordinates.json (100%) rename tests/json/{ => geojson}/invalid/polygon/no-type.json (100%) rename tests/json/{ => geojson}/valid/feature/basic.json (100%) rename tests/json/{ => geojson}/valid/feature/empty-polygon.json (100%) rename tests/json/{ => geojson}/valid/feature/null-geometry.json (100%) rename tests/json/{ => geojson}/valid/feature/with-number-id.json (100%) rename tests/json/{ => geojson}/valid/feature/with-string-id.json (100%) rename tests/json/{ => geojson}/valid/featurecollection/basic.json (100%) rename tests/json/{ => geojson}/valid/featurecollection/empty.json (100%) rename tests/json/{ => geojson}/valid/geometrycollection/3d.json (100%) rename tests/json/{ => geojson}/valid/geometrycollection/basic.json (100%) rename tests/json/{ => geojson}/valid/geometrycollection/bbox.json (100%) rename tests/json/{ => geojson}/valid/linestring/3d.json (100%) rename tests/json/{ => geojson}/valid/linestring/basic.json (100%) rename tests/json/{ => geojson}/valid/linestring/bbox.json (100%) rename tests/json/{ => geojson}/valid/multilinestring/3d.json (100%) rename tests/json/{ => geojson}/valid/multilinestring/basic.json (100%) rename tests/json/{ => geojson}/valid/multilinestring/bbox.json (100%) rename tests/json/{ => geojson}/valid/multipoint/3d.json (100%) rename tests/json/{ => geojson}/valid/multipoint/basic.json (100%) rename tests/json/{ => geojson}/valid/multipoint/bbox.json (100%) rename tests/json/{ => geojson}/valid/multipolygon/3d.json (100%) rename tests/json/{ => geojson}/valid/multipolygon/basic.json (100%) rename tests/json/{ => geojson}/valid/multipolygon/bbox.json (100%) rename tests/json/{ => geojson}/valid/point/3d.json (100%) rename tests/json/{ => geojson}/valid/point/basic.json (100%) rename tests/json/{ => geojson}/valid/point/bbox.json (100%) rename tests/json/{ => geojson}/valid/point/extended.json (100%) rename tests/json/{ => geojson}/valid/polygon/3d.json (100%) rename tests/json/{ => geojson}/valid/polygon/basic.json (100%) rename tests/json/{ => geojson}/valid/polygon/bbox.json (100%) rename tests/json/{ => geojson}/valid/polygon/extended.json (100%) create mode 100644 tests/json/microjson/invalid/empty-coordinatesystem.json create mode 100644 tests/json/microjson/invalid/noaxes.json create mode 100644 tests/json/microjson/valid/axesonly.json create mode 100644 tests/json/microjson/valid/fullexample.json create mode 100644 tests/json/microjson/valid/no-coordinatesystem.json create mode 100644 tests/json/microjson/valid/no-pixelsperunit.json create mode 100644 tests/test_geojson_schema.py create mode 100644 tests/test_original_schema.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..716620d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,23 @@ + name: ci + on: + push: + branches: + - master + - main + permissions: + contents: write + jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: 3.x + - uses: actions/cache@v2 + with: + key: ${{ github.ref }} + path: .cache + - run: pip install mkdocs-material + - run: pip install pillow cairosvg + - run: mkdocs gh-deploy --force \ No newline at end of file diff --git a/README.md b/README.md index b161b00..a01afa0 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ MicroJSON is a JSON-based format inspired by [GeoJSON](https://geojson.org), des MicroJSON offers a range of features designed to meet the needs of microscopy data representation: -- **Flexible Data Structures:** MicroJSON can represent diverse data structures, including geometries (such as points, multipoints, linestrings, polygons), features (individual entities with specific properties), and feature collections (groups of features). +- **Flexible Data Structures:** MicroJSON can represent diverse data structures, including geometries (such as points, multipoints, linestrings, polygons), features (individual entities with specific properties), feature collections (groups of features), and coordinate systems. - **Standardized Format:** MicroJSON uses the widely adopted JSON format, ensuring compatibility with a wide range of programming languages and tools. @@ -22,12 +22,11 @@ Refer to the examples foler to see samples of MicroJSON files as well as a simpl ## Specification -For more detailed information about the MicroJSON structure and its components, please refer to the [Specification](docs/index.md) file. +For more detailed information about the MicroJSON structure and its components, please refer to the [Specification](docs/index.md) file. It also includes a page on [MicroJSON and OME NGFF](docs/ome_ngff.md). +## External Resources -## Validation - -We provide validation through Pydantic data classes. [Validation documentation](docs/validation.md). +The GeoJSON test files are copied from the [GeoJSON Schema GitHub repository](https://github.com/geojson/schema), and are Copyright (c) 2018 Tim Schaub under MIT License. ## Contribution diff --git a/docs/example.md b/docs/example.md index e302175..b10f28a 100644 --- a/docs/example.md +++ b/docs/example.md @@ -3,93 +3,93 @@ This JSON file demonstrates how MicroJSON can be used to define and describe com ```json { - "coordinatesystem": { - "axes": [ - "x", - "y", - "z" + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + 200.0, + 150.0 + ], + "coordinatesystem": { + "axes": [ + "x", + "y" + ], + "units": [ + "micrometer", + "micrometer" + ], + "pixelsPerUnit": [ + 0.5, + 0.5 + ] + } + }, + "properties": { + "name": "Reference Point", + "description": "Specific point of interest", + "color": "red" + }, + "id": "1" + }, + { + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [ + 100.0, + 100.0 + ], + [ + 200.0, + 200.0 + ], + [ + 300.0, + 100.0 + ] + ], + "coordinatesystem": { + "axes": [ + "x", + "y" + ], + "units": [ + "micrometer", + "micrometer" + ], + "pixelsPerUnit": [ + 0.5, + 0.5 + ] + } + }, + "properties": { + "name": "Cell Path", + "description": "Path traced within a cell", + "color": "blue" + }, + "id": "2" + } ], - "units": [ - "micrometer", - "micrometer", - "micrometer" - ], - "pixelsPerUnit": [ - 0.5, - 0.5, - 2 - ], - }, - "type": "FeatureCollection", - "features": [ - { - "type": "Feature", - "properties": { - "id": "cell-1", - "type": "cell", - "label": "Cell A", - "color": "red" - }, - "geometry": { - "type": "Polygon", - "coordinates": [ - [ - [10, 10], - [10, 50], - [50, 50], - [50, 10], - [10, 10] - ] - ] - } - }, - { - "type": "Feature", - "properties": { - "id": "cell-2", - "type": "cell", - "label": "Cell B", - "color": "blue" - }, - "geometry": { - "type": "Polygon", - "coordinates": [ - [ - [60, 60], - [60, 100], - [100, 100], - [100, 60], - [60, 60] - ] + "coordinatesystem": { + "axes": [ + "x", + "y" + ], + "units": [ + "micrometer", + "micrometer" + ], + "pixelsPerUnit": [ + 0.5, + 0.5 ] - } - }, - { - "type": "Feature", - "properties": { - "id": "nucleus-1", - "type": "nucleus", - "label": "Nucleus A", - "parentCell": "cell-1" - }, - "geometry": { - "type": "Point", - "coordinates": [30, 30] - } - }, - { - "type": "Feature", - "properties": { - "id": "nucleus-2", - "type": "nucleus", - "label": "Nucleus B", - "parentCell": "cell-2" - }, - "geometry": { - "type": "Point", - "coordinates": [80, 80] - } } - ] } + ``` \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index ba6f66b..28db759 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,19 +2,26 @@ ## Introduction -MicroJSON is a format, inspired by GeoJSON, for encoding a variety of data structures related to microscopy images, including reference points, regions of interest, and other annotations. These data structures are represented using the widely adopted JSON format, making it easy to work with in various programming languages and applications. +MicroJSON is a format, inspired by [GeoJSON](https://geojson.org), for encoding a variety of data structures related to microscopy images, including reference points, regions of interest, and other annotations. These data structures are represented using the widely adopted JSON format, making it easy to work with in various programming languages and applications. It is fully backwards compatible with the [GeoJSON Specification, RFC 7946](https://datatracker.ietf.org/doc/html/rfc7946), since any GeoJSON also is accepted as a MicroJSON. As GeoJSON supports foreign top level properties, a MicroJSON is also a valid GeoJSON. This specification describes briefly the objects that exist in GeoJSON, and then in more detail describes the additional objects that are part of MicroJSON. For a more detailed description of the GeoJSON objects, please see the [GeoJSON Specification, RFC 7946](https://datatracker.ietf.org/doc/html/rfc7946). ## Objects ### MicroJSON Object -A MicroJSON object is a JSON object that represents a geometry, feature, or collection of features. +A MicroJSON object is a JSON object that represents a geometry, feature, or collection of features, or more precisely, be either of type (having value of top level field `type` as) `"Geometry"`, `"Feature"`, or `"Featurecollection"`, that is, the same as for GeoJSON. What separates MicroJSON from GeoJSON is that it MAY have of a top level property `"coordinatesystem"`: + +- `"coordinatesystem"`: (Optional) A coordinate system object as defined in the section [Coordinates object](#coordinates-object). If this property is not present, the default coordinate system is assumed to be the same as the image coordinate system, using cartesian coordinates and pixels as units. + +A MicroJSON object MAY have a `"bbox"` property": + +- `"bbox"`: (Optional) Bounding Box of the feature represented as an array of length 4 (2D) or length 6 (3D). + ### Geometry Object -A geometry object is a JSON object where the `type` member's value is one of the following strings: `"Point"`, `"MultiPoint"`, `"LineString"`, `"MultiLineString"`, `"Polygon"`, `"Rectangle"`, or `"MultiPolygon"`. +A geometry object is a JSON object where the `type` member's value is one of the following strings: `"Point"`, `"MultiPoint"`, `"LineString"`, `"MultiLineString"`, `"Polygon"`, `"Rectangle"`, `"MultiPolygon"`, or `"GeometryCollection"`. -Each geometry object must have a `"coordinates"` member with an array value. The structure of the coordinates array depends on the geometry type: +Each geometry object must have a `"coordinates"` member with an array value. The structure of the coordinates array depends on the geometry type. - **Point**: The coordinates array must contain two or three (if 3D) numbers representing the X and Y (and Z) coordinates of the point in the image. A “Point” Geometry MAY have a radius, if indicating a circular object, with the value in pixels, specified as a member `“radius”` of the Geometry object. @@ -26,9 +33,9 @@ Each geometry object must have a `"coordinates"` member with an array value. The - **Polygon**: The coordinates array must be an array of linear ring coordinate arrays, where the first linear ring represents the outer boundary and any additional rings represent holes within the polygon. - - A subtype of “Polygon” is the “Rectangle” geometry: A polygon with an array of four point coordinates representing the corners of the rectangle in a clockwise order. + - A subtype of “Polygon” is the “Rectangle” geometry: A polygon with an array of four 2D point coordinates representing the corners of the rectangle in a counterclockwise order. It has the property subtype with the value `“Rectangle”`. - - A subtype of “Polygon” is the “Cuboid”: A polygon with an array of eight point coordinates representing the corners of the rectangle in a clockwise order in the X-Y plane, starting with the plane with the lowest Z value. + - A subtype of “Polygon” is the “Cuboid”: A polygon with an array of eight 3D point coordinates representing the corners of the rectangle in a counterclockwise order in the X-Y plane, starting with the plane with the lowest Z value. It has the property subtype with the value `“Cuboid”`. - **MultiPolygon**: The coordinates array must be an array of Polygon coordinate arrays. @@ -38,13 +45,12 @@ A GeometryCollection is an array of geometries (Point, multipoint, LinesString, ### Feature Object -A feature object represents a spatially bounded entity associated with properties specific to that entity. A feature object is a JSON object with the following members: +A feature object represents a spatially bounded entity associated with properties specific to that entity. A feature object is a JSON object with the following members, required unless otherwise noted: - `"type"`: A string with the value `"Feature"`. - - `"geometry"`: A geometry object as defined in the section above or a JSON null value. - -- `"properties"`: A JSON object containing properties specific to the feature, or a JSON null value. +- `"properties"`: (Optional) A JSON object containing properties specific to the feature, or a JSON null value. +- `"id"`: (Optional) A unique identifier for this feature. #### Special Feature Objects @@ -58,11 +64,7 @@ A feature object represents a spatially bounded entity associated with propertie ### FeatureCollection Object -A FeatureCollection object is a JSON object representing a collection of feature objects. A FeatureCollection object has a member with the name `"features"`. The value of `"features"` is a JSON array. Each element of the array is - - a Feature object as defined above. It is possible for this array to be empty. - -It MUST have the top-level property `"coordinates"`. The value of this property is a Coordinates object. +A FeatureCollection object is a JSON object representing a collection of feature objects. A FeatureCollection object has a member with the name `"features"`. The value of `"features"` is a JSON array. Each element of the array is a Feature object as defined above. It is possible for this array to be empty. #### Special FeatureCollection Objects @@ -81,7 +83,6 @@ A coordinates object represents the choice of axes (2D or 3D) and potentially th It MAY contain the following properties: - `"units"`: Representing the units of the corresponding axis in the axes property. It MUST be an array with the elements having any of the following values: `[“pixel“, “meter”, ”decimeter”, “centimeter“, “millimeter”, “micrometer”, “nanometer”, “picometer“, “radian“, “degree“]` - - `"pixelsPerUnit"`: A decimal value, except for angles, where it SHOULD have the value “0”. diff --git a/docs/init_highlight.js b/docs/init_highlight.js new file mode 100644 index 0000000..9c4e0f7 --- /dev/null +++ b/docs/init_highlight.js @@ -0,0 +1,5 @@ +document.addEventListener('DOMContentLoaded', (event) => { + document.querySelectorAll('pre code').forEach((block) => { + hljs.highlightBlock(block); + }); + }); \ No newline at end of file diff --git a/docs/ome_ngff.md b/docs/ome_ngff.md new file mode 100644 index 0000000..38dd80f --- /dev/null +++ b/docs/ome_ngff.md @@ -0,0 +1,115 @@ +## OME NGFF and MicroJSON + +OME NGFF provides a clear, efficient, and extensible format for storing and interacting with bioimaging data. Its ability to incorporate high-dimensional data and the existing rich ecosystem of tools and libraries that support Zarr (the underlying storage format for OME NGFF) make it a robust choice for imaging applications. + +MicroJSON, as a minimal geometric data interchange format, offers a concise and streamlined way to represent and communicate geometric information. Its lightweight nature and the ability to precisely describe geometric shapes and positions make it an attractive choice to enrich the OME NGFF with geometric metadata. + +## MicroJSON within OME NGFF: A Proposal + +There are several ways to integrate MicroJSON within the OME NGFF data hierarchy. Here are a few potential placements: + +- **Multiscales Metadata - dataset level:** The `multiscales` metadata section describes various scales of the data. In this context, it's conceivable that MicroJSON could be used to add geometry metadata to specific scale levels. If the MicroJSON data directly relates to specific datasets (i.e., it describes geometric properties of the data within these datasets), then it might make sense to include this data as part of the metadata for each dataset. This could be achieved by adding a new key (e.g., "microjson") within each "datasets" dictionary. The value of this key would be the MicroJSON data relevant to that dataset. + + A possible solution could be to create a new key, "microjson", in each dataset dictionary. Each "microjson" entry could be a list of MicroJSON objects that apply to the corresponding dataset. For instance, if a MicroJSON object represents a spatial feature identified in the image data, that object could be linked to the corresponding image dataset. + + In this way, the MicroJSON data would be directly correlated to the relevant datasets, allowing users to directly connect the geometric data with the image data it describes. This seems to align well with the intent of the "multiscales" metadata to provide detailed, contextual information about how to interpret and understand the underlying image data. In examples where axes and units are the same for the MicroJSON data and the dataset, the MicroJSON "coordinatesystem" object could be omitted. + + Here is an example of how this could look: + +```json +{ + "multiscales": [ + { + "version": "0.5-dev", + "name": "example", + "axes": [ ... ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ ... ], + "microjson": [ + { + "type": "Polygon", + "coordinates": [ ... ], + "coordinatesystem": { + "axes": ["x", "y"], + "units": ["micrometer", "micrometer"] + } + } + // More MicroJSON objects... + ] + }, + // More datasets... + ], + "coordinateTransformations": [ ... ], + "type": "gaussian", + "metadata": { ... } + } + ] +} +``` + +In this example, each dataset has a "microjson" field, which is a list of MicroJSON objects that provide geometric context for that dataset. This approach allows users to directly relate the MicroJSON geometric data to the image data it describes, providing a rich, contextual interpretation of the data. + +- **Multiscales meta data - top level**: If the MicroJSON data is globally relevant , then it could be included as a new key (e.g., "microjson") at the top level of the "multiscales" metadata. + +```json +{ + "multiscales": [ + { + "version": "0.5-dev", + "name": "example", + "axes": [ ... ], + "datasets": [ ... ], + "coordinateTransformations": [ ... ], + "type": "gaussian", + "metadata": { ... } + } + ], + "microjson": [ + { + "type": "Polygon", + "coordinates": [ ... ], + "coordinatesystem": { + "axes": ["x", "y"], + "units": ["micrometer", "micrometer"] + } + } + // More MicroJSON objects... + ] +} +``` + + +- **Root metadata level:** The root metadata section describes the overall structure of the data. If the MicroJSON data is relevant to the entire dataset, then it could be included as a new key (e.g., "microjson") at the top level of the root metadata, that is, in the top folder of the OME NGFF data hierarchy. This would for example be suitable for a stitching vector, which would be a MicroJSON object in the root metadata, under a key `stitchingVectors`: + +```json +{ + "stitchingVectors": [ + { + "type": "FeatureCollection", + "properties": { + "type": "StitchingVector" + }, + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [...], + "subtype": "Rectangle" + }, + "properties": { + "type": "Image", + "URI": "./123.zarr", + } + }, + // More images... + ] + } + // More stitching vectors... + ], + // Other metadata... +} + +``` \ No newline at end of file diff --git a/docs/validation.md b/docs/validation.md deleted file mode 100644 index 46f7b5c..0000000 --- a/docs/validation.md +++ /dev/null @@ -1,3 +0,0 @@ -# MicroJSON Pydantic validation - -To ensure the correctness and validity of MicroJSON files, we provide a tool that uses the Python Pydantic library to create a model of the MicroJSON schema, which forms the basis of the validation process. The validation works by comparing your MicroJSON files against the defined Pydantic model. It checks for adherence to the schema, ensuring that required fields are present and that the data types for all fields are as expected. The pydantic model is in the `microjson.py` file. It contains both a pydantic model for GeoJSON, `microjson.GeoJSON`, as well as MicroJSON, `microjson.MicroJSON` \ No newline at end of file diff --git a/external/GeoJSON.json b/external/GeoJSON.json new file mode 100644 index 0000000..edeb56c --- /dev/null +++ b/external/GeoJSON.json @@ -0,0 +1,1491 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://geojson.org/schema/GeoJSON.json", + "title": "GeoJSON", + "oneOf": [ + { + "title": "GeoJSON Point", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Point" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON LineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "LineString" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON Polygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Polygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPoint", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPoint" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiLineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiLineString" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPolygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPolygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON GeometryCollection", + "type": "object", + "required": [ + "type", + "geometries" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "GeometryCollection" + ] + }, + "geometries": { + "type": "array", + "items": { + "oneOf": [ + { + "title": "GeoJSON Point", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Point" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON LineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "LineString" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON Polygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Polygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPoint", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPoint" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiLineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiLineString" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPolygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPolygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + ] + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON Feature", + "type": "object", + "required": [ + "type", + "properties", + "geometry" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Feature" + ] + }, + "id": { + "oneOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "properties": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "object" + } + ] + }, + "geometry": { + "oneOf": [ + { + "type": "null" + }, + { + "title": "GeoJSON Point", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Point" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON LineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "LineString" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON Polygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Polygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPoint", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPoint" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiLineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiLineString" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPolygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPolygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON GeometryCollection", + "type": "object", + "required": [ + "type", + "geometries" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "GeometryCollection" + ] + }, + "geometries": { + "type": "array", + "items": { + "oneOf": [ + { + "title": "GeoJSON Point", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Point" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON LineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "LineString" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON Polygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Polygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPoint", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPoint" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiLineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiLineString" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPolygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPolygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + ] + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + ] + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON FeatureCollection", + "type": "object", + "required": [ + "type", + "features" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "FeatureCollection" + ] + }, + "features": { + "type": "array", + "items": { + "title": "GeoJSON Feature", + "type": "object", + "required": [ + "type", + "properties", + "geometry" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Feature" + ] + }, + "id": { + "oneOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "properties": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "object" + } + ] + }, + "geometry": { + "oneOf": [ + { + "type": "null" + }, + { + "title": "GeoJSON Point", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Point" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON LineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "LineString" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON Polygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Polygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPoint", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPoint" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiLineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiLineString" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPolygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPolygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON GeometryCollection", + "type": "object", + "required": [ + "type", + "geometries" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "GeometryCollection" + ] + }, + "geometries": { + "type": "array", + "items": { + "oneOf": [ + { + "title": "GeoJSON Point", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Point" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON LineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "LineString" + ] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON Polygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "Polygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPoint", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPoint" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiLineString", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiLineString" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPolygon", + "type": "object", + "required": [ + "type", + "coordinates" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "MultiPolygon" + ] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + ] + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + ] + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + ] +} diff --git a/geojson_schema.json b/geojson_schema.json index 075833f..2a5c769 100644 --- a/geojson_schema.json +++ b/geojson_schema.json @@ -334,7 +334,7 @@ }, "geometry": { "title": "Geometry", - "description": "The geometric data\n of the feature", + "description": "The geometry of the\n feature", "anyOf": [ { "$ref": "#/definitions/Point" @@ -356,6 +356,9 @@ }, { "$ref": "#/definitions/GeometryCollection" + }, + { + "type": "null" } ] }, diff --git a/tests/test_microjson _auto.py b/inactive_tests/test_microjson _auto.py similarity index 96% rename from tests/test_microjson _auto.py rename to inactive_tests/test_microjson _auto.py index 0fd1d4d..9d3caaa 100644 --- a/tests/test_microjson _auto.py +++ b/inactive_tests/test_microjson _auto.py @@ -5,8 +5,8 @@ import os # Define the directories containing the example JSON files -VALID_EXAMPLES_DIR = 'tests/json/valid' -INVALID_EXAMPLES_DIR = 'tests/json/invalid' +VALID_EXAMPLES_DIR = 'tests/geojson/json/valid' +INVALID_EXAMPLES_DIR = 'tests/geojson/json/invalid' def gather_example_files(directory): diff --git a/inactive_tests/test_microjson_round.py b/inactive_tests/test_microjson_round.py new file mode 100644 index 0000000..99972ef --- /dev/null +++ b/inactive_tests/test_microjson_round.py @@ -0,0 +1,84 @@ +import json +import pytest +from pydantic import ValidationError +from microjson import MicroJSONRound, gather_example_files +from geojson.roundtrip import GeoJSON + +# Define the directories containing the example JSON files +VALID_EXAMPLES_DIR = 'tests/geojson/json/geojson/valid' +INVALID_EXAMPLES_DIR = 'tests/geojson/json/geojson/invalid' + +# Gather the example files +valid_examples = gather_example_files(VALID_EXAMPLES_DIR) +invalid_examples = gather_example_files(INVALID_EXAMPLES_DIR) + + +@pytest.mark.parametrize("filename", valid_examples) +def test_valid_geojsons(filename): + with open(filename, 'r') as f: + data = json.load(f, strict=True) + + # Try to parse the data as a GeoJSON object + try: + _ = GeoJSON.parse_obj(data) + except ValidationError as e: + pytest.fail(f"""ValidationError occurred + during validation of {filename}: {str(e)}""") + except Exception as e: + pytest.fail(f"""Unexpected error occurred + during validation of {filename}: {str(e)}""") + + +@pytest.mark.parametrize("filename", invalid_examples) +def test_invalid_geojsons(filename): + with open(filename, 'r') as f: + data = json.load(f, strict=True) + + # This will raise a ValidationError if the data does not match the GeoJSON + try: + _ = GeoJSON.parse_obj(data) + pytest.fail(f"""Parsing succeeded on {filename}, + but it should not have.""") + except ValidationError: + # The validation error is expected, so we just pass + pass + except Exception as e: + # An unexpected error occurred, so we fail the test + pytest.fail(f"""Unexpected error occurred + during validation of {filename}: {str(e)}""") + + +@pytest.mark.parametrize("filename", valid_examples) +def test_valid_microjsons(filename): + with open(filename, 'r') as f: + data = json.load(f, strict=True) + + # Try to parse the data as a GeoJSON object + try: + _ = MicroJSONRound.parse_obj(data) + except ValidationError as e: + pytest.fail(f"""ValidationError occurred + during validation of {filename}: {str(e)}""") + except Exception as e: + pytest.fail(f"""Unexpected error occurred + during validation of {filename}: {str(e)}""") + + +@pytest.mark.parametrize("filename", invalid_examples) +def test_invalid_microjsons(filename): + with open(filename, 'r') as f: + data = json.load(f, strict=True) + + # This will raise a ValidationError if the data does not + # match the GeoJSON schema + try: + _ = MicroJSONRound.parse_obj(data) + pytest.fail(f"""Parsing succeeded on {filename}, + but it should not have.""") + except ValidationError: + # The validation error is expected, so we just pass + pass + except Exception as e: + # An unexpected error occurred, so we fail the test + pytest.fail(f"""Unexpected error occurred + during validation of {filename}: {str(e)}""") diff --git a/microjson/__init__.py b/microjson/__init__.py index c52b1cb..6ca2914 100644 --- a/microjson/__init__.py +++ b/microjson/__init__.py @@ -1,2 +1,4 @@ from .model import MicroJSON, GeoJSON -from .automodel import GeoJSONAuto, MicroJSONAuto \ No newline at end of file +from .automodel import GeoJSONAuto, MicroJSONAuto +from .roundtrip import MicroJSON as MicroJSONRound +from .utils import gather_example_files \ No newline at end of file diff --git a/microjson/geojson.py b/microjson/geojson.py new file mode 100644 index 0000000..ccaaf1b --- /dev/null +++ b/microjson/geojson.py @@ -0,0 +1,455 @@ +# generated by datamodel-codegen: +# filename: GeoJSON.json +# timestamp: 2023-06-06T00:26:31+00:00 + +from __future__ import annotations + +from typing import List, Optional + +from pydantic import BaseModel, Field + + +class Type(BaseModel): + type: str + enum: List[str] + + +class Items3(BaseModel): + type: str + + +class Items2(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Optional[Items3] = None + + +class Items1(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Optional[Items2] = None + + +class Items(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Optional[Items1] = None + + +class Coordinates(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Items + + +class Items4(Items3): + pass + + +class Bbox(BaseModel): + type: str + min_items: int = Field(..., alias='minItems') + items: Items4 + + +class Type1(Type): + pass + + +class Items9(Items3): + pass + + +class Items8(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Optional[Items9] = None + + +class Items7(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Optional[Items8] = None + + +class Items6(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Optional[Items7] = None + + +class Coordinates1(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Items6 + + +class Items10(Items3): + pass + + +class Bbox1(BaseModel): + type: str + min_items: int = Field(..., alias='minItems') + items: Items10 + + +class Properties1(BaseModel): + type: Type1 + coordinates: Coordinates1 + bbox: Bbox1 + + +class OneOfItem1(BaseModel): + title: str + type: str + required: List[str] + properties: Properties1 + + +class Items5(BaseModel): + one_of: List[OneOfItem1] = Field(..., alias='oneOf') + + +class Geometries(BaseModel): + type: str + items: Items5 + + +class OneOfItem2(Items3): + pass + + +class Id(BaseModel): + one_of: List[OneOfItem2] = Field(..., alias='oneOf') + + +class OneOfItem3(Items3): + pass + + +class Properties2(BaseModel): + one_of: List[OneOfItem3] = Field(..., alias='oneOf') + + +class Type2(Type): + pass + + +class Items14(Items3): + pass + + +class Items13(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Optional[Items14] = None + + +class Items12(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Optional[Items13] = None + + +class Items11(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Optional[Items12] = None + + +class Coordinates2(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Items11 + + +class Items15(Items3): + pass + + +class Bbox2(BaseModel): + type: str + min_items: int = Field(..., alias='minItems') + items: Items15 + + +class Type3(Type): + pass + + +class Items20(Items3): + pass + + +class Items19(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Optional[Items20] = None + + +class Items18(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Optional[Items19] = None + + +class Items17(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Optional[Items18] = None + + +class Coordinates3(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Items17 + + +class Items21(Items3): + pass + + +class Bbox3(BaseModel): + type: str + min_items: int = Field(..., alias='minItems') + items: Items21 + + +class Properties4(BaseModel): + type: Type3 + coordinates: Coordinates3 + bbox: Bbox3 + + +class OneOfItem5(BaseModel): + title: str + type: str + required: List[str] + properties: Properties4 + + +class Items16(BaseModel): + one_of: List[OneOfItem5] = Field(..., alias='oneOf') + + +class Geometries1(BaseModel): + type: str + items: Items16 + + +class Properties3(BaseModel): + type: Type2 + coordinates: Optional[Coordinates2] = None + bbox: Bbox2 + geometries: Optional[Geometries1] = None + + +class OneOfItem4(BaseModel): + type: str + title: Optional[str] = None + required: Optional[List[str]] = None + properties: Optional[Properties3] = None + + +class Geometry(BaseModel): + one_of: List[OneOfItem4] = Field(..., alias='oneOf') + + +class Type4(Type): + pass + + +class OneOfItem6(Items3): + pass + + +class Id1(BaseModel): + one_of: List[OneOfItem6] = Field(..., alias='oneOf') + + +class OneOfItem7(Items3): + pass + + +class Properties6(BaseModel): + one_of: List[OneOfItem7] = Field(..., alias='oneOf') + + +class Type5(Type): + pass + + +class Items26(Items3): + pass + + +class Items25(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Optional[Items26] = None + + +class Items24(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Optional[Items25] = None + + +class Items23(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Optional[Items24] = None + + +class Coordinates4(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Items23 + + +class Items27(Items3): + pass + + +class Bbox4(BaseModel): + type: str + min_items: int = Field(..., alias='minItems') + items: Items27 + + +class Type6(Type): + pass + + +class Items32(Items3): + pass + + +class Items31(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Optional[Items32] = None + + +class Items30(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Optional[Items31] = None + + +class Items29(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Optional[Items30] = None + + +class Coordinates5(BaseModel): + type: str + min_items: Optional[int] = Field(None, alias='minItems') + items: Items29 + + +class Items33(Items3): + pass + + +class Bbox5(BaseModel): + type: str + min_items: int = Field(..., alias='minItems') + items: Items33 + + +class Properties8(BaseModel): + type: Type6 + coordinates: Coordinates5 + bbox: Bbox5 + + +class OneOfItem9(BaseModel): + title: str + type: str + required: List[str] + properties: Properties8 + + +class Items28(BaseModel): + one_of: List[OneOfItem9] = Field(..., alias='oneOf') + + +class Geometries2(BaseModel): + type: str + items: Items28 + + +class Properties7(BaseModel): + type: Type5 + coordinates: Optional[Coordinates4] = None + bbox: Bbox4 + geometries: Optional[Geometries2] = None + + +class OneOfItem8(BaseModel): + type: str + title: Optional[str] = None + required: Optional[List[str]] = None + properties: Optional[Properties7] = None + + +class Geometry1(BaseModel): + one_of: List[OneOfItem8] = Field(..., alias='oneOf') + + +class Items34(Items3): + pass + + +class Bbox6(BaseModel): + type: str + min_items: int = Field(..., alias='minItems') + items: Items34 + + +class Properties5(BaseModel): + type: Type4 + id: Id1 + properties: Properties6 + geometry: Geometry1 + bbox: Bbox6 + + +class Items22(BaseModel): + title: str + type: str + required: List[str] + properties: Properties5 + + +class Features(BaseModel): + type: str + items: Items22 + + +class Properties(BaseModel): + type: Type + coordinates: Optional[Coordinates] = None + bbox: Bbox + geometries: Optional[Geometries] = None + id: Optional[Id] = None + properties: Optional[Properties2] = None + geometry: Optional[Geometry] = None + features: Optional[Features] = None + + +class OneOfItem(BaseModel): + title: str + type: str + required: List[str] + properties: Properties + + +class Model(BaseModel): + _schema: str = Field(..., alias='$schema') + _id: str = Field(..., alias='$id') + title: str + one_of: List[OneOfItem] = Field(..., alias='oneOf') diff --git a/microjson/model.py b/microjson/model.py index 12687d8..4cdd571 100644 --- a/microjson/model.py +++ b/microjson/model.py @@ -59,14 +59,16 @@ class GeometryCollection(GeoAbstract): MultiLineString, Polygon, MultiPolygon, - GeometryCollection] + GeometryCollection, + type(None) + ] class Feature(GeoAbstract): type: Literal["Feature"] - geometry: Optional[GeometryType] = Field(..., - description="""The geometric data - of the feature""") + geometry: GeometryType = Field(..., + description="""The geometry of the + feature""") properties: Optional[Dict] = Field(..., description="""Properties of the feature""") @@ -97,17 +99,14 @@ class Unit(Enum): class Coordinatesystem(BaseModel): - axes: List[Literal["x", "y", "z", "r", "theta", "phi"]] = Field( - ..., description="The coordinate system of the coordinates" - ) - units: List[Unit] = Field(..., description="The units of the coordinates") - pixelsPerUnit: List[float] = Field( - ..., description="The number of pixels per unit" - ) + axes: List[Literal["x", "y", "z", "r", "theta", "phi"]] + units: Optional[List[Unit]] + pixelsPerUnit: Optional[List[float]] class MicroPoint(Point): coordinatesystem: Optional[Coordinatesystem] + radius: Optional[float] class MicroMultiPoint(MultiPoint): @@ -124,6 +123,7 @@ class MicroMultiLineString(MultiLineString): class MicroPolygon(Polygon): coordinatesystem: Optional[Coordinatesystem] + subtype: Optional[Literal["rectangle", "cuboid"]] class MicroMultiPolygon(MultiPolygon): diff --git a/microjson/roundtrip.py b/microjson/roundtrip.py new file mode 100644 index 0000000..7ec496e --- /dev/null +++ b/microjson/roundtrip.py @@ -0,0 +1,248 @@ +# generated by datamodel-codegen: +# filename: microjson_schema.json +# timestamp: 2023-06-13T12:15:57+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import Any, Dict, List, Optional, Union + +from pydantic import BaseModel, Field + + +class Type(Enum): + point = 'Point' + + +class Point(BaseModel): + bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + type: Type = Field(..., title='Type') + coordinates: List[float] = Field(..., max_items=3, min_items=2, title='Coordinates') + + +class Type1(Enum): + multi_point = 'MultiPoint' + + +class MultiPoint(BaseModel): + bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + type: Type1 = Field(..., title='Type') + coordinates: List[List[float]] = Field(..., title='Coordinates') + + +class Type2(Enum): + line_string = 'LineString' + + +class LineString(BaseModel): + bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + type: Type2 = Field(..., title='Type') + coordinates: List[List[float]] = Field(..., title='Coordinates') + + +class Type3(Enum): + multi_line_string = 'MultiLineString' + + +class MultiLineString(BaseModel): + bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + type: Type3 = Field(..., title='Type') + coordinates: List[List[List[float]]] = Field(..., title='Coordinates') + + +class Type4(Enum): + polygon = 'Polygon' + + +class Polygon(BaseModel): + bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + type: Type4 = Field(..., title='Type') + coordinates: List[List[List[float]]] = Field(..., title='Coordinates') + + +class Type5(Enum): + multi_polygon = 'MultiPolygon' + + +class MultiPolygon(BaseModel): + bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + type: Type5 = Field(..., title='Type') + coordinates: List[List[List[List[float]]]] = Field(..., title='Coordinates') + + +class Type6(Enum): + geometry_collection = 'GeometryCollection' + + +class GeometryCollection(BaseModel): + bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + type: Type6 = Field(..., title='Type') + geometries: List[ + Union[Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon] + ] = Field(..., title='Geometries') + + +class Unit(Enum): + pixel = 'pixel' + meter = 'meter' + decimeter = 'decimeter' + centimeter = 'centimeter' + millimeter = 'millimeter' + micrometer = 'micrometer' + nanometer = 'nanometer' + picometer = 'picometer' + radian = 'radian' + degree = 'degree' + + +class Axe(Enum): + x = 'x' + y = 'y' + z = 'z' + r = 'r' + theta = 'theta' + phi = 'phi' + + +class Coordinatesystem(BaseModel): + axes: List[Axe] = Field(..., title='Axes') + units: Optional[List[Unit]] = None + pixels_per_unit: Optional[List[float]] = Field( + None, alias='pixelsPerUnit', title='Pixelsperunit' + ) + + +class Type7(Enum): + feature = 'Feature' + + +class MicroFeature(BaseModel): + bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + type: Type7 = Field(..., title='Type') + geometry: Union[ + Point, + MultiPoint, + LineString, + MultiLineString, + Polygon, + MultiPolygon, + GeometryCollection, + ] = Field( + ..., + description='The geometry of the\n feature', + title='Geometry', + ) + properties: Dict[str, Any] = Field( + ..., + description='Properties of the\n feature', + title='Properties', + ) + id: Optional[Union[str, int]] = Field(None, title='Id') + coordinatesystem: Optional[Coordinatesystem] = None + + +class Feature(BaseModel): + bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + type: Type7 = Field(..., title='Type') + geometry: Union[ + Point, + MultiPoint, + LineString, + MultiLineString, + Polygon, + MultiPolygon, + GeometryCollection, + ] = Field( + ..., + description='The geometry of the\n feature', + title='Geometry', + ) + properties: Dict[str, Any] = Field( + ..., + description='Properties of the\n feature', + title='Properties', + ) + id: Optional[Union[str, int]] = Field(None, title='Id') + + +class Type9(Enum): + feature_collection = 'FeatureCollection' + + +class MicroFeatureCollection(BaseModel): + bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + type: Type9 = Field(..., title='Type') + features: List[Feature] = Field(..., title='Features') + coordinatesystem: Optional[Coordinatesystem] = None + + +class MicroPoint(BaseModel): + bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + type: Type = Field(..., title='Type') + coordinates: List[float] = Field(..., max_items=3, min_items=2, title='Coordinates') + coordinatesystem: Optional[Coordinatesystem] = None + radius: Optional[float] = Field(None, title='Radius') + + +class MicroMultiPoint(BaseModel): + bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + type: Type1 = Field(..., title='Type') + coordinates: List[List[float]] = Field(..., title='Coordinates') + coordinatesystem: Optional[Coordinatesystem] = None + + +class MicroLineString(BaseModel): + bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + type: Type2 = Field(..., title='Type') + coordinates: List[List[float]] = Field(..., title='Coordinates') + coordinatesystem: Optional[Coordinatesystem] = None + + +class MicroMultiLineString(BaseModel): + bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + type: Type3 = Field(..., title='Type') + coordinates: List[List[List[float]]] = Field(..., title='Coordinates') + coordinatesystem: Optional[Coordinatesystem] = None + + +class Subtype(Enum): + rectangle = 'rectangle' + cuboid = 'cuboid' + + +class MicroPolygon(BaseModel): + bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + type: Type4 = Field(..., title='Type') + coordinates: List[List[List[float]]] = Field(..., title='Coordinates') + coordinatesystem: Optional[Coordinatesystem] = None + subtype: Optional[Subtype] = Field(None, title='Subtype') + + +class MicroMultiPolygon(BaseModel): + bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + type: Type5 = Field(..., title='Type') + coordinates: List[List[List[List[float]]]] = Field(..., title='Coordinates') + coordinatesystem: Optional[Coordinatesystem] = None + + +class MicroGeometryCollection(BaseModel): + bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + type: Type6 = Field(..., title='Type') + geometries: List[ + Union[Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon] + ] = Field(..., title='Geometries') + coordinatesystem: Optional[Coordinatesystem] = None + + +class MicroJSON(BaseModel): + __root__: Union[ + MicroFeature, + MicroFeatureCollection, + MicroPoint, + MicroMultiPoint, + MicroLineString, + MicroMultiLineString, + MicroPolygon, + MicroMultiPolygon, + MicroGeometryCollection, + ] = Field(..., description='The root object of a MicroJSON file', title='MicroJSON') diff --git a/microjson/utils.py b/microjson/utils.py new file mode 100644 index 0000000..29bbe14 --- /dev/null +++ b/microjson/utils.py @@ -0,0 +1,12 @@ +import os + + +def gather_example_files(directory): + files = [] + # Walk through the directory + for dirpath, dirnames, filenames in os.walk(directory): + # Filter to just the .json files + example_files = [os.path.join(dirpath, f) + for f in filenames if f.endswith('.json')] + files.extend(example_files) + return files diff --git a/microjson_schema.json b/microjson_schema.json index 278ad42..99ddc5f 100644 --- a/microjson_schema.json +++ b/microjson_schema.json @@ -335,7 +335,6 @@ "properties": { "axes": { "title": "Axes", - "description": "The coordinate system of the coordinates", "type": "array", "items": { "enum": [ @@ -350,7 +349,6 @@ } }, "units": { - "description": "The units of the coordinates", "type": "array", "items": { "$ref": "#/definitions/Unit" @@ -358,7 +356,6 @@ }, "pixelsPerUnit": { "title": "Pixelsperunit", - "description": "The number of pixels per unit", "type": "array", "items": { "type": "number" @@ -366,9 +363,7 @@ } }, "required": [ - "axes", - "units", - "pixelsPerUnit" + "axes" ] }, "MicroFeature": { @@ -392,7 +387,7 @@ }, "geometry": { "title": "Geometry", - "description": "The geometric data\n of the feature", + "description": "The geometry of the\n feature", "anyOf": [ { "$ref": "#/definitions/Point" @@ -464,7 +459,7 @@ }, "geometry": { "title": "Geometry", - "description": "The geometric data\n of the feature", + "description": "The geometry of the\n feature", "anyOf": [ { "$ref": "#/definitions/Point" @@ -577,6 +572,10 @@ }, "coordinatesystem": { "$ref": "#/definitions/Coordinatesystem" + }, + "radius": { + "title": "Radius", + "type": "number" } }, "required": [ @@ -743,6 +742,14 @@ }, "coordinatesystem": { "$ref": "#/definitions/Coordinatesystem" + }, + "subtype": { + "title": "Subtype", + "enum": [ + "rectangle", + "cuboid" + ], + "type": "string" } }, "required": [ diff --git a/microjsonschema.ts b/microjsonschema.ts index a4de4d6..35ed56f 100644 --- a/microjsonschema.ts +++ b/microjsonschema.ts @@ -44,18 +44,9 @@ export type MicroJSON = | MicroGeometryCollection; export interface Coordinatesystem { - /** - * The coordinate system of the coordinates - */ axes: ("x" | "y" | "z" | "r" | "theta" | "phi")[]; - /** - * The units of the coordinates - */ - units: Unit[]; - /** - * The number of pixels per unit - */ - pixelsPerUnit: number[]; + units?: Unit[]; + pixelsPerUnit?: number[]; } export interface Feature { /** @@ -64,8 +55,8 @@ export interface Feature { bbox?: [number, number, number, number, ...number[]]; type: "Feature"; /** - * The geometric data - * of the feature + * The geometry of the + * feature */ geometry: Point | MultiPoint | LineString | MultiLineString | Polygon | MultiPolygon | GeometryCollection; /** @@ -158,8 +149,8 @@ export interface MicroFeature { bbox?: [number, number, number, number, ...number[]]; type: "Feature"; /** - * The geometric data - * of the feature + * The geometry of the + * feature */ geometry: Point | MultiPoint | LineString | MultiLineString | Polygon | MultiPolygon | GeometryCollection; /** @@ -202,6 +193,7 @@ export interface MicroPoint { */ coordinates: [number, number] | [number, number, number]; coordinatesystem?: Coordinatesystem; + radius?: number; } export interface MicroMultiPoint { /** @@ -238,6 +230,7 @@ export interface MicroPolygon { type: "Polygon"; coordinates: [number, number] | [number, number, number][][]; coordinatesystem?: Coordinatesystem; + subtype?: "rectangle" | "cuboid"; } export interface MicroMultiPolygon { /** diff --git a/mkdocs.yml b/mkdocs.yml index 725ce53..d681c34 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,7 +1,42 @@ -site_name: MicroJSON specification +site_name: MicroJSON Documentation site_author: Bengt Ljungquist copyright: (c) 2023 PolusAI +extra_javascript: + - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.5.0/highlight.min.js + - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.5.0/languages/json.min.js + - init_highlight.js +extra_css: + - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.5.0/styles/default.min.css + +markdown_extensions: + - codehilite +theme: + name: material + features: + - navigation.tabs + - navigation.sections + - toc.integrate + - navigation.top + - search.suggest + - search.highlight + - content.tabs.link + - content.code.annotation + - content.code.copy + language: en + palette: + - scheme: default + toggle: + icon: material/toggle-switch-off-outline + name: Switch to dark mode + primary: teal + accent: purple + - scheme: slate + toggle: + icon: material/toggle-switch + name: Switch to light mode + primary: teal + accent: lime nav: - - Specification: index.md - - Examples: example.md - - Validation Tool: validation.md + - Home: 'index.md' + - Example: 'example.md' + - OME NGFF integration: 'ome_ngff.md' diff --git a/noxfile.py b/noxfile.py index 23a4dd3..23cb187 100644 --- a/noxfile.py +++ b/noxfile.py @@ -16,9 +16,32 @@ def export_ts(session): # generate GeoJSON schema from microjson.model import GeoJSON + # Get the schema for the model as a dictionary + schema_dict = GeoJSON.schema() + # Add 'null' to the list of allowed types for the 'geometry' field of + # Feature object + schema_dict = schema_dict['definitions']['Feature'] + schema_dict['properties']['geometry']['anyOf'].append({'type': 'null'}) with open('geojson_schema.json', 'w') as f: f.write(GeoJSON.schema_json(indent=2)) from microjson.model import MicroJSON with open('microjson_schema.json', 'w') as f: f.write(MicroJSON.schema_json(indent=2)) + + # generate pydantic model from the GeoJSON schema + session.run("datamodel-codegen", + "--reuse-model", + "--snake-case-field", + "--input", "geojson_schema.json", + "--output", "geojson/roundtrip.py", + "--target", "3.9") + + # generate pydantic model from the MicroJSON schema + session.run("datamodel-codegen", + "--reuse-model", + "--snake-case-field", + "--input", "microjson_schema.json", + "--output", "microjson/roundtrip.py", + "--target", "3.9") + diff --git a/pyproject.toml b/pyproject.toml index d213876..fd20452 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "poetry.core.masonry.api" name = "microjson" version = "0.1.0" description = "MicroJSON is a library for validating, parsing, and manipulating MicroJSON data." -authors = ["Bengt Ljungquist "] +authors = ["Bengt Ljungquist "] license = "MIT" [tool.poetry.dependencies] @@ -16,3 +16,7 @@ pydantic = "^1.8.2" [tool.poetry.dev-dependencies] pytest = "^6.2.4" nox = "^2021.6.12" + + +[tool.pytest.ini_options] +testpaths = ["tests/"] \ No newline at end of file diff --git a/tests/json/invalid/feature/1d-point.json b/tests/json/geojson/invalid/feature/1d-point.json similarity index 100% rename from tests/json/invalid/feature/1d-point.json rename to tests/json/geojson/invalid/feature/1d-point.json diff --git a/tests/json/invalid/feature/boolean-id.json b/tests/json/geojson/invalid/feature/boolean-id.json similarity index 100% rename from tests/json/invalid/feature/boolean-id.json rename to tests/json/geojson/invalid/feature/boolean-id.json diff --git a/tests/json/invalid/feature/no-geometry.json b/tests/json/geojson/invalid/feature/no-geometry.json similarity index 100% rename from tests/json/invalid/feature/no-geometry.json rename to tests/json/geojson/invalid/feature/no-geometry.json diff --git a/tests/json/invalid/feature/no-properties.json b/tests/json/geojson/invalid/feature/no-properties.json similarity index 100% rename from tests/json/invalid/feature/no-properties.json rename to tests/json/geojson/invalid/feature/no-properties.json diff --git a/tests/json/invalid/feature/no-type.json b/tests/json/geojson/invalid/feature/no-type.json similarity index 100% rename from tests/json/invalid/feature/no-type.json rename to tests/json/geojson/invalid/feature/no-type.json diff --git a/tests/json/invalid/feature/object-id.json b/tests/json/geojson/invalid/feature/object-id.json similarity index 100% rename from tests/json/invalid/feature/object-id.json rename to tests/json/geojson/invalid/feature/object-id.json diff --git a/tests/json/invalid/featurecollection/no-features.json b/tests/json/geojson/invalid/featurecollection/no-features.json similarity index 100% rename from tests/json/invalid/featurecollection/no-features.json rename to tests/json/geojson/invalid/featurecollection/no-features.json diff --git a/tests/json/invalid/featurecollection/no-type.json b/tests/json/geojson/invalid/featurecollection/no-type.json similarity index 100% rename from tests/json/invalid/featurecollection/no-type.json rename to tests/json/geojson/invalid/featurecollection/no-type.json diff --git a/tests/json/invalid/geometrycollection/1d.json b/tests/json/geojson/invalid/geometrycollection/1d.json similarity index 100% rename from tests/json/invalid/geometrycollection/1d.json rename to tests/json/geojson/invalid/geometrycollection/1d.json diff --git a/tests/json/invalid/geometrycollection/bad-geometry.json b/tests/json/geojson/invalid/geometrycollection/bad-geometry.json similarity index 100% rename from tests/json/invalid/geometrycollection/bad-geometry.json rename to tests/json/geojson/invalid/geometrycollection/bad-geometry.json diff --git a/tests/json/invalid/geometrycollection/no-geometries.json b/tests/json/geojson/invalid/geometrycollection/no-geometries.json similarity index 100% rename from tests/json/invalid/geometrycollection/no-geometries.json rename to tests/json/geojson/invalid/geometrycollection/no-geometries.json diff --git a/tests/json/invalid/geometrycollection/no-type.json b/tests/json/geojson/invalid/geometrycollection/no-type.json similarity index 100% rename from tests/json/invalid/geometrycollection/no-type.json rename to tests/json/geojson/invalid/geometrycollection/no-type.json diff --git a/tests/json/invalid/linestring/1d.json b/tests/json/geojson/invalid/linestring/1d.json similarity index 100% rename from tests/json/invalid/linestring/1d.json rename to tests/json/geojson/invalid/linestring/1d.json diff --git a/tests/json/invalid/linestring/bad-bbox.json b/tests/json/geojson/invalid/linestring/bad-bbox.json similarity index 100% rename from tests/json/invalid/linestring/bad-bbox.json rename to tests/json/geojson/invalid/linestring/bad-bbox.json diff --git a/tests/json/invalid/linestring/no-coordinates.json b/tests/json/geojson/invalid/linestring/no-coordinates.json similarity index 100% rename from tests/json/invalid/linestring/no-coordinates.json rename to tests/json/geojson/invalid/linestring/no-coordinates.json diff --git a/tests/json/invalid/linestring/no-type.json b/tests/json/geojson/invalid/linestring/no-type.json similarity index 100% rename from tests/json/invalid/linestring/no-type.json rename to tests/json/geojson/invalid/linestring/no-type.json diff --git a/tests/json/invalid/multilinestring/1d.json b/tests/json/geojson/invalid/multilinestring/1d.json similarity index 100% rename from tests/json/invalid/multilinestring/1d.json rename to tests/json/geojson/invalid/multilinestring/1d.json diff --git a/tests/json/invalid/multilinestring/bad-bbox.json b/tests/json/geojson/invalid/multilinestring/bad-bbox.json similarity index 100% rename from tests/json/invalid/multilinestring/bad-bbox.json rename to tests/json/geojson/invalid/multilinestring/bad-bbox.json diff --git a/tests/json/invalid/multilinestring/no-coordinates.json b/tests/json/geojson/invalid/multilinestring/no-coordinates.json similarity index 100% rename from tests/json/invalid/multilinestring/no-coordinates.json rename to tests/json/geojson/invalid/multilinestring/no-coordinates.json diff --git a/tests/json/invalid/multilinestring/no-type.json b/tests/json/geojson/invalid/multilinestring/no-type.json similarity index 100% rename from tests/json/invalid/multilinestring/no-type.json rename to tests/json/geojson/invalid/multilinestring/no-type.json diff --git a/tests/json/invalid/multipoint/1d.json b/tests/json/geojson/invalid/multipoint/1d.json similarity index 100% rename from tests/json/invalid/multipoint/1d.json rename to tests/json/geojson/invalid/multipoint/1d.json diff --git a/tests/json/invalid/multipoint/bad-bbox.json b/tests/json/geojson/invalid/multipoint/bad-bbox.json similarity index 100% rename from tests/json/invalid/multipoint/bad-bbox.json rename to tests/json/geojson/invalid/multipoint/bad-bbox.json diff --git a/tests/json/invalid/multipoint/no-coordinates.json b/tests/json/geojson/invalid/multipoint/no-coordinates.json similarity index 100% rename from tests/json/invalid/multipoint/no-coordinates.json rename to tests/json/geojson/invalid/multipoint/no-coordinates.json diff --git a/tests/json/invalid/multipoint/no-type.json b/tests/json/geojson/invalid/multipoint/no-type.json similarity index 100% rename from tests/json/invalid/multipoint/no-type.json rename to tests/json/geojson/invalid/multipoint/no-type.json diff --git a/tests/json/invalid/multipolygon/1d.json b/tests/json/geojson/invalid/multipolygon/1d.json similarity index 100% rename from tests/json/invalid/multipolygon/1d.json rename to tests/json/geojson/invalid/multipolygon/1d.json diff --git a/tests/json/invalid/multipolygon/bad-bbox.json b/tests/json/geojson/invalid/multipolygon/bad-bbox.json similarity index 100% rename from tests/json/invalid/multipolygon/bad-bbox.json rename to tests/json/geojson/invalid/multipolygon/bad-bbox.json diff --git a/tests/json/invalid/multipolygon/no-coordinates.json b/tests/json/geojson/invalid/multipolygon/no-coordinates.json similarity index 100% rename from tests/json/invalid/multipolygon/no-coordinates.json rename to tests/json/geojson/invalid/multipolygon/no-coordinates.json diff --git a/tests/json/invalid/multipolygon/no-type.json b/tests/json/geojson/invalid/multipolygon/no-type.json similarity index 100% rename from tests/json/invalid/multipolygon/no-type.json rename to tests/json/geojson/invalid/multipolygon/no-type.json diff --git a/tests/json/invalid/point/1d.json b/tests/json/geojson/invalid/point/1d.json similarity index 100% rename from tests/json/invalid/point/1d.json rename to tests/json/geojson/invalid/point/1d.json diff --git a/tests/json/invalid/point/bad-bbox.json b/tests/json/geojson/invalid/point/bad-bbox.json similarity index 100% rename from tests/json/invalid/point/bad-bbox.json rename to tests/json/geojson/invalid/point/bad-bbox.json diff --git a/tests/json/invalid/point/no-coordinates.json b/tests/json/geojson/invalid/point/no-coordinates.json similarity index 100% rename from tests/json/invalid/point/no-coordinates.json rename to tests/json/geojson/invalid/point/no-coordinates.json diff --git a/tests/json/invalid/point/no-type.json b/tests/json/geojson/invalid/point/no-type.json similarity index 100% rename from tests/json/invalid/point/no-type.json rename to tests/json/geojson/invalid/point/no-type.json diff --git a/tests/json/invalid/polygon/1d.json b/tests/json/geojson/invalid/polygon/1d.json similarity index 100% rename from tests/json/invalid/polygon/1d.json rename to tests/json/geojson/invalid/polygon/1d.json diff --git a/tests/json/invalid/polygon/bad-type.json b/tests/json/geojson/invalid/polygon/bad-type.json similarity index 100% rename from tests/json/invalid/polygon/bad-type.json rename to tests/json/geojson/invalid/polygon/bad-type.json diff --git a/tests/json/invalid/polygon/no-coordinates.json b/tests/json/geojson/invalid/polygon/no-coordinates.json similarity index 100% rename from tests/json/invalid/polygon/no-coordinates.json rename to tests/json/geojson/invalid/polygon/no-coordinates.json diff --git a/tests/json/invalid/polygon/no-type.json b/tests/json/geojson/invalid/polygon/no-type.json similarity index 100% rename from tests/json/invalid/polygon/no-type.json rename to tests/json/geojson/invalid/polygon/no-type.json diff --git a/tests/json/valid/feature/basic.json b/tests/json/geojson/valid/feature/basic.json similarity index 100% rename from tests/json/valid/feature/basic.json rename to tests/json/geojson/valid/feature/basic.json diff --git a/tests/json/valid/feature/empty-polygon.json b/tests/json/geojson/valid/feature/empty-polygon.json similarity index 100% rename from tests/json/valid/feature/empty-polygon.json rename to tests/json/geojson/valid/feature/empty-polygon.json diff --git a/tests/json/valid/feature/null-geometry.json b/tests/json/geojson/valid/feature/null-geometry.json similarity index 100% rename from tests/json/valid/feature/null-geometry.json rename to tests/json/geojson/valid/feature/null-geometry.json diff --git a/tests/json/valid/feature/with-number-id.json b/tests/json/geojson/valid/feature/with-number-id.json similarity index 100% rename from tests/json/valid/feature/with-number-id.json rename to tests/json/geojson/valid/feature/with-number-id.json diff --git a/tests/json/valid/feature/with-string-id.json b/tests/json/geojson/valid/feature/with-string-id.json similarity index 100% rename from tests/json/valid/feature/with-string-id.json rename to tests/json/geojson/valid/feature/with-string-id.json diff --git a/tests/json/valid/featurecollection/basic.json b/tests/json/geojson/valid/featurecollection/basic.json similarity index 100% rename from tests/json/valid/featurecollection/basic.json rename to tests/json/geojson/valid/featurecollection/basic.json diff --git a/tests/json/valid/featurecollection/empty.json b/tests/json/geojson/valid/featurecollection/empty.json similarity index 100% rename from tests/json/valid/featurecollection/empty.json rename to tests/json/geojson/valid/featurecollection/empty.json diff --git a/tests/json/valid/geometrycollection/3d.json b/tests/json/geojson/valid/geometrycollection/3d.json similarity index 100% rename from tests/json/valid/geometrycollection/3d.json rename to tests/json/geojson/valid/geometrycollection/3d.json diff --git a/tests/json/valid/geometrycollection/basic.json b/tests/json/geojson/valid/geometrycollection/basic.json similarity index 100% rename from tests/json/valid/geometrycollection/basic.json rename to tests/json/geojson/valid/geometrycollection/basic.json diff --git a/tests/json/valid/geometrycollection/bbox.json b/tests/json/geojson/valid/geometrycollection/bbox.json similarity index 100% rename from tests/json/valid/geometrycollection/bbox.json rename to tests/json/geojson/valid/geometrycollection/bbox.json diff --git a/tests/json/valid/linestring/3d.json b/tests/json/geojson/valid/linestring/3d.json similarity index 100% rename from tests/json/valid/linestring/3d.json rename to tests/json/geojson/valid/linestring/3d.json diff --git a/tests/json/valid/linestring/basic.json b/tests/json/geojson/valid/linestring/basic.json similarity index 100% rename from tests/json/valid/linestring/basic.json rename to tests/json/geojson/valid/linestring/basic.json diff --git a/tests/json/valid/linestring/bbox.json b/tests/json/geojson/valid/linestring/bbox.json similarity index 100% rename from tests/json/valid/linestring/bbox.json rename to tests/json/geojson/valid/linestring/bbox.json diff --git a/tests/json/valid/multilinestring/3d.json b/tests/json/geojson/valid/multilinestring/3d.json similarity index 100% rename from tests/json/valid/multilinestring/3d.json rename to tests/json/geojson/valid/multilinestring/3d.json diff --git a/tests/json/valid/multilinestring/basic.json b/tests/json/geojson/valid/multilinestring/basic.json similarity index 100% rename from tests/json/valid/multilinestring/basic.json rename to tests/json/geojson/valid/multilinestring/basic.json diff --git a/tests/json/valid/multilinestring/bbox.json b/tests/json/geojson/valid/multilinestring/bbox.json similarity index 100% rename from tests/json/valid/multilinestring/bbox.json rename to tests/json/geojson/valid/multilinestring/bbox.json diff --git a/tests/json/valid/multipoint/3d.json b/tests/json/geojson/valid/multipoint/3d.json similarity index 100% rename from tests/json/valid/multipoint/3d.json rename to tests/json/geojson/valid/multipoint/3d.json diff --git a/tests/json/valid/multipoint/basic.json b/tests/json/geojson/valid/multipoint/basic.json similarity index 100% rename from tests/json/valid/multipoint/basic.json rename to tests/json/geojson/valid/multipoint/basic.json diff --git a/tests/json/valid/multipoint/bbox.json b/tests/json/geojson/valid/multipoint/bbox.json similarity index 100% rename from tests/json/valid/multipoint/bbox.json rename to tests/json/geojson/valid/multipoint/bbox.json diff --git a/tests/json/valid/multipolygon/3d.json b/tests/json/geojson/valid/multipolygon/3d.json similarity index 100% rename from tests/json/valid/multipolygon/3d.json rename to tests/json/geojson/valid/multipolygon/3d.json diff --git a/tests/json/valid/multipolygon/basic.json b/tests/json/geojson/valid/multipolygon/basic.json similarity index 100% rename from tests/json/valid/multipolygon/basic.json rename to tests/json/geojson/valid/multipolygon/basic.json diff --git a/tests/json/valid/multipolygon/bbox.json b/tests/json/geojson/valid/multipolygon/bbox.json similarity index 100% rename from tests/json/valid/multipolygon/bbox.json rename to tests/json/geojson/valid/multipolygon/bbox.json diff --git a/tests/json/valid/point/3d.json b/tests/json/geojson/valid/point/3d.json similarity index 100% rename from tests/json/valid/point/3d.json rename to tests/json/geojson/valid/point/3d.json diff --git a/tests/json/valid/point/basic.json b/tests/json/geojson/valid/point/basic.json similarity index 100% rename from tests/json/valid/point/basic.json rename to tests/json/geojson/valid/point/basic.json diff --git a/tests/json/valid/point/bbox.json b/tests/json/geojson/valid/point/bbox.json similarity index 100% rename from tests/json/valid/point/bbox.json rename to tests/json/geojson/valid/point/bbox.json diff --git a/tests/json/valid/point/extended.json b/tests/json/geojson/valid/point/extended.json similarity index 100% rename from tests/json/valid/point/extended.json rename to tests/json/geojson/valid/point/extended.json diff --git a/tests/json/valid/polygon/3d.json b/tests/json/geojson/valid/polygon/3d.json similarity index 100% rename from tests/json/valid/polygon/3d.json rename to tests/json/geojson/valid/polygon/3d.json diff --git a/tests/json/valid/polygon/basic.json b/tests/json/geojson/valid/polygon/basic.json similarity index 100% rename from tests/json/valid/polygon/basic.json rename to tests/json/geojson/valid/polygon/basic.json diff --git a/tests/json/valid/polygon/bbox.json b/tests/json/geojson/valid/polygon/bbox.json similarity index 100% rename from tests/json/valid/polygon/bbox.json rename to tests/json/geojson/valid/polygon/bbox.json diff --git a/tests/json/valid/polygon/extended.json b/tests/json/geojson/valid/polygon/extended.json similarity index 100% rename from tests/json/valid/polygon/extended.json rename to tests/json/geojson/valid/polygon/extended.json diff --git a/tests/json/microjson/invalid/empty-coordinatesystem.json b/tests/json/microjson/invalid/empty-coordinatesystem.json new file mode 100644 index 0000000..5aa341e --- /dev/null +++ b/tests/json/microjson/invalid/empty-coordinatesystem.json @@ -0,0 +1,21 @@ +{ + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ], + "coordinatesystem": { + } +} \ No newline at end of file diff --git a/tests/json/microjson/invalid/noaxes.json b/tests/json/microjson/invalid/noaxes.json new file mode 100644 index 0000000..4ed2961 --- /dev/null +++ b/tests/json/microjson/invalid/noaxes.json @@ -0,0 +1,25 @@ +{ + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ], + "coordinatesystem": { + "units": [ + "micrometer", + "micrometer" + ] + } +} \ No newline at end of file diff --git a/tests/json/microjson/valid/axesonly.json b/tests/json/microjson/valid/axesonly.json new file mode 100644 index 0000000..b57d9b5 --- /dev/null +++ b/tests/json/microjson/valid/axesonly.json @@ -0,0 +1,25 @@ +{ + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ], + "coordinatesystem": { + "axes": [ + "x", + "y" + ] + } +} \ No newline at end of file diff --git a/tests/json/microjson/valid/fullexample.json b/tests/json/microjson/valid/fullexample.json new file mode 100644 index 0000000..b071410 --- /dev/null +++ b/tests/json/microjson/valid/fullexample.json @@ -0,0 +1,89 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + 200.0, + 150.0 + ], + "coordinatesystem": { + "axes": [ + "x", + "y" + ], + "units": [ + "micrometer", + "micrometer" + ], + "pixelsPerUnit": [ + 0.5, + 0.5 + ] + } + }, + "properties": { + "name": "Reference Point", + "description": "Specific point of interest", + "color": "red" + }, + "id": "1" + }, + { + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [ + 100.0, + 100.0 + ], + [ + 200.0, + 200.0 + ], + [ + 300.0, + 100.0 + ] + ], + "coordinatesystem": { + "axes": [ + "x", + "y" + ], + "units": [ + "micrometer", + "micrometer" + ], + "pixelsPerUnit": [ + 0.5, + 0.5 + ] + } + }, + "properties": { + "name": "Cell Path", + "description": "Path traced within a cell", + "color": "blue" + }, + "id": "2" + } + ], + "coordinatesystem": { + "axes": [ + "x", + "y" + ], + "units": [ + "micrometer", + "micrometer" + ], + "pixelsPerUnit": [ + 0.5, + 0.5 + ] + } +} \ No newline at end of file diff --git a/tests/json/microjson/valid/no-coordinatesystem.json b/tests/json/microjson/valid/no-coordinatesystem.json new file mode 100644 index 0000000..23a8a0f --- /dev/null +++ b/tests/json/microjson/valid/no-coordinatesystem.json @@ -0,0 +1,19 @@ +{ + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ] + } \ No newline at end of file diff --git a/tests/json/microjson/valid/no-pixelsperunit.json b/tests/json/microjson/valid/no-pixelsperunit.json new file mode 100644 index 0000000..4d3dc98 --- /dev/null +++ b/tests/json/microjson/valid/no-pixelsperunit.json @@ -0,0 +1,29 @@ +{ + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ], + "coordinatesystem": { + "axes": [ + "x", + "y" + ], + "units": [ + "micrometer", + "micrometer" + ] + } +} \ No newline at end of file diff --git a/tests/test_geojson_schema.py b/tests/test_geojson_schema.py new file mode 100644 index 0000000..d6f3e5b --- /dev/null +++ b/tests/test_geojson_schema.py @@ -0,0 +1,51 @@ +import json +import jsonschema +import pytest +from jsonschema import validate +from microjson import gather_example_files + +# Load the generated GeoJSON schema +with open('geojson_schema.json') as f: + schema = json.load(f) + +# Define the directories containing the example JSON files +VALID_EXAMPLES_DIR = 'tests/json/geojson/valid' +INVALID_EXAMPLES_DIR = 'tests/json/geojson/invalid' + +# Gather the example files +valid_examples = gather_example_files(VALID_EXAMPLES_DIR) +invalid_examples = gather_example_files(INVALID_EXAMPLES_DIR) + + +@pytest.mark.parametrize("filename", valid_examples) +def test_valid_geojsons(filename): + with open(filename, 'r') as f: + data = json.load(f, strict=True) + + # Try to parse the data as a GeoJSON object + try: + validate(instance=data, schema=schema) + except jsonschema.exceptions.ValidationError as err: + pytest.fail(f"JSON Schema validation failed. Error: {err}") + except Exception as e: + pytest.fail(f"""Unexpected error occurred + during validation of {filename}: {str(e)}""") + + +@pytest.mark.parametrize("filename", invalid_examples) +def test_invalid_geojsons(filename): + with open(filename, 'r') as f: + data = json.load(f, strict=True) + + # Try to parse the data as a GeoJSON object + try: + validate(instance=data, schema=schema) + pytest.fail(f"""Parsing succeeded on {filename}, + but it should not have.""") + except jsonschema.exceptions.ValidationError: + # The validation error is expected, so we just pass + pass + except Exception as e: + # An unexpected error occurred, so we fail the test + pytest.fail(f"""Unexpected error occurred + during validation of {filename}: {str(e)}""") diff --git a/tests/test_microjson.py b/tests/test_microjson.py index 14980c1..6f8188d 100644 --- a/tests/test_microjson.py +++ b/tests/test_microjson.py @@ -1,31 +1,26 @@ import json import pytest from pydantic import ValidationError -from microjson import MicroJSON, GeoJSON -import os - -# Define the directories containing the example JSON files -VALID_EXAMPLES_DIR = 'tests/json/valid' -INVALID_EXAMPLES_DIR = 'tests/json/invalid' +from microjson import MicroJSON, GeoJSON, gather_example_files -def gather_example_files(directory): - files = [] - # Walk through the directory - for dirpath, dirnames, filenames in os.walk(directory): - # Filter to just the .json files - example_files = [os.path.join(dirpath, f) - for f in filenames if f.endswith('.json')] - files.extend(example_files) - return files +# Define the directories containing the example JSON files +VALID_EXAMPLES_DIR = 'tests/json/geojson/valid' +INVALID_EXAMPLES_DIR = 'tests/json/geojson/invalid' +VALID_MICROJSON_DIR = 'tests/json/microjson/valid' +INVALID_MICROJSON_DIR = 'tests/json/microjson/invalid' valid_examples = gather_example_files(VALID_EXAMPLES_DIR) invalid_examples = gather_example_files(INVALID_EXAMPLES_DIR) +valid_microjson = gather_example_files( + VALID_MICROJSON_DIR) + valid_examples +invalid_microjson = gather_example_files( + INVALID_MICROJSON_DIR) + invalid_examples @pytest.mark.parametrize("filename", valid_examples) -def test_valid_geojsons(filename): +def test_valid_geojson(filename): with open(filename, 'r') as f: data = json.load(f, strict=True) @@ -41,7 +36,7 @@ def test_valid_geojsons(filename): @pytest.mark.parametrize("filename", invalid_examples) -def test_invalid_geojsons(filename): +def test_invalid_geojson(filename): with open(filename, 'r') as f: data = json.load(f, strict=True) @@ -59,12 +54,12 @@ def test_invalid_geojsons(filename): during validation of {filename}: {str(e)}""") -@pytest.mark.parametrize("filename", valid_examples) -def test_valid_microjsons(filename): +@pytest.mark.parametrize("filename", valid_microjson) +def test_valid_microjson(filename): with open(filename, 'r') as f: data = json.load(f, strict=True) - # Try to parse the data as a GeoJSON object + # Try to parse the data as a MicroJSON object try: _ = MicroJSON.parse_obj(data) except ValidationError as e: @@ -75,13 +70,13 @@ def test_valid_microjsons(filename): during validation of {filename}: {str(e)}""") -@pytest.mark.parametrize("filename", invalid_examples) -def test_invalid_microjsons(filename): +@pytest.mark.parametrize("filename", invalid_microjson) +def test_invalid_microjson(filename): with open(filename, 'r') as f: data = json.load(f, strict=True) # This will raise a ValidationError if the data does not - # match the GeoJSON schema + # match the MicroJSON schema try: _ = MicroJSON.parse_obj(data) pytest.fail(f"""Parsing succeeded on {filename}, diff --git a/tests/test_original_schema.py b/tests/test_original_schema.py new file mode 100644 index 0000000..fd00ea1 --- /dev/null +++ b/tests/test_original_schema.py @@ -0,0 +1,51 @@ +import json +import jsonschema +import pytest +from jsonschema import validate +from microjson import gather_example_files + +# Load the original GeoJSON schema +with open('external/GeoJSON.json') as f: + schema = json.load(f) + +# Define the directories containing the example JSON files +VALID_EXAMPLES_DIR = 'tests/json/geojson/valid' +INVALID_EXAMPLES_DIR = 'tests/json/geojson/invalid' + +# Gather the example files +valid_examples = gather_example_files(VALID_EXAMPLES_DIR) +invalid_examples = gather_example_files(INVALID_EXAMPLES_DIR) + + +@pytest.mark.parametrize("filename", valid_examples) +def test_valid_geojsons(filename): + with open(filename, 'r') as f: + data = json.load(f, strict=True) + + # Try to parse the data as a GeoJSON object + try: + validate(instance=data, schema=schema) + except jsonschema.exceptions.ValidationError as err: + pytest.fail(f"JSON Schema validation failed. Error: {err}") + except Exception as e: + pytest.fail(f"""Unexpected error occurred + during validation of {filename}: {str(e)}""") + + +@pytest.mark.parametrize("filename", invalid_examples) +def test_invalid_geojsons(filename): + with open(filename, 'r') as f: + data = json.load(f, strict=True) + + # Try to parse the data as a GeoJSON object + try: + validate(instance=data, schema=schema) + pytest.fail(f"""Parsing succeeded on {filename}, + but it should not have.""") + except jsonschema.exceptions.ValidationError: + # The validation error is expected, so we just pass + pass + except Exception as e: + # An unexpected error occurred, so we fail the test + pytest.fail(f"""Unexpected error occurred + during validation of {filename}: {str(e)}""") From 537bf92a0ffba7b89a70b5ed1723a935bdb11a6f Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Mon, 26 Jun 2023 04:26:29 -0400 Subject: [PATCH 06/28] Edited docs and examples --- README.md | 2 +- docs/example.md | 66 +++++++++++----- docs/index.md | 10 ++- examples/csv_to_microjson.py | 66 ++++++++++++++++ examples/example.py | 27 ------- examples/json/sample_geojson.json | 50 ------------ examples/json/sample_geometrycollection.json | 13 ---- examples/json/sample_microjson.json | 67 ---------------- examples/load_validate.py | 19 +++++ microjson/model.py | 30 ++++++-- mkdocs.yml | 3 +- mypy.ini | 2 + tests/json/microjson/invalid/boolean-ref.json | 12 +++ .../invalid/{noaxes.json => no-axes.json} | 0 tests/json/microjson/invalid/object-ref.json | 14 ++++ tests/json/microjson/valid/3d.json | 26 +++++++ tests/json/microjson/valid/fullexample.json | 38 ++------- .../valid/multilevel-coordinatesystems.json | 77 +++++++++++++++++++ .../microjson/valid/no-pixelsperunit.json | 4 +- .../microjson/valid/stitching-vector.json | 36 +++++++++ tests/json/microjson/valid/stringref.json | 12 +++ 21 files changed, 352 insertions(+), 222 deletions(-) create mode 100644 examples/csv_to_microjson.py delete mode 100644 examples/example.py delete mode 100644 examples/json/sample_geojson.json delete mode 100644 examples/json/sample_geometrycollection.json delete mode 100644 examples/json/sample_microjson.json create mode 100644 examples/load_validate.py create mode 100644 mypy.ini create mode 100644 tests/json/microjson/invalid/boolean-ref.json rename tests/json/microjson/invalid/{noaxes.json => no-axes.json} (100%) create mode 100644 tests/json/microjson/invalid/object-ref.json create mode 100644 tests/json/microjson/valid/3d.json create mode 100644 tests/json/microjson/valid/multilevel-coordinatesystems.json create mode 100644 tests/json/microjson/valid/stitching-vector.json create mode 100644 tests/json/microjson/valid/stringref.json diff --git a/README.md b/README.md index a01afa0..aa0c249 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Refer to the examples foler to see samples of MicroJSON files as well as a simpl ## Specification -For more detailed information about the MicroJSON structure and its components, please refer to the [Specification](docs/index.md) file. It also includes a page on [MicroJSON and OME NGFF](docs/ome_ngff.md). +For more detailed information about the MicroJSON structure and its components, please refer to the [Specification](docs/index.md) file. ## External Resources diff --git a/docs/example.md b/docs/example.md index b10f28a..e12e05d 100755 --- a/docs/example.md +++ b/docs/example.md @@ -1,5 +1,6 @@ -# Sample MicroJSON File -This JSON file demonstrates how MicroJSON can be used to define and describe complex structures related to microscopy, such as cells and their nuclei, including their spatial relationships, identifiers, labels, and color representations. +# MicroJSON Examples +## Basic MicroJSON +This JSON file demonstrates how MicroJSON can be used to define and describe different structures related to microscopy, such as cells and their nuclei, including their spatial relationships, identifiers, labels, and color representations. ```json { @@ -12,21 +13,7 @@ This JSON file demonstrates how MicroJSON can be used to define and describe com "coordinates": [ 200.0, 150.0 - ], - "coordinatesystem": { - "axes": [ - "x", - "y" - ], - "units": [ - "micrometer", - "micrometer" - ], - "pixelsPerUnit": [ - 0.5, - 0.5 - ] - } + ] }, "properties": { "name": "Reference Point", @@ -63,8 +50,8 @@ This JSON file demonstrates how MicroJSON can be used to define and describe com "micrometer" ], "pixelsPerUnit": [ - 0.5, - 0.5 + 1, + 1 ] } }, @@ -92,4 +79,45 @@ This JSON file demonstrates how MicroJSON can be used to define and describe com } } +``` + +## Stitching Vector MicroJSON +This JSON file demonstrates how MicroJSON can be used to define and describe a stitching vector, which is used to describe the spatial relationship between multiple images that may be stitched together. +```json +{ + "type": "FeatureCollection", + "coordinatesystem": { + "axes": ["x", "y"] + }, + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "subtype": "Rectangle", + "coordinates": [[[0.0, 0.0], [0.0, 50.0], [50.0, 50.0], [50.0, 0.0], [0.0, 0.0]]] + }, + "properties": { + "type": "Image", + "URI": "./image_1.tif" + } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "subtype": "Rectangle", + "coordinates": [[[50.0, 0.0], [50.0, 50.0], [100.0, 50.0], [100.0, 0.0], [50.0, 0.0]]] + }, + "properties": { + "type": "Image", + "URI": "./image_2.tif" + } + } + ], + "properties": { + "type": "StitchingVector" + } +} + ``` \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 909bcea..fd66fd7 100755 --- a/docs/index.md +++ b/docs/index.md @@ -10,7 +10,7 @@ MicroJSON is a format, inspired by [GeoJSON](https://geojson.org), for encoding A MicroJSON object is a JSON object that represents a geometry, feature, or collection of features, or more precisely, be either of type (having value of top level field `type` as) `"Geometry"`, `"Feature"`, or `"Featurecollection"`, that is, the same as for GeoJSON. What separates MicroJSON from GeoJSON is that it MAY have of a top level property `"coordinatesystem"`: -- `"coordinatesystem"`: (Optional) A coordinate system object as defined in the section [Coordinates object](#coordinates-object). If this property is not present, the default coordinate system is assumed to be the same as the image coordinate system, using cartesian coordinates and pixels as units. +- `"coordinatesystem"`: (Optional) A coordinate system object as defined in the section [Coordinates object](#coordinates-object). If this property is not present, the default coordinate system is assumed to be the same as the image coordinate system, using cartesian coordinates and pixels as units. It is recommended to define this property at the top level of the MicroJSON object, but it MAY also be defined at the level of a Feature or Geometry object, in which case it overrides the top level coordinate system. A MicroJSON object MAY have a `"bbox"` property": @@ -21,7 +21,7 @@ A MicroJSON object MAY have a `"bbox"` property": A geometry object is a JSON object where the `type` member's value is one of the following strings: `"Point"`, `"MultiPoint"`, `"LineString"`, `"MultiLineString"`, `"Polygon"`, `"Rectangle"`, `"MultiPolygon"`, or `"GeometryCollection"`. -Each geometry object must have a `"coordinates"` member with an array value. The structure of the coordinates array depends on the geometry type. +Each geometry object MUST have a `"coordinates"` member with an array value. The structure of the coordinates array depends on the geometry type. - **Point**: The coordinates array must contain two or three (if 3D) numbers representing the X and Y (and Z) coordinates of the point in the image. A “Point” Geometry MAY have a radius, if indicating a circular object, with the value in pixels, specified as a member `“radius”` of the Geometry object. @@ -51,6 +51,7 @@ A feature object represents a spatially bounded entity associated with propertie - `"geometry"`: A geometry object as defined in the section above or a JSON null value. - `"properties"`: (Optional) A JSON object containing properties specific to the feature, or a JSON null value. - `"id"`: (Optional) A unique identifier for this feature. +- `"ref"`: (Optional) A reference to an external resource, e.g. URI to a zarr strcture, e.g. "s3://zarr-demo/store/my_array.zarr". #### Special Feature Objects @@ -61,7 +62,8 @@ A feature object represents a spatially bounded entity associated with propertie - `"URI"`: A string with the image URI, e.g. “./image_1.tif" - An Image MUST also have a geometry object (as its “geometry” member) of type “Rectangle”, indicating the shape of the image. + An Image MUST also have a geometry object (as its “geometry” member) of type "Polygon", subtype “Rectangle”, indicating the shape of the image. An Image MAY have the following additional key-value pairs in its “properties” object: + - `"correction"`: A list of coordinates indicating the relative correction of the image, e.g. `[1, 2]` indicating a correction of 1 units in the x direction and 2 units in the y direction, with units as defined by the coordinate system. If the coordinate system is not defined, the units are pixels. ### FeatureCollection Object @@ -83,7 +85,7 @@ A coordinates object represents the choice of axes (2D or 3D) and potentially th It MAY contain the following properties: -- `"units"`: Representing the units of the corresponding axis in the axes property. It MUST be an array with the elements having any of the following values: `[“pixel“, “meter”, ”decimeter”, “centimeter“, “millimeter”, “micrometer”, “nanometer”, “picometer“, “radian“, “degree“]` +- `"units"`: Representing the units of the corresponding axis in the axes property. It MUST be an array with the elements having any of the following values: `[“angstrom", "attometer", "centimeter", "decimeter", "exameter", "femtometer", "foot", "gigameter", "hectometer", "inch", "kilometer", "megameter", "meter", "micrometer", "mile", "millimeter", "nanometer", "parsec", "petameter", "picometer", "terameter", "yard", "yoctometer", "yottameter", "zeptometer", "zettameter“]` - `"pixelsPerUnit"`: A decimal value, except for angles, where it SHOULD have the value “0”. diff --git a/examples/csv_to_microjson.py b/examples/csv_to_microjson.py new file mode 100644 index 0000000..0ee4b38 --- /dev/null +++ b/examples/csv_to_microjson.py @@ -0,0 +1,66 @@ +import pandas as pd +import microjson.model as mj +from typing import List + + +def df_to_microjson(df: pd.DataFrame) -> mj.FeatureCollection: + """ + Transforms a pandas DataFrame into a FeatureCollection model. + + :param df: The pandas DataFrame to transform. + :return: The transformed FeatureCollection model. + """ + # Initialize a list to hold the Feature objects + features: List[mj.Feature] = [] + + # Iterate over the rows in the DataFrame + for _, row in df.iterrows(): + # Create a new Geometry object dynamically + GeometryClass = getattr(mj, row['geometry_type']) + geometry = GeometryClass( + type=row['geometry_type'], + coordinates=row['coordinates'] + ) + + # Create a new Feature object + feature = mj.Feature( + type=row['type'], + geometry=geometry, + properties={}, # Add an empty properties dictionary + coordinatesystem=mj.Coordinatesystem( + axes=row['coordinatesystem_axes'], + units=row['coordinatesystem_units'] + ) + ) + + # Add the Feature object to the list + features.append(feature) + + # Create a new FeatureCollection object + feature_collection = mj.FeatureCollection( + type='FeatureCollection', + features=features + ) + + return feature_collection + + +# Example usage: +# Create a list of dictionary +data = [{ + 'type': 'Feature', + 'geometry_type': 'Point', + 'coordinates': [0, 0], + 'coordinatesystem_axes': ['x', 'y'], + 'coordinatesystem_units': ['pixel', 'pixel'] +}, { + 'type': 'Feature', + 'geometry_type': 'Polygon', + 'coordinates': [[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]], + 'coordinatesystem_axes': ['x', 'y'], + 'coordinatesystem_units': ['pixel', 'pixel'] +}] + +df = pd.DataFrame(data) +feature_collection_model = df_to_microjson(df) +print(feature_collection_model.json(indent=2)) diff --git a/examples/example.py b/examples/example.py deleted file mode 100644 index 1a8daf0..0000000 --- a/examples/example.py +++ /dev/null @@ -1,27 +0,0 @@ -from microjson import MicroJSON, GeoJSON -import json - -# load the microjson file -with open('examples/json/sample_microjson.json') as f: - data = json.load(f) - -# validate the microjson file -microjsonobj = MicroJSON.parse_obj(data) -print("done validating: {}".format(microjsonobj)) - -# load the geojson file -with open('examples/json/sample_geometrycollection.json') as f: - data = json.load(f) - -# validate the geojson file -geojsonobj = GeoJSON.parse_obj(data) - -print("done validating: {}".format(geojsonobj)) - -# load the microjson file -with open('examples/json/sample_geojson.json') as f: - data = json.load(f) - -# validate the microjson file -geojsonobj = GeoJSON.parse_obj(data) -print("done validating: {}".format(geojsonobj)) diff --git a/examples/json/sample_geojson.json b/examples/json/sample_geojson.json deleted file mode 100644 index bb8cd6a..0000000 --- a/examples/json/sample_geojson.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "type": "FeatureCollection", - "features": [ - - { - "type": "Feature", - "geometry": { - "type": "Point", - "coordinates": [ - 20.522875, - 51.070292 - ] - }, - "properties": { - "id": 1, - "marker-size": "small", - "marker-color": "#f5a91a", - "marker-symbol": "b" - } - }, - { - "type": "Feature", - "geometry": { - "type": "MultiPolygon", - "coordinates": [ - [ - [ - [ - 20.522875, - 51.070292 - ], - [ - 20.522875, - 51.070292 - ], - [ - 20.522875, - 51.070292 - ], - [ - 20.522875, - 51.070292 - ] - ] - ] - ] - } - } - ] -} \ No newline at end of file diff --git a/examples/json/sample_geometrycollection.json b/examples/json/sample_geometrycollection.json deleted file mode 100644 index e6d57fd..0000000 --- a/examples/json/sample_geometrycollection.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "type": "GeometryCollection", - "geometries": [{ - "type": "Point", - "coordinates": [100.0, 0.0] - }, { - "type": "LineString", - "coordinates": [ - [101.0, 0.0], - [102.0, 1.0] - ] - }] -} \ No newline at end of file diff --git a/examples/json/sample_microjson.json b/examples/json/sample_microjson.json deleted file mode 100644 index 5dc19f6..0000000 --- a/examples/json/sample_microjson.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "coordinatesystem": { - "axes": [ - "x", - "y", - "z" - ], - "units": [ - "micrometer", - "micrometer", - "micrometer" - ], - "pixelsPerUnit": [ - 0.5, - 0.5, - 2 - ] - }, - "type": "FeatureCollection", - "features": [ - - { - "type": "Feature", - "geometry": { - "type": "Point", - "coordinates": [ - 20.522875, - 51.070292 - ] - }, - "properties": { - "id": 1, - "marker-size": "small", - "marker-color": "#f5a91a", - "marker-symbol": "b" - } - }, - { - "type": "Feature", - "geometry": { - "type": "MultiPolygon", - "coordinates": [ - [ - [ - [ - 20.522875, - 51.070292 - ], - [ - 20.522875, - 51.070292 - ], - [ - 20.522875, - 51.070292 - ], - [ - 20.522875, - 51.070292 - ] - ] - ] - ] - } - } - ] -} \ No newline at end of file diff --git a/examples/load_validate.py b/examples/load_validate.py new file mode 100644 index 0000000..8161846 --- /dev/null +++ b/examples/load_validate.py @@ -0,0 +1,19 @@ +import microjson.model as mj +import json + +# load the microjson file +with open('tests/json/microjson/valid/fullexample.json') as f: + data = json.load(f, strict=True) + +# validate the microjson file +microjsonobj = mj.MicroJSON.parse_obj(data) +print("done validating: {}".format(microjsonobj)) + +# load the geojson file +with open('tests/json/geojson/valid/featurecollection/basic.json') as f: + data = json.load(f, strict=True) + +# validate the geojson file +geojsonobj = mj.GeoJSON.parse_obj(data) + +print("done validating: {}".format(geojsonobj)) diff --git a/microjson/model.py b/microjson/model.py index 4cdd571..c72fb0a 100644 --- a/microjson/model.py +++ b/microjson/model.py @@ -86,14 +86,33 @@ class GeoJSON(BaseModel): class Unit(Enum): - PIXEL = 'pixel' - METER = 'meter' - DECIMETER = 'decimeter' + ANGSTROM = 'angstrom' + ATTOMETER = 'attometer' CENTIMETER = 'centimeter' - MILLIMETER = 'millimeter' + DECIMETER = 'decimeter' + EXAMETER = 'exameter' + FEMTOMETER = 'femtometer' + FOOT = 'foot' + GIGAMETER = 'gigameter' + HECTOMETER = 'hectometer' + INCH = 'inch' + KILOMETER = 'kilometer' + MEGAMETER = 'megameter' + METER = 'meter' MICROMETER = 'micrometer' + MILE = 'mile' + MILLIMETER = 'millimeter' NANOMETER = 'nanometer' + PARSEC = 'parsec' + PETAMETER = 'petameter' PICOMETER = 'picometer' + TERAMETER = 'terameter' + YARD = 'yard' + YOCTOMETER = 'yoctometer' + YOTTAMETER = 'yottameter' + ZEPTOMETER = 'zeptometer' + ZETTAMETER = 'zettameter' + PIXEL = 'pixel' RADIAN = 'radian' DEGREE = 'degree' @@ -123,7 +142,7 @@ class MicroMultiLineString(MultiLineString): class MicroPolygon(Polygon): coordinatesystem: Optional[Coordinatesystem] - subtype: Optional[Literal["rectangle", "cuboid"]] + subtype: Optional[Literal["Rectangle", "Cuboid"]] class MicroMultiPolygon(MultiPolygon): @@ -136,6 +155,7 @@ class MicroGeometryCollection(GeometryCollection): class MicroFeature(Feature): coordinatesystem: Optional[Coordinatesystem] + ref: Optional[Union[StrictStr, StrictInt]] class MicroFeatureCollection(FeatureCollection): diff --git a/mkdocs.yml b/mkdocs.yml index d681c34..399ff9e 100755 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -38,5 +38,4 @@ theme: accent: lime nav: - Home: 'index.md' - - Example: 'example.md' - - OME NGFF integration: 'ome_ngff.md' + - Examples: 'example.md' diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..6e3439c --- /dev/null +++ b/mypy.ini @@ -0,0 +1,2 @@ +[mypy] +ignore_errors = no-overlapping-overloads \ No newline at end of file diff --git a/tests/json/microjson/invalid/boolean-ref.json b/tests/json/microjson/invalid/boolean-ref.json new file mode 100644 index 0000000..9ea9d7c --- /dev/null +++ b/tests/json/microjson/invalid/boolean-ref.json @@ -0,0 +1,12 @@ +{ + "type": "Feature", + "id": "1", + "geometry": { + "type": "Point", + "coordinates": [0, 0] + }, + "properties": { + "name": "basic" + }, + "ref": false +} diff --git a/tests/json/microjson/invalid/noaxes.json b/tests/json/microjson/invalid/no-axes.json similarity index 100% rename from tests/json/microjson/invalid/noaxes.json rename to tests/json/microjson/invalid/no-axes.json diff --git a/tests/json/microjson/invalid/object-ref.json b/tests/json/microjson/invalid/object-ref.json new file mode 100644 index 0000000..9bd2aad --- /dev/null +++ b/tests/json/microjson/invalid/object-ref.json @@ -0,0 +1,14 @@ +{ + "type": "Feature", + "id": "1", + "geometry": { + "type": "Point", + "coordinates": [0, 0] + }, + "properties": { + "name": "basic" + }, + "ref": { + "foo": "bar" + } +} diff --git a/tests/json/microjson/valid/3d.json b/tests/json/microjson/valid/3d.json new file mode 100644 index 0000000..28aa479 --- /dev/null +++ b/tests/json/microjson/valid/3d.json @@ -0,0 +1,26 @@ +{ + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0, 1], + [101.0, 0.0, 1], + [101.0, 1.0, 1], + [100.0, 1.0, 1], + [100.0, 0.0, 1] + ], + [ + [100.8, 0.8, 1], + [100.8, 0.2, 1], + [100.2, 0.2, 1], + [100.2, 0.8, 1], + [100.8, 0.8, 1] + ] + ], + "coordinatesystem": { + "axes": [ + "x", + "y", + "z" + ] + } +} \ No newline at end of file diff --git a/tests/json/microjson/valid/fullexample.json b/tests/json/microjson/valid/fullexample.json index b071410..00e2ea3 100644 --- a/tests/json/microjson/valid/fullexample.json +++ b/tests/json/microjson/valid/fullexample.json @@ -8,26 +8,13 @@ "coordinates": [ 200.0, 150.0 - ], - "coordinatesystem": { - "axes": [ - "x", - "y" - ], - "units": [ - "micrometer", - "micrometer" - ], - "pixelsPerUnit": [ - 0.5, - 0.5 - ] - } + ] }, "properties": { "name": "Reference Point", "description": "Specific point of interest", - "color": "red" + "color": "red", + "property1": "value1" }, "id": "1" }, @@ -48,28 +35,15 @@ 300.0, 100.0 ] - ], - "coordinatesystem": { - "axes": [ - "x", - "y" - ], - "units": [ - "micrometer", - "micrometer" - ], - "pixelsPerUnit": [ - 0.5, - 0.5 - ] - } + ] }, "properties": { "name": "Cell Path", "description": "Path traced within a cell", "color": "blue" }, - "id": "2" + "id": "2", + "ref": "s3://mybucket/myfile.tif" } ], "coordinatesystem": { diff --git a/tests/json/microjson/valid/multilevel-coordinatesystems.json b/tests/json/microjson/valid/multilevel-coordinatesystems.json new file mode 100644 index 0000000..7b1ffda --- /dev/null +++ b/tests/json/microjson/valid/multilevel-coordinatesystems.json @@ -0,0 +1,77 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + 200.0, + 150.0 + ] + }, + "properties": { + "name": "Reference Point", + "description": "Specific point of interest", + "color": "red", + "property1": "value1" + }, + "id": "1" + }, + { + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [ + 100.0, + 100.0 + ], + [ + 200.0, + 200.0 + ], + [ + 300.0, + 100.0 + ] + ], + "coordinatesystem": { + "axes": [ + "x", + "y" + ], + "units": [ + "micrometer", + "micrometer" + ], + "pixelsPerUnit": [ + 1, + 1 + ] + } + }, + "properties": { + "name": "Cell Path", + "description": "Path traced within a cell", + "color": "blue" + }, + "id": "2", + "ref": "s3://mybucket/myfile.tif" + } + ], + "coordinatesystem": { + "axes": [ + "x", + "y" + ], + "units": [ + "micrometer", + "micrometer" + ], + "pixelsPerUnit": [ + 0.5, + 0.5 + ] + } +} \ No newline at end of file diff --git a/tests/json/microjson/valid/no-pixelsperunit.json b/tests/json/microjson/valid/no-pixelsperunit.json index 4d3dc98..d2f3dcd 100644 --- a/tests/json/microjson/valid/no-pixelsperunit.json +++ b/tests/json/microjson/valid/no-pixelsperunit.json @@ -22,8 +22,8 @@ "y" ], "units": [ - "micrometer", - "micrometer" + "pixel", + "pixel" ] } } \ No newline at end of file diff --git a/tests/json/microjson/valid/stitching-vector.json b/tests/json/microjson/valid/stitching-vector.json new file mode 100644 index 0000000..2279ced --- /dev/null +++ b/tests/json/microjson/valid/stitching-vector.json @@ -0,0 +1,36 @@ +{ + "type": "FeatureCollection", + "coordinatesystem": { + "axes": ["x", "y"] + }, + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "subtype": "Rectangle", + "coordinates": [[[0.0, 0.0], [0.0, 50.0], [50.0, 50.0], [50.0, 0.0], [0.0, 0.0]]] + }, + "properties": { + "type": "Image", + "URI": "./image_1.tif" + } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "subtype": "Rectangle", + "coordinates": [[[50.0, 0.0], [50.0, 50.0], [100.0, 50.0], [100.0, 0.0], [50.0, 0.0]]] + }, + "properties": { + "type": "Image", + "URI": "./image_2.tif" + } + } + ], + "properties": { + "type": "StitchingVector" + } + } + \ No newline at end of file diff --git a/tests/json/microjson/valid/stringref.json b/tests/json/microjson/valid/stringref.json new file mode 100644 index 0000000..2e7554a --- /dev/null +++ b/tests/json/microjson/valid/stringref.json @@ -0,0 +1,12 @@ +{ + "type": "Feature", + "id": "1", + "geometry": { + "type": "Point", + "coordinates": [0, 0] + }, + "properties": { + "name": "basic" + }, + "ref": "s3://my-bucket/my-key" +} From a0bf6ac4e640253dc948093c153514214ccd0629 Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Wed, 28 Jun 2023 10:20:44 -0400 Subject: [PATCH 07/28] Added strucured metadata, removed redudant classes --- docs/index.md | 2 +- geojson_schema.json | 32 ++ microjson/model.py | 72 ++- microjson/roundtrip.py | 135 +++--- microjson_schema.json | 444 +++++------------- microjsonschema.ts | 151 +++--- .../invalid/empty-coordinatesystem.json | 38 +- tests/json/microjson/invalid/no-axes.json | 37 +- tests/json/microjson/valid/metadata-full.json | 57 +++ 9 files changed, 410 insertions(+), 558 deletions(-) create mode 100644 tests/json/microjson/valid/metadata-full.json diff --git a/docs/index.md b/docs/index.md index fd66fd7..e8c922d 100755 --- a/docs/index.md +++ b/docs/index.md @@ -8,7 +8,7 @@ MicroJSON is a format, inspired by [GeoJSON](https://geojson.org), for encoding ### MicroJSON Object -A MicroJSON object is a JSON object that represents a geometry, feature, or collection of features, or more precisely, be either of type (having value of top level field `type` as) `"Geometry"`, `"Feature"`, or `"Featurecollection"`, that is, the same as for GeoJSON. What separates MicroJSON from GeoJSON is that it MAY have of a top level property `"coordinatesystem"`: +A MicroJSON object is a JSON object that represents a geometry, feature, or collection of features, or more precisely, be either of type (having value of top level field `type` as) `"Geometry"`, `"Feature"`, or `"Featurecollection"`, that is, the same as for GeoJSON. What separates MicroJSON from GeoJSON is that it MAY have a member `"coordinatesystem"` in a Feature or FeatureCollection object: - `"coordinatesystem"`: (Optional) A coordinate system object as defined in the section [Coordinates object](#coordinates-object). If this property is not present, the default coordinate system is assumed to be the same as the image coordinate system, using cartesian coordinates and pixels as units. It is recommended to define this property at the top level of the MicroJSON object, but it MAY also be defined at the level of a Feature or Geometry object, in which case it overrides the top level coordinate system. diff --git a/geojson_schema.json b/geojson_schema.json index 2a5c769..936dd15 100644 --- a/geojson_schema.json +++ b/geojson_schema.json @@ -385,6 +385,24 @@ "properties" ] }, + "ValueRange": { + "title": "ValueRange", + "type": "object", + "properties": { + "min": { + "title": "Min", + "type": "number" + }, + "max": { + "title": "Max", + "type": "number" + } + }, + "required": [ + "min", + "max" + ] + }, "FeatureCollection": { "title": "FeatureCollection", "type": "object", @@ -410,6 +428,20 @@ "items": { "$ref": "#/definitions/Feature" } + }, + "value_range": { + "title": "Value Range", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/ValueRange" + } + }, + "descriptive_fields": { + "title": "Descriptive Fields", + "type": "array", + "items": { + "type": "string" + } } }, "required": [ diff --git a/microjson/model.py b/microjson/model.py index c72fb0a..e4f7913 100644 --- a/microjson/model.py +++ b/microjson/model.py @@ -64,6 +64,28 @@ class GeometryCollection(GeoAbstract): ] +class Descriptive(BaseModel): + # Allows for a dictionary with string values + descriptive: Optional[Dict[str, str]] + + +class Numerical(BaseModel): + # Allows for a dictionary with numerical values + numerical: Optional[Dict[str, float]] + + +class MultiNumerical(BaseModel): + # Allows for a dictionary where each key maps to a list of numerical values + multi_numerical: Optional[Dict[str, List[float]]] + + +class Properties(BaseModel): + # other fields... + descriptive: Optional[Descriptive] + numerical: Optional[Numerical] + multi_numerical: Optional[MultiNumerical] + + class Feature(GeoAbstract): type: Literal["Feature"] geometry: GeometryType = Field(..., @@ -75,9 +97,16 @@ class Feature(GeoAbstract): id: Optional[Union[StrictStr, StrictInt]] +class ValueRange(BaseModel): + min: float + max: float + + class FeatureCollection(GeoAbstract): type: Literal["FeatureCollection"] features: List[Feature] + value_range: Optional[Dict[str, ValueRange]] + descriptive_fields: Optional[List[str]] class GeoJSON(BaseModel): @@ -123,56 +152,19 @@ class Coordinatesystem(BaseModel): pixelsPerUnit: Optional[List[float]] -class MicroPoint(Point): - coordinatesystem: Optional[Coordinatesystem] - radius: Optional[float] - - -class MicroMultiPoint(MultiPoint): - coordinatesystem: Optional[Coordinatesystem] - - -class MicroLineString(LineString): - coordinatesystem: Optional[Coordinatesystem] - - -class MicroMultiLineString(MultiLineString): - coordinatesystem: Optional[Coordinatesystem] - - -class MicroPolygon(Polygon): - coordinatesystem: Optional[Coordinatesystem] - subtype: Optional[Literal["Rectangle", "Cuboid"]] - - -class MicroMultiPolygon(MultiPolygon): - coordinatesystem: Optional[Coordinatesystem] - - -class MicroGeometryCollection(GeometryCollection): - coordinatesystem: Optional[Coordinatesystem] - - class MicroFeature(Feature): coordinatesystem: Optional[Coordinatesystem] ref: Optional[Union[StrictStr, StrictInt]] + # + properties: Properties class MicroFeatureCollection(FeatureCollection): coordinatesystem: Optional[Coordinatesystem] -MicroGeometryType = Union[MicroPoint, - MicroMultiPoint, - MicroLineString, - MicroMultiLineString, - MicroPolygon, - MicroMultiPolygon, - MicroGeometryCollection] - - class MicroJSON(BaseModel): """The root object of a MicroJSON file""" __root__: Union[MicroFeature, MicroFeatureCollection, - MicroGeometryType] + GeometryType] diff --git a/microjson/roundtrip.py b/microjson/roundtrip.py index 7ec496e..d1325e8 100644 --- a/microjson/roundtrip.py +++ b/microjson/roundtrip.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: microjson_schema.json -# timestamp: 2023-06-13T12:15:57+00:00 +# timestamp: 2023-06-28T14:16:15+00:00 from __future__ import annotations @@ -82,15 +82,54 @@ class GeometryCollection(BaseModel): ] = Field(..., title='Geometries') +class Descriptive(BaseModel): + descriptive: Optional[Dict[str, str]] = Field(None, title='Descriptive') + + +class Numerical(BaseModel): + numerical: Optional[Dict[str, float]] = Field(None, title='Numerical') + + +class MultiNumerical(BaseModel): + multi_numerical: Optional[Dict[str, List[float]]] = Field( + None, title='Multi Numerical' + ) + + +class Properties(BaseModel): + descriptive: Optional[Descriptive] = None + numerical: Optional[Numerical] = None + multi_numerical: Optional[MultiNumerical] = None + + class Unit(Enum): - pixel = 'pixel' - meter = 'meter' - decimeter = 'decimeter' + angstrom = 'angstrom' + attometer = 'attometer' centimeter = 'centimeter' - millimeter = 'millimeter' + decimeter = 'decimeter' + exameter = 'exameter' + femtometer = 'femtometer' + foot = 'foot' + gigameter = 'gigameter' + hectometer = 'hectometer' + inch = 'inch' + kilometer = 'kilometer' + megameter = 'megameter' + meter = 'meter' micrometer = 'micrometer' + mile = 'mile' + millimeter = 'millimeter' nanometer = 'nanometer' + parsec = 'parsec' + petameter = 'petameter' picometer = 'picometer' + terameter = 'terameter' + yard = 'yard' + yoctometer = 'yoctometer' + yottameter = 'yottameter' + zeptometer = 'zeptometer' + zettameter = 'zettameter' + pixel = 'pixel' radian = 'radian' degree = 'degree' @@ -132,13 +171,10 @@ class MicroFeature(BaseModel): description='The geometry of the\n feature', title='Geometry', ) - properties: Dict[str, Any] = Field( - ..., - description='Properties of the\n feature', - title='Properties', - ) + properties: Properties id: Optional[Union[str, int]] = Field(None, title='Id') coordinatesystem: Optional[Coordinatesystem] = None + ref: Optional[Union[str, int]] = Field(None, title='Ref') class Feature(BaseModel): @@ -165,6 +201,11 @@ class Feature(BaseModel): id: Optional[Union[str, int]] = Field(None, title='Id') +class ValueRange(BaseModel): + min: float = Field(..., title='Min') + max: float = Field(..., title='Max') + + class Type9(Enum): feature_collection = 'FeatureCollection' @@ -173,64 +214,8 @@ class MicroFeatureCollection(BaseModel): bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') type: Type9 = Field(..., title='Type') features: List[Feature] = Field(..., title='Features') - coordinatesystem: Optional[Coordinatesystem] = None - - -class MicroPoint(BaseModel): - bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') - type: Type = Field(..., title='Type') - coordinates: List[float] = Field(..., max_items=3, min_items=2, title='Coordinates') - coordinatesystem: Optional[Coordinatesystem] = None - radius: Optional[float] = Field(None, title='Radius') - - -class MicroMultiPoint(BaseModel): - bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') - type: Type1 = Field(..., title='Type') - coordinates: List[List[float]] = Field(..., title='Coordinates') - coordinatesystem: Optional[Coordinatesystem] = None - - -class MicroLineString(BaseModel): - bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') - type: Type2 = Field(..., title='Type') - coordinates: List[List[float]] = Field(..., title='Coordinates') - coordinatesystem: Optional[Coordinatesystem] = None - - -class MicroMultiLineString(BaseModel): - bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') - type: Type3 = Field(..., title='Type') - coordinates: List[List[List[float]]] = Field(..., title='Coordinates') - coordinatesystem: Optional[Coordinatesystem] = None - - -class Subtype(Enum): - rectangle = 'rectangle' - cuboid = 'cuboid' - - -class MicroPolygon(BaseModel): - bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') - type: Type4 = Field(..., title='Type') - coordinates: List[List[List[float]]] = Field(..., title='Coordinates') - coordinatesystem: Optional[Coordinatesystem] = None - subtype: Optional[Subtype] = Field(None, title='Subtype') - - -class MicroMultiPolygon(BaseModel): - bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') - type: Type5 = Field(..., title='Type') - coordinates: List[List[List[List[float]]]] = Field(..., title='Coordinates') - coordinatesystem: Optional[Coordinatesystem] = None - - -class MicroGeometryCollection(BaseModel): - bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') - type: Type6 = Field(..., title='Type') - geometries: List[ - Union[Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon] - ] = Field(..., title='Geometries') + value_range: Optional[Dict[str, ValueRange]] = Field(None, title='Value Range') + descriptive_fields: Optional[List[str]] = Field(None, title='Descriptive Fields') coordinatesystem: Optional[Coordinatesystem] = None @@ -238,11 +223,11 @@ class MicroJSON(BaseModel): __root__: Union[ MicroFeature, MicroFeatureCollection, - MicroPoint, - MicroMultiPoint, - MicroLineString, - MicroMultiLineString, - MicroPolygon, - MicroMultiPolygon, - MicroGeometryCollection, + Point, + MultiPoint, + LineString, + MultiLineString, + Polygon, + MultiPolygon, + GeometryCollection, ] = Field(..., description='The root object of a MicroJSON file', title='MicroJSON') diff --git a/microjson_schema.json b/microjson_schema.json index 99ddc5f..32dc2f6 100644 --- a/microjson_schema.json +++ b/microjson_schema.json @@ -9,25 +9,25 @@ "$ref": "#/definitions/MicroFeatureCollection" }, { - "$ref": "#/definitions/MicroPoint" + "$ref": "#/definitions/Point" }, { - "$ref": "#/definitions/MicroMultiPoint" + "$ref": "#/definitions/MultiPoint" }, { - "$ref": "#/definitions/MicroLineString" + "$ref": "#/definitions/LineString" }, { - "$ref": "#/definitions/MicroMultiLineString" + "$ref": "#/definitions/MultiLineString" }, { - "$ref": "#/definitions/MicroPolygon" + "$ref": "#/definitions/Polygon" }, { - "$ref": "#/definitions/MicroMultiPolygon" + "$ref": "#/definitions/MultiPolygon" }, { - "$ref": "#/definitions/MicroGeometryCollection" + "$ref": "#/definitions/GeometryCollection" } ], "definitions": { @@ -313,18 +313,94 @@ "geometries" ] }, + "Descriptive": { + "title": "Descriptive", + "type": "object", + "properties": { + "descriptive": { + "title": "Descriptive", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "Numerical": { + "title": "Numerical", + "type": "object", + "properties": { + "numerical": { + "title": "Numerical", + "type": "object", + "additionalProperties": { + "type": "number" + } + } + } + }, + "MultiNumerical": { + "title": "MultiNumerical", + "type": "object", + "properties": { + "multi_numerical": { + "title": "Multi Numerical", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "number" + } + } + } + } + }, + "Properties": { + "title": "Properties", + "type": "object", + "properties": { + "descriptive": { + "$ref": "#/definitions/Descriptive" + }, + "numerical": { + "$ref": "#/definitions/Numerical" + }, + "multi_numerical": { + "$ref": "#/definitions/MultiNumerical" + } + } + }, "Unit": { "title": "Unit", "description": "An enumeration.", "enum": [ - "pixel", - "meter", - "decimeter", + "angstrom", + "attometer", "centimeter", - "millimeter", + "decimeter", + "exameter", + "femtometer", + "foot", + "gigameter", + "hectometer", + "inch", + "kilometer", + "megameter", + "meter", "micrometer", + "mile", + "millimeter", "nanometer", + "parsec", + "petameter", "picometer", + "terameter", + "yard", + "yoctometer", + "yottameter", + "zeptometer", + "zettameter", + "pixel", "radian", "degree" ] @@ -413,9 +489,7 @@ ] }, "properties": { - "title": "Properties", - "description": "Properties of the\n feature", - "type": "object" + "$ref": "#/definitions/Properties" }, "id": { "title": "Id", @@ -430,6 +504,17 @@ }, "coordinatesystem": { "$ref": "#/definitions/Coordinatesystem" + }, + "ref": { + "title": "Ref", + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] } }, "required": [ @@ -507,258 +592,26 @@ "properties" ] }, - "MicroFeatureCollection": { - "title": "MicroFeatureCollection", + "ValueRange": { + "title": "ValueRange", "type": "object", "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } - }, - "type": { - "title": "Type", - "enum": [ - "FeatureCollection" - ], - "type": "string" - }, - "features": { - "title": "Features", - "type": "array", - "items": { - "$ref": "#/definitions/Feature" - } - }, - "coordinatesystem": { - "$ref": "#/definitions/Coordinatesystem" - } - }, - "required": [ - "type", - "features" - ] - }, - "MicroPoint": { - "title": "MicroPoint", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } - }, - "type": { - "title": "Type", - "enum": [ - "Point" - ], - "type": "string" - }, - "coordinates": { - "title": "Coordinates", - "minItems": 2, - "maxItems": 3, - "type": "array", - "items": { - "type": "number" - } - }, - "coordinatesystem": { - "$ref": "#/definitions/Coordinatesystem" - }, - "radius": { - "title": "Radius", + "min": { + "title": "Min", "type": "number" - } - }, - "required": [ - "type", - "coordinates" - ] - }, - "MicroMultiPoint": { - "title": "MicroMultiPoint", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } - }, - "type": { - "title": "Type", - "enum": [ - "MultiPoint" - ], - "type": "string" - }, - "coordinates": { - "title": "Coordinates", - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" - }, - "minItems": 2, - "maxItems": 3 - } }, - "coordinatesystem": { - "$ref": "#/definitions/Coordinatesystem" - } - }, - "required": [ - "type", - "coordinates" - ] - }, - "MicroLineString": { - "title": "MicroLineString", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } - }, - "type": { - "title": "Type", - "enum": [ - "LineString" - ], - "type": "string" - }, - "coordinates": { - "title": "Coordinates", - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" - }, - "minItems": 2, - "maxItems": 3 - } - }, - "coordinatesystem": { - "$ref": "#/definitions/Coordinatesystem" - } - }, - "required": [ - "type", - "coordinates" - ] - }, - "MicroMultiLineString": { - "title": "MicroMultiLineString", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } - }, - "type": { - "title": "Type", - "enum": [ - "MultiLineString" - ], - "type": "string" - }, - "coordinates": { - "title": "Coordinates", - "type": "array", - "items": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" - }, - "minItems": 2, - "maxItems": 3 - } - } - }, - "coordinatesystem": { - "$ref": "#/definitions/Coordinatesystem" - } - }, - "required": [ - "type", - "coordinates" - ] - }, - "MicroPolygon": { - "title": "MicroPolygon", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } - }, - "type": { - "title": "Type", - "enum": [ - "Polygon" - ], - "type": "string" - }, - "coordinates": { - "title": "Coordinates", - "type": "array", - "items": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" - }, - "minItems": 2, - "maxItems": 3 - } - } - }, - "coordinatesystem": { - "$ref": "#/definitions/Coordinatesystem" - }, - "subtype": { - "title": "Subtype", - "enum": [ - "rectangle", - "cuboid" - ], - "type": "string" + "max": { + "title": "Max", + "type": "number" } }, "required": [ - "type", - "coordinates" + "min", + "max" ] }, - "MicroMultiPolygon": { - "title": "MicroMultiPolygon", + "MicroFeatureCollection": { + "title": "MicroFeatureCollection", "type": "object", "properties": { "bbox": { @@ -772,80 +625,29 @@ "type": { "title": "Type", "enum": [ - "MultiPolygon" + "FeatureCollection" ], "type": "string" }, - "coordinates": { - "title": "Coordinates", + "features": { + "title": "Features", "type": "array", "items": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" - }, - "minItems": 2, - "maxItems": 3 - } - } + "$ref": "#/definitions/Feature" } }, - "coordinatesystem": { - "$ref": "#/definitions/Coordinatesystem" - } - }, - "required": [ - "type", - "coordinates" - ] - }, - "MicroGeometryCollection": { - "title": "MicroGeometryCollection", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" + "value_range": { + "title": "Value Range", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/ValueRange" } }, - "type": { - "title": "Type", - "enum": [ - "GeometryCollection" - ], - "type": "string" - }, - "geometries": { - "title": "Geometries", + "descriptive_fields": { + "title": "Descriptive Fields", "type": "array", "items": { - "anyOf": [ - { - "$ref": "#/definitions/Point" - }, - { - "$ref": "#/definitions/MultiPoint" - }, - { - "$ref": "#/definitions/LineString" - }, - { - "$ref": "#/definitions/MultiLineString" - }, - { - "$ref": "#/definitions/Polygon" - }, - { - "$ref": "#/definitions/MultiPolygon" - } - ] + "type": "string" } }, "coordinatesystem": { @@ -854,7 +656,7 @@ }, "required": [ "type", - "geometries" + "features" ] } } diff --git a/microjsonschema.ts b/microjsonschema.ts index 35ed56f..25f8c18 100644 --- a/microjsonschema.ts +++ b/microjsonschema.ts @@ -6,14 +6,33 @@ */ export type Unit = - | "pixel" - | "meter" - | "decimeter" + | "angstrom" + | "attometer" | "centimeter" - | "millimeter" + | "decimeter" + | "exameter" + | "femtometer" + | "foot" + | "gigameter" + | "hectometer" + | "inch" + | "kilometer" + | "megameter" + | "meter" | "micrometer" + | "mile" + | "millimeter" | "nanometer" + | "parsec" + | "petameter" | "picometer" + | "terameter" + | "yard" + | "yoctometer" + | "yottameter" + | "zeptometer" + | "zettameter" + | "pixel" | "radian" | "degree"; /** @@ -35,19 +54,24 @@ export type GeoJSON = export type MicroJSON = | MicroFeature | MicroFeatureCollection - | MicroPoint - | MicroMultiPoint - | MicroLineString - | MicroMultiLineString - | MicroPolygon - | MicroMultiPolygon - | MicroGeometryCollection; + | Point + | MultiPoint + | LineString + | MultiLineString + | Polygon + | MultiPolygon + | GeometryCollection; export interface Coordinatesystem { axes: ("x" | "y" | "z" | "r" | "theta" | "phi")[]; units?: Unit[]; pixelsPerUnit?: number[]; } +export interface Descriptive { + descriptive?: { + [k: string]: string; + }; +} export interface Feature { /** * @minItems 4 @@ -135,6 +159,14 @@ export interface FeatureCollection { bbox?: [number, number, number, number, ...number[]]; type: "FeatureCollection"; features: Feature[]; + value_range?: { + [k: string]: ValueRange; + }; + descriptive_fields?: string[]; +} +export interface ValueRange { + min: number; + max: number; } export interface GeoAbstract { /** @@ -153,91 +185,36 @@ export interface MicroFeature { * feature */ geometry: Point | MultiPoint | LineString | MultiLineString | Polygon | MultiPolygon | GeometryCollection; - /** - * Properties of the - * feature - */ - properties: { - [k: string]: unknown; - }; + properties: Properties; id?: string | number; coordinatesystem?: Coordinatesystem; + ref?: string | number; } -export interface MicroFeatureCollection { - /** - * @minItems 4 - */ - bbox?: [number, number, number, number, ...number[]]; - type: "FeatureCollection"; - features: Feature[]; - coordinatesystem?: Coordinatesystem; -} -export interface MicroGeometryCollection { - /** - * @minItems 4 - */ - bbox?: [number, number, number, number, ...number[]]; - type: "GeometryCollection"; - geometries: (Point | MultiPoint | LineString | MultiLineString | Polygon | MultiPolygon)[]; - coordinatesystem?: Coordinatesystem; -} -export interface MicroPoint { - /** - * @minItems 4 - */ - bbox?: [number, number, number, number, ...number[]]; - type: "Point"; - /** - * @minItems 2 - * @maxItems 3 - */ - coordinates: [number, number] | [number, number, number]; - coordinatesystem?: Coordinatesystem; - radius?: number; -} -export interface MicroMultiPoint { - /** - * @minItems 4 - */ - bbox?: [number, number, number, number, ...number[]]; - type: "MultiPoint"; - coordinates: [number, number] | [number, number, number][]; - coordinatesystem?: Coordinatesystem; -} -export interface MicroLineString { - /** - * @minItems 4 - */ - bbox?: [number, number, number, number, ...number[]]; - type: "LineString"; - coordinates: [number, number] | [number, number, number][]; - coordinatesystem?: Coordinatesystem; +export interface Properties { + descriptive?: Descriptive; + numerical?: Numerical; + multi_numerical?: MultiNumerical; } -export interface MicroMultiLineString { - /** - * @minItems 4 - */ - bbox?: [number, number, number, number, ...number[]]; - type: "MultiLineString"; - coordinates: [number, number] | [number, number, number][][]; - coordinatesystem?: Coordinatesystem; +export interface Numerical { + numerical?: { + [k: string]: number; + }; } -export interface MicroPolygon { - /** - * @minItems 4 - */ - bbox?: [number, number, number, number, ...number[]]; - type: "Polygon"; - coordinates: [number, number] | [number, number, number][][]; - coordinatesystem?: Coordinatesystem; - subtype?: "rectangle" | "cuboid"; +export interface MultiNumerical { + multi_numerical?: { + [k: string]: number[]; + }; } -export interface MicroMultiPolygon { +export interface MicroFeatureCollection { /** * @minItems 4 */ bbox?: [number, number, number, number, ...number[]]; - type: "MultiPolygon"; - coordinates: [number, number] | [number, number, number][][][]; + type: "FeatureCollection"; + features: Feature[]; + value_range?: { + [k: string]: ValueRange; + }; + descriptive_fields?: string[]; coordinatesystem?: Coordinatesystem; } diff --git a/tests/json/microjson/invalid/empty-coordinatesystem.json b/tests/json/microjson/invalid/empty-coordinatesystem.json index 5aa341e..eac2478 100644 --- a/tests/json/microjson/invalid/empty-coordinatesystem.json +++ b/tests/json/microjson/invalid/empty-coordinatesystem.json @@ -1,21 +1,25 @@ -{ - "type": "Polygon", - "coordinates": [ - [ - [100.0, 0.0], - [101.0, 0.0], - [101.0, 1.0], - [100.0, 1.0], - [100.0, 0.0] - ], - [ - [100.8, 0.8], - [100.8, 0.2], - [100.2, 0.2], - [100.2, 0.8], - [100.8, 0.8] +{ + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] ] - ], + } + , "coordinatesystem": { } } \ No newline at end of file diff --git a/tests/json/microjson/invalid/no-axes.json b/tests/json/microjson/invalid/no-axes.json index 4ed2961..acfae3b 100644 --- a/tests/json/microjson/invalid/no-axes.json +++ b/tests/json/microjson/invalid/no-axes.json @@ -1,21 +1,24 @@ -{ - "type": "Polygon", - "coordinates": [ - [ - [100.0, 0.0], - [101.0, 0.0], - [101.0, 1.0], - [100.0, 1.0], - [100.0, 0.0] - ], - [ - [100.8, 0.8], - [100.8, 0.2], - [100.2, 0.2], - [100.2, 0.8], - [100.8, 0.8] +{ + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] ] - ], + }, "coordinatesystem": { "units": [ "micrometer", diff --git a/tests/json/microjson/valid/metadata-full.json b/tests/json/microjson/valid/metadata-full.json new file mode 100644 index 0000000..1cc7578 --- /dev/null +++ b/tests/json/microjson/valid/metadata-full.json @@ -0,0 +1,57 @@ +{ + "type": "FeatureCollection", + "coordinatesystem": { + "axes": ["x", "y"] + }, + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "subtype": "Rectangle", + "coordinates": [[[0.0, 0.0], [0.0, 50.0], [50.0, 50.0], [50.0, 0.0], [0.0, 0.0]]] + }, + "properties": { + "descriptive": { + "well": "A1" + }, + "numerical": { + "cellCount": 5 + }, + "multi-numerical": { + "ratioInfectivity": [[0.1, 0.2, 0.3, 0.4, 0.5], [0.2, 0.3, 0.4, 0.5, 0.6]] + } + } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "subtype": "Rectangle", + "coordinates": [[[50.0, 0.0], [50.0, 50.0], [100.0, 50.0], [100.0, 0.0], [50.0, 0.0]]] + }, + "properties": { + "descriptive": { + "well": "A2" + }, + "numerical": { + "cellCount": 10 + }, + "multivalues": { + "ratioInfectivity": [[0.1, 0.2, 0.3, 0.4, 0.5], [0.2, 0.3, 0.4, 0.5, 0.6]] + } + } + } + ], + "value_range": { + "cellCount": { + "min": 0, + "max": 10 + }, + "ratioInfectivity": { + "min": 0, + "max": 1 + } + }, + "descriptive_fields": ["well"] + } \ No newline at end of file From b7dc088e072d21139a3540ae573d79c0c8fb6c8d Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Wed, 28 Jun 2023 11:40:03 -0400 Subject: [PATCH 08/28] Added example, corrected model --- ...csv_to_microjson.py => df_to_microjson.py} | 44 ++++++++++++++++--- microjson/model.py | 29 +++--------- microjson/roundtrip.py | 16 +------ microjson_schema.json | 35 ++------------- microjsonschema.ts | 15 ++----- 5 files changed, 53 insertions(+), 86 deletions(-) rename examples/{csv_to_microjson.py => df_to_microjson.py} (58%) diff --git a/examples/csv_to_microjson.py b/examples/df_to_microjson.py similarity index 58% rename from examples/csv_to_microjson.py rename to examples/df_to_microjson.py index 0ee4b38..a37d47a 100644 --- a/examples/csv_to_microjson.py +++ b/examples/df_to_microjson.py @@ -22,11 +22,18 @@ def df_to_microjson(df: pd.DataFrame) -> mj.FeatureCollection: coordinates=row['coordinates'] ) + # create a new properties object dynamically + properties = mj.Properties( + descriptive={'name': row['name']}, + numerical={'value': row['value']}, + multi_numerical={'values': row['values']} + ) + # Create a new Feature object - feature = mj.Feature( + feature = mj.MicroFeature( type=row['type'], geometry=geometry, - properties={}, # Add an empty properties dictionary + properties=properties, coordinatesystem=mj.Coordinatesystem( axes=row['coordinatesystem_axes'], units=row['coordinatesystem_units'] @@ -36,10 +43,27 @@ def df_to_microjson(df: pd.DataFrame) -> mj.FeatureCollection: # Add the Feature object to the list features.append(feature) + # Create a value range object + value_range = { + 'value': { + 'min': df['value'].min(), + 'max': df['value'].max() + }, + 'values': { + 'min': df['values'].apply(min).min(), + 'max': df['values'].apply(max).max() + } + } + + # Create a list of descriptive fields + descriptive_fields = ['name'] + # Create a new FeatureCollection object - feature_collection = mj.FeatureCollection( + feature_collection = mj.MicroFeatureCollection( type='FeatureCollection', - features=features + features=features, + value_range=value_range, + descriptive_fields=descriptive_fields ) return feature_collection @@ -52,15 +76,21 @@ def df_to_microjson(df: pd.DataFrame) -> mj.FeatureCollection: 'geometry_type': 'Point', 'coordinates': [0, 0], 'coordinatesystem_axes': ['x', 'y'], - 'coordinatesystem_units': ['pixel', 'pixel'] + 'coordinatesystem_units': ['pixel', 'pixel'], + 'name': 'Point 1', + 'value': 1, + 'values': [1, 2, 3] }, { 'type': 'Feature', 'geometry_type': 'Polygon', 'coordinates': [[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]], 'coordinatesystem_axes': ['x', 'y'], - 'coordinatesystem_units': ['pixel', 'pixel'] + 'coordinatesystem_units': ['pixel', 'pixel'], + 'name': 'Polygon 1', + 'value': 2, + 'values': [4, 5, 6] }] df = pd.DataFrame(data) feature_collection_model = df_to_microjson(df) -print(feature_collection_model.json(indent=2)) +print(feature_collection_model.json(indent=2, exclude_unset=True)) diff --git a/microjson/model.py b/microjson/model.py index e4f7913..80a15e9 100644 --- a/microjson/model.py +++ b/microjson/model.py @@ -64,28 +64,6 @@ class GeometryCollection(GeoAbstract): ] -class Descriptive(BaseModel): - # Allows for a dictionary with string values - descriptive: Optional[Dict[str, str]] - - -class Numerical(BaseModel): - # Allows for a dictionary with numerical values - numerical: Optional[Dict[str, float]] - - -class MultiNumerical(BaseModel): - # Allows for a dictionary where each key maps to a list of numerical values - multi_numerical: Optional[Dict[str, List[float]]] - - -class Properties(BaseModel): - # other fields... - descriptive: Optional[Descriptive] - numerical: Optional[Numerical] - multi_numerical: Optional[MultiNumerical] - - class Feature(GeoAbstract): type: Literal["Feature"] geometry: GeometryType = Field(..., @@ -152,6 +130,13 @@ class Coordinatesystem(BaseModel): pixelsPerUnit: Optional[List[float]] +class Properties(BaseModel): + # other fields... + descriptive: Optional[Dict[str, str]] + numerical: Optional[Dict[str, float]] + multi_numerical: Optional[Dict[str, List[float]]] + + class MicroFeature(Feature): coordinatesystem: Optional[Coordinatesystem] ref: Optional[Union[StrictStr, StrictInt]] diff --git a/microjson/roundtrip.py b/microjson/roundtrip.py index d1325e8..2a8e1f6 100644 --- a/microjson/roundtrip.py +++ b/microjson/roundtrip.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: microjson_schema.json -# timestamp: 2023-06-28T14:16:15+00:00 +# timestamp: 2023-06-28T15:38:44+00:00 from __future__ import annotations @@ -82,26 +82,14 @@ class GeometryCollection(BaseModel): ] = Field(..., title='Geometries') -class Descriptive(BaseModel): +class Properties(BaseModel): descriptive: Optional[Dict[str, str]] = Field(None, title='Descriptive') - - -class Numerical(BaseModel): numerical: Optional[Dict[str, float]] = Field(None, title='Numerical') - - -class MultiNumerical(BaseModel): multi_numerical: Optional[Dict[str, List[float]]] = Field( None, title='Multi Numerical' ) -class Properties(BaseModel): - descriptive: Optional[Descriptive] = None - numerical: Optional[Numerical] = None - multi_numerical: Optional[MultiNumerical] = None - - class Unit(Enum): angstrom = 'angstrom' attometer = 'attometer' diff --git a/microjson_schema.json b/microjson_schema.json index 32dc2f6..a2ff840 100644 --- a/microjson_schema.json +++ b/microjson_schema.json @@ -313,8 +313,8 @@ "geometries" ] }, - "Descriptive": { - "title": "Descriptive", + "Properties": { + "title": "Properties", "type": "object", "properties": { "descriptive": { @@ -323,26 +323,14 @@ "additionalProperties": { "type": "string" } - } - } - }, - "Numerical": { - "title": "Numerical", - "type": "object", - "properties": { + }, "numerical": { "title": "Numerical", "type": "object", "additionalProperties": { "type": "number" } - } - } - }, - "MultiNumerical": { - "title": "MultiNumerical", - "type": "object", - "properties": { + }, "multi_numerical": { "title": "Multi Numerical", "type": "object", @@ -355,21 +343,6 @@ } } }, - "Properties": { - "title": "Properties", - "type": "object", - "properties": { - "descriptive": { - "$ref": "#/definitions/Descriptive" - }, - "numerical": { - "$ref": "#/definitions/Numerical" - }, - "multi_numerical": { - "$ref": "#/definitions/MultiNumerical" - } - } - }, "Unit": { "title": "Unit", "description": "An enumeration.", diff --git a/microjsonschema.ts b/microjsonschema.ts index 25f8c18..e3acf15 100644 --- a/microjsonschema.ts +++ b/microjsonschema.ts @@ -67,11 +67,6 @@ export interface Coordinatesystem { units?: Unit[]; pixelsPerUnit?: number[]; } -export interface Descriptive { - descriptive?: { - [k: string]: string; - }; -} export interface Feature { /** * @minItems 4 @@ -191,16 +186,12 @@ export interface MicroFeature { ref?: string | number; } export interface Properties { - descriptive?: Descriptive; - numerical?: Numerical; - multi_numerical?: MultiNumerical; -} -export interface Numerical { + descriptive?: { + [k: string]: string; + }; numerical?: { [k: string]: number; }; -} -export interface MultiNumerical { multi_numerical?: { [k: string]: number[]; }; From 965c52982adf018c0fd22508289fd961ecbddd8d Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Tue, 11 Jul 2023 13:11:11 -0400 Subject: [PATCH 09/28] Change module exposure --- microjson/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microjson/__init__.py b/microjson/__init__.py index 6ca2914..21491d5 100644 --- a/microjson/__init__.py +++ b/microjson/__init__.py @@ -1,4 +1,4 @@ from .model import MicroJSON, GeoJSON -from .automodel import GeoJSONAuto, MicroJSONAuto -from .roundtrip import MicroJSON as MicroJSONRound +#from .automodel import GeoJSONAuto, MicroJSONAuto +#from .roundtrip import MicroJSON as MicroJSONRound from .utils import gather_example_files \ No newline at end of file From 4ceeccc0667e923d1e5e834994a40d160ecb0d1f Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Mon, 24 Jul 2023 18:35:34 -0400 Subject: [PATCH 10/28] Altered coordinatesystem, changed .toml --- examples/df_to_microjson.py | 36 +++-- geojson/Feature.py | 147 +++++++++++------- geojson/FeatureCollection.py | 139 +++++++++++------ geojson/Geometry.py | 49 +++--- geojson/GeometryCollection.py | 72 +++++---- geojson/LineString.py | 12 +- geojson/MultiLineString.py | 12 +- geojson/MultiPoint.py | 8 +- geojson/MultiPolygon.py | 12 +- geojson/Point.py | 8 +- geojson/Polygon.py | 12 +- microjson/automodel.py | 90 +++-------- microjson/geojson.py | 4 +- microjson/model.py | 53 +++++-- microjson/roundtrip.py | 37 +++-- pyproject.toml | 5 +- requirements-dev.txt | 4 - tests/json/microjson/valid/fullexample.json | 39 +++-- tests/json/microjson/valid/metadata-full.json | 8 +- .../valid/multilevel-coordinatesystems.json | 55 ++++--- .../microjson/valid/stitching-vector.json | 8 +- 21 files changed, 473 insertions(+), 337 deletions(-) delete mode 100644 requirements-dev.txt diff --git a/examples/df_to_microjson.py b/examples/df_to_microjson.py index a37d47a..85c7b6f 100644 --- a/examples/df_to_microjson.py +++ b/examples/df_to_microjson.py @@ -33,11 +33,7 @@ def df_to_microjson(df: pd.DataFrame) -> mj.FeatureCollection: feature = mj.MicroFeature( type=row['type'], geometry=geometry, - properties=properties, - coordinatesystem=mj.Coordinatesystem( - axes=row['coordinatesystem_axes'], - units=row['coordinatesystem_units'] - ) + properties=properties ) # Add the Feature object to the list @@ -63,7 +59,31 @@ def df_to_microjson(df: pd.DataFrame) -> mj.FeatureCollection: type='FeatureCollection', features=features, value_range=value_range, - descriptive_fields=descriptive_fields + descriptive_fields=descriptive_fields, + coordinatesystem= { + 'axes': [ + { + 'name': 'x', + 'type': 'cartesian', + 'unit': 'meter', + 'pixels_per_unit': 1, + 'description': 'The x-coordinate' + }, + { + 'name': 'y', + 'type': 'cartesian', + 'unit': 'meter', + 'pixels_per_unit': 1, + 'description': 'The y-coordinate' + } + ], + 'transformation_matrix': [ + [1, 0, 0], + [0, 1, 0], + [0, 0, 1] + ], + 'origo': 'top-left' + } ) return feature_collection @@ -75,8 +95,6 @@ def df_to_microjson(df: pd.DataFrame) -> mj.FeatureCollection: 'type': 'Feature', 'geometry_type': 'Point', 'coordinates': [0, 0], - 'coordinatesystem_axes': ['x', 'y'], - 'coordinatesystem_units': ['pixel', 'pixel'], 'name': 'Point 1', 'value': 1, 'values': [1, 2, 3] @@ -84,8 +102,6 @@ def df_to_microjson(df: pd.DataFrame) -> mj.FeatureCollection: 'type': 'Feature', 'geometry_type': 'Polygon', 'coordinates': [[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]], - 'coordinatesystem_axes': ['x', 'y'], - 'coordinatesystem_units': ['pixel', 'pixel'], 'name': 'Polygon 1', 'value': 2, 'values': [4, 5, 6] diff --git a/geojson/Feature.py b/geojson/Feature.py index 6b72476..db6dbc2 100644 --- a/geojson/Feature.py +++ b/geojson/Feature.py @@ -1,6 +1,4 @@ -# generated by datamodel-codegen: -# filename: Feature.json -# timestamp: 2023-06-01T19:31:21+00:00 +"""Microjson. Auto-generated by datamodel-codegen""" from __future__ import annotations @@ -10,123 +8,164 @@ from pydantic import BaseModel, Field -class Type(Enum): - feature = 'Feature' +class Type75(Enum): + feature = "Feature" -class Type34(Enum): - point = 'Point' +class Type76(Enum): + point = "Point" -class GeometryItem(BaseModel): - type: Type34 +class GeometryItem21(BaseModel): + type: Type76 coordinates: List[float] = Field(..., min_items=2) bbox: Optional[List[float]] = Field(None, min_items=4) -class Type35(Enum): - line_string = 'LineString' +class Type77(Enum): + line_string = "LineString" -class Coordinate(BaseModel): +class Coordinate43(BaseModel): __root__: List[Any] -class GeometryItem8(BaseModel): - type: Type35 - coordinates: List[Coordinate] = Field(..., min_items=2) +class GeometryItem22(BaseModel): + type: Type77 + coordinates: List[Coordinate43] = Field(..., min_items=2) bbox: Optional[List[float]] = Field(None, min_items=4) -class Type36(Enum): - polygon = 'Polygon' +class Type78(Enum): + polygon = "Polygon" -class GeometryItem9(BaseModel): - type: Type36 - coordinates: List[List[Coordinate]] +class Coordinate44(Coordinate43): + pass + + +class GeometryItem23(BaseModel): + type: Type78 + coordinates: List[List[Coordinate44]] bbox: Optional[List[float]] = Field(None, min_items=4) -class Type37(Enum): - multi_point = 'MultiPoint' +class Type79(Enum): + multi_point = "MultiPoint" -class GeometryItem10(BaseModel): - type: Type37 +class GeometryItem24(BaseModel): + type: Type79 coordinates: List[List[float]] bbox: Optional[List[float]] = Field(None, min_items=4) -class Type38(Enum): - multi_line_string = 'MultiLineString' +class Type80(Enum): + multi_line_string = "MultiLineString" + + +class Coordinate45(Coordinate43): + pass -class GeometryItem11(BaseModel): - type: Type38 - coordinates: List[List[Coordinate]] +class GeometryItem25(BaseModel): + type: Type80 + coordinates: List[List[Coordinate45]] bbox: Optional[List[float]] = Field(None, min_items=4) -class Type39(Enum): - multi_polygon = 'MultiPolygon' +class Type81(Enum): + multi_polygon = "MultiPolygon" -class GeometryItem12(BaseModel): - type: Type39 - coordinates: List[List[List[Coordinate]]] +class Coordinate46(Coordinate43): + pass + + +class GeometryItem26(BaseModel): + type: Type81 + coordinates: List[List[List[Coordinate46]]] bbox: Optional[List[float]] = Field(None, min_items=4) -class Type40(Enum): - geometry_collection = 'GeometryCollection' +class Type82(Enum): + geometry_collection = "GeometryCollection" -class Geometry(GeometryItem): +class Geometry30(GeometryItem21): pass -class Geometry13(GeometryItem8): +class Coordinate47(Coordinate43): pass -class Geometry14(GeometryItem9): +class Geometry31(BaseModel): + type: Type77 + coordinates: List[Coordinate47] = Field(..., min_items=2) + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Coordinate48(Coordinate43): pass -class Geometry15(GeometryItem10): +class Geometry32(BaseModel): + type: Type78 + coordinates: List[List[Coordinate48]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Geometry33(GeometryItem24): pass -class Geometry16(GeometryItem11): +class Coordinate49(Coordinate43): pass -class Geometry17(GeometryItem12): +class Geometry34(BaseModel): + type: Type80 + coordinates: List[List[Coordinate49]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Coordinate50(Coordinate43): pass -class GeometryItem13(BaseModel): - type: Type40 +class Geometry35(BaseModel): + type: Type81 + coordinates: List[List[List[Coordinate50]]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class GeometryItem27(BaseModel): + type: Type82 geometries: List[ - Union[Geometry, Geometry13, Geometry14, Geometry15, Geometry16, Geometry17] + Union[Geometry30, + Geometry31, + Geometry32, + Geometry33, + Geometry34, + Geometry35] ] bbox: Optional[List[float]] = Field(None, min_items=4) class GeojsonFeature(BaseModel): - type: Type + type: Type75 id: Optional[Union[float, str]] = None - properties: Optional[Dict[str, Any]] + properties: Union[Any, Dict[str, Any]] geometry: Optional[ Union[ - GeometryItem, - GeometryItem8, - GeometryItem9, - GeometryItem10, - GeometryItem11, - GeometryItem12, - GeometryItem13, + GeometryItem21, + GeometryItem22, + GeometryItem23, + GeometryItem24, + GeometryItem25, + GeometryItem26, + GeometryItem27, ] ] bbox: Optional[List[float]] = Field(None, min_items=4) diff --git a/geojson/FeatureCollection.py b/geojson/FeatureCollection.py index 9c0ee20..2dabc7d 100644 --- a/geojson/FeatureCollection.py +++ b/geojson/FeatureCollection.py @@ -1,6 +1,4 @@ -# generated by datamodel-codegen: -# filename: FeatureCollection.json -# timestamp: 2023-06-01T19:31:21+00:00 +"""Microjson. Auto-generated by datamodel-codegen""" from __future__ import annotations @@ -10,133 +8,174 @@ from pydantic import BaseModel, Field -class Type(Enum): +class Type53(Enum): feature_collection = 'FeatureCollection' -class Type12(Enum): +class Type54(Enum): feature = 'Feature' -class Type13(Enum): +class Type55(Enum): point = 'Point' -class GeometryItem(BaseModel): - type: Type13 +class GeometryItem14(BaseModel): + type: Type55 coordinates: List[float] = Field(..., min_items=2) bbox: Optional[List[float]] = Field(None, min_items=4) -class Type14(Enum): +class Type56(Enum): line_string = 'LineString' -class Coordinate(BaseModel): +class Coordinate31(BaseModel): __root__: List[Any] -class GeometryItem1(BaseModel): - type: Type14 - coordinates: List[Coordinate] = Field(..., min_items=2) +class GeometryItem15(BaseModel): + type: Type56 + coordinates: List[Coordinate31] = Field(..., min_items=2) bbox: Optional[List[float]] = Field(None, min_items=4) -class Type15(Enum): +class Type57(Enum): polygon = 'Polygon' -class GeometryItem2(BaseModel): - type: Type15 - coordinates: List[List[Coordinate]] +class Coordinate32(Coordinate31): + pass + + +class GeometryItem16(BaseModel): + type: Type57 + coordinates: List[List[Coordinate32]] bbox: Optional[List[float]] = Field(None, min_items=4) -class Type16(Enum): +class Type58(Enum): multi_point = 'MultiPoint' -class GeometryItem3(BaseModel): - type: Type16 +class GeometryItem17(BaseModel): + type: Type58 coordinates: List[List[float]] bbox: Optional[List[float]] = Field(None, min_items=4) -class Type17(Enum): +class Type59(Enum): multi_line_string = 'MultiLineString' -class GeometryItem4(BaseModel): - type: Type17 - coordinates: List[List[Coordinate]] +class Coordinate33(Coordinate31): + pass + + +class GeometryItem18(BaseModel): + type: Type59 + coordinates: List[List[Coordinate33]] bbox: Optional[List[float]] = Field(None, min_items=4) -class Type18(Enum): +class Type60(Enum): multi_polygon = 'MultiPolygon' -class GeometryItem5(BaseModel): - type: Type18 - coordinates: List[List[List[Coordinate]]] +class Coordinate34(Coordinate31): + pass + + +class GeometryItem19(BaseModel): + type: Type60 + coordinates: List[List[List[Coordinate34]]] bbox: Optional[List[float]] = Field(None, min_items=4) -class Type19(Enum): +class Type61(Enum): geometry_collection = 'GeometryCollection' -class Geometry(GeometryItem): +class Geometry24(GeometryItem14): pass -class Geometry7(GeometryItem1): +class Coordinate35(Coordinate31): pass -class Geometry8(GeometryItem2): +class Geometry25(BaseModel): + type: Type56 + coordinates: List[Coordinate35] = Field(..., min_items=2) + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Coordinate36(Coordinate31): pass -class Geometry9(GeometryItem3): +class Geometry26(BaseModel): + type: Type57 + coordinates: List[List[Coordinate36]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Geometry27(GeometryItem17): pass -class Geometry10(GeometryItem4): +class Coordinate37(Coordinate31): pass -class Geometry11(GeometryItem5): +class Geometry28(BaseModel): + type: Type59 + coordinates: List[List[Coordinate37]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Coordinate38(Coordinate31): pass -class GeometryItem6(BaseModel): - type: Type19 +class Geometry29(BaseModel): + type: Type60 + coordinates: List[List[List[Coordinate38]]] + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class GeometryItem20(BaseModel): + type: Type61 geometries: List[ - Union[Geometry, Geometry7, Geometry8, Geometry9, Geometry10, Geometry11] + Union[Geometry24, + Geometry25, + Geometry26, + Geometry27, + Geometry28, + Geometry29] ] bbox: Optional[List[float]] = Field(None, min_items=4) -class Feature(BaseModel): - type: Type12 +class Feature1(BaseModel): + type: Type54 id: Optional[Union[float, str]] = None - properties: Optional[Dict[str, Any]] + properties: Union[Any, Dict[str, Any]] geometry: Optional[ Union[ - GeometryItem, - GeometryItem1, - GeometryItem2, - GeometryItem3, - GeometryItem4, - GeometryItem5, - GeometryItem6, + GeometryItem14, + GeometryItem15, + GeometryItem16, + GeometryItem17, + GeometryItem18, + GeometryItem19, + GeometryItem20, ] ] bbox: Optional[List[float]] = Field(None, min_items=4) class GeojsonFeaturecollection(BaseModel): - type: Type - features: List[Feature] + type: Type53 + features: List[Feature1] bbox: Optional[List[float]] = Field(None, min_items=4) diff --git a/geojson/Geometry.py b/geojson/Geometry.py index bbf429e..631e865 100644 --- a/geojson/Geometry.py +++ b/geojson/Geometry.py @@ -1,6 +1,7 @@ # generated by datamodel-codegen: # filename: Geometry.json -# timestamp: 2023-06-01T19:31:21+00:00 +# timestamp: 2023-06-28T15:38:42+00:00 +"""Microjson. Auto-generated by datamodel-codegen""" from __future__ import annotations @@ -10,67 +11,79 @@ from pydantic import BaseModel, Field -class Type(Enum): +class Type68(Enum): point = 'Point' class GeojsonGeometryItem(BaseModel): - type: Type + type: Type68 coordinates: List[float] = Field(..., min_items=2) bbox: Optional[List[float]] = Field(None, min_items=4) -class Type27(Enum): +class Type69(Enum): line_string = 'LineString' -class Coordinate(BaseModel): +class Coordinate39(BaseModel): __root__: List[Any] class GeojsonGeometryItem1(BaseModel): - type: Type27 - coordinates: List[Coordinate] = Field(..., min_items=2) + type: Type69 + coordinates: List[Coordinate39] = Field(..., min_items=2) bbox: Optional[List[float]] = Field(None, min_items=4) -class Type28(Enum): +class Type70(Enum): polygon = 'Polygon' +class Coordinate40(Coordinate39): + pass + + class GeojsonGeometryItem2(BaseModel): - type: Type28 - coordinates: List[List[Coordinate]] + type: Type70 + coordinates: List[List[Coordinate40]] bbox: Optional[List[float]] = Field(None, min_items=4) -class Type29(Enum): +class Type71(Enum): multi_point = 'MultiPoint' class GeojsonGeometryItem3(BaseModel): - type: Type29 + type: Type71 coordinates: List[List[float]] bbox: Optional[List[float]] = Field(None, min_items=4) -class Type30(Enum): +class Type72(Enum): multi_line_string = 'MultiLineString' +class Coordinate41(Coordinate39): + pass + + class GeojsonGeometryItem4(BaseModel): - type: Type30 - coordinates: List[List[Coordinate]] + type: Type72 + coordinates: List[List[Coordinate41]] bbox: Optional[List[float]] = Field(None, min_items=4) -class Type31(Enum): +class Type73(Enum): multi_polygon = 'MultiPolygon' +class Coordinate42(Coordinate39): + pass + + class GeojsonGeometryItem5(BaseModel): - type: Type31 - coordinates: List[List[List[Coordinate]]] + type: Type73 + coordinates: List[List[List[Coordinate42]]] bbox: Optional[List[float]] = Field(None, min_items=4) diff --git a/geojson/GeometryCollection.py b/geojson/GeometryCollection.py index 536d0c1..ca1e6e6 100644 --- a/geojson/GeometryCollection.py +++ b/geojson/GeometryCollection.py @@ -1,6 +1,4 @@ -# generated by datamodel-codegen: -# filename: GeometryCollection.json -# timestamp: 2023-06-01T19:31:21+00:00 +"""Microjson. Auto-generated by datamodel-codegen""" from __future__ import annotations @@ -10,77 +8,89 @@ from pydantic import BaseModel, Field -class Type(Enum): +class Type45(Enum): geometry_collection = 'GeometryCollection' -class Type4(Enum): +class Type46(Enum): point = 'Point' -class Geometry(BaseModel): - type: Type4 - coordinates: List[float] = Field(..., min_items=2) - bbox: Optional[List[float]] = Field(None, min_items=4) +class Geometry18(BaseModel): + type: Type4# generated by datamodel-codegen: +# filename: GeoJSON.json +# timestamp: 2023-06-28T15:38:42+00:00onal[List[float]] = Field(None, min_items=4) -class Type5(Enum): +class Type47(Enum): line_string = 'LineString' -class Coordinate(BaseModel): +class Coordinate26(BaseModel): __root__: List[Any] -class Geometry1(BaseModel): - type: Type5 - coordinates: List[Coordinate] = Field(..., min_items=2) +class Geometry19(BaseModel): + type: Type47 + coordinates: List[Coordinate26] = Field(..., min_items=2) bbox: Optional[List[float]] = Field(None, min_items=4) -class Type6(Enum): +class Type48(Enum): polygon = 'Polygon' -class Geometry2(BaseModel): - type: Type6 - coordinates: List[List[Coordinate]] +class Coordinate27(Coordinate26): + pass + + +class Geometry20(BaseModel): + type: Type48 + coordinates: List[List[Coordinate27]] bbox: Optional[List[float]] = Field(None, min_items=4) -class Type7(Enum): +class Type49(Enum): multi_point = 'MultiPoint' -class Geometry3(BaseModel): - type: Type7 +class Geometry21(BaseModel): + type: Type49 coordinates: List[List[float]] bbox: Optional[List[float]] = Field(None, min_items=4) -class Type8(Enum): +class Type50(Enum): multi_line_string = 'MultiLineString' -class Geometry4(BaseModel): - type: Type8 - coordinates: List[List[Coordinate]] +class Coordinate28(Coordinate26): + pass + + +class Geometry22(BaseModel): + type: Type50 + coordinates: List[List[Coordinate28]] bbox: Optional[List[float]] = Field(None, min_items=4) -class Type9(Enum): +class Type51(Enum): multi_polygon = 'MultiPolygon' -class Geometry5(BaseModel): - type: Type9 - coordinates: List[List[List[Coordinate]]] +class Coordinate29(Coordinate26): + pass + + +class Geometry23(BaseModel): + type: Type51 + coordinates: List[List[List[Coordinate29]]] bbox: Optional[List[float]] = Field(None, min_items=4) class GeojsonGeometrycollection(BaseModel): - type: Type + type: Type45 geometries: List[ - Union[Geometry, Geometry1, Geometry2, Geometry3, Geometry4, Geometry5] + Union[Geometry18, Geometry19, Geometry20, Geometry21, Geometry22, Geometry23] ] bbox: Optional[List[float]] = Field(None, min_items=4) diff --git a/geojson/LineString.py b/geojson/LineString.py index 116059b..0f51acb 100644 --- a/geojson/LineString.py +++ b/geojson/LineString.py @@ -1,6 +1,4 @@ -# generated by datamodel-codegen: -# filename: LineString.json -# timestamp: 2023-06-01T19:31:21+00:00 +"""Microjson. Auto-generated by datamodel-codegen""" from __future__ import annotations @@ -10,15 +8,15 @@ from pydantic import BaseModel, Field -class Type(Enum): +class Type52(Enum): line_string = 'LineString' -class Coordinate(BaseModel): +class Coordinate30(BaseModel): __root__: List[Any] class GeojsonLinestring(BaseModel): - type: Type - coordinates: List[Coordinate] = Field(..., min_items=2) + type: Type52 + coordinates: List[Coordinate30] = Field(..., min_items=2) bbox: Optional[List[float]] = Field(None, min_items=4) diff --git a/geojson/MultiLineString.py b/geojson/MultiLineString.py index 1e95ba2..eeed124 100644 --- a/geojson/MultiLineString.py +++ b/geojson/MultiLineString.py @@ -1,6 +1,4 @@ -# generated by datamodel-codegen: -# filename: MultiLineString.json -# timestamp: 2023-06-01T19:31:21+00:00 +"""Microjson. Auto-generated by datamodel-codegen""" from __future__ import annotations @@ -10,15 +8,15 @@ from pydantic import BaseModel, Field -class Type(Enum): +class Type89(Enum): multi_line_string = 'MultiLineString' -class Coordinate(BaseModel): +class Coordinate51(BaseModel): __root__: List[Any] class GeojsonMultilinestring(BaseModel): - type: Type - coordinates: List[List[Coordinate]] + type: Type89 + coordinates: List[List[Coordinate51]] bbox: Optional[List[float]] = Field(None, min_items=4) diff --git a/geojson/MultiPoint.py b/geojson/MultiPoint.py index 77f2245..f777e6d 100644 --- a/geojson/MultiPoint.py +++ b/geojson/MultiPoint.py @@ -1,6 +1,4 @@ -# generated by datamodel-codegen: -# filename: MultiPoint.json -# timestamp: 2023-06-01T19:31:21+00:00 +"""Microjson. Auto-generated by datamodel-codegen""" from __future__ import annotations @@ -10,11 +8,11 @@ from pydantic import BaseModel, Field -class Type(Enum): +class Type44(Enum): multi_point = 'MultiPoint' class GeojsonMultipoint(BaseModel): - type: Type + type: Type44 coordinates: List[List[float]] bbox: Optional[List[float]] = Field(None, min_items=4) diff --git a/geojson/MultiPolygon.py b/geojson/MultiPolygon.py index 8aec150..f1a6fa8 100644 --- a/geojson/MultiPolygon.py +++ b/geojson/MultiPolygon.py @@ -1,6 +1,4 @@ -# generated by datamodel-codegen: -# filename: MultiPolygon.json -# timestamp: 2023-06-01T19:31:21+00:00 +"""Microjson. Auto-generated by datamodel-codegen""" from __future__ import annotations @@ -10,15 +8,15 @@ from pydantic import BaseModel, Field -class Type(Enum): +class Type43(Enum): multi_polygon = 'MultiPolygon' -class Coordinate(BaseModel): +class Coordinate25(BaseModel): __root__: List[Any] class GeojsonMultipolygon(BaseModel): - type: Type - coordinates: List[List[List[Coordinate]]] + type: Type43 + coordinates: List[List[List[Coordinate25]]] bbox: Optional[List[float]] = Field(None, min_items=4) diff --git a/geojson/Point.py b/geojson/Point.py index 0ce9115..bf19671 100644 --- a/geojson/Point.py +++ b/geojson/Point.py @@ -1,6 +1,4 @@ -# generated by datamodel-codegen: -# filename: Point.json -# timestamp: 2023-06-01T19:31:21+00:00 +"""Microjson. Auto-generated by datamodel-codegen""" from __future__ import annotations @@ -10,11 +8,11 @@ from pydantic import BaseModel, Field -class Type(Enum): +class Type74(Enum): point = 'Point' class GeojsonPoint(BaseModel): - type: Type + type: Type74 coordinates: List[float] = Field(..., min_items=2) bbox: Optional[List[float]] = Field(None, min_items=4) diff --git a/geojson/Polygon.py b/geojson/Polygon.py index faf2c53..f3af661 100644 --- a/geojson/Polygon.py +++ b/geojson/Polygon.py @@ -1,6 +1,4 @@ -# generated by datamodel-codegen: -# filename: Polygon.json -# timestamp: 2023-06-01T19:31:21+00:00 +"""Microjson. Auto-generated by datamodel-codegen""" from __future__ import annotations @@ -10,15 +8,15 @@ from pydantic import BaseModel, Field -class Type(Enum): +class Type42(Enum): polygon = 'Polygon' -class Coordinate(BaseModel): +class Coordinate24(BaseModel): __root__: List[Any] class GeojsonPolygon(BaseModel): - type: Type - coordinates: List[List[Coordinate]] + type: Type42 + coordinates: List[List[Coordinate24]] bbox: Optional[List[float]] = Field(None, min_items=4) diff --git a/microjson/automodel.py b/microjson/automodel.py index de56774..c8239d5 100644 --- a/microjson/automodel.py +++ b/microjson/automodel.py @@ -1,3 +1,5 @@ +"""Microjson using manual MicroJSON pydantic models, +but autogenerated GeoJSON models.""" from typing import List, Optional, Union, Dict, Literal from enum import Enum from pydantic import BaseModel, Field, StrictInt, StrictStr @@ -11,9 +13,12 @@ from geojson.MultiPolygon import GeojsonMultipolygon from geojson.Point import GeojsonPoint from geojson.Polygon import GeojsonPolygon +from microjson.model import Unit, Axis, AxisType, CoordinateSystem +from microjson.model import Properties, ValueRange class GeoJSONAuto(BaseModel): + """The root object of a GeoJSON file""" __root__: Union[ GeojsonFeature, GeojsonFeaturecollection, @@ -22,82 +27,25 @@ class GeoJSONAuto(BaseModel): ] -class Unit(Enum): - PIXEL = "pixel" - METER = "meter" - DECIMETER = "decimeter" - CENTIMETER = "centimeter" - MILLIMETER = "millimeter" - MICROMETER = "micrometer" - NANOMETER = "nanometer" - PICOMETER = "picometer" - RADIAN = "radian" - DEGREE = "degree" +class MicroFeature(GeojsonFeature): + """A MicroJSON feature, which is a GeoJSON feature with additional + metadata""" + coordinatesystem: Optional[List[Axis]] + ref: Optional[Union[StrictStr, StrictInt]] + properties: Properties -class Coordinatesystem(BaseModel): - axes: List[Literal["x", "y", "z", "r", "theta", "phi"]] = Field( - ..., description="The coordinate system of the coordinates" - ) - units: List[Unit] = Field(..., description="The units of the coordinates") - pixelsPerUnit: List[float] = Field( - ..., description="The number of pixels per unit" - ) - - -class MicroFeature(BaseModel): - type: Literal["Feature"] - properties: Optional[Dict] - geometry: Optional[GeojsonGeometry] - coordinatesystem: Optional[Coordinatesystem] - id: Optional[Union[StrictStr, StrictInt]] - - -class MicroFeatureCollection(BaseModel): - type: Literal["FeatureCollection"] - features: List[GeojsonFeature] - coordinatesystem: Optional[Coordinatesystem] - - -class MicroPoint(GeojsonPoint): - coordinatesystem: Optional[Coordinatesystem] - - -class MicroMultiPoint(GeojsonMultipoint): - coordinatesystem: Optional[Coordinatesystem] - - -class MicroLineString(GeojsonLinestring): - coordinatesystem: Optional[Coordinatesystem] - - -class MicroMultiLineString(GeojsonMultilinestring): - coordinatesystem: Optional[Coordinatesystem] - - -class MicroPolygon(GeojsonPolygon): - coordinatesystem: Optional[Coordinatesystem] - - -class MicroMultiPolygon(GeojsonMultipolygon): - coordinatesystem: Optional[Coordinatesystem] - - -class MicroGeometryCollection(GeojsonGeometrycollection): - coordinatesystem: Optional[Coordinatesystem] - - -MicroGeometryType = Union[MicroPoint, - MicroMultiPoint, - MicroLineString, - MicroMultiLineString, - MicroPolygon, - MicroMultiPolygon, - MicroGeometryCollection] +class MicroFeatureCollection(GeojsonFeaturecollection): + """A MicroJSON feature collection, which is a GeoJSON feature + collection with additional metadata""" + coordinatesystem: Optional[CoordinateSystem] + value_range: Optional[Dict[str, ValueRange]] + descriptive_fields: Optional[List[str]] class MicroJSONAuto(BaseModel): """The root object of a MicroJSON file""" __root__: Union[MicroFeature, MicroFeatureCollection, - MicroGeometryType] + GeojsonGeometry, + GeojsonGeometrycollection] diff --git a/microjson/geojson.py b/microjson/geojson.py index ccaaf1b..0ff7de2 100644 --- a/microjson/geojson.py +++ b/microjson/geojson.py @@ -1,6 +1,4 @@ -# generated by datamodel-codegen: -# filename: GeoJSON.json -# timestamp: 2023-06-06T00:26:31+00:00 +"""Microjson GeoJSON auto-generated by datamodel-codegen""" from __future__ import annotations diff --git a/microjson/model.py b/microjson/model.py index 80a15e9..bfc502e 100644 --- a/microjson/model.py +++ b/microjson/model.py @@ -1,3 +1,4 @@ +"""MicroJSON and GeoJSON models, defined manually using pydantic.""" from typing import List, Optional, Union, Dict, Literal from enum import Enum from pydantic import BaseModel, Field, StrictInt, StrictStr, conlist @@ -7,35 +8,42 @@ class GeoAbstract(BaseModel): + """Abstract base class for all GeoJSON objects""" bbox: Optional[List[float]] = Field(None, min_items=4) class Point(GeoAbstract): + """A GeoJSON Point object""" type: Literal["Point"] coordinates: Coordinates class MultiPoint(GeoAbstract): + """A GeoJSON MultiPoint object""" type: Literal["MultiPoint"] coordinates: List[Coordinates] class LineString(GeoAbstract): + """A GeoJSON LineString object""" type: Literal["LineString"] coordinates: List[Coordinates] class MultiLineString(GeoAbstract): + """A GeoJSON MultiLineString object""" type: Literal["MultiLineString"] coordinates: List[List[Coordinates]] class Polygon(GeoAbstract): + """A GeoJSON Polygon object""" type: Literal["Polygon"] coordinates: List[List[Coordinates]] class MultiPolygon(GeoAbstract): + """A GeoJSON MultiPolygon object""" type: Literal["MultiPolygon"] coordinates: List[List[List[Coordinates]]] @@ -49,6 +57,7 @@ class MultiPolygon(GeoAbstract): class GeometryCollection(GeoAbstract): + """A GeoJSON GeometryCollection object""" type: Literal["GeometryCollection"] geometries: List[GeometryBaseType] @@ -65,6 +74,7 @@ class GeometryCollection(GeoAbstract): class Feature(GeoAbstract): + """A GeoJSON Feature object""" type: Literal["Feature"] geometry: GeometryType = Field(..., description="""The geometry of the @@ -76,15 +86,15 @@ class Feature(GeoAbstract): class ValueRange(BaseModel): + """A range of values for MicroJSON quantitative properties""" min: float max: float class FeatureCollection(GeoAbstract): + """A GeoJSON FeatureCollection object""" type: Literal["FeatureCollection"] features: List[Feature] - value_range: Optional[Dict[str, ValueRange]] - descriptive_fields: Optional[List[str]] class GeoJSON(BaseModel): @@ -93,6 +103,7 @@ class GeoJSON(BaseModel): class Unit(Enum): + """A unit of measurement""" ANGSTROM = 'angstrom' ATTOMETER = 'attometer' CENTIMETER = 'centimeter' @@ -124,28 +135,50 @@ class Unit(Enum): DEGREE = 'degree' -class Coordinatesystem(BaseModel): - axes: List[Literal["x", "y", "z", "r", "theta", "phi"]] - units: Optional[List[Unit]] - pixelsPerUnit: Optional[List[float]] +class AxisType(Enum): + """The type of an axis""" + CARTESIAN = 'cartesian' + ANGULAR = 'angular' + TEMPORAL = 'temporal' + SPECTRAL = 'spectral' + + +class Axis(BaseModel): + """An axis of a coordinate system""" + name: StrictStr + type: Optional[AxisType] + unit: Optional[Unit] + pixels_per_unit: Optional[float] + description: Optional[str] + + +class CoordinateSystem(BaseModel): + """A coordinate system for MicroJSON coordinates""" + axes: List[Axis] + transformation_matrix: Optional[List[List[float]]] class Properties(BaseModel): - # other fields... + """Metadata properties of a MicroJSON feature""" descriptive: Optional[Dict[str, str]] numerical: Optional[Dict[str, float]] multi_numerical: Optional[Dict[str, List[float]]] class MicroFeature(Feature): - coordinatesystem: Optional[Coordinatesystem] + """A MicroJSON feature, which is a GeoJSON feature with additional + metadata""" + coordinatesystem: Optional[List[Axis]] ref: Optional[Union[StrictStr, StrictInt]] - # properties: Properties class MicroFeatureCollection(FeatureCollection): - coordinatesystem: Optional[Coordinatesystem] + """A MicroJSON feature collection, which is a GeoJSON feature + collection with additional metadata""" + coordinatesystem: Optional[CoordinateSystem] + value_range: Optional[Dict[str, ValueRange]] + descriptive_fields: Optional[List[str]] class MicroJSON(BaseModel): diff --git a/microjson/roundtrip.py b/microjson/roundtrip.py index 2a8e1f6..e7ca7ea 100644 --- a/microjson/roundtrip.py +++ b/microjson/roundtrip.py @@ -1,6 +1,6 @@ -# generated by datamodel-codegen: -# filename: microjson_schema.json -# timestamp: 2023-06-28T15:38:44+00:00 +"""MicroJSON Roundtrip for GeoJSON, generated automatically +by datamodel-codegen from models exported from +Pydantic models""" from __future__ import annotations @@ -17,7 +17,10 @@ class Type(Enum): class Point(BaseModel): bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') type: Type = Field(..., title='Type') - coordinates: List[float] = Field(..., max_items=3, min_items=2, title='Coordinates') + coordinates: List[float] = Field(..., + max_items=3, + min_items=2, + title='Coordinates') class Type1(Enum): @@ -67,7 +70,8 @@ class Type5(Enum): class MultiPolygon(BaseModel): bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') type: Type5 = Field(..., title='Type') - coordinates: List[List[List[List[float]]]] = Field(..., title='Coordinates') + coordinates: List[List[List[List[float]]]] = Field(..., + title='Coordinates') class Type6(Enum): @@ -78,7 +82,12 @@ class GeometryCollection(BaseModel): bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') type: Type6 = Field(..., title='Type') geometries: List[ - Union[Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon] + Union[Point, + MultiPoint, + LineString, + MultiLineString, + Polygon, + MultiPolygon] ] = Field(..., title='Geometries') @@ -156,7 +165,7 @@ class MicroFeature(BaseModel): GeometryCollection, ] = Field( ..., - description='The geometry of the\n feature', + description='The geometry of the feature', title='Geometry', ) properties: Properties @@ -178,12 +187,12 @@ class Feature(BaseModel): GeometryCollection, ] = Field( ..., - description='The geometry of the\n feature', + description='The geometry of the feature', title='Geometry', ) properties: Dict[str, Any] = Field( ..., - description='Properties of the\n feature', + description='Properties of the feature', title='Properties', ) id: Optional[Union[str, int]] = Field(None, title='Id') @@ -202,8 +211,10 @@ class MicroFeatureCollection(BaseModel): bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') type: Type9 = Field(..., title='Type') features: List[Feature] = Field(..., title='Features') - value_range: Optional[Dict[str, ValueRange]] = Field(None, title='Value Range') - descriptive_fields: Optional[List[str]] = Field(None, title='Descriptive Fields') + value_range: Optional[Dict[str, ValueRange]] = Field(None, + title='Value Range') + descriptive_fields: Optional[List[str]] = Field(None, + title='Descriptive Fields') coordinatesystem: Optional[Coordinatesystem] = None @@ -218,4 +229,6 @@ class MicroJSON(BaseModel): Polygon, MultiPolygon, GeometryCollection, - ] = Field(..., description='The root object of a MicroJSON file', title='MicroJSON') + ] = Field(..., + description='The root object of a MicroJSON file', + title='MicroJSON') diff --git a/pyproject.toml b/pyproject.toml index fd20452..fe93b7b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,10 @@ pydantic = "^1.8.2" [tool.poetry.dev-dependencies] pytest = "^6.2.4" -nox = "^2021.6.12" +nox = "^2022.1.7" +pre-commit = "^2.15.0" +pydantic-to-typescript = "^1.0.8" +datamodel-code-generator = "^0.13.1" [tool.pytest.ini_options] diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index 793f7e6..0000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,4 +0,0 @@ -nox==2022.1.7 -pre-commit==2.15.0 -pydantic-to-typescript==1.0.8 -datamodel-code-generator==0.13.1 diff --git a/tests/json/microjson/valid/fullexample.json b/tests/json/microjson/valid/fullexample.json index 00e2ea3..0db334a 100644 --- a/tests/json/microjson/valid/fullexample.json +++ b/tests/json/microjson/valid/fullexample.json @@ -48,16 +48,37 @@ ], "coordinatesystem": { "axes": [ - "x", - "y" - ], - "units": [ - "micrometer", - "micrometer" + { + "name": "x", + "unit": "micrometer", + "type": "cartesian", + "pixelsPerUnit": 1, + "description": "x-axis" + }, + { + "name": "y", + "unit": "micrometer", + "type": "cartesian", + "pixelsPerUnit": 1, + "description": "y-axis" + } ], - "pixelsPerUnit": [ - 0.5, - 0.5 + "transformation_matrix": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 0.0 + ] ] } } \ No newline at end of file diff --git a/tests/json/microjson/valid/metadata-full.json b/tests/json/microjson/valid/metadata-full.json index 1cc7578..9bff332 100644 --- a/tests/json/microjson/valid/metadata-full.json +++ b/tests/json/microjson/valid/metadata-full.json @@ -1,7 +1,13 @@ { "type": "FeatureCollection", "coordinatesystem": { - "axes": ["x", "y"] + "axes": [{ + "name": "x" + } + , { + "name": "y" + } + ] }, "features": [ { diff --git a/tests/json/microjson/valid/multilevel-coordinatesystems.json b/tests/json/microjson/valid/multilevel-coordinatesystems.json index 7b1ffda..73a7a3a 100644 --- a/tests/json/microjson/valid/multilevel-coordinatesystems.json +++ b/tests/json/microjson/valid/multilevel-coordinatesystems.json @@ -35,21 +35,7 @@ 300.0, 100.0 ] - ], - "coordinatesystem": { - "axes": [ - "x", - "y" - ], - "units": [ - "micrometer", - "micrometer" - ], - "pixelsPerUnit": [ - 1, - 1 - ] - } + ] }, "properties": { "name": "Cell Path", @@ -62,16 +48,37 @@ ], "coordinatesystem": { "axes": [ - "x", - "y" - ], - "units": [ - "micrometer", - "micrometer" + { + "name": "x", + "unit": "micrometer", + "type": "cartesian", + "pixelsPerUnit": 1, + "description": "x-axis" + }, + { + "name": "y", + "unit": "micrometer", + "type": "cartesian", + "pixelsPerUnit": 1, + "description": "y-axis" + } ], - "pixelsPerUnit": [ - 0.5, - 0.5 + "tranformation_matrix": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 0.0 + ] ] } } \ No newline at end of file diff --git a/tests/json/microjson/valid/stitching-vector.json b/tests/json/microjson/valid/stitching-vector.json index 2279ced..26d7d1a 100644 --- a/tests/json/microjson/valid/stitching-vector.json +++ b/tests/json/microjson/valid/stitching-vector.json @@ -1,7 +1,13 @@ { "type": "FeatureCollection", "coordinatesystem": { - "axes": ["x", "y"] + "axes": [{ + "name": "x" + } + , { + "name": "y" + } + ] }, "features": [ { From a9d064c66825ab0cfe4ea03012fe90816ab8c3f0 Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Tue, 1 Aug 2023 03:01:19 -0400 Subject: [PATCH 11/28] Updated structure --- pyproject.toml | 27 ++++++++++++++++++++++- {microjson => src/microjson}/__init__.py | 4 +++- {microjson => src/microjson}/automodel.py | 0 {microjson => src/microjson}/geojson.py | 0 {microjson => src/microjson}/model.py | 0 {microjson => src/microjson}/roundtrip.py | 0 {microjson => src/microjson}/utils.py | 0 7 files changed, 29 insertions(+), 2 deletions(-) rename {microjson => src/microjson}/__init__.py (69%) rename {microjson => src/microjson}/automodel.py (100%) rename {microjson => src/microjson}/geojson.py (100%) rename {microjson => src/microjson}/model.py (100%) rename {microjson => src/microjson}/roundtrip.py (100%) rename {microjson => src/microjson}/utils.py (100%) diff --git a/pyproject.toml b/pyproject.toml index fe93b7b..01bb7d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,18 @@ version = "0.1.0" description = "MicroJSON is a library for validating, parsing, and manipulating MicroJSON data." authors = ["Bengt Ljungquist "] license = "MIT" +classifiers = [ + "License :: OSI Approved :: MIT License", + "Programming Language :: Python", + "Programming Language :: Python :: 3", +] +keywords = ["json", "microscopy", "microjson"] + +[tool.setuptools] +package-dir = {"" = "src"} + +[tool.setuptools.packages.find] +where = ["src"] [tool.poetry.dependencies] python = "^3.9" @@ -19,7 +31,20 @@ nox = "^2022.1.7" pre-commit = "^2.15.0" pydantic-to-typescript = "^1.0.8" datamodel-code-generator = "^0.13.1" +bumpver = "^2023.1125" [tool.pytest.ini_options] -testpaths = ["tests/"] \ No newline at end of file +testpaths = ["tests/"] + +[tool.bumpver] +current_version = "0.1.0" +version_pattern = "MAJOR.MINOR.PATCH" +commit_message = "Bump version {old_version} -> {new_version}" +commit = true +tag = true +push = false + +[tool.bumpver.file_patterns] +"pyproject.toml" = ['current_version = "{version}"', 'version = "{version}"'] +"src/microjson/__init__.py" = ["{version}"] \ No newline at end of file diff --git a/microjson/__init__.py b/src/microjson/__init__.py similarity index 69% rename from microjson/__init__.py rename to src/microjson/__init__.py index 21491d5..3a024de 100644 --- a/microjson/__init__.py +++ b/src/microjson/__init__.py @@ -1,4 +1,6 @@ from .model import MicroJSON, GeoJSON #from .automodel import GeoJSONAuto, MicroJSONAuto #from .roundtrip import MicroJSON as MicroJSONRound -from .utils import gather_example_files \ No newline at end of file +from .utils import gather_example_files + +__version__ = "0.1.1" \ No newline at end of file diff --git a/microjson/automodel.py b/src/microjson/automodel.py similarity index 100% rename from microjson/automodel.py rename to src/microjson/automodel.py diff --git a/microjson/geojson.py b/src/microjson/geojson.py similarity index 100% rename from microjson/geojson.py rename to src/microjson/geojson.py diff --git a/microjson/model.py b/src/microjson/model.py similarity index 100% rename from microjson/model.py rename to src/microjson/model.py diff --git a/microjson/roundtrip.py b/src/microjson/roundtrip.py similarity index 100% rename from microjson/roundtrip.py rename to src/microjson/roundtrip.py diff --git a/microjson/utils.py b/src/microjson/utils.py similarity index 100% rename from microjson/utils.py rename to src/microjson/utils.py From 6d5609e46bf0a718017ea1b306a35545642231c3 Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Tue, 1 Aug 2023 03:04:04 -0400 Subject: [PATCH 12/28] Bump version 0.1.0 -> 0.1.1 --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 01bb7d5..c9bff63 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "microjson" -version = "0.1.0" +version = "0.1.1" description = "MicroJSON is a library for validating, parsing, and manipulating MicroJSON data." authors = ["Bengt Ljungquist "] license = "MIT" @@ -38,7 +38,7 @@ bumpver = "^2023.1125" testpaths = ["tests/"] [tool.bumpver] -current_version = "0.1.0" +current_version = "0.1.1" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true From a75a50e5f7aafa164e94bb32d6319adbf8ae143f Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Tue, 1 Aug 2023 03:04:33 -0400 Subject: [PATCH 13/28] Bump version 0.1.1 -> 0.1.2 --- pyproject.toml | 4 ++-- src/microjson/__init__.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c9bff63..cf445f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "microjson" -version = "0.1.1" +version = "0.1.2" description = "MicroJSON is a library for validating, parsing, and manipulating MicroJSON data." authors = ["Bengt Ljungquist "] license = "MIT" @@ -38,7 +38,7 @@ bumpver = "^2023.1125" testpaths = ["tests/"] [tool.bumpver] -current_version = "0.1.1" +current_version = "0.1.2" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true diff --git a/src/microjson/__init__.py b/src/microjson/__init__.py index 3a024de..16c1d25 100644 --- a/src/microjson/__init__.py +++ b/src/microjson/__init__.py @@ -3,4 +3,4 @@ #from .roundtrip import MicroJSON as MicroJSONRound from .utils import gather_example_files -__version__ = "0.1.1" \ No newline at end of file +__version__ = "0.1.2" \ No newline at end of file From 073fa15c4aa2c6d837cd8da189f7fb083bb0d851 Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Tue, 1 Aug 2023 03:24:16 -0400 Subject: [PATCH 14/28] Updated build --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index cf445f8..c3f26fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,7 @@ build-backend = "poetry.core.masonry.api" name = "microjson" version = "0.1.2" description = "MicroJSON is a library for validating, parsing, and manipulating MicroJSON data." +readme = "README.md" authors = ["Bengt Ljungquist "] license = "MIT" classifiers = [ From 6018f92c490c8455a4a57f2b4864df74ace15939 Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Tue, 1 Aug 2023 03:24:22 -0400 Subject: [PATCH 15/28] Bump version 0.1.2 -> 0.1.3 --- pyproject.toml | 4 ++-- src/microjson/__init__.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c3f26fc..0539527 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "microjson" -version = "0.1.2" +version = "0.1.3" description = "MicroJSON is a library for validating, parsing, and manipulating MicroJSON data." readme = "README.md" authors = ["Bengt Ljungquist "] @@ -39,7 +39,7 @@ bumpver = "^2023.1125" testpaths = ["tests/"] [tool.bumpver] -current_version = "0.1.2" +current_version = "0.1.3" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true diff --git a/src/microjson/__init__.py b/src/microjson/__init__.py index 16c1d25..0ed0247 100644 --- a/src/microjson/__init__.py +++ b/src/microjson/__init__.py @@ -3,4 +3,4 @@ #from .roundtrip import MicroJSON as MicroJSONRound from .utils import gather_example_files -__version__ = "0.1.2" \ No newline at end of file +__version__ = "0.1.3" \ No newline at end of file From a7d20389ce7e8747529644c021f710211c411f7f Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Tue, 1 Aug 2023 09:49:09 -0400 Subject: [PATCH 16/28] Updated versioning --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0539527..15ba748 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ where = ["src"] [tool.poetry.dependencies] python = "^3.9" -pydantic = "^1.8.2" +pydantic = "<2.0.0" [tool.poetry.dev-dependencies] pytest = "^6.2.4" From 9d9ed0bc682560f7224ecf5896b480e4e9301bc0 Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Tue, 1 Aug 2023 09:49:17 -0400 Subject: [PATCH 17/28] Bump version 0.1.3 -> 0.1.4 --- pyproject.toml | 4 ++-- src/microjson/__init__.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 15ba748..b551967 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "microjson" -version = "0.1.3" +version = "0.1.4" description = "MicroJSON is a library for validating, parsing, and manipulating MicroJSON data." readme = "README.md" authors = ["Bengt Ljungquist "] @@ -39,7 +39,7 @@ bumpver = "^2023.1125" testpaths = ["tests/"] [tool.bumpver] -current_version = "0.1.3" +current_version = "0.1.4" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true diff --git a/src/microjson/__init__.py b/src/microjson/__init__.py index 0ed0247..147c5ee 100644 --- a/src/microjson/__init__.py +++ b/src/microjson/__init__.py @@ -3,4 +3,4 @@ #from .roundtrip import MicroJSON as MicroJSONRound from .utils import gather_example_files -__version__ = "0.1.3" \ No newline at end of file +__version__ = "0.1.4" \ No newline at end of file From 74872f880a3632494e9f957af1a5ab8c5c72835e Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Tue, 12 Sep 2023 08:17:44 -0400 Subject: [PATCH 18/28] Updated docs, altered model --- docs/index.md | 25 ++++++++++++++++++++----- examples/df_to_microjson.py | 2 +- pyproject.toml | 4 ++-- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/docs/index.md b/docs/index.md index e8c922d..e7e2723 100755 --- a/docs/index.md +++ b/docs/index.md @@ -49,7 +49,10 @@ A feature object represents a spatially bounded entity associated with propertie - `"type"`: A string with the value `"Feature"`. - `"geometry"`: A geometry object as defined in the section above or a JSON null value. -- `"properties"`: (Optional) A JSON object containing properties specific to the feature, or a JSON null value. +- `"properties"`: (Optional) A JSON object containing properties and metadata specific to the feature, or a JSON null value. It MAY have the following sub-properties: + - `"descriptive"`: (Optional) A list with key-value pairs, with string keys and string values, e.g. `{"color": "red", "size": "large"}`. + - `"numerical"`: (Optional) A list with key-value pairs, with string keys and numerical values, e.g. `{"area": 123.45, "volume": 678.90}`. + - `"multi-numerical"`: (Optional) A list with key-value pairs, with string keys and list of numerical values, e.g. `{"area": [123.45, 234.56], "volume": [678.90, 789.01]}`. - `"id"`: (Optional) A unique identifier for this feature. - `"ref"`: (Optional) A reference to an external resource, e.g. URI to a zarr strcture, e.g. "s3://zarr-demo/store/my_array.zarr". @@ -67,7 +70,11 @@ A feature object represents a spatially bounded entity associated with propertie ### FeatureCollection Object -A FeatureCollection object is a JSON object representing a collection of feature objects. A FeatureCollection object has a member with the name `"features"`. The value of `"features"` is a JSON array. Each element of the array is a Feature object as defined above. It is possible for this array to be empty. +A FeatureCollection object is a JSON object representing a collection of feature objects. A FeatureCollection object has a member with the name `"features"`. The value of `"features"` is a JSON array. Each element of the array is a Feature object as defined above. It is possible for this array to be empty. Additionally, it MAY have the following members: +- `"descriptive_fields"`: (Optional) A list of strings, indicating the descriptive fields of the features in the collection, e.g. `["color", "size"]`. +- `"value_range"`: (Optional) A list of key-value pairs, with string keys and as keys another object with the fields: + * `"min"`: The minimum value of the field in the key (both `"numerical"` and `"multi-numerical"`) of the features in the collection, e.g. `{"area": 123.45, "volume": 678.90}`. + * `"max"`: The maximum value as above. #### Special FeatureCollection Objects @@ -81,11 +88,19 @@ A FeatureCollection object is a JSON object representing a collection of feature A coordinates object represents the choice of axes (2D or 3D) and potentially their scale. It MUST have the following properties: -- `"axes"`: Representing the choice of axes. It MUST contain an array of length two from either of the following: `[“x“, ”y”, “z“]`, `[“r“, ”theta”, “z“]`, or `[“r“, ”theta”, “phi“]`. +- `"axes"`: Representing the choice of axes as an Axis object. It MAY contain the following properties: +- `"transformation_matrix"`: Representing the transformation matrix from the coordinate system of the image to the coordinate system of the MicroJSON object. It MUST be an array of arrays of numbers, with the number of rows equal to the number of axes in the coordinate system, and the number of columns equal to the number of axes in the image coordinate system. The transformation matrix MUST be invertible. -- `"units"`: Representing the units of the corresponding axis in the axes property. It MUST be an array with the elements having any of the following values: `[“angstrom", "attometer", "centimeter", "decimeter", "exameter", "femtometer", "foot", "gigameter", "hectometer", "inch", "kilometer", "megameter", "meter", "micrometer", "mile", "millimeter", "nanometer", "parsec", "petameter", "picometer", "terameter", "yard", "yoctometer", "yottameter", "zeptometer", "zettameter“]` -- `"pixelsPerUnit"`: A decimal value, except for angles, where it SHOULD have the value “0”. + +### Axis Object + +An axis object represents the choice of axes (2D or 3D). It MUST have the following properties: +- `"name"`: Representing the name of the axis. It MUST be a string. +It MAY contain the following properties: +- `"unit"`: Representing the units of the corresponding axis in the axes property. It MUST be an array with the elements having any of the following values: `[“angstrom", "attometer", "centimeter", "decimeter", "exameter", "femtometer", "foot", "gigameter", "hectometer", "inch", "kilometer", "megameter", "meter", "micrometer", "mile", "millimeter", "nanometer", "parsec", "petameter", "picometer", "terameter", "yard", "yoctometer", "yottameter", "zeptometer", "zettameter“]` +- `"pixels_per_unit"`: A decimal value, except for angles, where it SHOULD have the value “0”. +- `"description"`: A string describing the axis. diff --git a/examples/df_to_microjson.py b/examples/df_to_microjson.py index 85c7b6f..f30735b 100644 --- a/examples/df_to_microjson.py +++ b/examples/df_to_microjson.py @@ -1,5 +1,5 @@ import pandas as pd -import microjson.model as mj +from microjson import model as mj from typing import List diff --git a/pyproject.toml b/pyproject.toml index b551967..18e7722 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,14 +24,14 @@ where = ["src"] [tool.poetry.dependencies] python = "^3.9" -pydantic = "<2.0.0" +pydantic = ">2.0.0" [tool.poetry.dev-dependencies] pytest = "^6.2.4" nox = "^2022.1.7" pre-commit = "^2.15.0" pydantic-to-typescript = "^1.0.8" -datamodel-code-generator = "^0.13.1" +datamodel-code-generator = "^0.21.4" bumpver = "^2023.1125" From f3e9478c91301f6a5637eb382468efde03671b2d Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Tue, 12 Sep 2023 08:19:22 -0400 Subject: [PATCH 19/28] Added short README --- README_short.md | 37 +++++++++++++++++++++++++++++++++++++ pyproject.toml | 4 ++-- 2 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 README_short.md diff --git a/README_short.md b/README_short.md new file mode 100644 index 0000000..84a29c0 --- /dev/null +++ b/README_short.md @@ -0,0 +1,37 @@ +# MicroJSON + +MicroJSON is a JSON-based format inspired by [GeoJSON](https://geojson.org), designed to encode a variety of data structures related to microscopy images. It can handle representations of reference points, regions of interest, and other annotations, making it an ideal solution for conveying complex microscopy data in a straightforward, easy-to-use format. + +## Features + +MicroJSON offers a range of features designed to meet the needs of microscopy data representation: + +- **Flexible Data Structures:** MicroJSON can represent diverse data structures, including geometries (such as points, multipoints, linestrings, polygons), features (individual entities with specific properties), feature collections (groups of features), and coordinate systems. + +- **Standardized Format:** MicroJSON uses the widely adopted JSON format, ensuring compatibility with a wide range of programming languages and tools. + +- **Extensibility:** MicroJSON can handle additional properties associated with specific features, such as metadata relating to microscopy images. + +## Usage + +MicroJSON can be used with any application or tool that can process JSON data. Due to its design, it is particularly suited to applications related to the analysis, visualization, and manipulation of microscopy images. + +## External Resources + +The GeoJSON test files are copied from the [GeoJSON Schema GitHub repository](https://github.com/geojson/schema), and are Copyright (c) 2018 Tim Schaub under MIT License. + +## Contribution + +We welcome contributions to the development and enhancement of MicroJSON. Whether you're reporting bugs, suggesting enhancements, or contributing to the code, your input is highly appreciated. + +## License + +MicroJSON is licensed under MIT License, (c) 2023 Polus AI & Nextonic Solutions LLC. + +## Authors +Bengt Ljungquist [bengt.ljungquist@nextonicsolutions.com](bengt.ljungquist@nextonicsolutions.com) + + +--- + +This project is maintained by Polus AI. For any queries or further discussion, please contact the author on email address above. diff --git a/pyproject.toml b/pyproject.toml index 18e7722..f963104 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "poetry.core.masonry.api" name = "microjson" version = "0.1.4" description = "MicroJSON is a library for validating, parsing, and manipulating MicroJSON data." -readme = "README.md" +readme = "README_short.md" authors = ["Bengt Ljungquist "] license = "MIT" classifiers = [ @@ -24,7 +24,7 @@ where = ["src"] [tool.poetry.dependencies] python = "^3.9" -pydantic = ">2.0.0" +pydantic = "<2.0.0" [tool.poetry.dev-dependencies] pytest = "^6.2.4" From e4625649124aba8edb8917ed3967c9f4433db268 Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Tue, 12 Sep 2023 08:32:49 -0400 Subject: [PATCH 20/28] updated example --- docs/example.md | 72 ++++++++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/docs/example.md b/docs/example.md index e12e05d..4b8f77a 100755 --- a/docs/example.md +++ b/docs/example.md @@ -1,6 +1,6 @@ # MicroJSON Examples ## Basic MicroJSON -This JSON file demonstrates how MicroJSON can be used to define and describe different structures related to microscopy, such as cells and their nuclei, including their spatial relationships, identifiers, labels, and color representations. +This JSON file demonstrates how MicroJSON can be used to define and describe different structures related to imaging, such as cells and their nuclei, including their spatial relationships, identifiers, labels, and color representations. ```json { @@ -18,7 +18,8 @@ This JSON file demonstrates how MicroJSON can be used to define and describe dif "properties": { "name": "Reference Point", "description": "Specific point of interest", - "color": "red" + "color": "red", + "property1": "value1" }, "id": "1" }, @@ -39,42 +40,50 @@ This JSON file demonstrates how MicroJSON can be used to define and describe dif 300.0, 100.0 ] - ], - "coordinatesystem": { - "axes": [ - "x", - "y" - ], - "units": [ - "micrometer", - "micrometer" - ], - "pixelsPerUnit": [ - 1, - 1 - ] - } + ] }, "properties": { "name": "Cell Path", "description": "Path traced within a cell", "color": "blue" }, - "id": "2" + "id": "2", + "ref": "s3://mybucket/myfile.tif" } ], "coordinatesystem": { "axes": [ - "x", - "y" - ], - "units": [ - "micrometer", - "micrometer" + { + "name": "x", + "unit": "micrometer", + "type": "cartesian", + "pixelsPerUnit": 1, + "description": "x-axis" + }, + { + "name": "y", + "unit": "micrometer", + "type": "cartesian", + "pixelsPerUnit": 1, + "description": "y-axis" + } ], - "pixelsPerUnit": [ - 0.5, - 0.5 + "transformation_matrix": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 0.0 + ] ] } } @@ -87,7 +96,14 @@ This JSON file demonstrates how MicroJSON can be used to define and describe a s { "type": "FeatureCollection", "coordinatesystem": { - "axes": ["x", "y"] + "axes": [ + { + "name": "x" + }, + { + "name": "y" + } + ], }, "features": [ { From be4e02e2fb71ea0302a3187fedb07b292ad8da04 Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Tue, 12 Sep 2023 08:37:28 -0400 Subject: [PATCH 21/28] Bump version 0.1.4 -> 0.1.5 --- pyproject.toml | 4 ++-- src/microjson/__init__.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f963104..f3b7348 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "microjson" -version = "0.1.4" +version = "0.1.5" description = "MicroJSON is a library for validating, parsing, and manipulating MicroJSON data." readme = "README_short.md" authors = ["Bengt Ljungquist "] @@ -39,7 +39,7 @@ bumpver = "^2023.1125" testpaths = ["tests/"] [tool.bumpver] -current_version = "0.1.4" +current_version = "0.1.5" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true diff --git a/src/microjson/__init__.py b/src/microjson/__init__.py index 147c5ee..9583300 100644 --- a/src/microjson/__init__.py +++ b/src/microjson/__init__.py @@ -3,4 +3,4 @@ #from .roundtrip import MicroJSON as MicroJSONRound from .utils import gather_example_files -__version__ = "0.1.4" \ No newline at end of file +__version__ = "0.1.5" \ No newline at end of file From a4c52b19d29d226851ffa8385029255a42a9882c Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Wed, 13 Sep 2023 03:56:45 -0400 Subject: [PATCH 22/28] Adding for pydantic2 --- noxfile.py | 13 +-- pyproject.toml | 4 +- src/microjson/model.py | 33 ++++--- src/microjson/modelv1.py | 188 +++++++++++++++++++++++++++++++++++++ src/microjson/roundtrip.py | 26 ++--- 5 files changed, 226 insertions(+), 38 deletions(-) create mode 100644 src/microjson/modelv1.py diff --git a/noxfile.py b/noxfile.py index 23cb187..61c6093 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,10 +1,11 @@ -import nox +from nox_poetry import session -@nox.session(python="3.9") +@session(python="3.9") def export_ts(session): - session.install("-r", "requirements-dev.txt") - session.install("-e", ".") + #session.install("-r", "requirements-dev.txt") + #session.install("-e", ".") + session.install("microjson") session.run("datamodel-codegen", "--reuse-model", "--snake-case-field", @@ -15,7 +16,7 @@ def export_ts(session): "--output", "microjsonschema.ts") # generate GeoJSON schema - from microjson.model import GeoJSON + from microjson.modelv1 import GeoJSON # Get the schema for the model as a dictionary schema_dict = GeoJSON.schema() # Add 'null' to the list of allowed types for the 'geometry' field of @@ -25,7 +26,7 @@ def export_ts(session): with open('geojson_schema.json', 'w') as f: f.write(GeoJSON.schema_json(indent=2)) - from microjson.model import MicroJSON + from microjson.modelv1 import MicroJSON with open('microjson_schema.json', 'w') as f: f.write(MicroJSON.schema_json(indent=2)) diff --git a/pyproject.toml b/pyproject.toml index f3b7348..79b9805 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ where = ["src"] [tool.poetry.dependencies] python = "^3.9" -pydantic = "<2.0.0" +pydantic = "^2.3.0" [tool.poetry.dev-dependencies] pytest = "^6.2.4" @@ -48,4 +48,4 @@ push = false [tool.bumpver.file_patterns] "pyproject.toml" = ['current_version = "{version}"', 'version = "{version}"'] -"src/microjson/__init__.py" = ["{version}"] \ No newline at end of file +"src/microjson/__init__.py" = ["{version}"] diff --git a/src/microjson/model.py b/src/microjson/model.py index bfc502e..37bb53d 100644 --- a/src/microjson/model.py +++ b/src/microjson/model.py @@ -79,10 +79,9 @@ class Feature(GeoAbstract): geometry: GeometryType = Field(..., description="""The geometry of the feature""") - properties: Optional[Dict] = Field(..., - description="""Properties of the - feature""") - id: Optional[Union[StrictStr, StrictInt]] + properties: Dict = Field(..., + description="""Properties of the feature""") + id: Optional[Union[StrictStr, StrictInt]] = None class ValueRange(BaseModel): @@ -146,39 +145,39 @@ class AxisType(Enum): class Axis(BaseModel): """An axis of a coordinate system""" name: StrictStr - type: Optional[AxisType] - unit: Optional[Unit] - pixels_per_unit: Optional[float] - description: Optional[str] + type: Optional[AxisType] = None + unit: Optional[Unit] = None + pixels_per_unit: Optional[float] = None + description: Optional[str] = None class CoordinateSystem(BaseModel): """A coordinate system for MicroJSON coordinates""" axes: List[Axis] - transformation_matrix: Optional[List[List[float]]] + transformation_matrix: Optional[List[List[float]]] = None class Properties(BaseModel): """Metadata properties of a MicroJSON feature""" - descriptive: Optional[Dict[str, str]] - numerical: Optional[Dict[str, float]] - multi_numerical: Optional[Dict[str, List[float]]] + descriptive: Optional[Dict[str, str]] = None + numerical: Optional[Dict[str, float]] = None + multi_numerical: Optional[Dict[str, List[float]]] = None class MicroFeature(Feature): """A MicroJSON feature, which is a GeoJSON feature with additional metadata""" - coordinatesystem: Optional[List[Axis]] - ref: Optional[Union[StrictStr, StrictInt]] + coordinatesystem: Optional[List[Axis]] = None + ref: Optional[Union[StrictStr, StrictInt]] = None properties: Properties class MicroFeatureCollection(FeatureCollection): """A MicroJSON feature collection, which is a GeoJSON feature collection with additional metadata""" - coordinatesystem: Optional[CoordinateSystem] - value_range: Optional[Dict[str, ValueRange]] - descriptive_fields: Optional[List[str]] + coordinatesystem: Optional[CoordinateSystem] = None + value_range: Optional[Dict[str, ValueRange]] = None + descriptive_fields: Optional[List[str]] = None class MicroJSON(BaseModel): diff --git a/src/microjson/modelv1.py b/src/microjson/modelv1.py new file mode 100644 index 0000000..eae46c2 --- /dev/null +++ b/src/microjson/modelv1.py @@ -0,0 +1,188 @@ +"""MicroJSON and GeoJSON models, defined manually using pydantic.""" +from typing import List, Optional, Union, Dict, Literal +from enum import Enum +from pydantic.v1 import BaseModel, Field, StrictInt, StrictStr, conlist + + +Coordinates = conlist(float, min_items=2, max_items=3) + + +class GeoAbstract(BaseModel): + """Abstract base class for all GeoJSON objects""" + bbox: Optional[List[float]] = Field(None, min_items=4) + + +class Point(GeoAbstract): + """A GeoJSON Point object""" + type: Literal["Point"] + coordinates: Coordinates + + +class MultiPoint(GeoAbstract): + """A GeoJSON MultiPoint object""" + type: Literal["MultiPoint"] + coordinates: List[Coordinates] + + +class LineString(GeoAbstract): + """A GeoJSON LineString object""" + type: Literal["LineString"] + coordinates: List[Coordinates] + + +class MultiLineString(GeoAbstract): + """A GeoJSON MultiLineString object""" + type: Literal["MultiLineString"] + coordinates: List[List[Coordinates]] + + +class Polygon(GeoAbstract): + """A GeoJSON Polygon object""" + type: Literal["Polygon"] + coordinates: List[List[Coordinates]] + + +class MultiPolygon(GeoAbstract): + """A GeoJSON MultiPolygon object""" + type: Literal["MultiPolygon"] + coordinates: List[List[List[Coordinates]]] + + +GeometryBaseType = Union[Point, + MultiPoint, + LineString, + MultiLineString, + Polygon, + MultiPolygon] + + +class GeometryCollection(GeoAbstract): + """A GeoJSON GeometryCollection object""" + type: Literal["GeometryCollection"] + geometries: List[GeometryBaseType] + + +GeometryType = Union[Point, + MultiPoint, + LineString, + MultiLineString, + Polygon, + MultiPolygon, + GeometryCollection, + type(None) + ] + + +class Feature(GeoAbstract): + """A GeoJSON Feature object""" + type: Literal["Feature"] + geometry: GeometryType = Field(..., + description="""The geometry of the + feature""") + properties: Optional[Dict] = Field(..., + description="""Properties of the + feature""") + id: Optional[Union[StrictStr, StrictInt]] + + +class ValueRange(BaseModel): + """A range of values for MicroJSON quantitative properties""" + min: float + max: float + + +class FeatureCollection(GeoAbstract): + """A GeoJSON FeatureCollection object""" + type: Literal["FeatureCollection"] + features: List[Feature] + + +class GeoJSON(BaseModel): + """The root object of a GeoJSON file""" + __root__: Union[Feature, FeatureCollection, GeometryType] + + +class Unit(Enum): + """A unit of measurement""" + ANGSTROM = 'angstrom' + ATTOMETER = 'attometer' + CENTIMETER = 'centimeter' + DECIMETER = 'decimeter' + EXAMETER = 'exameter' + FEMTOMETER = 'femtometer' + FOOT = 'foot' + GIGAMETER = 'gigameter' + HECTOMETER = 'hectometer' + INCH = 'inch' + KILOMETER = 'kilometer' + MEGAMETER = 'megameter' + METER = 'meter' + MICROMETER = 'micrometer' + MILE = 'mile' + MILLIMETER = 'millimeter' + NANOMETER = 'nanometer' + PARSEC = 'parsec' + PETAMETER = 'petameter' + PICOMETER = 'picometer' + TERAMETER = 'terameter' + YARD = 'yard' + YOCTOMETER = 'yoctometer' + YOTTAMETER = 'yottameter' + ZEPTOMETER = 'zeptometer' + ZETTAMETER = 'zettameter' + PIXEL = 'pixel' + RADIAN = 'radian' + DEGREE = 'degree' + + +class AxisType(Enum): + """The type of an axis""" + CARTESIAN = 'cartesian' + ANGULAR = 'angular' + TEMPORAL = 'temporal' + SPECTRAL = 'spectral' + + +class Axis(BaseModel): + """An axis of a coordinate system""" + name: StrictStr + type: Optional[AxisType] + unit: Optional[Unit] + pixels_per_unit: Optional[float] + description: Optional[str] + + +class CoordinateSystem(BaseModel): + """A coordinate system for MicroJSON coordinates""" + axes: List[Axis] + transformation_matrix: Optional[List[List[float]]] + + +class Properties(BaseModel): + """Metadata properties of a MicroJSON feature""" + descriptive: Optional[Dict[str, str]] + numerical: Optional[Dict[str, float]] + multi_numerical: Optional[Dict[str, List[float]]] + + +class MicroFeature(Feature): + """A MicroJSON feature, which is a GeoJSON feature with additional + metadata""" + coordinatesystem: Optional[List[Axis]] + ref: Optional[Union[StrictStr, StrictInt]] + properties: Properties + + +class MicroFeatureCollection(FeatureCollection): + """A MicroJSON feature collection, which is a GeoJSON feature + collection with additional metadata""" + coordinatesystem: Optional[CoordinateSystem] + value_range: Optional[Dict[str, ValueRange]] + descriptive_fields: Optional[List[str]] + + +class MicroJSON(BaseModel): + """The root object of a MicroJSON file""" + __root__: Union[MicroFeature, + MicroFeatureCollection, + GeometryType] diff --git a/src/microjson/roundtrip.py b/src/microjson/roundtrip.py index e7ca7ea..c184163 100644 --- a/src/microjson/roundtrip.py +++ b/src/microjson/roundtrip.py @@ -7,7 +7,7 @@ from enum import Enum from typing import Any, Dict, List, Optional, Union -from pydantic import BaseModel, Field +from pydantic.v1 import BaseModel, Field class Type(Enum): @@ -15,11 +15,11 @@ class Type(Enum): class Point(BaseModel): - bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + bbox: Optional[List[float]] = Field(None, min_length=4, title='Bbox') type: Type = Field(..., title='Type') coordinates: List[float] = Field(..., - max_items=3, - min_items=2, + max_length=3, + min_length=2, title='Coordinates') @@ -28,7 +28,7 @@ class Type1(Enum): class MultiPoint(BaseModel): - bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + bbox: Optional[List[float]] = Field(None, min_length=4, title='Bbox') type: Type1 = Field(..., title='Type') coordinates: List[List[float]] = Field(..., title='Coordinates') @@ -38,7 +38,7 @@ class Type2(Enum): class LineString(BaseModel): - bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + bbox: Optional[List[float]] = Field(None, min_length=4, title='Bbox') type: Type2 = Field(..., title='Type') coordinates: List[List[float]] = Field(..., title='Coordinates') @@ -48,7 +48,7 @@ class Type3(Enum): class MultiLineString(BaseModel): - bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + bbox: Optional[List[float]] = Field(None, min_length=4, title='Bbox') type: Type3 = Field(..., title='Type') coordinates: List[List[List[float]]] = Field(..., title='Coordinates') @@ -58,7 +58,7 @@ class Type4(Enum): class Polygon(BaseModel): - bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + bbox: Optional[List[float]] = Field(None, min_length=4, title='Bbox') type: Type4 = Field(..., title='Type') coordinates: List[List[List[float]]] = Field(..., title='Coordinates') @@ -68,7 +68,7 @@ class Type5(Enum): class MultiPolygon(BaseModel): - bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + bbox: Optional[List[float]] = Field(None, min_length=4, title='Bbox') type: Type5 = Field(..., title='Type') coordinates: List[List[List[List[float]]]] = Field(..., title='Coordinates') @@ -79,7 +79,7 @@ class Type6(Enum): class GeometryCollection(BaseModel): - bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + bbox: Optional[List[float]] = Field(None, min_length=4, title='Bbox') type: Type6 = Field(..., title='Type') geometries: List[ Union[Point, @@ -153,7 +153,7 @@ class Type7(Enum): class MicroFeature(BaseModel): - bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + bbox: Optional[List[float]] = Field(None, min_length=4, title='Bbox') type: Type7 = Field(..., title='Type') geometry: Union[ Point, @@ -175,7 +175,7 @@ class MicroFeature(BaseModel): class Feature(BaseModel): - bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + bbox: Optional[List[float]] = Field(None, min_length=4, title='Bbox') type: Type7 = Field(..., title='Type') geometry: Union[ Point, @@ -208,7 +208,7 @@ class Type9(Enum): class MicroFeatureCollection(BaseModel): - bbox: Optional[List[float]] = Field(None, min_items=4, title='Bbox') + bbox: Optional[List[float]] = Field(None, min_length=4, title='Bbox') type: Type9 = Field(..., title='Type') features: List[Feature] = Field(..., title='Features') value_range: Optional[Dict[str, ValueRange]] = Field(None, From dc9c0f1defbcc3a971062063b14505d7af16039b Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Wed, 13 Sep 2023 03:58:08 -0400 Subject: [PATCH 23/28] Bump version 0.1.5 -> 0.1.6 --- pyproject.toml | 4 ++-- src/microjson/__init__.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 79b9805..0a7afdd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "microjson" -version = "0.1.5" +version = "0.1.6" description = "MicroJSON is a library for validating, parsing, and manipulating MicroJSON data." readme = "README_short.md" authors = ["Bengt Ljungquist "] @@ -39,7 +39,7 @@ bumpver = "^2023.1125" testpaths = ["tests/"] [tool.bumpver] -current_version = "0.1.5" +current_version = "0.1.6" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true diff --git a/src/microjson/__init__.py b/src/microjson/__init__.py index 9583300..6f549af 100644 --- a/src/microjson/__init__.py +++ b/src/microjson/__init__.py @@ -3,4 +3,4 @@ #from .roundtrip import MicroJSON as MicroJSONRound from .utils import gather_example_files -__version__ = "0.1.5" \ No newline at end of file +__version__ = "0.1.6" \ No newline at end of file From ffa898062d5a60087803bd494040e93807570a6b Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Fri, 15 Sep 2023 08:22:28 -0400 Subject: [PATCH 24/28] Updated nox workflow, examples --- docs/example.md | 149 +- docs/index.md | 11 +- examples/df_to_microjson.py | 8 +- examples/load_validate.py | 4 +- geojson_schema.json | 885 +++++----- microjson_schema.json | 1432 ++++++++++------- noxfile.py | 57 +- pyproject.toml | 3 +- src/microjson/__init__.py | 2 - src/microjson/model.py | 21 +- src/microjson/modelv1.py | 188 --- tests/json/microjson/valid/metadata-full.json | 58 +- tests/test_microjson.py | 8 +- 13 files changed, 1467 insertions(+), 1359 deletions(-) delete mode 100644 src/microjson/modelv1.py diff --git a/docs/example.md b/docs/example.md index 4b8f77a..f67b8d8 100755 --- a/docs/example.md +++ b/docs/example.md @@ -6,87 +6,92 @@ This JSON file demonstrates how MicroJSON can be used to define and describe dif { "type": "FeatureCollection", "features": [ - { - "type": "Feature", - "geometry": { - "type": "Point", - "coordinates": [ - 200.0, - 150.0 - ] + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "subtype": "Rectangle", + "coordinates": [[[0.0, 0.0], [0.0, 50.0], [50.0, 50.0], [50.0, 0.0], [0.0, 0.0]]] + }, + "properties": { + "string": { + "well": "A1" }, - "properties": { - "name": "Reference Point", - "description": "Specific point of interest", - "color": "red", - "property1": "value1" + "numeric": { + "cellCount": 5 }, - "id": "1" + "multi-numeric": { + "ratioInfectivity": [[0.1, 0.2, 0.3, 0.4, 0.5], [0.2, 0.3, 0.4, 0.5, 0.6]] + } + } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "subtype": "Rectangle", + "coordinates": [[[50.0, 0.0], [50.0, 50.0], [100.0, 50.0], [100.0, 0.0], [50.0, 0.0]]] }, - { - "type": "Feature", - "geometry": { - "type": "LineString", - "coordinates": [ - [ - 100.0, - 100.0 - ], - [ - 200.0, - 200.0 - ], - [ - 300.0, - 100.0 - ] - ] + "properties": { + "string": { + "well": "A2" }, - "properties": { - "name": "Cell Path", - "description": "Path traced within a cell", - "color": "blue" + "numeric": { + "cellCount": 10 }, - "id": "2", - "ref": "s3://mybucket/myfile.tif" + "multi-numeric": { + "ratioInfectivity": [[0.1, 0.2, 0.3, 0.4, 0.5], [0.2, 0.3, 0.4, 0.5, 0.6]] + } } + } ], + "value_range": { + "cellCount": { + "min": 0, + "max": 10 + }, + "ratioInfectivity": { + "min": 0, + "max": 1 + } + }, + "string_fields": ["well","imagename"], "coordinatesystem": { - "axes": [ - { - "name": "x", - "unit": "micrometer", - "type": "cartesian", - "pixelsPerUnit": 1, - "description": "x-axis" - }, - { - "name": "y", - "unit": "micrometer", - "type": "cartesian", - "pixelsPerUnit": 1, - "description": "y-axis" - } - ], - "transformation_matrix": [ - [ - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ] - ] + "axes": [ + { + "name": "x", + "unit": "micrometer", + "type": "cartesian", + "pixelsPerUnit": 1, + "description": "x-axis" + }, + { + "name": "y", + "unit": "micrometer", + "type": "cartesian", + "pixelsPerUnit": 1, + "description": "y-axis" + } + ], + "transformation_matrix": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 0.0 + ] + ] } -} + } ``` diff --git a/docs/index.md b/docs/index.md index e7e2723..d342df7 100755 --- a/docs/index.md +++ b/docs/index.md @@ -50,9 +50,9 @@ A feature object represents a spatially bounded entity associated with propertie - `"type"`: A string with the value `"Feature"`. - `"geometry"`: A geometry object as defined in the section above or a JSON null value. - `"properties"`: (Optional) A JSON object containing properties and metadata specific to the feature, or a JSON null value. It MAY have the following sub-properties: - - `"descriptive"`: (Optional) A list with key-value pairs, with string keys and string values, e.g. `{"color": "red", "size": "large"}`. - - `"numerical"`: (Optional) A list with key-value pairs, with string keys and numerical values, e.g. `{"area": 123.45, "volume": 678.90}`. - - `"multi-numerical"`: (Optional) A list with key-value pairs, with string keys and list of numerical values, e.g. `{"area": [123.45, 234.56], "volume": [678.90, 789.01]}`. + - `"string"`: (Optional) A list with key-value pairs, with string keys and string values, e.g. `{"color": "red", "size": "large"}`. + - `"numeric"`: (Optional) A list with key-value pairs, with string keys and numerical values, e.g. `{"area": 123.45, "volume": 678.90}`. + - `"multi-numeric"`: (Optional) A list with key-value pairs, with string keys and list of numerical values, e.g. `{"area": [123.45, 234.56], "volume": [678.90, 789.01]}`. - `"id"`: (Optional) A unique identifier for this feature. - `"ref"`: (Optional) A reference to an external resource, e.g. URI to a zarr strcture, e.g. "s3://zarr-demo/store/my_array.zarr". @@ -71,10 +71,11 @@ A feature object represents a spatially bounded entity associated with propertie ### FeatureCollection Object A FeatureCollection object is a JSON object representing a collection of feature objects. A FeatureCollection object has a member with the name `"features"`. The value of `"features"` is a JSON array. Each element of the array is a Feature object as defined above. It is possible for this array to be empty. Additionally, it MAY have the following members: -- `"descriptive_fields"`: (Optional) A list of strings, indicating the descriptive fields of the features in the collection, e.g. `["color", "size"]`. +- `"string_fields"`: (Optional) A list of strings, indicating the descriptive fields of the features in the collection, e.g. `["color", "size"]`. - `"value_range"`: (Optional) A list of key-value pairs, with string keys and as keys another object with the fields: - * `"min"`: The minimum value of the field in the key (both `"numerical"` and `"multi-numerical"`) of the features in the collection, e.g. `{"area": 123.45, "volume": 678.90}`. + * `"min"`: The minimum value of the field in the key (both `"numeric"` and `"multi-numeric"`) of the features in the collection, e.g. `{"area": 123.45, "volume": 678.90}`. * `"max"`: The maximum value as above. +- `"properties"`: (Optional) A JSON object containing properties and metadata specific to the feature collection, and which apply to all features of the collection, or a JSON null value. It has the same structure as the `"properties"` member of a Feature object. #### Special FeatureCollection Objects diff --git a/examples/df_to_microjson.py b/examples/df_to_microjson.py index f30735b..a096966 100644 --- a/examples/df_to_microjson.py +++ b/examples/df_to_microjson.py @@ -24,9 +24,9 @@ def df_to_microjson(df: pd.DataFrame) -> mj.FeatureCollection: # create a new properties object dynamically properties = mj.Properties( - descriptive={'name': row['name']}, - numerical={'value': row['value']}, - multi_numerical={'values': row['values']} + string={'name': row['name']}, + numeric={'value': row['value']}, + multi_numeric={'values': row['values']} ) # Create a new Feature object @@ -109,4 +109,4 @@ def df_to_microjson(df: pd.DataFrame) -> mj.FeatureCollection: df = pd.DataFrame(data) feature_collection_model = df_to_microjson(df) -print(feature_collection_model.json(indent=2, exclude_unset=True)) +print(feature_collection_model.model_dump_json(indent=2, exclude_unset=True)) diff --git a/examples/load_validate.py b/examples/load_validate.py index 8161846..ae2a1e9 100644 --- a/examples/load_validate.py +++ b/examples/load_validate.py @@ -6,7 +6,7 @@ data = json.load(f, strict=True) # validate the microjson file -microjsonobj = mj.MicroJSON.parse_obj(data) +microjsonobj = mj.MicroJSON.model_validate(data) print("done validating: {}".format(microjsonobj)) # load the geojson file @@ -14,6 +14,6 @@ data = json.load(f, strict=True) # validate the geojson file -geojsonobj = mj.GeoJSON.parse_obj(data) +geojsonobj = mj.GeoJSON.model_validate(data) print("done validating: {}".format(geojsonobj)) diff --git a/geojson_schema.json b/geojson_schema.json index 936dd15..3a0a315 100644 --- a/geojson_schema.json +++ b/geojson_schema.json @@ -1,453 +1,482 @@ { - "title": "GeoJSON", - "description": "The root object of a GeoJSON file", - "anyOf": [ - { - "$ref": "#/definitions/Feature" - }, - { - "$ref": "#/definitions/FeatureCollection" - }, - { - "$ref": "#/definitions/Point" - }, - { - "$ref": "#/definitions/MultiPoint" - }, - { - "$ref": "#/definitions/LineString" - }, - { - "$ref": "#/definitions/MultiLineString" - }, - { - "$ref": "#/definitions/Polygon" - }, - { - "$ref": "#/definitions/MultiPolygon" - }, - { - "$ref": "#/definitions/GeometryCollection" - } - ], - "definitions": { - "Point": { - "title": "Point", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } - }, - "type": { - "title": "Type", - "enum": [ - "Point" - ], - "type": "string" - }, - "coordinates": { - "title": "Coordinates", - "minItems": 2, - "maxItems": 3, - "type": "array", - "items": { - "type": "number" - } - } - }, - "required": [ - "type", - "coordinates" - ] - }, - "MultiPoint": { - "title": "MultiPoint", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } - }, - "type": { - "title": "Type", - "enum": [ - "MultiPoint" - ], - "type": "string" - }, - "coordinates": { - "title": "Coordinates", - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" + "$defs": { + "Feature": { + "description": "A GeoJSON Feature object", + "properties": { + "bbox": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "minItems": 4, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Bbox" + }, + "type": { + "const": "Feature", + "title": "Type" + }, + "geometry": { + "anyOf": [ + { + "$ref": "#/$defs/Point" + }, + { + "$ref": "#/$defs/MultiPoint" + }, + { + "$ref": "#/$defs/LineString" + }, + { + "$ref": "#/$defs/MultiLineString" + }, + { + "$ref": "#/$defs/Polygon" + }, + { + "$ref": "#/$defs/MultiPolygon" + }, + { + "$ref": "#/$defs/GeometryCollection" + }, + { + "type": "null" + } + ], + "description": "The geometry of the\n feature", + "title": "Geometry" + }, + "properties": { + "type": "object", + "description": "Properties of the feature", + "title": "Properties" + }, + "id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Id" + } }, - "minItems": 2, - "maxItems": 3 - } - } - }, - "required": [ - "type", - "coordinates" - ] - }, - "LineString": { - "title": "LineString", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } + "required": [ + "type", + "geometry", + "properties" + ], + "title": "Feature", + "type": "object" }, - "type": { - "title": "Type", - "enum": [ - "LineString" - ], - "type": "string" - }, - "coordinates": { - "title": "Coordinates", - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" + "FeatureCollection": { + "description": "A GeoJSON FeatureCollection object", + "properties": { + "bbox": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "minItems": 4, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Bbox" + }, + "type": { + "const": "FeatureCollection", + "title": "Type" + }, + "features": { + "items": { + "$ref": "#/$defs/Feature" + }, + "title": "Features", + "type": "array" + } }, - "minItems": 2, - "maxItems": 3 - } - } - }, - "required": [ - "type", - "coordinates" - ] - }, - "MultiLineString": { - "title": "MultiLineString", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } - }, - "type": { - "title": "Type", - "enum": [ - "MultiLineString" - ], - "type": "string" + "required": [ + "type", + "features" + ], + "title": "FeatureCollection", + "type": "object" }, - "coordinates": { - "title": "Coordinates", - "type": "array", - "items": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" - }, - "minItems": 2, - "maxItems": 3 - } - } - } - }, - "required": [ - "type", - "coordinates" - ] - }, - "Polygon": { - "title": "Polygon", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } + "GeometryCollection": { + "description": "A GeoJSON GeometryCollection object", + "properties": { + "bbox": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "minItems": 4, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Bbox" + }, + "type": { + "const": "GeometryCollection", + "title": "Type" + }, + "geometries": { + "items": { + "anyOf": [ + { + "$ref": "#/$defs/Point" + }, + { + "$ref": "#/$defs/MultiPoint" + }, + { + "$ref": "#/$defs/LineString" + }, + { + "$ref": "#/$defs/MultiLineString" + }, + { + "$ref": "#/$defs/Polygon" + }, + { + "$ref": "#/$defs/MultiPolygon" + } + ] + }, + "title": "Geometries", + "type": "array" + } + }, + "required": [ + "type", + "geometries" + ], + "title": "GeometryCollection", + "type": "object" }, - "type": { - "title": "Type", - "enum": [ - "Polygon" - ], - "type": "string" + "LineString": { + "description": "A GeoJSON LineString object", + "properties": { + "bbox": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "minItems": 4, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Bbox" + }, + "type": { + "const": "LineString", + "title": "Type" + }, + "coordinates": { + "items": { + "items": { + "type": "number" + }, + "maxItems": 3, + "minItems": 2, + "type": "array" + }, + "title": "Coordinates", + "type": "array" + } + }, + "required": [ + "type", + "coordinates" + ], + "title": "LineString", + "type": "object" }, - "coordinates": { - "title": "Coordinates", - "type": "array", - "items": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" - }, - "minItems": 2, - "maxItems": 3 - } - } - } - }, - "required": [ - "type", - "coordinates" - ] - }, - "MultiPolygon": { - "title": "MultiPolygon", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } + "MultiLineString": { + "description": "A GeoJSON MultiLineString object", + "properties": { + "bbox": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "minItems": 4, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Bbox" + }, + "type": { + "const": "MultiLineString", + "title": "Type" + }, + "coordinates": { + "items": { + "items": { + "items": { + "type": "number" + }, + "maxItems": 3, + "minItems": 2, + "type": "array" + }, + "type": "array" + }, + "title": "Coordinates", + "type": "array" + } + }, + "required": [ + "type", + "coordinates" + ], + "title": "MultiLineString", + "type": "object" }, - "type": { - "title": "Type", - "enum": [ - "MultiPolygon" - ], - "type": "string" + "MultiPoint": { + "description": "A GeoJSON MultiPoint object", + "properties": { + "bbox": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "minItems": 4, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Bbox" + }, + "type": { + "const": "MultiPoint", + "title": "Type" + }, + "coordinates": { + "items": { + "items": { + "type": "number" + }, + "maxItems": 3, + "minItems": 2, + "type": "array" + }, + "title": "Coordinates", + "type": "array" + } + }, + "required": [ + "type", + "coordinates" + ], + "title": "MultiPoint", + "type": "object" }, - "coordinates": { - "title": "Coordinates", - "type": "array", - "items": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" + "MultiPolygon": { + "description": "A GeoJSON MultiPolygon object", + "properties": { + "bbox": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "minItems": 4, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Bbox" }, - "minItems": 2, - "maxItems": 3 - } - } - } - } - }, - "required": [ - "type", - "coordinates" - ] - }, - "GeometryCollection": { - "title": "GeometryCollection", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } + "type": { + "const": "MultiPolygon", + "title": "Type" + }, + "coordinates": { + "items": { + "items": { + "items": { + "items": { + "type": "number" + }, + "maxItems": 3, + "minItems": 2, + "type": "array" + }, + "type": "array" + }, + "type": "array" + }, + "title": "Coordinates", + "type": "array" + } + }, + "required": [ + "type", + "coordinates" + ], + "title": "MultiPolygon", + "type": "object" }, - "type": { - "title": "Type", - "enum": [ - "GeometryCollection" - ], - "type": "string" + "Point": { + "description": "A GeoJSON Point object", + "properties": { + "bbox": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "minItems": 4, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Bbox" + }, + "type": { + "const": "Point", + "title": "Type" + }, + "coordinates": { + "items": { + "type": "number" + }, + "maxItems": 3, + "minItems": 2, + "title": "Coordinates", + "type": "array" + } + }, + "required": [ + "type", + "coordinates" + ], + "title": "Point", + "type": "object" }, - "geometries": { - "title": "Geometries", - "type": "array", - "items": { - "anyOf": [ - { - "$ref": "#/definitions/Point" - }, - { - "$ref": "#/definitions/MultiPoint" - }, - { - "$ref": "#/definitions/LineString" - }, - { - "$ref": "#/definitions/MultiLineString" - }, - { - "$ref": "#/definitions/Polygon" - }, - { - "$ref": "#/definitions/MultiPolygon" - } - ] - } + "Polygon": { + "description": "A GeoJSON Polygon object", + "properties": { + "bbox": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "minItems": 4, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Bbox" + }, + "type": { + "const": "Polygon", + "title": "Type" + }, + "coordinates": { + "items": { + "items": { + "items": { + "type": "number" + }, + "maxItems": 3, + "minItems": 2, + "type": "array" + }, + "type": "array" + }, + "title": "Coordinates", + "type": "array" + } + }, + "required": [ + "type", + "coordinates" + ], + "title": "Polygon", + "type": "object" } - }, - "required": [ - "type", - "geometries" - ] }, - "Feature": { - "title": "Feature", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } + "anyOf": [ + { + "$ref": "#/$defs/Feature" }, - "type": { - "title": "Type", - "enum": [ - "Feature" - ], - "type": "string" + { + "$ref": "#/$defs/FeatureCollection" }, - "geometry": { - "title": "Geometry", - "description": "The geometry of the\n feature", - "anyOf": [ - { - "$ref": "#/definitions/Point" - }, - { - "$ref": "#/definitions/MultiPoint" - }, - { - "$ref": "#/definitions/LineString" - }, - { - "$ref": "#/definitions/MultiLineString" - }, - { - "$ref": "#/definitions/Polygon" - }, - { - "$ref": "#/definitions/MultiPolygon" - }, - { - "$ref": "#/definitions/GeometryCollection" - }, - { - "type": "null" - } - ] + { + "$ref": "#/$defs/Point" }, - "properties": { - "title": "Properties", - "description": "Properties of the\n feature", - "type": "object" + { + "$ref": "#/$defs/MultiPoint" }, - "id": { - "title": "Id", - "anyOf": [ - { - "type": "string" - }, - { - "type": "integer" - } - ] - } - }, - "required": [ - "type", - "geometry", - "properties" - ] - }, - "ValueRange": { - "title": "ValueRange", - "type": "object", - "properties": { - "min": { - "title": "Min", - "type": "number" + { + "$ref": "#/$defs/LineString" }, - "max": { - "title": "Max", - "type": "number" - } - }, - "required": [ - "min", - "max" - ] - }, - "FeatureCollection": { - "title": "FeatureCollection", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } + { + "$ref": "#/$defs/MultiLineString" }, - "type": { - "title": "Type", - "enum": [ - "FeatureCollection" - ], - "type": "string" + { + "$ref": "#/$defs/Polygon" }, - "features": { - "title": "Features", - "type": "array", - "items": { - "$ref": "#/definitions/Feature" - } + { + "$ref": "#/$defs/MultiPolygon" }, - "value_range": { - "title": "Value Range", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ValueRange" - } + { + "$ref": "#/$defs/GeometryCollection" }, - "descriptive_fields": { - "title": "Descriptive Fields", - "type": "array", - "items": { - "type": "string" - } + { + "type": "null" } - }, - "required": [ - "type", - "features" - ] - } - } + ], + "description": "The root object of a GeoJSON file", + "title": "GeoJSON" } \ No newline at end of file diff --git a/microjson_schema.json b/microjson_schema.json index a2ff840..dd35e66 100644 --- a/microjson_schema.json +++ b/microjson_schema.json @@ -1,636 +1,860 @@ { - "title": "MicroJSON", - "description": "The root object of a MicroJSON file", - "anyOf": [ - { - "$ref": "#/definitions/MicroFeature" - }, - { - "$ref": "#/definitions/MicroFeatureCollection" - }, - { - "$ref": "#/definitions/Point" - }, - { - "$ref": "#/definitions/MultiPoint" - }, - { - "$ref": "#/definitions/LineString" - }, - { - "$ref": "#/definitions/MultiLineString" - }, - { - "$ref": "#/definitions/Polygon" - }, - { - "$ref": "#/definitions/MultiPolygon" - }, - { - "$ref": "#/definitions/GeometryCollection" - } - ], - "definitions": { - "Point": { - "title": "Point", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } - }, - "type": { - "title": "Type", - "enum": [ - "Point" - ], - "type": "string" - }, - "coordinates": { - "title": "Coordinates", - "minItems": 2, - "maxItems": 3, - "type": "array", - "items": { - "type": "number" - } - } - }, - "required": [ - "type", - "coordinates" - ] - }, - "MultiPoint": { - "title": "MultiPoint", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } - }, - "type": { - "title": "Type", - "enum": [ - "MultiPoint" - ], - "type": "string" - }, - "coordinates": { - "title": "Coordinates", - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" - }, - "minItems": 2, - "maxItems": 3 - } - } - }, - "required": [ - "type", - "coordinates" - ] - }, - "LineString": { - "title": "LineString", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } - }, - "type": { - "title": "Type", - "enum": [ - "LineString" - ], - "type": "string" - }, - "coordinates": { - "title": "Coordinates", - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" + "$defs": { + "Axis": { + "description": "An axis of a coordinate system", + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "type": { + "anyOf": [ + { + "$ref": "#/$defs/AxisType" + }, + { + "type": "null" + } + ], + "default": null + }, + "unit": { + "anyOf": [ + { + "$ref": "#/$defs/Unit" + }, + { + "type": "null" + } + ], + "default": null + }, + "pixels_per_unit": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Pixels Per Unit" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + } }, - "minItems": 2, - "maxItems": 3 - } - } - }, - "required": [ - "type", - "coordinates" - ] - }, - "MultiLineString": { - "title": "MultiLineString", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } - }, - "type": { - "title": "Type", - "enum": [ - "MultiLineString" - ], - "type": "string" - }, - "coordinates": { - "title": "Coordinates", - "type": "array", - "items": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" - }, - "minItems": 2, - "maxItems": 3 - } - } - } - }, - "required": [ - "type", - "coordinates" - ] - }, - "Polygon": { - "title": "Polygon", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } - }, - "type": { - "title": "Type", - "enum": [ - "Polygon" - ], - "type": "string" - }, - "coordinates": { - "title": "Coordinates", - "type": "array", - "items": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" - }, - "minItems": 2, - "maxItems": 3 - } - } - } - }, - "required": [ - "type", - "coordinates" - ] - }, - "MultiPolygon": { - "title": "MultiPolygon", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } - }, - "type": { - "title": "Type", - "enum": [ - "MultiPolygon" - ], - "type": "string" - }, - "coordinates": { - "title": "Coordinates", - "type": "array", - "items": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" - }, - "minItems": 2, - "maxItems": 3 - } - } - } - } - }, - "required": [ - "type", - "coordinates" - ] - }, - "GeometryCollection": { - "title": "GeometryCollection", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } - }, - "type": { - "title": "Type", - "enum": [ - "GeometryCollection" - ], - "type": "string" - }, - "geometries": { - "title": "Geometries", - "type": "array", - "items": { - "anyOf": [ - { - "$ref": "#/definitions/Point" - }, - { - "$ref": "#/definitions/MultiPoint" - }, - { - "$ref": "#/definitions/LineString" - }, - { - "$ref": "#/definitions/MultiLineString" - }, - { - "$ref": "#/definitions/Polygon" - }, - { - "$ref": "#/definitions/MultiPolygon" - } - ] - } - } - }, - "required": [ - "type", - "geometries" - ] - }, - "Properties": { - "title": "Properties", - "type": "object", - "properties": { - "descriptive": { - "title": "Descriptive", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "numerical": { - "title": "Numerical", - "type": "object", - "additionalProperties": { - "type": "number" - } + "required": [ + "name" + ], + "title": "Axis", + "type": "object" }, - "multi_numerical": { - "title": "Multi Numerical", - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "number" - } - } - } - } - }, - "Unit": { - "title": "Unit", - "description": "An enumeration.", - "enum": [ - "angstrom", - "attometer", - "centimeter", - "decimeter", - "exameter", - "femtometer", - "foot", - "gigameter", - "hectometer", - "inch", - "kilometer", - "megameter", - "meter", - "micrometer", - "mile", - "millimeter", - "nanometer", - "parsec", - "petameter", - "picometer", - "terameter", - "yard", - "yoctometer", - "yottameter", - "zeptometer", - "zettameter", - "pixel", - "radian", - "degree" - ] - }, - "Coordinatesystem": { - "title": "Coordinatesystem", - "type": "object", - "properties": { - "axes": { - "title": "Axes", - "type": "array", - "items": { + "AxisType": { + "description": "The type of an axis", "enum": [ - "x", - "y", - "z", - "r", - "theta", - "phi" + "cartesian", + "angular", + "temporal", + "spectral" ], + "title": "AxisType", "type": "string" - } - }, - "units": { - "type": "array", - "items": { - "$ref": "#/definitions/Unit" - } - }, - "pixelsPerUnit": { - "title": "Pixelsperunit", - "type": "array", - "items": { - "type": "number" - } - } - }, - "required": [ - "axes" - ] - }, - "MicroFeature": { - "title": "MicroFeature", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } - }, - "type": { - "title": "Type", - "enum": [ - "Feature" - ], - "type": "string" }, - "geometry": { - "title": "Geometry", - "description": "The geometry of the\n feature", - "anyOf": [ - { - "$ref": "#/definitions/Point" - }, - { - "$ref": "#/definitions/MultiPoint" - }, - { - "$ref": "#/definitions/LineString" + "CoordinateSystem": { + "description": "A coordinate system for MicroJSON coordinates", + "properties": { + "axes": { + "items": { + "$ref": "#/$defs/Axis" + }, + "title": "Axes", + "type": "array" + }, + "transformation_matrix": { + "anyOf": [ + { + "items": { + "items": { + "type": "number" + }, + "type": "array" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Transformation Matrix" + } }, - { - "$ref": "#/definitions/MultiLineString" + "required": [ + "axes" + ], + "title": "CoordinateSystem", + "type": "object" + }, + "Feature": { + "description": "A GeoJSON Feature object", + "properties": { + "bbox": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "minItems": 4, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Bbox" + }, + "type": { + "const": "Feature", + "title": "Type" + }, + "geometry": { + "anyOf": [ + { + "$ref": "#/$defs/Point" + }, + { + "$ref": "#/$defs/MultiPoint" + }, + { + "$ref": "#/$defs/LineString" + }, + { + "$ref": "#/$defs/MultiLineString" + }, + { + "$ref": "#/$defs/Polygon" + }, + { + "$ref": "#/$defs/MultiPolygon" + }, + { + "$ref": "#/$defs/GeometryCollection" + }, + { + "type": "null" + } + ], + "description": "The geometry of the\n feature", + "title": "Geometry" + }, + "properties": { + "type": "object", + "description": "Properties of the feature", + "title": "Properties" + }, + "id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Id" + } }, - { - "$ref": "#/definitions/Polygon" + "required": [ + "type", + "geometry", + "properties" + ], + "title": "Feature", + "type": "object" + }, + "GeometryCollection": { + "description": "A GeoJSON GeometryCollection object", + "properties": { + "bbox": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "minItems": 4, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Bbox" + }, + "type": { + "const": "GeometryCollection", + "title": "Type" + }, + "geometries": { + "items": { + "anyOf": [ + { + "$ref": "#/$defs/Point" + }, + { + "$ref": "#/$defs/MultiPoint" + }, + { + "$ref": "#/$defs/LineString" + }, + { + "$ref": "#/$defs/MultiLineString" + }, + { + "$ref": "#/$defs/Polygon" + }, + { + "$ref": "#/$defs/MultiPolygon" + } + ] + }, + "title": "Geometries", + "type": "array" + } }, - { - "$ref": "#/definitions/MultiPolygon" + "required": [ + "type", + "geometries" + ], + "title": "GeometryCollection", + "type": "object" + }, + "LineString": { + "description": "A GeoJSON LineString object", + "properties": { + "bbox": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "minItems": 4, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Bbox" + }, + "type": { + "const": "LineString", + "title": "Type" + }, + "coordinates": { + "items": { + "items": { + "type": "number" + }, + "maxItems": 3, + "minItems": 2, + "type": "array" + }, + "title": "Coordinates", + "type": "array" + } }, - { - "$ref": "#/definitions/GeometryCollection" - } - ] - }, - "properties": { - "$ref": "#/definitions/Properties" - }, - "id": { - "title": "Id", - "anyOf": [ - { - "type": "string" + "required": [ + "type", + "coordinates" + ], + "title": "LineString", + "type": "object" + }, + "MicroFeature": { + "description": "A MicroJSON feature, which is a GeoJSON feature with additional\nmetadata", + "properties": { + "bbox": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "minItems": 4, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Bbox" + }, + "type": { + "const": "Feature", + "title": "Type" + }, + "geometry": { + "anyOf": [ + { + "$ref": "#/$defs/Point" + }, + { + "$ref": "#/$defs/MultiPoint" + }, + { + "$ref": "#/$defs/LineString" + }, + { + "$ref": "#/$defs/MultiLineString" + }, + { + "$ref": "#/$defs/Polygon" + }, + { + "$ref": "#/$defs/MultiPolygon" + }, + { + "$ref": "#/$defs/GeometryCollection" + }, + { + "type": "null" + } + ], + "description": "The geometry of the\n feature", + "title": "Geometry" + }, + "properties": { + "$ref": "#/$defs/Properties" + }, + "id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Id" + }, + "coordinatesystem": { + "anyOf": [ + { + "items": { + "$ref": "#/$defs/Axis" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Coordinatesystem" + }, + "ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref" + } }, - { - "type": "integer" - } - ] - }, - "coordinatesystem": { - "$ref": "#/definitions/Coordinatesystem" - }, - "ref": { - "title": "Ref", - "anyOf": [ - { - "type": "string" + "required": [ + "type", + "geometry", + "properties" + ], + "title": "MicroFeature", + "type": "object" + }, + "MicroFeatureCollection": { + "description": "A MicroJSON feature collection, which is a GeoJSON feature\ncollection with additional metadata", + "properties": { + "bbox": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "minItems": 4, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Bbox" + }, + "type": { + "const": "FeatureCollection", + "title": "Type" + }, + "features": { + "items": { + "$ref": "#/$defs/Feature" + }, + "title": "Features", + "type": "array" + }, + "coordinatesystem": { + "anyOf": [ + { + "$ref": "#/$defs/CoordinateSystem" + }, + { + "type": "null" + } + ], + "default": null + }, + "value_range": { + "anyOf": [ + { + "additionalProperties": { + "$ref": "#/$defs/ValueRange" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Value Range" + }, + "descriptive_fields": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Descriptive Fields" + }, + "propertie": { + "anyOf": [ + { + "$ref": "#/$defs/Properties" + }, + { + "type": "null" + } + ], + "default": null + } }, - { - "type": "integer" - } - ] - } - }, - "required": [ - "type", - "geometry", - "properties" - ] - }, - "Feature": { - "title": "Feature", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } - }, - "type": { - "title": "Type", - "enum": [ - "Feature" - ], - "type": "string" - }, - "geometry": { - "title": "Geometry", - "description": "The geometry of the\n feature", - "anyOf": [ - { - "$ref": "#/definitions/Point" + "required": [ + "type", + "features" + ], + "title": "MicroFeatureCollection", + "type": "object" + }, + "MultiLineString": { + "description": "A GeoJSON MultiLineString object", + "properties": { + "bbox": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "minItems": 4, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Bbox" + }, + "type": { + "const": "MultiLineString", + "title": "Type" + }, + "coordinates": { + "items": { + "items": { + "items": { + "type": "number" + }, + "maxItems": 3, + "minItems": 2, + "type": "array" + }, + "type": "array" + }, + "title": "Coordinates", + "type": "array" + } }, - { - "$ref": "#/definitions/MultiPoint" + "required": [ + "type", + "coordinates" + ], + "title": "MultiLineString", + "type": "object" + }, + "MultiPoint": { + "description": "A GeoJSON MultiPoint object", + "properties": { + "bbox": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "minItems": 4, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Bbox" + }, + "type": { + "const": "MultiPoint", + "title": "Type" + }, + "coordinates": { + "items": { + "items": { + "type": "number" + }, + "maxItems": 3, + "minItems": 2, + "type": "array" + }, + "title": "Coordinates", + "type": "array" + } }, - { - "$ref": "#/definitions/LineString" + "required": [ + "type", + "coordinates" + ], + "title": "MultiPoint", + "type": "object" + }, + "MultiPolygon": { + "description": "A GeoJSON MultiPolygon object", + "properties": { + "bbox": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "minItems": 4, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Bbox" + }, + "type": { + "const": "MultiPolygon", + "title": "Type" + }, + "coordinates": { + "items": { + "items": { + "items": { + "items": { + "type": "number" + }, + "maxItems": 3, + "minItems": 2, + "type": "array" + }, + "type": "array" + }, + "type": "array" + }, + "title": "Coordinates", + "type": "array" + } }, - { - "$ref": "#/definitions/MultiLineString" + "required": [ + "type", + "coordinates" + ], + "title": "MultiPolygon", + "type": "object" + }, + "Point": { + "description": "A GeoJSON Point object", + "properties": { + "bbox": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "minItems": 4, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Bbox" + }, + "type": { + "const": "Point", + "title": "Type" + }, + "coordinates": { + "items": { + "type": "number" + }, + "maxItems": 3, + "minItems": 2, + "title": "Coordinates", + "type": "array" + } }, - { - "$ref": "#/definitions/Polygon" + "required": [ + "type", + "coordinates" + ], + "title": "Point", + "type": "object" + }, + "Polygon": { + "description": "A GeoJSON Polygon object", + "properties": { + "bbox": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "minItems": 4, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Bbox" + }, + "type": { + "const": "Polygon", + "title": "Type" + }, + "coordinates": { + "items": { + "items": { + "items": { + "type": "number" + }, + "maxItems": 3, + "minItems": 2, + "type": "array" + }, + "type": "array" + }, + "title": "Coordinates", + "type": "array" + } }, - { - "$ref": "#/definitions/MultiPolygon" + "required": [ + "type", + "coordinates" + ], + "title": "Polygon", + "type": "object" + }, + "Properties": { + "description": "Metadata properties of a MicroJSON feature", + "properties": { + "string": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "String" + }, + "numeric": { + "anyOf": [ + { + "additionalProperties": { + "type": "number" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Numeric" + }, + "multi_numeric": { + "anyOf": [ + { + "additionalProperties": { + "items": { + "type": "number" + }, + "type": "array" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Multi Numeric" + } }, - { - "$ref": "#/definitions/GeometryCollection" - } - ] + "title": "Properties", + "type": "object" }, - "properties": { - "title": "Properties", - "description": "Properties of the\n feature", - "type": "object" + "Unit": { + "description": "A unit of measurement", + "enum": [ + "angstrom", + "attometer", + "centimeter", + "decimeter", + "exameter", + "femtometer", + "foot", + "gigameter", + "hectometer", + "inch", + "kilometer", + "megameter", + "meter", + "micrometer", + "mile", + "millimeter", + "nanometer", + "parsec", + "petameter", + "picometer", + "terameter", + "yard", + "yoctometer", + "yottameter", + "zeptometer", + "zettameter", + "pixel", + "radian", + "degree" + ], + "title": "Unit", + "type": "string" }, - "id": { - "title": "Id", - "anyOf": [ - { - "type": "string" + "ValueRange": { + "description": "A range of values for MicroJSON quantitative properties", + "properties": { + "min": { + "title": "Min", + "type": "number" + }, + "max": { + "title": "Max", + "type": "number" + } }, - { - "type": "integer" - } - ] + "required": [ + "min", + "max" + ], + "title": "ValueRange", + "type": "object" } - }, - "required": [ - "type", - "geometry", - "properties" - ] }, - "ValueRange": { - "title": "ValueRange", - "type": "object", - "properties": { - "min": { - "title": "Min", - "type": "number" + "anyOf": [ + { + "$ref": "#/$defs/MicroFeature" }, - "max": { - "title": "Max", - "type": "number" - } - }, - "required": [ - "min", - "max" - ] - }, - "MicroFeatureCollection": { - "title": "MicroFeatureCollection", - "type": "object", - "properties": { - "bbox": { - "title": "Bbox", - "minItems": 4, - "type": "array", - "items": { - "type": "number" - } + { + "$ref": "#/$defs/MicroFeatureCollection" }, - "type": { - "title": "Type", - "enum": [ - "FeatureCollection" - ], - "type": "string" + { + "$ref": "#/$defs/Point" }, - "features": { - "title": "Features", - "type": "array", - "items": { - "$ref": "#/definitions/Feature" - } + { + "$ref": "#/$defs/MultiPoint" }, - "value_range": { - "title": "Value Range", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ValueRange" - } + { + "$ref": "#/$defs/LineString" }, - "descriptive_fields": { - "title": "Descriptive Fields", - "type": "array", - "items": { - "type": "string" - } + { + "$ref": "#/$defs/MultiLineString" + }, + { + "$ref": "#/$defs/Polygon" + }, + { + "$ref": "#/$defs/MultiPolygon" + }, + { + "$ref": "#/$defs/GeometryCollection" }, - "coordinatesystem": { - "$ref": "#/definitions/Coordinatesystem" + { + "type": "null" } - }, - "required": [ - "type", - "features" - ] - } - } + ], + "description": "The root object of a MicroJSON file", + "title": "MicroJSON" } \ No newline at end of file diff --git a/noxfile.py b/noxfile.py index 61c6093..b57e5b3 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,36 +1,42 @@ -from nox_poetry import session +import nox +from microjson.model import GeoJSON, MicroJSON +import json -@session(python="3.9") -def export_ts(session): - #session.install("-r", "requirements-dev.txt") - #session.install("-e", ".") - session.install("microjson") +@nox.session(python="3.9") +def generate_geojson_pydantic(session): + session.install("-r", "requirements-dev.txt") + session.install("-e", ".") session.run("datamodel-codegen", "--reuse-model", "--snake-case-field", "--input", "external", "--output", "geojson", "--target", "3.9") - session.run("pydantic2ts", "--module", "microjson/model.py", - "--output", "microjsonschema.ts") + +@nox.session(python="3.9") +def generate_roundtrip_json_schema(session): + session.install("-r", "requirements-dev.txt") + session.install("-e", ".") # generate GeoJSON schema - from microjson.modelv1 import GeoJSON # Get the schema for the model as a dictionary - schema_dict = GeoJSON.schema() - # Add 'null' to the list of allowed types for the 'geometry' field of + schema_dict = GeoJSON.model_json_schema() + # Add 'null' to the list of allowed types for the 'geometry' field of # Feature object - schema_dict = schema_dict['definitions']['Feature'] + schema_dict = schema_dict['$defs']['Feature'] schema_dict['properties']['geometry']['anyOf'].append({'type': 'null'}) with open('geojson_schema.json', 'w') as f: - f.write(GeoJSON.schema_json(indent=2)) + f.write(json.dumps(GeoJSON.model_json_schema(), indent=4)) - from microjson.modelv1 import MicroJSON with open('microjson_schema.json', 'w') as f: - f.write(MicroJSON.schema_json(indent=2)) - + f.write(json.dumps(MicroJSON.model_json_schema(), indent=4)) + + +@nox.session(python="3.9") +def generate_roundtrip_pydantic(session): # generate pydantic model from the GeoJSON schema + session.install("-r", "requirements-dev.txt") session.run("datamodel-codegen", "--reuse-model", "--snake-case-field", @@ -38,11 +44,16 @@ def export_ts(session): "--output", "geojson/roundtrip.py", "--target", "3.9") - # generate pydantic model from the MicroJSON schema - session.run("datamodel-codegen", - "--reuse-model", - "--snake-case-field", - "--input", "microjson_schema.json", - "--output", "microjson/roundtrip.py", - "--target", "3.9") +@nox.session +def typescript(session): + # Install json-schema-to-typescript + session.run("npm", "install", "json-schema-to-typescript", external=True) + + # Use json-schema-to-typescript + session.run("npx", + "json-schema-to-typescript", + "microjson_schema.json", + "-o", + "microjson.d.ts", + external=True) diff --git a/pyproject.toml b/pyproject.toml index 0a7afdd..0b7fed0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,9 +30,10 @@ pydantic = "^2.3.0" pytest = "^6.2.4" nox = "^2022.1.7" pre-commit = "^2.15.0" -pydantic-to-typescript = "^1.0.8" +pydantic-to-typescript = "^1.0.10" datamodel-code-generator = "^0.21.4" bumpver = "^2023.1125" +nox-poetry = "^1.0.3" [tool.pytest.ini_options] diff --git a/src/microjson/__init__.py b/src/microjson/__init__.py index 6f549af..5a54bee 100644 --- a/src/microjson/__init__.py +++ b/src/microjson/__init__.py @@ -1,6 +1,4 @@ from .model import MicroJSON, GeoJSON -#from .automodel import GeoJSONAuto, MicroJSONAuto -#from .roundtrip import MicroJSON as MicroJSONRound from .utils import gather_example_files __version__ = "0.1.6" \ No newline at end of file diff --git a/src/microjson/model.py b/src/microjson/model.py index 37bb53d..e5541a7 100644 --- a/src/microjson/model.py +++ b/src/microjson/model.py @@ -1,15 +1,15 @@ """MicroJSON and GeoJSON models, defined manually using pydantic.""" from typing import List, Optional, Union, Dict, Literal from enum import Enum -from pydantic import BaseModel, Field, StrictInt, StrictStr, conlist +from pydantic import BaseModel, Field, StrictInt, StrictStr, conlist, RootModel -Coordinates = conlist(float, min_items=2, max_items=3) +Coordinates = conlist(float, min_length=2, max_length=3) class GeoAbstract(BaseModel): """Abstract base class for all GeoJSON objects""" - bbox: Optional[List[float]] = Field(None, min_items=4) + bbox: Optional[List[float]] = Field(None, min_length=4) class Point(GeoAbstract): @@ -96,9 +96,9 @@ class FeatureCollection(GeoAbstract): features: List[Feature] -class GeoJSON(BaseModel): +class GeoJSON(RootModel): """The root object of a GeoJSON file""" - __root__: Union[Feature, FeatureCollection, GeometryType] + root: Union[Feature, FeatureCollection, GeometryType] class Unit(Enum): @@ -159,9 +159,9 @@ class CoordinateSystem(BaseModel): class Properties(BaseModel): """Metadata properties of a MicroJSON feature""" - descriptive: Optional[Dict[str, str]] = None - numerical: Optional[Dict[str, float]] = None - multi_numerical: Optional[Dict[str, List[float]]] = None + string: Optional[Dict[str, str]] = None + numeric: Optional[Dict[str, float]] = None + multi_numeric: Optional[Dict[str, List[float]]] = None class MicroFeature(Feature): @@ -178,10 +178,11 @@ class MicroFeatureCollection(FeatureCollection): coordinatesystem: Optional[CoordinateSystem] = None value_range: Optional[Dict[str, ValueRange]] = None descriptive_fields: Optional[List[str]] = None + propertie: Optional[Properties] = None -class MicroJSON(BaseModel): +class MicroJSON(RootModel): """The root object of a MicroJSON file""" - __root__: Union[MicroFeature, + root: Union[MicroFeature, MicroFeatureCollection, GeometryType] diff --git a/src/microjson/modelv1.py b/src/microjson/modelv1.py deleted file mode 100644 index eae46c2..0000000 --- a/src/microjson/modelv1.py +++ /dev/null @@ -1,188 +0,0 @@ -"""MicroJSON and GeoJSON models, defined manually using pydantic.""" -from typing import List, Optional, Union, Dict, Literal -from enum import Enum -from pydantic.v1 import BaseModel, Field, StrictInt, StrictStr, conlist - - -Coordinates = conlist(float, min_items=2, max_items=3) - - -class GeoAbstract(BaseModel): - """Abstract base class for all GeoJSON objects""" - bbox: Optional[List[float]] = Field(None, min_items=4) - - -class Point(GeoAbstract): - """A GeoJSON Point object""" - type: Literal["Point"] - coordinates: Coordinates - - -class MultiPoint(GeoAbstract): - """A GeoJSON MultiPoint object""" - type: Literal["MultiPoint"] - coordinates: List[Coordinates] - - -class LineString(GeoAbstract): - """A GeoJSON LineString object""" - type: Literal["LineString"] - coordinates: List[Coordinates] - - -class MultiLineString(GeoAbstract): - """A GeoJSON MultiLineString object""" - type: Literal["MultiLineString"] - coordinates: List[List[Coordinates]] - - -class Polygon(GeoAbstract): - """A GeoJSON Polygon object""" - type: Literal["Polygon"] - coordinates: List[List[Coordinates]] - - -class MultiPolygon(GeoAbstract): - """A GeoJSON MultiPolygon object""" - type: Literal["MultiPolygon"] - coordinates: List[List[List[Coordinates]]] - - -GeometryBaseType = Union[Point, - MultiPoint, - LineString, - MultiLineString, - Polygon, - MultiPolygon] - - -class GeometryCollection(GeoAbstract): - """A GeoJSON GeometryCollection object""" - type: Literal["GeometryCollection"] - geometries: List[GeometryBaseType] - - -GeometryType = Union[Point, - MultiPoint, - LineString, - MultiLineString, - Polygon, - MultiPolygon, - GeometryCollection, - type(None) - ] - - -class Feature(GeoAbstract): - """A GeoJSON Feature object""" - type: Literal["Feature"] - geometry: GeometryType = Field(..., - description="""The geometry of the - feature""") - properties: Optional[Dict] = Field(..., - description="""Properties of the - feature""") - id: Optional[Union[StrictStr, StrictInt]] - - -class ValueRange(BaseModel): - """A range of values for MicroJSON quantitative properties""" - min: float - max: float - - -class FeatureCollection(GeoAbstract): - """A GeoJSON FeatureCollection object""" - type: Literal["FeatureCollection"] - features: List[Feature] - - -class GeoJSON(BaseModel): - """The root object of a GeoJSON file""" - __root__: Union[Feature, FeatureCollection, GeometryType] - - -class Unit(Enum): - """A unit of measurement""" - ANGSTROM = 'angstrom' - ATTOMETER = 'attometer' - CENTIMETER = 'centimeter' - DECIMETER = 'decimeter' - EXAMETER = 'exameter' - FEMTOMETER = 'femtometer' - FOOT = 'foot' - GIGAMETER = 'gigameter' - HECTOMETER = 'hectometer' - INCH = 'inch' - KILOMETER = 'kilometer' - MEGAMETER = 'megameter' - METER = 'meter' - MICROMETER = 'micrometer' - MILE = 'mile' - MILLIMETER = 'millimeter' - NANOMETER = 'nanometer' - PARSEC = 'parsec' - PETAMETER = 'petameter' - PICOMETER = 'picometer' - TERAMETER = 'terameter' - YARD = 'yard' - YOCTOMETER = 'yoctometer' - YOTTAMETER = 'yottameter' - ZEPTOMETER = 'zeptometer' - ZETTAMETER = 'zettameter' - PIXEL = 'pixel' - RADIAN = 'radian' - DEGREE = 'degree' - - -class AxisType(Enum): - """The type of an axis""" - CARTESIAN = 'cartesian' - ANGULAR = 'angular' - TEMPORAL = 'temporal' - SPECTRAL = 'spectral' - - -class Axis(BaseModel): - """An axis of a coordinate system""" - name: StrictStr - type: Optional[AxisType] - unit: Optional[Unit] - pixels_per_unit: Optional[float] - description: Optional[str] - - -class CoordinateSystem(BaseModel): - """A coordinate system for MicroJSON coordinates""" - axes: List[Axis] - transformation_matrix: Optional[List[List[float]]] - - -class Properties(BaseModel): - """Metadata properties of a MicroJSON feature""" - descriptive: Optional[Dict[str, str]] - numerical: Optional[Dict[str, float]] - multi_numerical: Optional[Dict[str, List[float]]] - - -class MicroFeature(Feature): - """A MicroJSON feature, which is a GeoJSON feature with additional - metadata""" - coordinatesystem: Optional[List[Axis]] - ref: Optional[Union[StrictStr, StrictInt]] - properties: Properties - - -class MicroFeatureCollection(FeatureCollection): - """A MicroJSON feature collection, which is a GeoJSON feature - collection with additional metadata""" - coordinatesystem: Optional[CoordinateSystem] - value_range: Optional[Dict[str, ValueRange]] - descriptive_fields: Optional[List[str]] - - -class MicroJSON(BaseModel): - """The root object of a MicroJSON file""" - __root__: Union[MicroFeature, - MicroFeatureCollection, - GeometryType] diff --git a/tests/json/microjson/valid/metadata-full.json b/tests/json/microjson/valid/metadata-full.json index 9bff332..da119ab 100644 --- a/tests/json/microjson/valid/metadata-full.json +++ b/tests/json/microjson/valid/metadata-full.json @@ -1,14 +1,5 @@ { "type": "FeatureCollection", - "coordinatesystem": { - "axes": [{ - "name": "x" - } - , { - "name": "y" - } - ] - }, "features": [ { "type": "Feature", @@ -18,13 +9,13 @@ "coordinates": [[[0.0, 0.0], [0.0, 50.0], [50.0, 50.0], [50.0, 0.0], [0.0, 0.0]]] }, "properties": { - "descriptive": { + "string": { "well": "A1" }, - "numerical": { + "numeric": { "cellCount": 5 }, - "multi-numerical": { + "multi-numeric": { "ratioInfectivity": [[0.1, 0.2, 0.3, 0.4, 0.5], [0.2, 0.3, 0.4, 0.5, 0.6]] } } @@ -37,13 +28,13 @@ "coordinates": [[[50.0, 0.0], [50.0, 50.0], [100.0, 50.0], [100.0, 0.0], [50.0, 0.0]]] }, "properties": { - "descriptive": { + "string": { "well": "A2" }, - "numerical": { + "numeric": { "cellCount": 10 }, - "multivalues": { + "multi-numeric": { "ratioInfectivity": [[0.1, 0.2, 0.3, 0.4, 0.5], [0.2, 0.3, 0.4, 0.5, 0.6]] } } @@ -59,5 +50,40 @@ "max": 1 } }, - "descriptive_fields": ["well"] + "string_fields": ["well","imagename"], + "coordinatesystem": { + "axes": [ + { + "name": "x", + "unit": "micrometer", + "type": "cartesian", + "pixelsPerUnit": 1, + "description": "x-axis" + }, + { + "name": "y", + "unit": "micrometer", + "type": "cartesian", + "pixelsPerUnit": 1, + "description": "y-axis" + } + ], + "transformation_matrix": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 0.0 + ] + ] + } } \ No newline at end of file diff --git a/tests/test_microjson.py b/tests/test_microjson.py index 6f8188d..36711d9 100644 --- a/tests/test_microjson.py +++ b/tests/test_microjson.py @@ -26,7 +26,7 @@ def test_valid_geojson(filename): # Try to parse the data as a GeoJSON object try: - _ = GeoJSON.parse_obj(data) + _ = GeoJSON.model_validate(data) except ValidationError as e: pytest.fail(f"""ValidationError occurred during validation of {filename}: {str(e)}""") @@ -42,7 +42,7 @@ def test_invalid_geojson(filename): # This will raise a ValidationError if the data does not match the GeoJSON try: - _ = GeoJSON.parse_obj(data) + _ = GeoJSON.model_validate(data) pytest.fail(f"""Parsing succeeded on {filename}, but it should not have.""") except ValidationError: @@ -61,7 +61,7 @@ def test_valid_microjson(filename): # Try to parse the data as a MicroJSON object try: - _ = MicroJSON.parse_obj(data) + _ = MicroJSON.model_validate(data) except ValidationError as e: pytest.fail(f"""ValidationError occurred during validation of {filename}: {str(e)}""") @@ -78,7 +78,7 @@ def test_invalid_microjson(filename): # This will raise a ValidationError if the data does not # match the MicroJSON schema try: - _ = MicroJSON.parse_obj(data) + _ = MicroJSON.model_validate(data) pytest.fail(f"""Parsing succeeded on {filename}, but it should not have.""") except ValidationError: From b52f28f9005fdbc45136b6f1ff89008b07f8381a Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Fri, 15 Sep 2023 08:24:01 -0400 Subject: [PATCH 25/28] Bump version 0.1.6 -> 0.1.7 --- pyproject.toml | 4 ++-- src/microjson/__init__.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0b7fed0..36e1f66 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "microjson" -version = "0.1.6" +version = "0.1.7" description = "MicroJSON is a library for validating, parsing, and manipulating MicroJSON data." readme = "README_short.md" authors = ["Bengt Ljungquist "] @@ -40,7 +40,7 @@ nox-poetry = "^1.0.3" testpaths = ["tests/"] [tool.bumpver] -current_version = "0.1.6" +current_version = "0.1.7" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "Bump version {old_version} -> {new_version}" commit = true diff --git a/src/microjson/__init__.py b/src/microjson/__init__.py index 5a54bee..68f13fd 100644 --- a/src/microjson/__init__.py +++ b/src/microjson/__init__.py @@ -1,4 +1,4 @@ from .model import MicroJSON, GeoJSON from .utils import gather_example_files -__version__ = "0.1.6" \ No newline at end of file +__version__ = "0.1.7" \ No newline at end of file From b44f292c9e9ff235524c37cc8f422a97a69f3f05 Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Fri, 15 Sep 2023 08:47:06 -0400 Subject: [PATCH 26/28] Updated typescript --- microjson.d.ts | 285 +++++++++++++++++++++++++++++++++++++++++++++ microjsonschema.ts | 211 --------------------------------- 2 files changed, 285 insertions(+), 211 deletions(-) create mode 100644 microjson.d.ts delete mode 100644 microjsonschema.ts diff --git a/microjson.d.ts b/microjson.d.ts new file mode 100644 index 0000000..03b94d7 --- /dev/null +++ b/microjson.d.ts @@ -0,0 +1,285 @@ +/* eslint-disable */ +/** + * This file was automatically generated by json-schema-to-typescript. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run json-schema-to-typescript to regenerate this file. + */ + +/** + * The root object of a MicroJSON file + */ +export type MicroJSON = + | MicroFeature + | MicroFeatureCollection + | Point + | MultiPoint + | LineString + | MultiLineString + | Polygon + | MultiPolygon + | GeometryCollection + | null; +export type Bbox = [number, number, number, number, ...number[]] | null; +export type Type = "Feature"; +/** + * The geometry of the + * feature + */ +export type Geometry = + | Point + | MultiPoint + | LineString + | MultiLineString + | Polygon + | MultiPolygon + | GeometryCollection + | null; +export type Bbox1 = [number, number, number, number, ...number[]] | null; +export type Type1 = "Point"; +/** + * @minItems 2 + * @maxItems 3 + */ +export type Coordinates = [number, number] | [number, number, number]; +export type Bbox2 = [number, number, number, number, ...number[]] | null; +export type Type2 = "MultiPoint"; +export type Coordinates1 = [number, number] | [number, number, number][]; +export type Bbox3 = [number, number, number, number, ...number[]] | null; +export type Type3 = "LineString"; +export type Coordinates2 = [number, number] | [number, number, number][]; +export type Bbox4 = [number, number, number, number, ...number[]] | null; +export type Type4 = "MultiLineString"; +export type Coordinates3 = [number, number] | [number, number, number][][]; +export type Bbox5 = [number, number, number, number, ...number[]] | null; +export type Type5 = "Polygon"; +export type Coordinates4 = [number, number] | [number, number, number][][]; +export type Bbox6 = [number, number, number, number, ...number[]] | null; +export type Type6 = "MultiPolygon"; +export type Coordinates5 = [number, number] | [number, number, number][][][]; +export type Bbox7 = [number, number, number, number, ...number[]] | null; +export type Type7 = "GeometryCollection"; +export type Geometries = (Point | MultiPoint | LineString | MultiLineString | Polygon | MultiPolygon)[]; +export type String = { + [k: string]: string; +} | null; +export type Numeric = { + [k: string]: number; +} | null; +export type MultiNumeric = { + [k: string]: number[]; +} | null; +export type Id = string | number | null; +export type Coordinatesystem = Axis[] | null; +export type Name = string; +/** + * The type of an axis + */ +export type AxisType = "cartesian" | "angular" | "temporal" | "spectral"; +/** + * A unit of measurement + */ +export type Unit = + | "angstrom" + | "attometer" + | "centimeter" + | "decimeter" + | "exameter" + | "femtometer" + | "foot" + | "gigameter" + | "hectometer" + | "inch" + | "kilometer" + | "megameter" + | "meter" + | "micrometer" + | "mile" + | "millimeter" + | "nanometer" + | "parsec" + | "petameter" + | "picometer" + | "terameter" + | "yard" + | "yoctometer" + | "yottameter" + | "zeptometer" + | "zettameter" + | "pixel" + | "radian" + | "degree"; +export type PixelsPerUnit = number | null; +export type Description = string | null; +export type Ref = string | number | null; +export type Bbox8 = [number, number, number, number, ...number[]] | null; +export type Type8 = "FeatureCollection"; +export type Bbox9 = [number, number, number, number, ...number[]] | null; +export type Type9 = "Feature"; +/** + * The geometry of the + * feature + */ +export type Geometry1 = + | Point + | MultiPoint + | LineString + | MultiLineString + | Polygon + | MultiPolygon + | GeometryCollection + | null; +export type Id1 = string | number | null; +export type Features = Feature[]; +export type Axes = Axis[]; +export type TransformationMatrix = number[][] | null; +export type ValueRange = { + [k: string]: ValueRange1; +} | null; +export type Min = number; +export type Max = number; +export type DescriptiveFields = string[] | null; + +/** + * A MicroJSON feature, which is a GeoJSON feature with additional + * metadata + */ +export interface MicroFeature { + bbox?: Bbox; + type: Type; + geometry: Geometry; + properties: Properties; + id?: Id; + coordinatesystem?: Coordinatesystem; + ref?: Ref; + [k: string]: unknown; +} +/** + * A GeoJSON Point object + */ +export interface Point { + bbox?: Bbox1; + type: Type1; + coordinates: Coordinates; + [k: string]: unknown; +} +/** + * A GeoJSON MultiPoint object + */ +export interface MultiPoint { + bbox?: Bbox2; + type: Type2; + coordinates: Coordinates1; + [k: string]: unknown; +} +/** + * A GeoJSON LineString object + */ +export interface LineString { + bbox?: Bbox3; + type: Type3; + coordinates: Coordinates2; + [k: string]: unknown; +} +/** + * A GeoJSON MultiLineString object + */ +export interface MultiLineString { + bbox?: Bbox4; + type: Type4; + coordinates: Coordinates3; + [k: string]: unknown; +} +/** + * A GeoJSON Polygon object + */ +export interface Polygon { + bbox?: Bbox5; + type: Type5; + coordinates: Coordinates4; + [k: string]: unknown; +} +/** + * A GeoJSON MultiPolygon object + */ +export interface MultiPolygon { + bbox?: Bbox6; + type: Type6; + coordinates: Coordinates5; + [k: string]: unknown; +} +/** + * A GeoJSON GeometryCollection object + */ +export interface GeometryCollection { + bbox?: Bbox7; + type: Type7; + geometries: Geometries; + [k: string]: unknown; +} +/** + * Metadata properties of a MicroJSON feature + */ +export interface Properties { + string?: String; + numeric?: Numeric; + multi_numeric?: MultiNumeric; + [k: string]: unknown; +} +/** + * An axis of a coordinate system + */ +export interface Axis { + name: Name; + type?: AxisType | null; + unit?: Unit | null; + pixels_per_unit?: PixelsPerUnit; + description?: Description; + [k: string]: unknown; +} +/** + * A MicroJSON feature collection, which is a GeoJSON feature + * collection with additional metadata + */ +export interface MicroFeatureCollection { + bbox?: Bbox8; + type: Type8; + features: Features; + coordinatesystem?: CoordinateSystem | null; + value_range?: ValueRange; + descriptive_fields?: DescriptiveFields; + propertie?: Properties | null; + [k: string]: unknown; +} +/** + * A GeoJSON Feature object + */ +export interface Feature { + bbox?: Bbox9; + type: Type9; + geometry: Geometry1; + properties: Properties1; + id?: Id1; + [k: string]: unknown; +} +/** + * Properties of the feature + */ +export interface Properties1 { + [k: string]: unknown; +} +/** + * A coordinate system for MicroJSON coordinates + */ +export interface CoordinateSystem { + axes: Axes; + transformation_matrix?: TransformationMatrix; + [k: string]: unknown; +} +/** + * A range of values for MicroJSON quantitative properties + */ +export interface ValueRange1 { + min: Min; + max: Max; + [k: string]: unknown; +} diff --git a/microjsonschema.ts b/microjsonschema.ts deleted file mode 100644 index e3acf15..0000000 --- a/microjsonschema.ts +++ /dev/null @@ -1,211 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -/** -/* This file was automatically generated from pydantic models by running pydantic2ts. -/* Do not modify it by hand - just update the pydantic models and then re-run the script -*/ - -export type Unit = - | "angstrom" - | "attometer" - | "centimeter" - | "decimeter" - | "exameter" - | "femtometer" - | "foot" - | "gigameter" - | "hectometer" - | "inch" - | "kilometer" - | "megameter" - | "meter" - | "micrometer" - | "mile" - | "millimeter" - | "nanometer" - | "parsec" - | "petameter" - | "picometer" - | "terameter" - | "yard" - | "yoctometer" - | "yottameter" - | "zeptometer" - | "zettameter" - | "pixel" - | "radian" - | "degree"; -/** - * The root object of a GeoJSON file - */ -export type GeoJSON = - | Feature - | FeatureCollection - | Point - | MultiPoint - | LineString - | MultiLineString - | Polygon - | MultiPolygon - | GeometryCollection; -/** - * The root object of a MicroJSON file - */ -export type MicroJSON = - | MicroFeature - | MicroFeatureCollection - | Point - | MultiPoint - | LineString - | MultiLineString - | Polygon - | MultiPolygon - | GeometryCollection; - -export interface Coordinatesystem { - axes: ("x" | "y" | "z" | "r" | "theta" | "phi")[]; - units?: Unit[]; - pixelsPerUnit?: number[]; -} -export interface Feature { - /** - * @minItems 4 - */ - bbox?: [number, number, number, number, ...number[]]; - type: "Feature"; - /** - * The geometry of the - * feature - */ - geometry: Point | MultiPoint | LineString | MultiLineString | Polygon | MultiPolygon | GeometryCollection; - /** - * Properties of the - * feature - */ - properties: { - [k: string]: unknown; - }; - id?: string | number; -} -export interface Point { - /** - * @minItems 4 - */ - bbox?: [number, number, number, number, ...number[]]; - type: "Point"; - /** - * @minItems 2 - * @maxItems 3 - */ - coordinates: [number, number] | [number, number, number]; -} -export interface MultiPoint { - /** - * @minItems 4 - */ - bbox?: [number, number, number, number, ...number[]]; - type: "MultiPoint"; - coordinates: [number, number] | [number, number, number][]; -} -export interface LineString { - /** - * @minItems 4 - */ - bbox?: [number, number, number, number, ...number[]]; - type: "LineString"; - coordinates: [number, number] | [number, number, number][]; -} -export interface MultiLineString { - /** - * @minItems 4 - */ - bbox?: [number, number, number, number, ...number[]]; - type: "MultiLineString"; - coordinates: [number, number] | [number, number, number][][]; -} -export interface Polygon { - /** - * @minItems 4 - */ - bbox?: [number, number, number, number, ...number[]]; - type: "Polygon"; - coordinates: [number, number] | [number, number, number][][]; -} -export interface MultiPolygon { - /** - * @minItems 4 - */ - bbox?: [number, number, number, number, ...number[]]; - type: "MultiPolygon"; - coordinates: [number, number] | [number, number, number][][][]; -} -export interface GeometryCollection { - /** - * @minItems 4 - */ - bbox?: [number, number, number, number, ...number[]]; - type: "GeometryCollection"; - geometries: (Point | MultiPoint | LineString | MultiLineString | Polygon | MultiPolygon)[]; -} -export interface FeatureCollection { - /** - * @minItems 4 - */ - bbox?: [number, number, number, number, ...number[]]; - type: "FeatureCollection"; - features: Feature[]; - value_range?: { - [k: string]: ValueRange; - }; - descriptive_fields?: string[]; -} -export interface ValueRange { - min: number; - max: number; -} -export interface GeoAbstract { - /** - * @minItems 4 - */ - bbox?: [number, number, number, number, ...number[]]; -} -export interface MicroFeature { - /** - * @minItems 4 - */ - bbox?: [number, number, number, number, ...number[]]; - type: "Feature"; - /** - * The geometry of the - * feature - */ - geometry: Point | MultiPoint | LineString | MultiLineString | Polygon | MultiPolygon | GeometryCollection; - properties: Properties; - id?: string | number; - coordinatesystem?: Coordinatesystem; - ref?: string | number; -} -export interface Properties { - descriptive?: { - [k: string]: string; - }; - numerical?: { - [k: string]: number; - }; - multi_numerical?: { - [k: string]: number[]; - }; -} -export interface MicroFeatureCollection { - /** - * @minItems 4 - */ - bbox?: [number, number, number, number, ...number[]]; - type: "FeatureCollection"; - features: Feature[]; - value_range?: { - [k: string]: ValueRange; - }; - descriptive_fields?: string[]; - coordinatesystem?: Coordinatesystem; -} From a9c67aedf11c15195d61f02e42af40f097c30f28 Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Fri, 15 Sep 2023 09:15:47 -0400 Subject: [PATCH 27/28] Corrected example --- examples/df_to_microjson.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/df_to_microjson.py b/examples/df_to_microjson.py index a096966..447516b 100644 --- a/examples/df_to_microjson.py +++ b/examples/df_to_microjson.py @@ -59,7 +59,7 @@ def df_to_microjson(df: pd.DataFrame) -> mj.FeatureCollection: type='FeatureCollection', features=features, value_range=value_range, - descriptive_fields=descriptive_fields, + string_fields=string_fields, coordinatesystem= { 'axes': [ { @@ -82,7 +82,6 @@ def df_to_microjson(df: pd.DataFrame) -> mj.FeatureCollection: [0, 1, 0], [0, 0, 1] ], - 'origo': 'top-left' } ) From fc2ee7758dec7bde4abd71616dfebcdb54ac53af Mon Sep 17 00:00:00 2001 From: Bengt Ljungquist Date: Fri, 15 Sep 2023 09:17:09 -0400 Subject: [PATCH 28/28] Corrected example --- examples/df_to_microjson.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/df_to_microjson.py b/examples/df_to_microjson.py index 447516b..eb4b4ae 100644 --- a/examples/df_to_microjson.py +++ b/examples/df_to_microjson.py @@ -52,7 +52,7 @@ def df_to_microjson(df: pd.DataFrame) -> mj.FeatureCollection: } # Create a list of descriptive fields - descriptive_fields = ['name'] + string_fields = ['name'] # Create a new FeatureCollection object feature_collection = mj.MicroFeatureCollection(