Here's an improved version. I noticed that, due to the way C3D works, the first version sometimes created duplicates for some points. And occasionally, it would skip a point. So I changed the routine to scan through all parcels, grab the coordinates of both ends of each segment and throw them in a list of coordinates, throw out duplicates, and create only one Cogo points for each unique coordinate. The code counts two coordinates as being the same point if they fall within .01 of each other. This precision is currently hard-coded, but could be made a user-configurable parameter. Likewise, it sets the description for all points to "LOTCORNER" in the code; this could be configured to use any user-selected string, or the current default description.
I also put in some code to allow the user to select a single site, or just hit return to create points for all parcels on all sites. I started off trying to create a dialog box, but it looks like it's going to take a bit longer to figure out Windows Forms programming than it took to figure out the basics of C#...
/*
* Created by SharpDevelop.
* User: Sinc
* Date: 2/17/2007
* Time: 1:38 PM
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AECC.Interop.Land;
using Autodesk.AECC.Interop.UiLand;
namespace LotCorner
{
/// <summary>
/// Description of MyClass.
/// </summary>
public class LotCornerApp
{
private Autodesk.AutoCAD.Interop.IAcadApplication m_oAcadApp = null;
private Autodesk.AECC.Interop.UiLand.IAeccApplication m_oAeccApp = null;
private Autodesk.AECC.Interop.UiLand.IAeccDocument m_oAeccDoc = null;
private IAeccDatabase m_oAeccDb = null;
private CoordinateList m_oCoordinateList = null;
string m_sAcadProdID = "AutoCAD.Application";
string m_sAeccAppProgId = "AeccXUiLand.AeccApplication";
private string m_sMessage="";
[CommandMethod("LOTCORNERS")]
public void LotCorners()
{
//Start Civil-3D, or get it if it's already running
try
{
m_oAcadApp = (Autodesk.AutoCAD.Interop.IAcadApplication)System.Runtime.InteropServices.Marshal.GetActiveObject(m_sAcadProdID);
}
catch (System.Exception /*ex*/)
{
System.Type AcadProg = System.Type.GetTypeFromProgID(m_sAcadProdID);
m_oAcadApp = (Autodesk.AutoCAD.Interop.IAcadApplication)System.Activator.CreateInstance(AcadProg, true);
//Instead above two lines of code, simply use following new ().
//However, this always creates an instance of the
//AutoCAD even if it is already running.
//m_oAcadApp = new Autodesk.AutoCAD.Interop.AcadApplicationClass();
}
if (m_oAcadApp != null)
{
m_oAcadApp.Visible = true;
m_oAeccApp = (IAeccApplication)m_oAcadApp.GetInterfaceObject(m_sAeccAppProgId);
m_oAeccDoc = (IAeccDocument)m_oAeccApp.ActiveDocument;
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
// get the Database object via a late bind
m_oAeccDb = (IAeccDatabase)m_oAeccDoc.GetType().GetProperty("Database").GetValue(m_oAeccDoc, null);
int siteCount = m_oAeccDb.Sites.Count;
int idx = 0;
m_sMessage += "Number of Sites = " + siteCount.ToString() + "\n";
while (idx < siteCount) {
m_sMessage += idx.ToString() + " -- " + m_oAeccDb.Sites.Item(idx++).DisplayName + "\n";
}
ed.WriteMessage(m_sMessage);
m_sMessage="";
PromptIntegerOptions ops = new PromptIntegerOptions("Select a Site, or hit <RETURN> for all: ");
ops.AllowNone = true;
ops.LowerLimit = 0;
ops.UpperLimit = siteCount - 1;
PromptIntegerResult choice = ed.GetInteger(ops);
m_oCoordinateList = new CoordinateList();
switch (choice.Status) {
case PromptStatus.None:
foreach (IAeccSite site in m_oAeccDb.Sites) {
addPointsForSite(site);
}
goto default;
case PromptStatus.OK:
addPointsForSite(m_oAeccDb.Sites.Item(choice.Value));
goto default;
case PromptStatus.Cancel:
break;
default:
createAllPoints();
break;
}
ed.WriteMessage(m_sMessage);
m_sMessage="";
}
}
private void addPointsForSite(IAeccSite site) {
foreach (IAeccParcelSegment parcelSegment in site.ParcelSegments) {
foreach (IAeccParcelSegmentElement parcelSegmentElement in parcelSegment) {
addPointsForElement(parcelSegmentElement);
}
}
}
private void addPointsForElement(IAeccParcelSegmentElement parcelSegmentElement) {
m_oCoordinateList.Add(new Coordinate(parcelSegmentElement.StartX, parcelSegmentElement.StartY, 0.0));
m_oCoordinateList.Add(new Coordinate(parcelSegmentElement.EndX, parcelSegmentElement.EndY, 0.0));
}
private void createAllPoints() {
IEnumerator<Coordinate> e = m_oCoordinateList.GetEnumerator();
while (e.MoveNext()) {
IAeccPoint cogoPoint = m_oAeccDb.Points.Add(e.Current.CoordinateArray());
cogoPoint.RawDescription = "LOTCORNER";
}
}
}
public class Coordinate
{
private double x, y, z;
public Coordinate(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public double[] CoordinateArray() {
// this returns full precision for coordinates
double[] cArray = {this.x, this.y, this.z};
return cArray;
}
public string key() {
// key rounds coordinates to 2 decimal places
string key = this.x.ToString("F2") + "," + this.y.ToString("F2") + "," + this.z.ToString("F2");
return key;
}
}
public class CoordinateList : KeyedCollection<string, Coordinate>
{
public CoordinateList() : base() {}
protected override string GetKeyForItem(Coordinate item)
{
return item.key();
}
public new void Add(Coordinate item) {
// because key is rounded to 2 decimal places, coordinate will
// not be added to list if it is within ~0.01 feet of a point
// that is already in the list
if (!this.Contains(item.key())) {
base.Add(item);
}
}
}
}