In the script below, input the location link from www.openstreetmap.org and the size of a square to grab, in meters.
You get back the geometry with the keys and values assigned.
I tested with Brussels central area and it took a few minutes. 2000m by 2000m
# downloads OSM data from OpenStreetMap including Keys and Values.
# this script has to run in a CPython3 environment or higher.
# to run with an alias use: _-ScriptEditor _Run C:\ your file path \OSMsimple.py
import re
import json
import math
import urllib.request
import urllib.error
import Rhino
import scriptcontext as sc
import rhinoscriptsyntax as rs
import System
OVERPASS_URL = "https://overpass-api.de/api/interpreter"
def parse_osm_url(url):
m = re.search(r"#map=\d+/([-0-9.]+)/([-0-9.]+)", url)
if not m:
raise Exception("Invalid OSM link")
lat = float(m.group(1))
lon = float(m.group(2))
print("Center:", lat, lon)
return lat, lon
def make_bbox(lat, lon, side_m):
half = side_m * 0.5
dlat = half / 111320.0
dlon = half / (111320.0 * math.cos(math.radians(lat)))
south = lat - dlat
west = lon - dlon
north = lat + dlat
east = lon + dlon
print("Area size:", side_m, "m x", side_m, "m")
print("BBox:", (south, west, north, east))
return south, west, north, east
def run_overpass_query(bbox):
s, w, n, e = bbox
query = """
[out:json][timeout:25];
(
node({},{},{},{})[~"."~"."];
way({},{},{},{});
relation({},{},{},{});
);
out body geom;
""".format(s,w,n,e,s,w,n,e,s,w,n,e)
print("Querying Overpass...")
req = urllib.request.Request(
OVERPASS_URL,
data=query.encode("utf-8"),
headers={"User-Agent": "RhinoOSM"}
)
with urllib.request.urlopen(req, timeout=60) as f:
data = json.loads(f.read().decode("utf-8"))
elements = data.get("elements", [])
print("Elements received:", len(elements))
return elements
def latlon_to_xy(lat, lon, origin_lat, origin_lon):
x = (lon - origin_lon) * 111320.0 * math.cos(math.radians(origin_lat))
y = (lat - origin_lat) * 111320.0
return Rhino.Geometry.Point3d(x, y, 0.0)
def make_attributes(el, geom_type=None, extra_tags=None):
attr = Rhino.DocObjects.ObjectAttributes()
attr.SetUserString("osm_id", str(el.get("id", "")))
attr.SetUserString("osm_type", str(el.get("type", "")))
if geom_type:
attr.SetUserString("geom_type", str(geom_type))
tags = dict(el.get("tags", {}))
if extra_tags:
tags.update(extra_tags)
for k, v in tags.items():
if v is not None:
attr.SetUserString(str(k), str(v))
return attr
def add_node(el, origin):
if "lat" not in el or "lon" not in el:
return False
pt = latlon_to_xy(el["lat"], el["lon"], origin[0], origin[1])
attr = make_attributes(el, geom_type="Point")
gid = sc.doc.Objects.AddPoint(pt, attr)
return gid != System.Guid.Empty
def add_curve_or_surface(coords, el, origin, extra_tags=None):
if not coords or len(coords) < 2:
return False
pts = [latlon_to_xy(c["lat"], c["lon"], origin[0], origin[1]) for c in coords]
is_closed = len(pts) > 2 and pts[0].DistanceTo(pts[-1]) < 0.01
crv = Rhino.Geometry.Polyline(pts).ToPolylineCurve()
if is_closed:
attr = make_attributes(el, geom_type="Polygon", extra_tags=extra_tags)
breps = Rhino.Geometry.Brep.CreatePlanarBreps(crv, sc.doc.ModelAbsoluteTolerance)
if breps and len(breps) > 0:
gid = sc.doc.Objects.AddBrep(breps[0], attr)
return gid != System.Guid.Empty
attr = make_attributes(el, geom_type="LineString", extra_tags=extra_tags)
gid = sc.doc.Objects.AddCurve(crv, attr)
return gid != System.Guid.Empty
def add_way(el, origin):
geometry = el.get("geometry")
if not geometry:
return False
ok = add_curve_or_surface(geometry, el, origin)
if ok:
print("Way:", el.get("id"))
return ok
def add_relation(el, origin):
made = 0
geometry = el.get("geometry")
if geometry:
if add_curve_or_surface(geometry, el, origin):
made += 1
members = el.get("members", [])
for mem in members:
geom = mem.get("geometry")
if not geom:
continue
extra = {}
role = mem.get("role")
if role:
extra["member_role"] = role
mem_type = mem.get("type")
if mem_type:
extra["member_type"] = mem_type
mem_ref = mem.get("ref")
if mem_ref is not None:
extra["member_ref"] = mem_ref
if add_curve_or_surface(geom, el, origin, extra):
made += 1
if made > 0:
print("Relation:", el.get("id"), "created parts:", made)
return made
def main():
url = rs.GetString(
"Paste OSM link",
"https://www.openstreetmap.org/#map=17/52.379322/4.921720"
)
if not url:
return
SIDE_M = rs.GetReal("Area size in meters", 1000.0)
if SIDE_M is None:
return
lat, lon = parse_osm_url(url)
bbox = make_bbox(lat, lon, SIDE_M)
elements = run_overpass_query(bbox)
origin = (lat, lon)
node_count = 0
way_count = 0
rel_count = 0
point_count = 0
shape_count = 0
print("Creating Rhino geometry...")
for el in elements:
t = el.get("type")
if t == "node":
node_count += 1
if add_node(el, origin):
point_count += 1
elif t == "way":
way_count += 1
if add_way(el, origin):
shape_count += 1
elif t == "relation":
rel_count += 1
shape_count += add_relation(el, origin)
sc.doc.Views.Redraw()
print("Nodes returned:", node_count)
print("Ways returned:", way_count)
print("Relations returned:", rel_count)
print("Points created:", point_count)
print("Curves/Surfaces created:", shape_count)
print("Done.")
if __name__ == "__main__":
main()


