{"id":29721153,"url":"https://github.com/folt/marshmallow-geojson","last_synced_at":"2026-03-11T21:03:06.745Z","repository":{"id":38205333,"uuid":"297289262","full_name":"folt/marshmallow-geojson","owner":"folt","description":"Marshmallow schema validation for GeoJson","archived":false,"fork":false,"pushed_at":"2026-03-01T12:05:32.000Z","size":546,"stargazers_count":16,"open_issues_count":2,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-03-01T14:38:02.784Z","etag":null,"topics":["geo","geojson","gis","json","json-schema","linux","marshmallow","openapi","python","python3","rest","swagger"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/folt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-09-21T09:19:28.000Z","updated_at":"2026-03-01T11:58:16.000Z","dependencies_parsed_at":"2024-02-02T21:22:31.299Z","dependency_job_id":"0c18954a-6da4-494c-88a9-af8ded319f4d","html_url":"https://github.com/folt/marshmallow-geojson","commit_stats":{"total_commits":68,"total_committers":4,"mean_commits":17.0,"dds":"0.38235294117647056","last_synced_commit":"aef6b02974fe7d53aca0a299517bda29ad92ab37"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/folt/marshmallow-geojson","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/folt%2Fmarshmallow-geojson","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/folt%2Fmarshmallow-geojson/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/folt%2Fmarshmallow-geojson/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/folt%2Fmarshmallow-geojson/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/folt","download_url":"https://codeload.github.com/folt/marshmallow-geojson/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/folt%2Fmarshmallow-geojson/sbom","scorecard":{"id":406226,"data":{"date":"2025-08-11","repo":{"name":"github.com/folt/marshmallow-geojson","commit":"f1262990d33a4262dcc55af265a20d8fb1223c65"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4,"checks":[{"name":"Code-Review","score":2,"reason":"Found 1/4 approved changesets -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/codecov.yml:1","Warn: no topLevel permission defined: .github/workflows/main.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codecov.yml:11: update your workflow using https://app.stepsecurity.io/secureworkflow/folt/marshmallow-geojson/codecov.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codecov.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/folt/marshmallow-geojson/codecov.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codecov.yml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/folt/marshmallow-geojson/codecov.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/codecov.yml:44: update your workflow using https://app.stepsecurity.io/secureworkflow/folt/marshmallow-geojson/codecov.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/folt/marshmallow-geojson/main.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/folt/marshmallow-geojson/main.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:37: update your workflow using https://app.stepsecurity.io/secureworkflow/folt/marshmallow-geojson/main.yml/master?enable=pin","Warn: downloadThenRun not pinned by hash: .github/workflows/codecov.yml:20","Warn: downloadThenRun not pinned by hash: .github/workflows/main.yml:31","Info:   0 out of   6 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned","Info:   0 out of   2 downloadThenRun dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-18T21:19:35.467Z","repository_id":38205333,"created_at":"2025-08-18T21:19:35.467Z","updated_at":"2025-08-18T21:19:35.467Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30400682,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-11T21:02:20.017Z","status":"ssl_error","status_checked_at":"2026-03-11T20:59:32.667Z","response_time":84,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["geo","geojson","gis","json","json-schema","linux","marshmallow","openapi","python","python3","rest","swagger"],"created_at":"2025-07-24T14:19:09.021Z","updated_at":"2026-03-11T21:03:06.738Z","avatar_url":"https://github.com/folt.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![GitHub Actions status for master branch](https://github.com/folt/marshmallow-geojson/actions/workflows/main.yml/badge.svg)](https://github.com/folt/marshmallow-geojson/actions?query=workflow%3A%22Python+package%22)\n[![Latest PyPI package version](https://badge.fury.io/py/marshmallow-geojson.svg)](https://pypi.org/project/marshmallow-geojson/)\n[![Codecov](https://codecov.io/gh/folt/marshmallow-geojson/branch/master/graph/badge.svg?token=B5ATYXLBHO)](https://codecov.io/gh/folt/marshmallow-geojson)\n[![Downloads](https://pepy.tech/badge/marshmallow-geojson)](https://pepy.tech/project/marshmallow-geojson)\n\n# marshmallow-geojson 🌍\n\nA schema-based, Marshmallow library for validating and working with [GeoJSON](http://geojson.org/) data according to [RFC 7946](https://tools.ietf.org/html/rfc7946) specification.\n\n## Supported GeoJSON Objects\n\n| GeoJSON Objects        | Status | Description                          |\n|------------------------|--------|--------------------------------------|\n| Point                  | ✅      | A single geographic coordinate       |\n| MultiPoint             | ✅      | Multiple points                       |\n| LineString             | ✅      | A sequence of connected points forming a line |\n| MultiLineString        | ✅      | Multiple line strings                 |\n| Polygon                | ✅      | A closed area, optionally with holes |\n| MultiPolygon           | ✅      | Multiple polygons                     |\n| GeometryCollection     | ✅      | Collection of different geometry types |\n| Feature                | ✅      | Geometry with properties              |\n| FeatureCollection      | ✅      | Collection of features               |\n| Bbox                   | ✅      | Bounding box validation               |\n\n## Installation\n\nmarshmallow-geojson is compatible with Python 3.10 and up.\n\nThe recommended way to install is via [poetry](https://python-poetry.org/):\n\n```shell\npoetry add marshmallow_geojson\n```\n\nUsing pip to install is also possible:\n\n```shell\npip install marshmallow-geojson\n```\n\n## Quick Start\n\n```python\nfrom marshmallow_geojson import GeoJSONSchema\n\n# Create a schema instance\nschema = GeoJSONSchema()\n\n# Load GeoJSON from JSON string\ngeojson_text = '{\"type\": \"Point\", \"coordinates\": [-105.01621, 39.57422]}'\ndata = schema.loads(geojson_text)\nprint(data)\n# {'type': 'Point', 'coordinates': [-105.01621, 39.57422]}\n\n# Load GeoJSON from dictionary\ngeojson_dict = {\"type\": \"Point\", \"coordinates\": [-105.01621, 39.57422]}\ndata = schema.load(geojson_dict)\nprint(data)\n# {'type': 'Point', 'coordinates': [-105.01621, 39.57422]}\n\n# Dump GeoJSON to JSON string\njson_str = schema.dumps(geojson_dict)\nprint(json_str)\n# '{\"type\": \"Point\", \"coordinates\": [-105.01621, 39.57422]}'\n```\n\n## GeoJSON Types with Visualizations\n\n\u003ctable style=\"width: 100%;\"\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth style=\"width: 20%;\"\u003eType\u003c/th\u003e\n\u003cth style=\"width: 20%; text-align: center;\"\u003eVisualization\u003c/th\u003e\n\u003cth style=\"width: 60%;\"\u003eUsage\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003ePoint\u003c/strong\u003e\u003cbr\u003eA single geographic coordinate\u003c/td\u003e\n\u003ctd style=\"text-align: center;\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/folt/marshmallow-geojson/master/assets/point.jpg\" alt=\"Point\" width=\"150\"\u003e\u003c/td\u003e\n\u003ctd\u003e\n\n```python\nfrom marshmallow_geojson import GeoJSONSchema\n\ndata = {\n    \"type\": \"Point\",\n    \"coordinates\": [-105.01621, 39.57422]\n}\nschema = GeoJSONSchema()\npoint = schema.load(data)\n\n# Or with altitude\ndata_3d = {\n    \"type\": \"Point\",\n    \"coordinates\": [-105.01621, 39.57422, 100.5]\n}\npoint_3d = schema.load(data_3d)\n\n# From JSON string\njson_str = '{\"type\": \"Point\", \"coordinates\": [-105.01621, 39.57422]}'\npoint = schema.loads(json_str)\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eMultiPoint\u003c/strong\u003e\u003cbr\u003eMultiple points\u003c/td\u003e\n\u003ctd style=\"text-align: center;\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/folt/marshmallow-geojson/master/assets/multi_point.jpg\" alt=\"MultiPoint\" width=\"150\"\u003e\u003c/td\u003e\n\u003ctd\u003e\n\n```python\nfrom marshmallow_geojson import GeoJSONSchema\n\ndata = {\n    \"type\": \"MultiPoint\",\n    \"coordinates\": [\n        [-105.01621, 39.57422],\n        [-80.666513, 35.053994]\n    ]\n}\nschema = GeoJSONSchema()\nmulti_point = schema.load(data)\n\n# Dump to JSON\njson_output = schema.dumps(multi_point)\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eLineString\u003c/strong\u003e\u003cbr\u003eA sequence of connected points forming a line\u003c/td\u003e\n\u003ctd style=\"text-align: center;\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/folt/marshmallow-geojson/master/assets/line_string.jpg\" alt=\"LineString\" width=\"150\"\u003e\u003c/td\u003e\n\u003ctd\u003e\n\n```python\nfrom marshmallow_geojson import GeoJSONSchema\n\ndata = {\n    \"type\": \"LineString\",\n    \"coordinates\": [\n        [-99.113159, 38.869651],\n        [-99.0802, 38.85682],\n        [-98.822021, 38.85682],\n        [-98.448486, 38.848264]\n    ]\n}\nschema = GeoJSONSchema()\nline_string = schema.load(data)\n\n# Minimal LineString (2 points)\nminimal = {\n    \"type\": \"LineString\",\n    \"coordinates\": [[-99.113159, 38.869651], [-99.0802, 38.85682]]\n}\nline = schema.load(minimal)\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eMultiLineString\u003c/strong\u003e\u003cbr\u003eMultiple line strings\u003c/td\u003e\n\u003ctd style=\"text-align: center;\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/folt/marshmallow-geojson/master/assets/multi_line_string.jpg\" alt=\"MultiLineString\" width=\"150\"\u003e\u003c/td\u003e\n\u003ctd\u003e\n\n```python\nfrom marshmallow_geojson import GeoJSONSchema\n\ndata = {\n    \"type\": \"MultiLineString\",\n    \"coordinates\": [\n        [[-105.019898, 39.574997],\n         [-105.019598, 39.574898],\n         [-105.019061, 39.574782]],\n        [[-105.017173, 39.574402],\n         [-105.01698, 39.574385],\n         [-105.016636, 39.574385]]\n    ]\n}\nschema = GeoJSONSchema()\nmulti_line_string = schema.load(data)\n\n# With bbox\ndata_with_bbox = {\n    \"type\": \"MultiLineString\",\n    \"bbox\": [-180.0, -90.0, 180.0, 90.0],\n    \"coordinates\": [[[-105.019898, 39.574997], [-105.019598, 39.574898]]]\n}\nresult = schema.load(data_with_bbox)\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003ePolygon\u003c/strong\u003e\u003cbr\u003eA closed area, optionally with holes\u003c/td\u003e\n\u003ctd style=\"text-align: center;\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/folt/marshmallow-geojson/master/assets/polygon.jpg\" alt=\"Polygon\" width=\"150\"\u003e\u003c/td\u003e\n\u003ctd\u003e\n\n```python\nfrom marshmallow_geojson import GeoJSONSchema\n\n# Simple polygon\ndata = {\n    \"type\": \"Polygon\",\n    \"coordinates\": [\n        [[100, 0],\n         [101, 0],\n         [101, 1],\n         [100, 1],\n         [100, 0]]\n    ]\n}\nschema = GeoJSONSchema()\npolygon = schema.load(data)\n\n# Polygon with holes (interior rings)\ndata_with_holes = {\n    \"type\": \"Polygon\",\n    \"coordinates\": [\n        [[100, 0], [101, 0], [101, 1], [100, 1], [100, 0]],  # Exterior\n        [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]  # Hole\n    ]\n}\npolygon_with_holes = schema.load(data_with_holes)\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eMultiPolygon\u003c/strong\u003e\u003cbr\u003eMultiple polygons\u003c/td\u003e\n\u003ctd style=\"text-align: center;\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/folt/marshmallow-geojson/master/assets/multi_polygon.jpg\" alt=\"MultiPolygon\" width=\"150\"\u003e\u003c/td\u003e\n\u003ctd\u003e\n\n```python\nfrom marshmallow_geojson import GeoJSONSchema\n\ndata = {\n    \"type\": \"MultiPolygon\",\n    \"coordinates\": [\n        [[[107, 7], [108, 7],\n          [108, 8], [107, 8],\n          [107, 7]]],\n        [[[100, 0], [101, 0],\n          [101, 1], [100, 1],\n          [100, 0]]]\n    ]\n}\nschema = GeoJSONSchema()\nmulti_polygon = schema.load(data)\n\n# Serialize back to dict\nserialized = schema.dump(multi_polygon)\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eGeometryCollection\u003c/strong\u003e\u003cbr\u003eCollection of different geometry types\u003c/td\u003e\n\u003ctd style=\"text-align: center;\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/folt/marshmallow-geojson/master/assets/geometry_collection.jpg\" alt=\"GeometryCollection\" width=\"150\"\u003e\u003c/td\u003e\n\u003ctd\u003e\n\n```python\nfrom marshmallow_geojson import GeoJSONSchema\n\ndata = {\n    \"type\": \"GeometryCollection\",\n    \"geometries\": [\n        {\n            \"type\": \"Point\",\n            \"coordinates\": [-80.660805, 35.049392]\n        },\n        {\n            \"type\": \"Polygon\",\n            \"coordinates\": [[[-80.664582, 35.044965],\n                             [-80.663874, 35.04428],\n                             [-80.662586, 35.04558],\n                             [-80.663444, 35.046036],\n                             [-80.664582, 35.044965]]]\n        },\n        {\n            \"type\": \"LineString\",\n            \"coordinates\": [[-80.662372, 35.059509],\n                            [-80.662693, 35.059263],\n                            [-80.662844, 35.05893]]\n        }\n    ]\n}\nschema = GeoJSONSchema()\ngeometry_collection = schema.load(data)\n\n# Empty GeometryCollection\nempty = {\n    \"type\": \"GeometryCollection\",\n    \"geometries\": []\n}\nempty_collection = schema.load(empty)\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\n## Features and FeatureCollections\n\n### Feature\n\nA geometry with properties.\n\n```python\nfrom marshmallow_geojson import GeoJSONSchema\n\n# Basic Feature\ndata = {\n    \"type\": \"Feature\",\n    \"properties\": {\n        \"name\": \"Dinagat Islands\",\n        \"population\": 10000\n    },\n    \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n            [\n                [-80.724878, 35.265454],\n                [-80.722646, 35.260338],\n                [-80.720329, 35.260618],\n                [-80.71681, 35.255361],\n                [-80.704793, 35.268397],\n                [-80.724878, 35.265454]\n            ]\n        ]\n    }\n}\nschema = GeoJSONSchema()\nfeature = schema.load(data)\n\n# Feature with Point geometry\npoint_feature = {\n    \"type\": \"Feature\",\n    \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [-74.006, 40.7128]\n    },\n    \"properties\": {\n        \"name\": \"New York\",\n        \"population\": 8336817\n    }\n}\nfeature = schema.load(point_feature)\n\n# Feature with null geometry (unlocated feature)\nnull_geometry_feature = {\n    \"type\": \"Feature\",\n    \"geometry\": None,\n    \"properties\": {\n        \"name\": \"Unknown Location\"\n    }\n}\nfeature = schema.load(null_geometry_feature)\n\n# Feature with ID\nfeature_with_id = {\n    \"type\": \"Feature\",\n    \"id\": \"feature-123\",\n    \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [-105.01621, 39.57422]\n    },\n    \"properties\": {\n        \"name\": \"Test Feature\"\n    }\n}\nfeature = schema.load(feature_with_id)\n\n# Feature with bbox\nfeature_with_bbox = {\n    \"type\": \"Feature\",\n    \"bbox\": [-180.0, -90.0, 180.0, 90.0],\n    \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [-105.01621, 39.57422]\n    },\n    \"properties\": {}\n}\nfeature = schema.load(feature_with_bbox)\n```\n\n### FeatureCollection\n\nA collection of features.\n\n```python\nfrom marshmallow_geojson import GeoJSONSchema\n\n# Basic FeatureCollection\ndata = {\n    \"type\": \"FeatureCollection\",\n    \"features\": [\n        {\n            \"type\": \"Feature\",\n            \"geometry\": {\n                \"type\": \"Point\",\n                \"coordinates\": [-80.870885, 35.215151]\n            },\n            \"properties\": {\n                \"name\": \"Location 1\"\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-80.724878, 35.265454],\n                        [-80.722646, 35.260338],\n                        [-80.720329, 35.260618],\n                        [-80.704793, 35.268397],\n                        [-80.724878, 35.265454]\n                    ]\n                ]\n            },\n            \"properties\": {\n                \"name\": \"Location 2\"\n            }\n        }\n    ]\n}\nschema = GeoJSONSchema()\nfeature_collection = schema.load(data)\n\n# Empty FeatureCollection\nempty_fc = {\n    \"type\": \"FeatureCollection\",\n    \"features\": []\n}\nempty_collection = schema.load(empty_fc)\n\n# FeatureCollection with mixed geometry types\nmixed_fc = {\n    \"type\": \"FeatureCollection\",\n    \"features\": [\n        {\n            \"type\": \"Feature\",\n            \"geometry\": {\"type\": \"Point\", \"coordinates\": [-105.01621, 39.57422]},\n            \"properties\": {\"type\": \"point\"}\n        },\n        {\n            \"type\": \"Feature\",\n            \"geometry\": {\n                \"type\": \"LineString\",\n                \"coordinates\": [[-99.113159, 38.869651], [-99.0802, 38.85682]]\n            },\n            \"properties\": {\"type\": \"line\"}\n        },\n        {\n            \"type\": \"Feature\",\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [[[100, 0], [101, 0], [101, 1], [100, 1], [100, 0]]]\n            },\n            \"properties\": {\"type\": \"polygon\"}\n        }\n    ]\n}\nmixed_collection = schema.load(mixed_fc)\n\n# FeatureCollection with bbox\nfc_with_bbox = {\n    \"type\": \"FeatureCollection\",\n    \"bbox\": [-180.0, -90.0, 180.0, 90.0],\n    \"features\": [\n        {\n            \"type\": \"Feature\",\n            \"geometry\": {\"type\": \"Point\", \"coordinates\": [-105.01621, 39.57422]},\n            \"properties\": {}\n        }\n    ]\n}\ncollection = schema.load(fc_with_bbox)\n\n# Serialize FeatureCollection to JSON\njson_output = schema.dumps(feature_collection)\n```\n\n## Universal GeoJSONSchema\n\nmarshmallow-geojson provides a universal `GeoJSONSchema` that automatically handles all GeoJSON object types. This is a unique feature not available in other GeoJSON libraries.\n\n```python\nfrom marshmallow_geojson import GeoJSONSchema\n\nschema = GeoJSONSchema()\n\n# Automatically handles Point\npoint_data = {\"type\": \"Point\", \"coordinates\": [-105.01621, 39.57422]}\npoint = schema.load(point_data)\n\n# Automatically handles Feature\nfeature_data = {\n    \"type\": \"Feature\",\n    \"geometry\": {\"type\": \"Point\", \"coordinates\": [-105.01621, 39.57422]},\n    \"properties\": {\"name\": \"Test\"}\n}\nfeature = schema.load(feature_data)\n\n# Automatically handles FeatureCollection\nfc_data = {\n    \"type\": \"FeatureCollection\",\n    \"features\": [feature_data]\n}\nfeature_collection = schema.load(fc_data)\n\n# Works with mixed types in many=True mode\nschema_many = GeoJSONSchema(many=True)\nmixed_data = [point_data, feature_data, fc_data]\nresults = schema_many.load(mixed_data)\n\n# Load from JSON string - automatically detects type\njson_point = '{\"type\": \"Point\", \"coordinates\": [-105.01621, 39.57422]}'\npoint = schema.loads(json_point)\n\njson_feature = '{\"type\": \"Feature\", \"geometry\": {\"type\": \"Point\", \"coordinates\": [-105.01621, 39.57422]}, \"properties\": {}}'\nfeature = schema.loads(json_feature)\n\n# Get specific schema for a type\npoint_schema_class = schema.get_schema(\"Point\")\nfeature_schema_class = schema.get_schema(\"Feature\")\n\n# Handle multiple objects with many=True\njson_array = '[{\"type\": \"Point\", \"coordinates\": [-105.01621, 39.57422]}, {\"type\": \"LineString\", \"coordinates\": [[-99.113159, 38.869651], [-99.0802, 38.85682]]}]'\nschema_many = GeoJSONSchema(many=True)\nresults = schema_many.loads(json_array)\n# Returns list of validated objects\n```\n\n## GeometriesSchema\n\nFor working with geometry objects only (excluding Feature and FeatureCollection), use `GeometriesSchema`:\n\n```python\nfrom marshmallow_geojson import GeometriesSchema\n\nschema = GeometriesSchema()\n\n# Works with all geometry types\npoint_data = {\"type\": \"Point\", \"coordinates\": [-105.01621, 39.57422]}\npoint = schema.load(point_data)\n\npolygon_data = {\"type\": \"Polygon\", \"coordinates\": [[[100, 0], [101, 0], [101, 1], [100, 1], [100, 0]]]}\npolygon = schema.load(polygon_data)\n\n# Rejects Feature and FeatureCollection\n# schema.load({\"type\": \"Feature\", ...})  # Raises ValidationError\n```\n\n## Custom Properties Schemas\n\nYou can define typed properties schemas for type-safe feature properties:\n\n```python\nfrom marshmallow.fields import Str, Int, Nested\nfrom marshmallow_geojson import GeoJSONSchema, PropertiesSchema, FeatureSchema\n\nclass CityPropertiesSchema(PropertiesSchema):\n    name = Str(required=True)\n    population = Int(required=True)\n    country = Str(required=True)\n\nclass CityFeatureSchema(FeatureSchema):\n    properties = Nested(\n        CityPropertiesSchema,\n        required=True,\n    )\n\nclass CityGeoJSONSchema(GeoJSONSchema):\n    feature_schema = CityFeatureSchema\n\n# Usage\nschema = CityGeoJSONSchema()\ndata = {\n    \"type\": \"Feature\",\n    \"properties\": {\n        \"name\": \"New York\",\n        \"population\": 8336817,\n        \"country\": \"USA\"\n    },\n    \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [-74.006, 40.7128]\n    }\n}\ncity = schema.load(data)\nprint(city['properties']['name'])  # \"New York\"\nprint(city['properties']['population'])  # 8336817\n```\n\n## Marshmallow-Specific Features\n\nmarshmallow-geojson supports all standard Marshmallow schema features:\n\n### Field Filtering (only/exclude)\n\n```python\nfrom marshmallow_geojson import GeoJSONSchema\n\n# Include only specific fields\nschema = GeoJSONSchema(only=('type', 'geometry'))\ndata = schema.load(feature_data)\n# Only 'type' and 'geometry' fields are included\n\n# Exclude specific fields\nschema = GeoJSONSchema(exclude=('properties',))\ndata = schema.load(feature_data)\n# 'properties' field is excluded\n```\n\n### Partial Loading\n\n```python\nfrom marshmallow_geojson import GeoJSONSchema\n\nschema = GeoJSONSchema(partial=True)\n# Allows partial data loading\npartial_data = {\n    \"type\": \"Feature\",\n    \"geometry\": {\"type\": \"Point\", \"coordinates\": [-105.01621, 39.57422]}\n}\nresult = schema.load(partial_data, partial=('properties',))\n```\n\n### Unknown Field Handling\n\n```python\nfrom marshmallow_geojson import GeoJSONSchema\n\n# Exclude unknown fields\nschema = GeoJSONSchema(unknown='exclude')\ndata_with_extra = {\n    \"type\": \"Point\",\n    \"coordinates\": [-105.01621, 39.57422],\n    \"extra_field\": \"extra_value\"\n}\nresult = schema.load(data_with_extra)\n# 'extra_field' is automatically excluded\n\n# Raise error on unknown fields\nschema = GeoJSONSchema(unknown='raise')\n# schema.load(data_with_extra)  # Raises ValidationError\n```\n\n### Many Mode\n\nHandle multiple GeoJSON objects at once:\n\n```python\nfrom marshmallow_geojson import GeoJSONSchema\n\nschema = GeoJSONSchema(many=True)\n\n# Load multiple objects\ndata_list = [\n    {\"type\": \"Point\", \"coordinates\": [-105.01621, 39.57422]},\n    {\"type\": \"LineString\", \"coordinates\": [[-99.113159, 38.869651], [-99.0802, 38.85682]]},\n    {\"type\": \"Feature\", \"geometry\": {\"type\": \"Point\", \"coordinates\": [-80.870885, 35.215151]}, \"properties\": {}}\n]\nresults = schema.load(data_list)\n\n# Works with JSON strings too\njson_str = '[{\"type\": \"Point\", \"coordinates\": [-105.01621, 39.57422]}]'\nresults = schema.loads(json_str)\n```\n\n## Validation\n\nmarshmallow-geojson automatically validates:\n\n- **Coordinate ranges**: Longitude must be between -180 and 180, latitude between -90 and 90\n- **Geometry types**: Ensures correct type strings according to RFC 7946\n- **Structure**: Validates GeoJSON object structure and required fields\n- **Linear Rings**: Polygon rings must have at least 4 positions and be closed\n- **LineString**: Must have at least 2 positions\n- **Bounding Box**: Validates bbox structure (2, 4, or 6 elements) and coordinate order\n- **Type mixing**: Prevents forbidden members according to RFC 7946 Section 7.1\n\n### Example: Invalid Coordinates\n\n```python\nfrom marshmallow_geojson import GeoJSONSchema\nfrom marshmallow.exceptions import ValidationError\n\nschema = GeoJSONSchema()\ntry:\n    data = {\"type\": \"Point\", \"coordinates\": [200, 50]}  # Invalid longitude (\u003e 180)\n    schema.load(data)\nexcept ValidationError as e:\n    print(e.messages)\n    # {'coordinates': ['Longitude must be between -180, 180']}\n```\n\n### Example: Invalid Polygon\n\n```python\nfrom marshmallow_geojson import GeoJSONSchema\nfrom marshmallow.exceptions import ValidationError\n\nschema = GeoJSONSchema()\ntry:\n    # Polygon ring must have at least 4 positions and be closed\n    data = {\n        \"type\": \"Polygon\",\n        \"coordinates\": [[[100, 0], [101, 0], [100, 0]]]  # Only 3 positions, not closed\n    }\n    schema.load(data)\nexcept ValidationError as e:\n    print(e.messages)\n    # {'coordinates': ['Linear Ring must have at least 4 positions']}\n```\n\n## Bounding Box Validation\n\nmarshmallow-geojson includes comprehensive bounding box validation:\n\n```python\nfrom marshmallow.fields import Float, List\nfrom marshmallow_geojson import PointSchema\nfrom marshmallow_geojson.validate import Bbox\n\nclass PointWithBboxSchema(PointSchema):\n    bbox = List(\n        Float(),\n        required=False,\n        allow_none=True,\n        validate=Bbox()\n    )\n\nschema = PointWithBboxSchema()\n\n# Valid 2D bbox\ndata = {\n    \"type\": \"Point\",\n    \"coordinates\": [-105.01621, 39.57422],\n    \"bbox\": [-180.0, -90.0, 180.0, 90.0]\n}\nresult = schema.load(data)\n\n# Valid 3D bbox\ndata = {\n    \"type\": \"Point\",\n    \"coordinates\": [-105.01621, 39.57422],\n    \"bbox\": [-180.0, -90.0, -100.0, 180.0, 90.0, 100.0]\n}\nresult = schema.load(data)\n```\n\n## Flask Integration\n\nmarshmallow-geojson works seamlessly with Flask for building GeoJSON APIs:\n\n```python\nfrom flask import Flask, request, jsonify\nfrom marshmallow_geojson import GeoJSONSchema\nfrom marshmallow.exceptions import ValidationError\n\napp = Flask(__name__)\nschema = GeoJSONSchema()\n\n@app.route('/geojson', methods=['POST'])\ndef create_geojson():\n    try:\n        data = schema.loads(request.data)\n        # Your business logic here\n        return jsonify(schema.dump(data)), 201\n    except ValidationError as e:\n        return jsonify({'errors': e.messages}), 400\n\n@app.route('/geojson/many', methods=['POST'])\ndef create_geojson_many():\n    schema_many = GeoJSONSchema(many=True)\n    try:\n        data = schema_many.loads(request.data)\n        return jsonify(schema_many.dump(data)), 201\n    except ValidationError as e:\n        return jsonify({'errors': e.messages}), 400\n```\n\n## Compatibility with Other Libraries\n\nmarshmallow-geojson is designed to work well with popular Python libraries:\n\n### Marshmallow\n\nBuilt on Marshmallow, so you get all the benefits:\n\n- Schema-based validation\n- JSON serialization/deserialization\n- Field filtering and partial loading\n- Custom validators\n- Integration with Flask, FastAPI, and other frameworks\n\n### GeoPandas\n\nYou can convert between marshmallow-geojson and GeoPandas:\n\n```python\nimport geopandas as gpd\nfrom marshmallow_geojson import GeoJSONSchema\n\n# Convert FeatureCollection to GeoDataFrame\nschema = GeoJSONSchema()\nfeature_collection = schema.load(fc_data)\ngeojson_dict = schema.dump(feature_collection)\ngdf = gpd.GeoDataFrame.from_features(geojson_dict[\"features\"])\n\n# Convert GeoDataFrame to FeatureCollection\nfeatures = gdf.to_dict(\"records\")\nfc_data = {\"type\": \"FeatureCollection\", \"features\": features}\nfeature_collection = schema.load(fc_data)\n```\n\n### Shapely\n\nConvert to/from Shapely geometries:\n\n```python\nfrom shapely.geometry import Point as ShapelyPoint\nfrom marshmallow_geojson import GeoJSONSchema\n\nschema = GeoJSONSchema()\n\n# Marshmallow GeoJSON to Shapely\npoint_data = {\"type\": \"Point\", \"coordinates\": [-105.01621, 39.57422]}\npoint = schema.load(point_data)\nshapely_point = ShapelyPoint(point['coordinates'][0], point['coordinates'][1])\n\n# Shapely to Marshmallow GeoJSON\nshapely_geom = ShapelyPoint(-105.01621, 39.57422)\npoint_data = {\n    \"type\": \"Point\",\n    \"coordinates\": [shapely_geom.x, shapely_geom.y]\n}\npoint = schema.load(point_data)\n```\n\n## Testing\n\nRun the test suite:\n\n```shell\npoetry run pytest\n```\n\n## Contributing\n\nContributions are welcome! Please see [CONTRIBUTING.md](https://github.com/folt/marshmallow-geojson/blob/master/CONTRIBUTING.md) for guidelines.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](https://github.com/folt/marshmallow-geojson/blob/master/LICENSE) file for details.\n\n## Links\n\n- [GitHub Repository](https://github.com/folt/marshmallow-geojson)\n- [PyPI Package](https://pypi.org/project/marshmallow-geojson/)\n- [GeoJSON Specification](http://geojson.org/)\n- [RFC 7946](https://tools.ietf.org/html/rfc7946)\n- [Marshmallow Documentation](https://marshmallow.readthedocs.io/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffolt%2Fmarshmallow-geojson","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffolt%2Fmarshmallow-geojson","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffolt%2Fmarshmallow-geojson/lists"}