esy-osm-shape


Nameesy-osm-shape JSON
Version 0.4 PyPI version JSON
download
home_pageNone
SummaryConvert OpenStreetMap primitives to shapely objects
upload_time2025-08-14 09:36:31
maintainerNone
docs_urlNone
authorNone
requires_python>=3.5
licenseBSD-3-Clause
keywords osm openstreetmap shapely
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            `esy.osm.shape` is a Python library to convert
[OpenStreetMap](https://www.openstreetmap.org) primitives to
[shapely](https://shapely.readthedocs.io/en/latest/) objects.

![Andorra OpenStreetMap dataset](https://gitlab.com/dlr-ve-esy/esy-osm-shape/-/raw/main/figures/andorra.png)

The [drawing maps](#drawing-maps) example shows, how this figure is constructed.

# Features

What it provides:

- Functionality to generate shapely objects from OpenStreetMap `.pbf` data
  file entries.
- Basic plotting tools for matplotlib.

In addition to the [shapely](https://shapely.readthedocs.io/) geometry, the
OpenStreetMap `id` and `tags` properties are generated as provided by
`esy.osm.pbf`.

What it *doesn't* provide:

- A mechanism to spatially query OpenStreetMap entries.
- Handling of non-geometry objects (like many relations).

# Installation

`esy.osm.shape` depends on a Python version of 3.8 or above as well as on
`esy.osm.pbf`. Use `pip` to install `esy.osm.shape`:

```sh
$ pip install esy-osm-shape
```

# Usage

An `esy.osm.shape.Shape` object requires a filename of an OpenStreetMap
protocol buffers file or an instance of `esy.osm.pbf.File` as argument on
construction. Upon being called, the `esy.osm.shape.Shape` returns generator
which yields a tuple consisting of the
[shapely](https://shapely.readthedocs.io/) geometry and both the OpenStreetMap
`id` and `tags`. There are two optional arguments:

- `filter`: can be used to select OpenStreetMap primitives for which to generate
  [shapely](https://shapely.readthedocs.io/) objects. By default, every
  OpenStreetMap primitive is selected.
- `max_tasks`: can be used to trade-off memory versus speed. If this value
  increases, more memory is used to cache OpenStreetMap entries, thereby
  reducing the number of file read operations.

# Examples

The following examples operate on a historic dataset for Andorra from
[geofabrik](https://www.geofabrik.de/). Let's download the dataset first:

```python
>>> import os, urllib.request
>>> if not os.path.exists('andorra.osm.pbf'):
...     filename, headers = urllib.request.urlretrieve(
...         'https://download.geofabrik.de/europe/andorra-190101.osm.pbf',
...         filename='andorra.osm.pbf'
...     )

```

## Construct geometries

Open the file and generate linestrings for each `highway` OpenStreetMap entry.

```python
>>> import shapely, esy.osm.shape
>>> shape = esy.osm.shape.Shape('andorra.osm.pbf')
>>> highways = [
...     shape for shape, id, tags in shape(lambda e: e.tags.get('highway'))
...     if type(shape) is shapely.geometry.LineString
... ]
>>> len(highways)
3948

```

Shapely objects also expose geometric properties, like for example the length or
area. However, OpenStreetMap uses geodetic coordinates and shapely assumes
planar coordinates, rendering most geometry properties invalid.

## Geodetic computations

[pyproj](https://pyproj4.github.io/pyproj/) offers geodetic computations and
can be used to estimate area and length properties of sphere geometries
accurately:

```python
>>> import pyproj
>>> geod = pyproj.Geod(ellps='WGS84')
>>> round(geod.geometry_length(shapely.MultiLineString(highways)))
1577598

```

## Drawing maps

`esy.osm.shape` provides functionality to convert shapely objects into
[matplotlib](https://matplotlib.org/) objects, which enables simple map
rendering:

```python
>>> import matplotlib.pyplot as plt, esy.osm.shape.mpl
>>> fig, ax = plt.subplots(1, 1)
>>> _ = ax.set(
...     title='andorra-190101.osm.pbf', aspect='equal',
...     xlabel='lon', xlim=(1.40, 1.75), ylabel='lat', ylim=(42.40, 42.70)
... )
>>> iax = ax.inset_axes(
...     [0.61, 0.02, 0.4, 0.4], xlim=(1.525, 1.535), ylim=(42.504, 42.514),
...     aspect='equal', xticks=[], yticks=[]
... )
>>> style = esy.osm.shape.mpl.simple_style
>>> items = tuple(shape(esy.osm.shape.mpl.filter(style)))
>>> _ = ax.add_artist(esy.osm.shape.mpl.render_map(style, items))
>>> _ = iax.add_artist(esy.osm.shape.mpl.render_map(style, items))
>>> _ = ax.indicate_inset_zoom(iax, edgecolor='black')
>>> os.makedirs('figures/', exist_ok=True)
>>> fig.savefig('figures/andorra.png')

```

## Invalid entries

Some OpenStreetMap entries might be broken shapes or logical groupings not
representable as shapes. These entries are mostly relations and generate
`Invalid` objects. `Invalid` objects provide four properties which give a
description of the exception that happened during processing:

- `entry`: The invalid OpenStreetMap entry.
- `exc_type`: The type of the exception.
- `exc_args`: The arguments of the exception.
- `exc_description`: The traceback text of the exception.

The following example reads every OpenStreetMap entry from the Andorra dataset
and collects all invalid ones into a dictionary:

```python
>>> count, invalid = 0, {}
>>> for shape, id, tags in shape():
...     count += 1
...     if type(shape) is esy.osm.shape.Invalid:
...         invalid[id] = shape
>>> count, len(invalid)
(217238, 186)

```

About 0.1% of the Andorra dataset entries cannot be handled by
`esy.osm.shape`. Some of these entries are broken:

```python
>>> print(invalid[8473237]) #doctest: +ELLIPSIS
Invalid Relation (id=8473237)
  Traceback (most recent call last):
    ...
    File "...shape.py", line ..., in multipolygon_shape
      raise ValueError('Invalid segments')
  ValueError: Invalid segments

```

Unrepresentable OpenStreetMap entries are usually logical relations, like
[routing information](https://wiki.openstreetmap.org/wiki/Relation:route).
These are reported as `Invalid` objects with an `exc_type` of
`NotImplementedError`. For example:

```python
>>> print(invalid[6745201]) #doctest: +ELLIPSIS
Invalid Relation (id=6745201)
  Traceback (most recent call last):
    File "...shape.py", line ..., in unsupported
      raise NotImplementedError(description)
  NotImplementedError: Relation (id=6745201)

```

# License

`esy.osm.shape` is published under the
[BSD-3-Clause](https://spdx.org/licenses/BSD-3-Clause.html) license.

# Design, Development & Contributing

Design and development notes are available in `esy.osm.shape.test`.

We would be happy to accept contributions via merge requests, but due to
corporate policy we can only accept contributions if you have send us the signed
[contributor license agreement](CLA.md).

# Contact

Please use the projects issue tracker to get in touch.

# Team

`esy.osm.shape` is developed by the
[DLR](https://www.dlr.de/EN/Home/home_node.html) Institute of
[Networked Energy Systems](https://www.dlr.de/ve/en/desktopdefault.aspx/tabid-12472/21440_read-49440/)
in the departement for
[Energy Systems Analysis (ESY)](https://www.dlr.de/ve/en/desktopdefault.aspx/tabid-12471/21741_read-49802/).

# Acknowledgements

The authors would like to thank the Federal Government and the Heads of
Government of the Länder, as well as the Joint Science Conference (GWK), for
their funding and support within the framework of the NFDI4Ing consortium.
Funded by the German Research Foundation (DFG) - project number 442146713.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "esy-osm-shape",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.5",
    "maintainer_email": null,
    "keywords": "OSM, OpenStreetMap, shapely",
    "author": null,
    "author_email": "Ontje L\u00fcnsdorf <ontje.luensdorf@dlr.de>",
    "download_url": "https://files.pythonhosted.org/packages/d3/66/72f4fde77bd1c7402e42a3ea2a3546dac310699cfc1a8afd108a2241245a/esy_osm_shape-0.4.tar.gz",
    "platform": null,
    "description": "`esy.osm.shape` is a Python library to convert\n[OpenStreetMap](https://www.openstreetmap.org) primitives to\n[shapely](https://shapely.readthedocs.io/en/latest/) objects.\n\n![Andorra OpenStreetMap dataset](https://gitlab.com/dlr-ve-esy/esy-osm-shape/-/raw/main/figures/andorra.png)\n\nThe [drawing maps](#drawing-maps) example shows, how this figure is constructed.\n\n# Features\n\nWhat it provides:\n\n- Functionality to generate shapely objects from OpenStreetMap `.pbf` data\n  file entries.\n- Basic plotting tools for matplotlib.\n\nIn addition to the [shapely](https://shapely.readthedocs.io/) geometry, the\nOpenStreetMap `id` and `tags` properties are generated as provided by\n`esy.osm.pbf`.\n\nWhat it *doesn't* provide:\n\n- A mechanism to spatially query OpenStreetMap entries.\n- Handling of non-geometry objects (like many relations).\n\n# Installation\n\n`esy.osm.shape` depends on a Python version of 3.8 or above as well as on\n`esy.osm.pbf`. Use `pip` to install `esy.osm.shape`:\n\n```sh\n$ pip install esy-osm-shape\n```\n\n# Usage\n\nAn `esy.osm.shape.Shape` object requires a filename of an OpenStreetMap\nprotocol buffers file or an instance of `esy.osm.pbf.File` as argument on\nconstruction. Upon being called, the `esy.osm.shape.Shape` returns generator\nwhich yields a tuple consisting of the\n[shapely](https://shapely.readthedocs.io/) geometry and both the OpenStreetMap\n`id` and `tags`. There are two optional arguments:\n\n- `filter`: can be used to select OpenStreetMap primitives for which to generate\n  [shapely](https://shapely.readthedocs.io/) objects. By default, every\n  OpenStreetMap primitive is selected.\n- `max_tasks`: can be used to trade-off memory versus speed. If this value\n  increases, more memory is used to cache OpenStreetMap entries, thereby\n  reducing the number of file read operations.\n\n# Examples\n\nThe following examples operate on a historic dataset for Andorra from\n[geofabrik](https://www.geofabrik.de/). Let's download the dataset first:\n\n```python\n>>> import os, urllib.request\n>>> if not os.path.exists('andorra.osm.pbf'):\n...     filename, headers = urllib.request.urlretrieve(\n...         'https://download.geofabrik.de/europe/andorra-190101.osm.pbf',\n...         filename='andorra.osm.pbf'\n...     )\n\n```\n\n## Construct geometries\n\nOpen the file and generate linestrings for each `highway` OpenStreetMap entry.\n\n```python\n>>> import shapely, esy.osm.shape\n>>> shape = esy.osm.shape.Shape('andorra.osm.pbf')\n>>> highways = [\n...     shape for shape, id, tags in shape(lambda e: e.tags.get('highway'))\n...     if type(shape) is shapely.geometry.LineString\n... ]\n>>> len(highways)\n3948\n\n```\n\nShapely objects also expose geometric properties, like for example the length or\narea. However, OpenStreetMap uses geodetic coordinates and shapely assumes\nplanar coordinates, rendering most geometry properties invalid.\n\n## Geodetic computations\n\n[pyproj](https://pyproj4.github.io/pyproj/) offers geodetic computations and\ncan be used to estimate area and length properties of sphere geometries\naccurately:\n\n```python\n>>> import pyproj\n>>> geod = pyproj.Geod(ellps='WGS84')\n>>> round(geod.geometry_length(shapely.MultiLineString(highways)))\n1577598\n\n```\n\n## Drawing maps\n\n`esy.osm.shape` provides functionality to convert shapely objects into\n[matplotlib](https://matplotlib.org/) objects, which enables simple map\nrendering:\n\n```python\n>>> import matplotlib.pyplot as plt, esy.osm.shape.mpl\n>>> fig, ax = plt.subplots(1, 1)\n>>> _ = ax.set(\n...     title='andorra-190101.osm.pbf', aspect='equal',\n...     xlabel='lon', xlim=(1.40, 1.75), ylabel='lat', ylim=(42.40, 42.70)\n... )\n>>> iax = ax.inset_axes(\n...     [0.61, 0.02, 0.4, 0.4], xlim=(1.525, 1.535), ylim=(42.504, 42.514),\n...     aspect='equal', xticks=[], yticks=[]\n... )\n>>> style = esy.osm.shape.mpl.simple_style\n>>> items = tuple(shape(esy.osm.shape.mpl.filter(style)))\n>>> _ = ax.add_artist(esy.osm.shape.mpl.render_map(style, items))\n>>> _ = iax.add_artist(esy.osm.shape.mpl.render_map(style, items))\n>>> _ = ax.indicate_inset_zoom(iax, edgecolor='black')\n>>> os.makedirs('figures/', exist_ok=True)\n>>> fig.savefig('figures/andorra.png')\n\n```\n\n## Invalid entries\n\nSome OpenStreetMap entries might be broken shapes or logical groupings not\nrepresentable as shapes. These entries are mostly relations and generate\n`Invalid` objects. `Invalid` objects provide four properties which give a\ndescription of the exception that happened during processing:\n\n- `entry`: The invalid OpenStreetMap entry.\n- `exc_type`: The type of the exception.\n- `exc_args`: The arguments of the exception.\n- `exc_description`: The traceback text of the exception.\n\nThe following example reads every OpenStreetMap entry from the Andorra dataset\nand collects all invalid ones into a dictionary:\n\n```python\n>>> count, invalid = 0, {}\n>>> for shape, id, tags in shape():\n...     count += 1\n...     if type(shape) is esy.osm.shape.Invalid:\n...         invalid[id] = shape\n>>> count, len(invalid)\n(217238, 186)\n\n```\n\nAbout 0.1% of the Andorra dataset entries cannot be handled by\n`esy.osm.shape`. Some of these entries are broken:\n\n```python\n>>> print(invalid[8473237]) #doctest: +ELLIPSIS\nInvalid Relation (id=8473237)\n  Traceback (most recent call last):\n    ...\n    File \"...shape.py\", line ..., in multipolygon_shape\n      raise ValueError('Invalid segments')\n  ValueError: Invalid segments\n\n```\n\nUnrepresentable OpenStreetMap entries are usually logical relations, like\n[routing information](https://wiki.openstreetmap.org/wiki/Relation:route).\nThese are reported as `Invalid` objects with an `exc_type` of\n`NotImplementedError`. For example:\n\n```python\n>>> print(invalid[6745201]) #doctest: +ELLIPSIS\nInvalid Relation (id=6745201)\n  Traceback (most recent call last):\n    File \"...shape.py\", line ..., in unsupported\n      raise NotImplementedError(description)\n  NotImplementedError: Relation (id=6745201)\n\n```\n\n# License\n\n`esy.osm.shape` is published under the\n[BSD-3-Clause](https://spdx.org/licenses/BSD-3-Clause.html) license.\n\n# Design, Development & Contributing\n\nDesign and development notes are available in `esy.osm.shape.test`.\n\nWe would be happy to accept contributions via merge requests, but due to\ncorporate policy we can only accept contributions if you have send us the signed\n[contributor license agreement](CLA.md).\n\n# Contact\n\nPlease use the projects issue tracker to get in touch.\n\n# Team\n\n`esy.osm.shape` is developed by the\n[DLR](https://www.dlr.de/EN/Home/home_node.html) Institute of\n[Networked Energy Systems](https://www.dlr.de/ve/en/desktopdefault.aspx/tabid-12472/21440_read-49440/)\nin the departement for\n[Energy Systems Analysis (ESY)](https://www.dlr.de/ve/en/desktopdefault.aspx/tabid-12471/21741_read-49802/).\n\n# Acknowledgements\n\nThe authors would like to thank the Federal Government and the Heads of\nGovernment of the L\u00e4nder, as well as the Joint Science Conference (GWK), for\ntheir funding and support within the framework of the NFDI4Ing consortium.\nFunded by the German Research Foundation (DFG) - project number 442146713.\n",
    "bugtrack_url": null,
    "license": "BSD-3-Clause",
    "summary": "Convert OpenStreetMap primitives to shapely objects",
    "version": "0.4",
    "project_urls": {
        "documentation": "https://dlr-ve-esy.gitlab.io/esy-osm-shape",
        "homepage": "https://gitlab.com/dlr-ve-esy/esy-osm-shape"
    },
    "split_keywords": [
        "osm",
        " openstreetmap",
        " shapely"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "18080afea1c1cc0926e78023985b4457b66afacf9bbe6ab6b2fd5da14de46e17",
                "md5": "c0894d7e031f79ecffaad1d26475a77c",
                "sha256": "4f076848f46b2eb5dcb63fda28ecd7e9fc199becbad7ff431f596fede6ec7a70"
            },
            "downloads": -1,
            "filename": "esy_osm_shape-0.4-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "c0894d7e031f79ecffaad1d26475a77c",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.5",
            "size": 16625,
            "upload_time": "2025-08-14T09:36:29",
            "upload_time_iso_8601": "2025-08-14T09:36:29.960670Z",
            "url": "https://files.pythonhosted.org/packages/18/08/0afea1c1cc0926e78023985b4457b66afacf9bbe6ab6b2fd5da14de46e17/esy_osm_shape-0.4-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "d36672f4fde77bd1c7402e42a3ea2a3546dac310699cfc1a8afd108a2241245a",
                "md5": "77a7e300d9cb837c52928b2ce72975e9",
                "sha256": "c10e58a0c60c1e89b42cc6ab86fad1c50531f643991d801ac0c43a4db34c10dc"
            },
            "downloads": -1,
            "filename": "esy_osm_shape-0.4.tar.gz",
            "has_sig": false,
            "md5_digest": "77a7e300d9cb837c52928b2ce72975e9",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.5",
            "size": 15532,
            "upload_time": "2025-08-14T09:36:31",
            "upload_time_iso_8601": "2025-08-14T09:36:31.245065Z",
            "url": "https://files.pythonhosted.org/packages/d3/66/72f4fde77bd1c7402e42a3ea2a3546dac310699cfc1a8afd108a2241245a/esy_osm_shape-0.4.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-14 09:36:31",
    "github": false,
    "gitlab": true,
    "bitbucket": false,
    "codeberg": false,
    "gitlab_user": "dlr-ve-esy",
    "gitlab_project": "esy-osm-shape",
    "lcname": "esy-osm-shape"
}
        
Elapsed time: 1.67892s