I asked ChatGPT for OpenStreetMap GIS data

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()
1 Like