SpecklePy, Rhino3dm objects to speckle using python

Hi everyone,

This is an extract of an application I’ve made which creates rhino objects using the python library rhino3dm and add them to a rhino file. I want to send these rhino objects straight to a speckle stream but I’m not sure if I’m converting the objects to speckle objects correctly. Thanks

created_curves = []
admin_response = requests.get(adminboundaries_url, params=params)
if admin_response.status_code == 200 and admin_response.json():
        admin_data = json.loads(admin_response.text)
        if "features" in admin_data:
            for feature in admin_data["features"]:
                suburb_name = feature['attributes']['suburbname']
                geometry = feature["geometry"]
                for ring in geometry["rings"]:
                    points = []
                    for coord in ring:
                        point = rh.Point3d(coord[0], coord[1], 0)
                        points.append(point)
                        all_points.append(point)
                    polyline = rh.Polyline(points)
                    curve = polyline.ToNurbsCurve()
                    created_curves.append(curve)
                    att1 = rh.ObjectAttributes()
                    att1.LayerIndex = admin_layerIndex
                    att1.SetUserString("Suburb Name", str(suburb_name))
                    model.Objects.AddCurve(curve, att1)

speckle_server = "speckle.xyz"
speckle_token = ""
client = SpeckleClient(host=speckle_server)
account = get_account_from_token(speckle_token, speckle_server)
client.authenticate_with_account(account)

send_button = st.button(label="Send", key="mybutton")
if send_button:
    server_transport = ServerTransport(stream_id='c883549443', client=client)
    data = created_curves
    speckle_geometry = speckle_objects.Base(data=data)
    object_id = operations.send(speckle_geometry, transports=[server_transport])
    client.commit.create(stream_id='c883549443', object_id=object_id)

Nice question.

To summarise, your backend serves GeoJSON, and you want to create Rhino Objects that you will share using Speckle.

You are nearly there in principle but there are a couple of key points for clarification:

  1. You are attempting to send Rhino geometry generated with the Rhinoscript syntax - you need to use the Specklepy Objects API to create Speckle geometry. Specklepy doesn’t perform the conversions for you.
  2. The Rhino Connector client plugin will perform conversion to Rhino Objects
  3. How critical is it that you send a NurbsCurve specifically? If we generate a Speckle Polyline, will that work for you?
from specklepy.objects.geometry import Polyline, Point

# ...

if "features" in demo_data:
    for feature in demo_data["features"]:
        suburb_name = feature['attributes']['suburbname']
        geometry = feature["geometry"]
        for ring in geometry["rings"]:
            points = []
            for coord in ring:
                point = Point(coord[0], coord[1], 0)
                points.append(point)
                all_points.append(point)
            polyline = Polyline(points)
            created_curves.append(polyline)

If you need further help - or clarifications on next steps I can develop this further

Hi Jonathan,

Thank you so much for the prompt response, I’m starting to understand it a bit more however I keep getting this error now.

point = Point(coord[0], coord[1], 0)
TypeError: Base.__init__() takes 1 positional argument but 4 were given

This is my current code.

created_curves = []
while True:
  admin_response = requests.get(adminboundaries_url, params=params)
  if admin_response.status_code == 200 and admin_response.json():
    admin_data = json.loads(admin_response.text)
    if "features" in admin_data:
      for feature in admin_data["features"]:
        suburb_name = feature['attributes']['suburbname']
        geometry = feature["geometry"]
        for ring in geometry["rings"]:
          points = []
          for coord in ring:
            point = Point(coord[0], coord[1], 0)
            points.append(point)
            all_points.append(point)
          polyline = Polyline(points)
          created_curves.append(polyline)
      break  # exit the loop if data is found
    else:
      time.sleep(0)
  else:
    # wait for some time before retrying
    time.sleep(0)

Is it the way I’m sending the geometry or am I constructing the points wrong?

Thanks again.

Try instead:

point = Point(x=coord[0], y=coord[1], z=0)

Works like a charm! For Polyline would it be 'polyline = Polyline(points=points)'

Thanks so much again!