Can I add/remove CAD entity through AutoCAD .NET API - c#

Have a requirement to add/remove from a CAD drawing using the .NET API an item drawn as an individual CAD entity in the Title Block area which is not an attribute. Is there any way using point references on a CAD drawing or any other method that this could be done through the API?
The CAD entity been asked to remove has an image, client address and web address.
Use this for accessing attributes
BlockTableRecord blkTblRecTitle =
transaction.GetObject(blockTable[BlockTableRecord.PaperSpace],
OpenMode.ForWrite) as BlockTableRecord;
foreach (ObjectId id in blkTblRecTitle)
{
DBObject obj = transaction.GetObject(id, OpenMode.ForWrite);
BlockReference blkRef = obj as BlockReference;
if (blkRef != null)
{
AttributeCollection attCol = blkRef.AttributeCollection;
foreach (ObjectId objID in attCol)
{
DBObject dbObj = transaction.GetObject(objID,
OpenMode.ForWrite) as DBObject;
AttributeReference acAttRef = dbObj as
AttributeReference;
}
}
}
Have managed to find how to add multi line text at a location
using (MText acText = new MText())
{
acText.Location = new Point3d(802, 106.5, 0);
acText.TextHeight = 2;
acText.Contents = "Hello World.\nNow need to right align text.";
blkTblRecTitle.AppendEntity(acText);
transaction.AddNewlyCreatedDBObject(acText, true);
}
Ideally would like to right align but can't see anywhere how I do this with MText which have to use for multiple lines.
Given problems adding text to Paper Space can't see how ever be able to add an image.

To edit .DWG files you need AutoCAD engine, which can be:
desktop: using the existing AutoCAD on your machine, create a .NET/VBA/LISP/C++ plugin that will open, read, modify and save the file. Here is a .NET tutorial.
cloud: using Forge Design Automation webservice to run custom commands/routines with your files. Check the documentation.
Then I would suggest reviewing the blogs here and here for sample codes.

Related

How can I add a customised Carto Map Marker via Carto Map Moblie SDK.UWP?

I'm implementing a Universal Windows Platform (UWP) app, and I m using the Carto Map Mobile SDK (UWP). However, I don't know how to add a .png image as a Map Marker programmatically. Here is my code:
/// Preparation - create layer and datasource
// projection will be needed later
Projection projection = map.Options.BaseProjection;
// Initialize an local data source - a bucket for your objects created in code
LocalVectorDataSource datasource = new LocalVectorDataSource(projection);
// Initialize a vector layer with the previous data source
VectorLayer layer = new VectorLayer(datasource);
// Add layer to map
map.Layers.Add(layer);
/// Now we real adding objects
// Create marker style
MarkerStyleBuilder builder = new MarkerStyleBuilder();
builder.Size = 20;
BinaryData iconBytes = AssetUtils.LoadAsset("Z:/FolderName/ProjectName/Assets/markers_mdpi/mapmarker.png");
byte[] bytearray = iconBytes.GetData();
int size = Marshal.SizeOf(bytearray[0]) * bytearray.Length;
IntPtr pnt = Marshal.AllocHGlobal(size);
builder.Bitmap = new Bitmap(pnt, true);
MarkerStyle style = null;
style = builder.BuildStyle();
// Create a marker with the style we defined previously and add it to the source
Marker marker = new Marker(position, style);
datasource.Add(marker);
The Carto Map official technical document didn't help at all, and here is the screenshotCarto Mobile SDK document. However, when I installed the official SDK.UWP via the Nuget, there aren't any relevant functions that mentioned in the document in the library.
Can anyone help me solve this problem? Otherwise it is meaningless for me to create this UWP app further. Many thanks.
Okay, I just solved this problem, and the Carto Map Support team replied me as well. The official technical document is not updated in time, so it misleads new people who first contact with the carto map (especially the UWP one).
The solution is:
/// Preparation - create layer and datasource
// projection will be needed later
Projection projection = map.Options.BaseProjection;
// Initialize an local data source - a bucket for your objects created in code
LocalVectorDataSource datasource = new LocalVectorDataSource(projection);
// Initialize a vector layer with the previous data source
VectorLayer layer = new VectorLayer(datasource);
// Add layer to map
map.Layers.Add(layer);
/// Now we real adding objects
// Create marker style
MarkerStyleBuilder builder = new MarkerStyleBuilder();
builder.Size = 30;
//here we generate a filePath string then pass it into AssetUtils.LoadAsset
string filePath = System.IO.Path.Combine("SubfolderName", "imagefileName.png");
var data = AssetUtils.LoadAsset("SubfolderName\\imagefileName.png");
var bitmap = Bitmap.CreateFromCompressed(data);
if (bitmap != null)
{
builder.Bitmap = bitmap;
bitmap.Dispose();
}
MarkerStyle style = builder.BuildStyle();
// Create a marker with the style we defined previously and add it to the source
Marker marker = new Marker(position, style);
datasource.Add(marker);
Please make sure all the files/sources come from the Assets folder.

Adding point geometries to shapefile in GDAL (C#)

I am trying to make shapefiles with point geometries using GDAL. I am following the example given here. I am using Microsoft Visual Studio and the programming language is C#. For now, I am just making one point at the origin and I'm viewing the resulting shapefiles in QGIS. For some reason, I am not being able to view the point which I make. I have tried making a polygon too but I am facing the same problem. Below is the code I have written:
public void testSF(Dataset ds)
{
Console.WriteLine("Writing ERSI shapefile");
// Registering drivers
OSGeo.OGR.Ogr.RegisterAll();
OSGeo.OGR.Driver driverSH = OSGeo.OGR.Ogr.GetDriverByName("ESRI Shapefile");
if (driverSH == null)
{
Console.WriteLine("Cannot get drivers. Exiting");
System.Environment.Exit(-1);
}
Console.WriteLine("Drivers fetched");
// Creating a shapefile
OSGeo.OGR.DataSource dataSourceSH = driverSH.CreateDataSource("ERSI_TEST_ShapeFile.shp", new string[] { });
if (dataSourceSH == null)
{
Console.WriteLine("Cannot create datasource");
System.Environment.Exit(-1);
}
Console.WriteLine("Shapefile created");
// Creating a point layer
OSGeo.OGR.Layer layerSH;
layerSH = dataSourceSH.CreateLayer("PolygonLayer", null, OSGeo.OGR.wkbGeometryType.wkbPoint, new string[] { });
if (layerSH == null)
{
Console.WriteLine("Layer creation failed, exiting...");
System.Environment.Exit(-1);
}
Console.WriteLine("Polygon Layer created");
// Creating and adding attribute fields to layer
OSGeo.OGR.FieldDefn fdefnName = new OSGeo.OGR.FieldDefn("Name", OSGeo.OGR.FieldType.OFTString);
fdefnName.SetWidth(32);
OSGeo.OGR.FieldDefn fdefnGPS = new OSGeo.OGR.FieldDefn("GPS", OSGeo.OGR.FieldType.OFTString);
fdefnGPS.SetWidth(32);
if (layerSH.CreateField(fdefnName, 1) != 0)
{
Console.WriteLine("Creating Name field failed");
System.Environment.Exit(-1);
}
if (layerSH.CreateField(fdefnGPS, 1) != 0)
{
Console.WriteLine("Creating GPS field failed");
System.Environment.Exit(-1);
}
Console.WriteLine("Fields created and added to layer");
OSGeo.OGR.Feature featureSH = new OSGeo.OGR.Feature(layerSH.GetLayerDefn());
featureSH.SetField("Name", "This is a NAME");
featureSH.SetField("GPS", "Test GPS point");
// Outer Ring
// Methodology: Create a linear ring geometry, add it to a polygon geometry. Add polygon geometry to feature. Add feature to layer
OSGeo.OGR.Feature feature = new OSGeo.OGR.Feature( layerSH.GetLayerDefn() );
OSGeo.OGR.Geometry geom = OSGeo.OGR.Geometry.CreateFromWkt("POINT(0.0 0.0)");
feature.SetGeometry(geom);
layerSH.CreateFeature(feature);
}
This code adds a point to the polygon layer at 0.0 and 0.0. However, when I open the resulting layer in QGIS, I can not see/locate the point. Any help would be appreciated.
I have tested your code with last GDAL release and it works.
You have to close the datasource (call Dispose method) to update the file with the created geometry otherwise you will just view an empty file.
I think You are missing to define the spatial reference

Programmatically script SSIS package - Dynamic XML to OLE DB

I've been trying to piece together how other users have finished their projects, but my understanding is still limited.
I want to take any given XML source, make a Data Flow Task, and pass its data to an OLE DB destination matching the table name of the XML file. Running it with the visual tool means I cannot do dynamic data flow tasks because the Metadata does not refresh.
I have created a script that creates a package, but when I open the package in Visual Studio, it has a red-x saying that there cannot be zero Input Columns. When I drill down and look at the mappings of the OLE DB Destination, then click OK - it corrects it for me. I cannot figure out how to do that programmatically.
I've seen others solve it by using foreach loops and going through the Input columns, but I cannot seem to figure it out.
I also have a separate script that I tried to mimic several people's scripts with, and it has different issues. Not sure how to post it as an attachment
Thank you in advance for the help :)
EDIT
I've been getting positive feedback for trying out BIML, and I will...but I want to know if in the short term anyone can help me figure out why this doesn't fill in ExternalMetaDataColumnId for my input. I've posted my updated code below with foreach loops that aren't doing what I expect them to.
Thank you
#region
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.SqlServer.Dts.Runtime;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using System.Xml;
#endregion
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
#region Initial Setup
Application a = new Application();
Package p = new Package();
TaskHost t = p.Executables.Add("DTS.Pipeline") as TaskHost;
t.Name = "DataFlow Task";
t.Description = "Flat File to Database";
MainPipe mp = t.InnerObject as MainPipe;
#endregion
#region Flat File Source in Dataflow Task
IDTSComponentMetaData100 md = mp.ComponentMetaDataCollection.New();
md.ComponentClassID = "Microsoft.XmlSourceAdapter";
md.Name = "XML Source";
CManagedComponentWrapper wrp = md.Instantiate();
wrp.ProvideComponentProperties();
#endregion
#region Add connection manager to OLE DB
ConnectionManager conn = p.Connections.Add("OLEDB");
conn.Name = "westcoastuserDBO";
conn.ConnectionString = "Data Source=SERVER;Initial Catalog=DBO;Provider=SQLNCLI11.1;Integrated Security=SSPI;Auto Translate=False;";
#endregion
#region XML Source Properties
wrp.SetComponentProperty("XMLData", #"C:\Users\file.xml");
wrp.SetComponentProperty("XMLSchemaDefinition", #"C:\Users\file.xsd");
wrp.SetComponentProperty("AccessMode", 0);
wrp.SetComponentProperty("UseInlineSchema", false);
//below does not work
//wrp.SetComponentProperty("XMLIntegerMapping", 0).TypeConverter = "Microsoft.SqlServer.Dts.Pipeline.XmlSourceAdapter + XMLIntegerMappingConverter";
wrp.ReinitializeMetaData();
wrp.ReleaseConnections();
IDTSComponentMetaData100 md2 = mp.ComponentMetaDataCollection.New();
md2.ComponentClassID = "Microsoft.OLEDBDestination";
CManagedComponentWrapper wrp2 = md2.Instantiate();
wrp2.ProvideComponentProperties();
md2.Name = "OLE DB Connection";
md2.UsesDispositions = true;
md2.Version = 4;
wrp2.SetComponentProperty("OpenRowset", "dbo.authorizations");
#endregion
IDTSPath100 path = mp.PathCollection.New();
path.AttachPathAndPropagateNotifications(md.OutputCollection[0], md2.InputCollection[0]);
IDTSInput100 input = md2.InputCollection[0];
IDTSVirtualInput100 vInput = input.GetVirtualInput();
//below taken from https://stackoverflow.com/questions/12587709/c-sharp-ssis-data-flow-component-creating-custom-input-columns
IDTSExternalMetadataColumnCollection100 externalColumnCollection = input.ExternalMetadataColumnCollection;
// Iterate through the virtual input column collection.
foreach (IDTSVirtualInputColumn100 vColumn in vInput.VirtualInputColumnCollection)
{
// Call the SetUsageType method of the destination
// to add each available virtual input column as an input column.
wrp2.SetUsageType(
input.ID, vInput, vColumn.LineageID, DTSUsageType.UT_READONLY);
}
// Get the destination's default output collection
IDTSOutputCollection100 outColl = md2.OutputCollection;
// Iterate through the outputs in default output collection
foreach (IDTSOutput100 output in outColl)
{
// Iterate through the default output columns in the output
int count = output.OutputColumnCollection.Count;
foreach (IDTSOutputColumn100 outputColumn in output.OutputColumnCollection)
{
// Get the output's external metadata column collection
IDTSExternalMetadataColumnCollection100 extMetadataColumnColl = output.ExternalMetadataColumnCollection;
// Iterate through the external metadata column collection's external metadata columns
foreach (IDTSExternalMetadataColumn100 extMetadataColumn in extMetadataColumnColl)
{
// Call the MapOutPutColumn method of the destination to map
// each available output column to an external metadata column
wrp2.MapOutputColumn(
output.ID, outputColumn.ID, extMetadataColumn.ID, true);
}
}
}
md2.RuntimeConnectionCollection[0].ConnectionManager = DtsConvert.GetExtendedInterface(conn);
md2.RuntimeConnectionCollection[0].ConnectionManagerID = conn.ID;
conn.AcquireConnection(null);
#region Save Package to FileSystem
string packageXml = #"C:\Users\test.dtsx";
XmlDocument myPkgDocument = new XmlDocument();
p.SaveToXML(ref myPkgDocument, null, null);
a.SaveToXml(packageXml, p, null);
#endregion
}
}
}
I think the problem that you are not mapping the input columns to the OLEDB Destination, and after opening the package, if you click on the OLEDB Destination and go to the Mapping section, it will automatically map the columns based on their names. The Foreach loop that is used by others are to loop over columns and map them to the related Destination columns.
There are many articles talking about creating SSIS package dynamically, you can refer to them for more information:
Dynamic Data Flow in SSIS using .NET/C#
Programmatically map the columns of a flat file destination?
Building Packages Programmatically
Samples for creating SSIS packages programmatically
Generating SSIS Packages Programmatically (Part I)

Erasing AutoCAD drawing objects after exploding them, using C#

I am working on a Task in which the code automatically opens a drawing selected by the user [in a UI] and selects all the objects in the drawing and starts to explode all the of them till they cant be exploded anymore. While doing this I face a problem, the original (un-exploded 3D object) is still present in the drawing, super imposed by the Exploded object. Every recursive call of the Explode function creates a new exploded 3D object of that object.
Here is a snippet of the code I working on:
PromptSelectionResult ss = ed.SelectAll();
using (DocumentLock acLckDoc = doc.LockDocument())
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
objs = new DBObjectCollection();
foreach (SelectedObject so in ss.Value)
{
Entity ent = (Entity)tr.GetObject(so.ObjectId, OpenMode.ForWrite);
if (!(ent is Solid3d))
{
ent.Explode(objs);
ent.UpgradeOpen();
ent.Erase();
}
}
tr.Commit();
}
}
As soon as the control comes on to the ent.Erase() statement - it throws an exception, eCannotBeErasedByCaller. I cant figure out why? I have unlocked all layers, opened the entity for Write, CommandFlags have been set to Session and UsePickSet (shuffled through all).
Anybody got any suggestions?
Looking at your description, you probably need a recursive explode. Sometime ago I did a code around this, for other type of entities, but you can adjust it.
private List<DBObject> FullExplode(Entity ent)
{
// final result
List<DBObject> fullList = new List<DBObject>();
// explode the entity
DBObjectCollection explodedObjects = new DBObjectCollection();
ent.Explode(explodedObjects);
foreach (Entity explodedObj in explodedObjects)
{
// if the exploded entity is a blockref or mtext
// then explode again
if (explodedObj.GetType() == typeof(BlockReference) ||
explodedObj.GetType() == typeof(MText))
{
fullList.AddRange(FullExplode(explodedObj));
}
else
fullList.Add(explodedObj);
}
return fullList;
}
source: http://adndevblog.typepad.com/infrastructure/2013/04/get-cogopoint-label-text.html
I finally found out the reason why the Original objects werent getting erased.
In the earlier part of the code, a AutoCAD Plant3D dwg is exported to AutoCAD (ExporttoAutoCAD / Saveas), this was creating Proxy items. These cant be deleted manually or via code.
Only way is to explode the PipeLines and Inline assets before exporting the file. This happens automatically if you export the file, but if you use saveas, you will have to explode the Pipe components before you export the file.
Wasted a lot of time understanding the cause, but finally got it!

Autocad API detect shapes inside layer

I am using Autocad 2012 with the API provided. I am developing in c#.
What I am trying to do is select a certain layer, and "detect" all rectangles / squares in that layer. Ultimateley, I would like to be able to draw inside of all of those rectangles that I have "detected" (using their coordinates).
So far, I am using the LayerTable class along with GetObjects to associate layers with objects, like so:
LayerTable layers;
layers = acTrans.GetObject(acCurDb.LayerTableId, OpenMode.ForRead) as LayerTable;
String layerNames = "";
foreach (ObjectId layer in layers)
{
LayerTableRecord layerTableRec;
layerTableRec = acTrans.GetObject(layer, OpenMode.ForRead) as LayerTableRecord;
layerNames += layerTableRec.Name+"\n";
}
I can't seem to figure out where to go from here though. How to select just one layer, and then detect shapes inside of it. Can someone point me in the correct direction, in terms of what classes / methods to look into? Thanks.
Ultimately, you need to take another look at the AutoCAD object model. The BlockTableRecord "ModelSpace" is what will contain all* of the AutoCAD entities that have layer assignments. Once you have the BlockTableRecord open for read, you can filter down to entities matching whatever layers you're interested in. LINQ can come in handy here.
You don't actually care about the layer's objectID in this instance, just the name. You only really open up the LayerTableRecord when you want to change a layer. If you'll be changing entity properties, you really need to familiarize yourself with the Transaction class. There's also a faster alternative to using 'As' in AutoCAD by leveraging RXObject.GetClass().
*Entities can also live in other BlockTableRecords (any additional layouts for example) but for now you'll likely be fine with just modelspace.
Here's a little snippet to get you started:
var acDoc = Application.DocumentManager.MdiActiveDocument;
var acDb = acDoc.Database;
using (var tr = database.TransactionManager.StartTransaction())
{
try
{
var entClass = RXObject.GetClass(typeof(Entity));
var modelSpaceId = SymbolUtilityServices.GetBlockModelSpaceId(acDb);
var modelSpace = (BlockTableRecord)tr.GetObject(modelSpaceId, OpenMode.ForRead);
foreach (ObjectId id in modelSpace)
{
if (!id.ObjectClass.IsDerivedFrom(entClass)) // For entity this is a little redundant, but it works well with derived classes
continue;
var ent = (Entity)tr.GetObject(id, OpenMode.ForRead)
// Check for the entity's layer
// You'll need to upgrade the entity to OpenMode.ForWrite if you want to change anything
}
tr.Commit();
}
catch (Autodesk.AutoCAD.Runtime.Exception ex)
{
acDoc.Editor.WriteMessage(ex.Message);
}
}
var acDoc = Application.DocumentManager.MdiActiveDocument;
var acDb = acDoc.Database;
using (var tr = database.TransactionManager.StartTransaction())
{
try
{
var entClass = RXObject.GetClass(typeof(Entity));
var modelSpaceId = SymbolUtilityServices.GetBlockModelSpaceId(acDb);
var modelSpace = (BlockTableRecord)tr.GetObject(modelSpaceId, OpenMode.ForRead);
foreach (ObjectId id in modelSpace)
{
Entity acEnt = (Entity)tr.GetObject(id, OpenMode.ForRead);
string layerName = acEnt.Name;
}
tr.Commit();
}
catch (Autodesk.AutoCAD.Runtime.Exception ex)
{
acDoc.Editor.WriteMessage(ex.Message);
}
}

Categories

Resources