Now from the interesting experience at AU Russia, I would like to share this cool picture of the Новый Арбат (Novyy Arbat) avenue near the Kremlin, I was impressed by the light show at the buildings facade! Cool!
Back to our technical topic, as announced early this year, the Infraworks 360 API Pilot is live. The first sample was around connecting IW360 with other providers (Four Square and Google Maps). Now it’s time for another sample, connecting to Civil 3D.
This sample uses the Infraworks 360 sample and the ADN OAuth sample, both available on Github. Also, as IW360 is using LL84 coordinate system, we need to convert the values to the project coordinate system used on Civil 3D using the code at this blog post.
There are a few steps on the code. (1) Connect to Autodesk server using the user SSO (Autodesk Single Sign On account), after the authentication we have the token that allow access to files, so we can (2) get the IW360 list of models. With the model number, we can (3) get the roads of this model. Note that this list contains only the IDs, so we also need to read the road information to finally (4) create one alignment for each road.
Some comments before you read the code: I’m not creating a fancy interface, just listing the options and asking the user to type a number. Also, to avoid reading all the roads, I’m limiting the number. The alignment is created, which by design contains only 2D data, so if you need elevation data, you’ll need to also create profiles (which was not done here for simplicity). As IW360 may have more than one road with the same name (for instance, for each direction or due intersections), this sample code is only creating the first one, so one improvement may be treat this scenario to merge the roads before creating the alignments.
What is needed to use IW360 API: you need to be part of the Pilot. That involves using the Beta version of Infraworks 360 and a key & secret pair. If you have a project and would like to join, email me at augusto.goncalves[AT]autodesk.com with more information about your idea.
// System namespaces
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
// AutoCAD Namespaces (main code for commands and interface)
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
// Civil 3D namespaces (to create alingmnet)
using Autodesk.Civil.ApplicationServices;
using Autodesk.Civil.DatabaseServices;
// Autodesk OAuth namespaces (implemented by ADN)
using Autodesk.Adn.OAuthentication;
// Infraworks namespaces (model data implemented by ADN)
using Autodesk.Adn.InfraworksService.Service;
using Autodesk.Adn.InfraworksService.Models;
using Autodesk.Adn.InfraworksService.Models.Geometries;
using Autodesk.AutoCAD.Geometry;
namespace Civil3DSample
{
public class Commands
{
// static variables for local cache
private static OAuthService _oauth = null;
private static InfraworksRestService _iw360 = null;
private static List<ModelInfo> _models = null;
private static int _modelId = 0;
private static List<Road> _roads = null;
[CommandMethod("importIW360Alignment")]
public static void CmdImportIW360Alignment()
{
// *********************************************
// 1. Connect to Autodesk servers
// *********************************************
// connect to Autodesk SSO (Single Sign On) accounts
// this will request the user to login (username & password)
if (_oauth == null)
{
_oauth = new OAuthService(IW360Constants.CONSUMER_KEY,
IW360Constants.CONSUMER_SECRET, IW360Constants.BASE_OAUTH_URL);
if (!_oauth.StartOAuth()) return;
}
// connect to Infraworks 360 server
// using the OAuth authentication from the previous step
// therefore all the information is specific to this user
if (_iw360 == null) _iw360 = new InfraworksRestService(_oauth);
// *********************************************
// 2. Ask user to select one IW360 Model
// *********************************************
// get a list of models
if (_models == null) _models = _iw360.GetModels();
// list models
int m = 0;
foreach (ModelInfo mode in _models)
{
m++;
ed.WriteMessage("\n{0} - {1}", m, mode.name);
}
{
// ask the user to choose one model
PromptIntegerOptions pio = new
PromptIntegerOptions("\nEnter model number: ");
pio.AllowZero = false;
pio.AllowNegative = false;
pio.UpperLimit = m;
PromptIntegerResult pir = ed.GetInteger(pio);
if (pir.Status != PromptStatus.OK) return;
ModelInfo selectedModel = _models[pir.Value - 1]; // zero-based array
int selectedModelId = int.Parse(selectedModel.id);
// if is NOT the sample model, then the local
// cache is invalid applies (if exists)
if (selectedModelId != _modelId) _roads = null;
// and store the selected model ID
_modelId = selectedModelId;
}
// *********************************************
// 3. Ask user to select one IW360 Road
// *********************************************
if (_roads == null) // if the local cache is empty
{
// get all roads, this list returns IDs
Collection roadsProxy = _iw360.GetItemsByClass(_modelId, "roads");
if (roadsProxy == null)
{
ed.WriteMessage("\nCould not load roads. Please try again.");
return;
}
// from the IDs, let get the full object (which contains the names)
_roads = new List<Road>();
foreach (Item item in roadsProxy.items)
{
int itemId = int.Parse(item.id);
Road road = _iw360.GetModelItem<Road>(_modelId, "roads", "roads", itemId);
if (string.IsNullOrWhiteSpace(road.name)) continue; // skip roads with no name
_roads.Add(road);
if (_roads.Count > 2) break; // list only the first 3, due internet speed
}
}
// list all roads
int r = 0;
foreach (Road road in _roads)
{
r++;
ed.WriteMessage("\n{0} - {1} (ID: {2})", r, road.name, road.id);
}
{
// ask the user to choose one model
PromptIntegerOptions pio = new PromptIntegerOptions("\nEnter road number: ");
pio.AllowZero = true;
pio.AllowNegative = false;
pio.UpperLimit = r;
PromptIntegerResult pir = ed.GetInteger(pio);
if (pir.Status != PromptStatus.OK) return;
// *********************************************
// 4. Import points from the selected Road
// *********************************************
if (pir.Value == 0)
{
// create all roads
foreach(Road road in _roads)
CreateCivil3DAlignment(road);
}
else
{
// create just the selected road
CreateCivil3DAlignment(_roads[pir.Value - 1]);
}
}
}
private static void CreateCivil3DAlignment(Road road)
{
if (road.geometry.Type != AiwGeometryType.LineString) return;
AiwLineString lineString = road.geometry as AiwLineString;
// start an AutoCAD transaction
Database db = Application.DocumentManager.MdiActiveDocument.Database;
using (Transaction trans = db.TransactionManager.StartTransaction())
{
// create a polyline with the coordinates
Polyline pline = new Polyline();
int p = 0;
foreach (AiwCoordinate coordinate in lineString.Coordinates)
{
pline.AddVertexAt(p,
PointConverter.ConvertPointCoordinate(new Point2d(coordinate.X, coordinate.Y)),
0, 0, 0);
p++;
}
// add this polyline to AutoCAD model space
BlockTableRecord currentSpace = trans.GetObject(db.CurrentSpaceId,
OpenMode.ForWrite) as BlockTableRecord;
currentSpace.AppendEntity(pline);
trans.AddNewlyCreatedDBObject(pline, true);
// create the alignment
PolylineOptions alignOptions = new PolylineOptions();
alignOptions.EraseExistingEntities = true;
alignOptions.PlineId = pline.Id;
CivilDocument civilDoc = CivilDocument.GetCivilDocument(db);
// create a siteless alignment, at layer 0,
// using Standard style and Standard label set
try
{
Alignment.Create(civilDoc, alignOptions, road.name, string.Empty,
"0", "Basic", "Major and Minor only");
}
catch (System.Exception ex)
{
// skip creating problems, such as duplicated alignment name
ed.WriteMessage("\nCould not create {0}: {1}", road.name, ex.Message);
}
trans.Commit();
}
}
private static Editor ed
{
get
{
return Application.DocumentManager.MdiActiveDocument.Editor;
}
}
}
}
To run this code, you’ll need a model on IW360. The following model from Moscow Kremlin area was created using the Model Builder feature.
After run this code, we’ll have a good number of alignments with the same name as the roads on the IW360 project. Note that the coordinate system was adjusted, I removed the styles labels for simplicity.