Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an example to display OpenStreetMap-sourced data on the map view #8044

Merged
merged 7 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ examples = [
"clock",
"dna",
"log_file",
"osm_data",
"minimal",
"multiprocess_logging",
"multithreading",
Expand Down
2 changes: 1 addition & 1 deletion examples/python/nuscenes_dataset/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ rr.log(f"world/ego_vehicle/{sensor_name}", rr.Points3D(points, colors=point_colo
### GPS data

GPS data is calculated from the scene's reference coordinates and the transformations (starting map point + odometry).
The GPS coordinates are logged as [`GeoPoints`](https://www.rerun.io/docs/reference/types/archetypes/geopoints).
The GPS coordinates are logged as [`GeoPoints`](https://www.rerun.io/docs/reference/types/archetypes/geopoints?speculative-link).

```python
rr.log(
Expand Down
1 change: 1 addition & 0 deletions examples/python/osm_data/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/cache
41 changes: 41 additions & 0 deletions examples/python/osm_data/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!--[metadata]
title = "OSM data"
tags = ["Map", "Blueprint"]
thumbnail_dimensions = [480, 480]
thumbnail = "https://static.rerun.io/osm_data/0be94071469c49f98326d85456ed2a3af8d1733a/480w.png"
channel = "release"
-->


Download [`OpenStreetMap`](https://www.openstreetmap.org) data via the [Overpass](https://overpass-api.de) API and [query language](https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL),
and display it on a [map view](https://www.rerun.io/docs/reference/types/view/map_view?speculative-link).

<picture>
<img src="https://static.rerun.io/osm-data/926e89e0587b0d66a1cd620b3f5b77ac79eca272/full.png" alt="">
<source media="(max-width: 480px)" srcset="https://static.rerun.io/osm-data/926e89e0587b0d66a1cd620b3f5b77ac79eca272/480w.png">
<source media="(max-width: 768px)" srcset="https://static.rerun.io/osm-data/926e89e0587b0d66a1cd620b3f5b77ac79eca272/768w.png">
<source media="(max-width: 1024px)" srcset="https://static.rerun.io/osm-data/926e89e0587b0d66a1cd620b3f5b77ac79eca272/1024w.png">
<source media="(max-width: 1200px)" srcset="https://static.rerun.io/osm-data/926e89e0587b0d66a1cd620b3f5b77ac79eca272/1200w.png">
</picture>

## Run the code

To run this example, make sure you have Python version at least 3.9, the Rerun repository checked out and the latest SDK installed:
```bash
pip install --upgrade rerun-sdk # install the latest Rerun SDK
git clone git@github.com:rerun-io/rerun.git # Clone the repository
cd rerun
git checkout latest # Check out the commit matching the latest SDK release
```
Install the necessary libraries specified in the requirements file:
```bash
pip install -e examples/python/osm_data
```
To experiment with the provided example, simply execute the main Python script:
```bash
python -m osm_data # run the example
```
If you wish to customize it, explore additional features, or save it use the CLI with the `--help` option for guidance:
```bash
python -m osm_data --help
```
104 changes: 104 additions & 0 deletions examples/python/osm_data/osm_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from __future__ import annotations

import hashlib
import json
from argparse import ArgumentParser
from pathlib import Path
from typing import Any
from urllib.parse import urlencode

import requests
import rerun as rr
import rerun.blueprint as rrb

CACHE_DIR = Path(__file__).parent / "cache"
if not CACHE_DIR.exists():
CACHE_DIR.mkdir()

OVERPASS_API_URL = "https://overpass-api.de/api/interpreter"

# Find some hotels in the area of Lausanne, Switzerland
Copy link
Contributor

Choose a reason for hiding this comment

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

such a "randomly" picked location :)

Copy link
Member Author

Choose a reason for hiding this comment

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

😇

# This uses the Overpass API query language: https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL
HOTELS_IN_LAUSANNE_QUERY = """
[out:json][timeout:90];
(
nw["tourism"="hotel"]
(46.4804134576154,6.533088904998318,46.647800100605984,6.7706675099821165);
);
out geom;
"""


def execute_query(query: str) -> dict[str, Any]:
"""Execute an Overpass query, caching its result locally."""
query_hash = hashlib.sha256(query.encode()).hexdigest()

cache_file = CACHE_DIR / f"{query_hash}.json"
if cache_file.exists():
result = json.loads(cache_file.read_text())
else:
params = {"data": query}
encoded_query = urlencode(params)
headers = {"Content-Type": "application/x-www-form-urlencoded"}
response = requests.post(OVERPASS_API_URL, data=encoded_query, headers=headers)
result = response.json()

cache_file.write_text(json.dumps(result))

# very basic validation
if not isinstance(result, dict):
raise ValueError("Unexpected result from Overpass API")

return result


def log_node(node: dict[str, Any]) -> None:
node_id = node["id"]
entity_path = f"nodes/{node_id}"

rr.log(entity_path, rr.GeoPoints(lat_lon=[node["lat"], node["lon"]], radii=rr.components.Radius.ui_points(7.0)))
rr.log(entity_path, rr.AnyValues(**node.get("tags", {})))


def log_way(way: dict[str, Any]) -> None:
way_id = way["id"]
entity_path = f"ways/{way_id}"

coords = [(node["lat"], node["lon"]) for node in way["geometry"]]

rr.log(entity_path, rr.GeoLineStrings(lat_lon=[coords], radii=rr.components.Radius.ui_points(2.0)))
rr.log(entity_path, rr.AnyValues(**way.get("tags", {})))


def log_data(data: dict[str, Any]) -> None:
try:
copyright_text = data["osm3s"]["copyright"]
rr.log_components("copyright", [rr.components.Text(copyright_text)])
except KeyError:
pass

for element in data["elements"]:
if element["type"] == "node":
log_node(element)
elif element["type"] == "way":
log_way(element)


def main() -> None:
parser = ArgumentParser(description="Visualize OSM data")
rr.script_add_args(parser)
args = parser.parse_args()

blueprint = rrb.Blueprint(
rrb.MapView(origin="/"),
collapse_panels=True,
)

rr.script_setup(args, "rerun_example_osm_data", default_blueprint=blueprint)

data = execute_query(HOTELS_IN_LAUSANNE_QUERY)
log_data(data)


if __name__ == "__main__":
main()
12 changes: 12 additions & 0 deletions examples/python/osm_data/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[project]
name = "osm_data"
version = "0.1.0"
readme = "README.md"
dependencies = ["requests", "rerun-sdk"]

[project.scripts]
osm_data = "osm_data:main"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
2 changes: 2 additions & 0 deletions lychee.toml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ exclude = [
'https://fonts.gstatic.com/', # Font API entrypoint, not a link.
'https://tel.rerun.io/', # Analytics endpoint.
'https://docs-assets.developer.apple.com/ml-research/datasets/arkitscenes/v1', # Used by arkit_scenes.
'https://overpass-api.de/api/interpreter', # Used by osm_data example

# Avoid rate limiting.
'https://crates.io/crates/.*', # Avoid crates.io rate-limiting
Expand All @@ -111,6 +112,7 @@ exclude = [
'https://link.to',
'https://static.rerun.io/my_screenshot/',
'https://your-hosted-asset-url.com/widget.js',
'file:///path/to/file',

# Link fragments and data links in examples.
'https://raw.githubusercontent.com/googlefonts/noto-emoji/', # URL fragment.
Expand Down
Loading
Loading