Exporting AutoCAD Entities to KML – Color Issues with ByLayer/ByBlock

Exporting AutoCAD Entities to KML – Color Issues with ByLayer/ByBlock

yu85.info
Collaborator Collaborator
401 Views
6 Replies
Message 1 of 7

Exporting AutoCAD Entities to KML – Color Issues with ByLayer/ByBlock

yu85.info
Collaborator
Collaborator

 

Hi everyone,

I'm developing an AutoCAD plugin that exports selected entities to KML for visualization in Google Earth. The plugin handles various entity types like lines, polylines, circles, blocks, and points. 

The issue I'm facing is with color representation. When entities have their color set to ByLayer or ByBlock, the exported KML elements appear black in Google Earth, even though they have distinct colors in AutoCAD. I've attempted to retrieve the actual color by accessing the layer's color when the entity's color is ByLayer, but the problem persists.

Here's a snippet of the code handling color conversion:

private string ColorToKML(Color color)
{
    Color trueColor = color;

    if (color.ColorMethod == ColorMethod.ByAci)
    {
        trueColor = Color.FromColorIndex(ColorMethod.ByAci, color.ColorIndex);
    }
    else if (color.ColorMethod == ColorMethod.ByLayer || color.ColorMethod == ColorMethod.ByBlock)
    {
        // Default to a light gray color
        return "ffAAAAAA";
    }

    byte r = trueColor.Red;
    byte g = trueColor.Green;
    byte b = trueColor.Blue;

    return "ff" + b.ToString("X2") + g.ToString("X2") + r.ToString("X2");
}

 

Despite this, the colors in Google Earth don't match the ones in AutoCAD. Has anyone encountered a similar issue or can suggest a solution to accurately reflect entity colors in the exported KML?

Any insights would be greatly appreciated!

0 Likes
Accepted solutions (1)
402 Views
6 Replies
Replies (6)
Message 2 of 7

ActivistInvestor
Mentor
Mentor

I don't see any issue with your posted code, however for nested objects (e.g., those within block references) you would have to calculate the 'effective color' of each nested entity, using the nested entity's color, and the colors of all block references it is nested within, which is more complicated.

 

If a nested entity's color is 'BYBLOCK', then it inherits the color of each block reference it appears in. So you have to look at the latter to determine what the effective color of each entity is.

 

One way to make this simpler may be to explode everything before doing the extraction. You can find some code here that emulates the legacy BURST express tool (or you can just use that as well).

Message 3 of 7

yu85.info
Collaborator
Collaborator

Thanks @ActivistInvestor for your reply. I wish I knew where is the problem because eventually when I import the kml file it is all in black colour

Here is the complete code 

using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Colors; using System; using System.Collections.Generic; using System.IO; using System.Text; using ProjNet.CoordinateSystems; using ProjNet.CoordinateSystems.Transformations;

[assembly: CommandClass(typeof(ExportToKML.Commands))]

namespace ExportToKML { public class Commands { private static readonly ICoordinateTransformation transform; private const double ShiftLonDegrees = -0.000075; private const double ShiftLatDegrees = -0.000067;

static Commands()
    {
        CoordinateSystemFactory csFactory = new CoordinateSystemFactory();
        var source = csFactory.CreateFromWkt("PROJCS[\"Israel 1993 / Israeli TM Grid\",GEOGCS[\"GCS_Israel_1993\",DATUM[\"D_Israel_1993\",SPHEROID[\"GRS_1980\",6378137,298.257222101],TOWGS84[-48,55,52,0,0,0,0]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",31.73439361111111],PARAMETER[\"central_meridian\",35.20451694444445],PARAMETER[\"scale_factor\",1.0000067],PARAMETER[\"false_easting\",219529.584],PARAMETER[\"false_northing\",626907.39],UNIT[\"Meter\",1]]");
        var target = GeographicCoordinateSystem.WGS84;
        transform = new CoordinateTransformationFactory().CreateFromCoordinateSystems(source, target);
    }

    [CommandMethod("KML")]
    public void ExportSelectionToKML()
    {
        Document doc = Application.DocumentManager.MdiActiveDocument;
        Editor ed = doc.Editor;
        Database db = doc.Database;

        PromptSelectionResult psr = ed.GetSelection();
        if (psr.Status != PromptStatus.OK)
            return;

        PromptSaveFileOptions saveOpts = new PromptSaveFileOptions("Select KML output path:");
        saveOpts.Filter = "KML Files (*.kml)|*.kml";
        PromptFileNameResult saveResult = ed.GetFileNameForSave(saveOpts);
        if (saveResult.Status != PromptStatus.OK)
            return;

        string filePath = saveResult.StringResult;

        using (Transaction tr = db.TransactionManager.StartTransaction())
        {
            LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable;
            StringBuilder kml = new StringBuilder();
            Dictionary<string, string> layerStyles = new Dictionary<string, string>();

            kml.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
            kml.AppendLine("<kml xmlns=\"http://www.opengis.net/kml/2.2\">");
            kml.AppendLine("<Document>");

            SelectionSet ss = psr.Value;
            foreach (SelectedObject obj in ss)
            {
                if (obj == null) continue;
                Entity ent = tr.GetObject(obj.ObjectId, OpenMode.ForRead) as Entity;
                if (ent == null) continue;

                string layerName = ent.Layer;
                string kmlColor = ResolveEntityColor(ent, db, tr);
                string styleId = "style_" + layerName.Replace(" ", "_");

                if (!layerStyles.ContainsKey(layerName))
                {
                    layerStyles[layerName] = kmlColor;
                    kml.AppendLine($"<Style id=\"{styleId}\">");
                    kml.AppendLine($"  <LineStyle><color>{kmlColor}</color><width>2</width></LineStyle>");
                    kml.AppendLine($"  <IconStyle><color>{kmlColor}</color><scale>1.2</scale><Icon><href>http://maps.google.com/mapfiles/kml/shapes/placemark_circle.png</href></Icon></IconStyle>");
                    kml.AppendLine($"  <LabelStyle><scale>0</scale></LabelStyle>");
                    kml.AppendLine("</Style>");
                }

                if (ent is DBPoint point)
                {
                    WritePointToKML(kml, point.Position, "", "", styleId);
                }
                else if (ent is BlockReference blockRef)
                {
                    string blockData = GetBlockAttributes(blockRef, tr);
                    WritePointToKML(kml, blockRef.Position, "", blockData, styleId);
                }
                else if (ent is Polyline poly)
                {
                    List<Point3d> pts = SamplePolyline(poly);
                    WriteLineToKML(kml, pts, layerName, styleId);
                }
                else if (ent is Polyline3d poly3d)
                {
                    List<Point3d> pts = new List<Point3d>();
                    foreach (ObjectId vtxId in poly3d)
                    {
                        PolylineVertex3d vtx = tr.GetObject(vtxId, OpenMode.ForRead) as PolylineVertex3d;
                        pts.Add(vtx.Position);
                    }
                    WriteLineToKML(kml, pts, layerName, styleId);
                }
                else if (ent is Line line)
                {
                    WriteLineToKML(kml, new List<Point3d> { line.StartPoint, line.EndPoint }, layerName, styleId);
                }
                else if (ent is DBText text)
                {
                    WritePointToKML(kml, text.Position, "", text.TextString, styleId);
                }
                else if (ent is Circle circle)
                {
                    List<Point3d> pts = SampleCircle(circle);
                    WriteLineToKML(kml, pts, layerName + " (Circle)", styleId);
                }
            }

            kml.AppendLine("</Document>");
            kml.AppendLine("</kml>");

            File.WriteAllText(filePath, kml.ToString(), Encoding.UTF8);
            ed.WriteMessage($"\nKML saved to: {filePath}");

            tr.Commit();
        }
    }

    private void WritePointToKML(StringBuilder kml, Point3d pt, string name, string description, string styleId)
    {
        var (lon, lat) = ConvertITMtoWGS84(pt.X, pt.Y);
        kml.AppendLine("<Placemark>");
        if (!string.IsNullOrEmpty(name))
            kml.AppendLine($"  <name>{name}</name>");
        if (!string.IsNullOrEmpty(description))
            kml.AppendLine($"  <description><![CDATA[{description}]]></description>");
        kml.AppendLine($"  <styleUrl>#{styleId}</styleUrl>");
        kml.AppendLine("  <Point>");
        kml.AppendLine($"    <coordinates>{lon},{lat},0</coordinates>");
        kml.AppendLine("  </Point>");
        kml.AppendLine("</Placemark>");
    }

    private void WriteLineToKML(StringBuilder kml, List<Point3d> pts, string name, string styleId)
    {
        kml.AppendLine("<Placemark>");
        kml.AppendLine($"  <name>{name}</name>");
        kml.AppendLine($"  <styleUrl>#{styleId}</styleUrl>");
        kml.AppendLine("  <LineString>");
        kml.AppendLine("    <coordinates>");
        foreach (var pt in pts)
        {
            var (lon, lat) = ConvertITMtoWGS84(pt.X, pt.Y);
            kml.AppendLine($"      {lon},{lat},0");
        }
        kml.AppendLine("    </coordinates>");
        kml.AppendLine("  </LineString>");
        kml.AppendLine("</Placemark>");
    }

    private List<Point3d> SamplePolyline(Polyline poly)
    {
        List<Point3d> pts = new List<Point3d>();
        double length = poly.Length;
        int segments = (int)(length / 1.0);
        if (segments < 2) segments = 2;
        for (int i = 0; i <= segments; i++)
        {
            double param = poly.GetParameterAtDistance(length * i / segments);
            pts.Add(poly.GetPointAtParameter(param));
        }
        return pts;
    }

    private List<Point3d> SampleCircle(Circle circle)
    {
        List<Point3d> pts = new List<Point3d>();
        int segments = 36;
        for (int i = 0; i <= segments; i++)
        {
            double angle = 2 * Math.PI * i / segments;
            Point3d pt = circle.Center + new Vector3d(Math.Cos(angle), Math.Sin(angle), 0) * circle.Radius;
            pts.Add(pt);
        }
        return pts;
    }

    private string GetBlockAttributes(BlockReference blkRef, Transaction tr)
    {
        StringBuilder desc = new StringBuilder();
        foreach (ObjectId id in blkRef.AttributeCollection)
        {
            AttributeReference attRef = tr.GetObject(id, OpenMode.ForRead) as AttributeReference;
            if (attRef != null)
            {
                desc.AppendLine($"{attRef.Tag}: {attRef.TextString}<br>");
            }
        }
        return desc.ToString();
    }

    private (double lon, double lat) ConvertITMtoWGS84(double x, double y)
    {
        double[] result = transform.MathTransform.Transform(new double[] { x, y });
        return (result[0] + ShiftLonDegrees, result[1] + ShiftLatDegrees);
    }

    private string ResolveEntityColor(Entity entity, Database db, Transaction tr)
    {
        Color trueColor = entity.Color;

        if (trueColor.ColorMethod == ColorMethod.ByLayer)
        {
            LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable;
            LayerTableRecord ltr = tr.GetObject(entity.LayerId, OpenMode.ForRead) as LayerTableRecord;
            trueColor = ltr.Color;
        }
        else if (trueColor.ColorMethod == ColorMethod.ByBlock)
        {
            if (entity is BlockReference br)
            {
                trueColor = br.Color;
            }
            else
            {
                LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable;
                LayerTableRecord ltr = tr.GetObject(entity.LayerId, OpenMode.ForRead) as LayerTableRecord;
                trueColor = ltr.Color;
            }
        }

        if (trueColor.ColorMethod == ColorMethod.ByAci)
        {
            trueColor = Color.FromColorIndex(ColorMethod.ByAci, trueColor.ColorIndex);
        }

        byte r = trueColor.Red;
        byte g = trueColor.Green;
        byte b = trueColor.Blue;

        return "ff" + b.ToString("X2") + g.ToString("X2") + r.ToString("X2");
    }
}

}

 

0 Likes
Message 4 of 7

ActivistInvestor
Mentor
Mentor

I see a few issues with your code, but I'm not going to get into all of them here. The one thing that I noticed is that your code doesn't export the geometry of a block reference, only a placement with attribute description, so I don't think the problem is nested geometry.

 

What you might want to do is to create a Dictionary<ObjectId, Color> where the ObjectId or 'key' is the ObjectId of a LayerTableRecord, and the value is the layer's Color. Then, when you find an entity having a color of 'BYLAYER', you just use the entity's LayerId property to look up the layer color in the dictionary. This would eliminate a lot of confusion in the code in addition to making it a bit more efficient. If an entity's color is BYBLOCK and it's not nested, then you can use whatever color you want to assign to 'BYBLOCK'. So, the algorithm would be If an entity's color is not BYLAYER, then emit the entity's color, else emit the color of the layer the entity is on.

 

Here is some code I've used in the past that does what I describe above. With this code added to your project, you only need to use the GetEffectiveColor() extension method included below.

public static class LayerColorMap
{
   static LayerColorMap()
   {
      ByBlockColor = Color.FromColorIndex(ColorMethod.ByBlock, 0);
   }

   /// <summary>
   /// Can be assigned to any color to use 
   /// for entities having color = BYBLOCK;
   /// </summary>
   public static Color ByBlockColor { get; set; }

   static Dictionary<ObjectId, Color> map = new Dictionary<ObjectId, Color>();

   static Color GetLayerColor(ObjectId layerTableRecordId)
   {
      if(map.Count == 0)
      {
         using(var tr = new OpenCloseTransaction())
         {
            Database db = layerTableRecordId.Database;
            var layers = (LayerTable) tr.GetObject(db.LayerTableId, OpenMode.ForRead);
            foreach(ObjectId id in layers)
            {
               var layer = (LayerTableRecord) tr.GetObject(id, OpenMode.ForRead);
               map[id] = layer.Color;
            }
            tr.Commit();
         }
      }
      return map[layerTableRecordId];
   }

   /// <summary>
   /// The Entity.GetEffectiveColor() method returns the
   /// 'effective color' of an entity. If the entity's
   /// color is bylayer, it returns the color of the layer
   /// the entity is on. If the entity's color is BYBLOCK,
   /// it returns the value assigned to the ByBlockColor
   /// property. Otherwise, it returns the entity's color.
   ///
   /// Usage:
   ///
   ///    Entity entity = /// assign to an entity
   ///
   ///    Color effectiveColor = entity.GetEffectiveColor();
   ///
   /// </summary>
   /// <param name="entity">The entity whose color is requested</param>
   /// <returns>The 'effective' color of the entity</returns>

   public static Color GetEffectiveColor(this Entity entity)
   {
      switch(entity.ColorIndex)
      {
         case 0: 
            return ByBlockColor;
         case 256: 
            return GetLayerColor(entity.LayerId);
         default:
            return entity.Color;
      }
   }
}

 

 

Message 5 of 7

yu85.info
Collaborator
Collaborator

Hi, thanks for your time. I have tried to follow your instructions but still getting the same black result. Can't find the problem 😞

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Colors;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ProjNet.CoordinateSystems;
using ProjNet.CoordinateSystems.Transformations;

[assembly: CommandClass(typeof(ExportToKML.Commands))]

namespace ExportToKML
{
    public static class LayerColorMap
    {
        public static Color ByBlockColor { get; set; } = Color.FromColorIndex(ColorMethod.ByAci, 7); // Default light gray
        private static Dictionary<ObjectId, Color> map = new Dictionary<ObjectId, Color>();

        private static Color GetLayerColor(ObjectId layerTableRecordId)
        {
            if (!map.ContainsKey(layerTableRecordId))
            {
                Database db = layerTableRecordId.Database;
                using (Transaction tr = db.TransactionManager.StartTransaction())
                {
                    LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable;
                    foreach (ObjectId id in lt)
                    {
                        LayerTableRecord ltr = tr.GetObject(id, OpenMode.ForRead) as LayerTableRecord;
                        map[id] = ltr.Color;
                    }
                    tr.Commit();
                }
            }
            return map[layerTableRecordId];
        }

        public static Color GetEffectiveColor(this Entity entity)
        {
            switch (entity.ColorIndex)
            {
                case 0: return ByBlockColor;
                case 256: return GetLayerColor(entity.LayerId);
                default: return entity.Color;
            }
        }

        // KML uses AABBGGRR format (alpha, blue, green, red)
        public static string ToKmlColor(this Color color, byte alpha = 255)
        {
            // Fixed method to properly handle different color types
            byte red, green, blue;
            
            if (color.ColorMethod == ColorMethod.ByAci)
            {
                // Get RGB from ACI color index directly
                Autodesk.AutoCAD.Colors.ColorMethod oldMethod = color.ColorMethod;
                short colorIndex = color.ColorIndex;
                
                // Use Autodesk's built-in method to convert ACI to RGB
                Color aciColor = Color.FromColorIndex(oldMethod, colorIndex);
                red = aciColor.Red;
                green = aciColor.Green;
                blue = aciColor.Blue;
            }
            else if (color.ColorMethod == ColorMethod.ByColor)
            {
                red = color.Red;
                green = color.Green;
                blue = color.Blue;
            }
            else
            {
                // Default to white for any other color method
                red = green = blue = 255;
            }
            
            // Ensure no black colors (fixes Google Earth display issue)
            if (red == 0 && green == 0 && blue == 0)
            {
                red = green = blue = 1; // Near black but not exactly black
            }
            
            return alpha.ToString("X2") + blue.ToString("X2") + green.ToString("X2") + red.ToString("X2");
        }
    }

    public class Commands
    {
        private static readonly ICoordinateTransformation transform;
        private const double ShiftLonDegrees = -0.000075;
        private const double ShiftLatDegrees = -0.000067;

        static Commands()
        {
            CoordinateSystemFactory csFactory = new CoordinateSystemFactory();
            var source = csFactory.CreateFromWkt("PROJCS[\"Israel 1993 / Israeli TM Grid\",GEOGCS[\"GCS_Israel_1993\",DATUM[\"D_Israel_1993\",SPHEROID[\"GRS_1980\",6378137,298.257222101],TOWGS84[-48,55,52,0,0,0,0]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",31.73439361111111],PARAMETER[\"central_meridian\",35.20451694444445],PARAMETER[\"scale_factor\",1.0000067],PARAMETER[\"false_easting\",219529.584],PARAMETER[\"false_northing\",626907.39],UNIT[\"Meter\",1]]");
            var target = GeographicCoordinateSystem.WGS84;
            transform = new CoordinateTransformationFactory().CreateFromCoordinateSystems(source, target);
        }

        [CommandMethod("KML")]
        public void ExportSelectionToKML()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            Database db = doc.Database;

            PromptSelectionResult psr = ed.GetSelection();
            if (psr.Status != PromptStatus.OK)
                return;

            PromptSaveFileOptions saveOpts = new PromptSaveFileOptions("Select KML output path:");
            saveOpts.Filter = "KML Files (*.kml)|*.kml";
            PromptFileNameResult saveResult = ed.GetFileNameForSave(saveOpts);
            if (saveResult.Status != PromptStatus.OK)
                return;

            string filePath = saveResult.StringResult;

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable;
                StringBuilder kml = new StringBuilder();
                Dictionary<string, string> layerStyles = new Dictionary<string, string>();
                List<string> placemarks = new List<string>();

                kml.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
                kml.AppendLine("<kml xmlns=\"http://www.opengis.net/kml/2.2\">");
                kml.AppendLine("<Document>");
                kml.AppendLine("<name>AutoCAD Export</name>");
                kml.AppendLine("<description>Generated from AutoCAD</description>");

                SelectionSet ss = psr.Value;
                foreach (SelectedObject obj in ss)
                {
                    if (obj == null) continue;
                    Entity ent = tr.GetObject(obj.ObjectId, OpenMode.ForRead) as Entity;
                    if (ent == null) continue;

                    string layerName = ent.Layer;
                    Color color = ent.GetEffectiveColor();
                    string kmlColor = color.ToKmlColor();
                    
                    // Create a unique style ID for each entity to preserve exact colors
                    string styleId = "style_" + layerName.Replace(" ", "_") + "_" + obj.ObjectId.Handle.ToString();

                    // Create style for this entity
                    kml.AppendLine($"<Style id=\"{styleId}\">");
                    kml.AppendLine($"  <LineStyle>");
                    kml.AppendLine($"    <color>{kmlColor}</color>");
                    kml.AppendLine($"    <width>2</width>");
                    kml.AppendLine($"  </LineStyle>");
                    kml.AppendLine($"  <PolyStyle>");
                    kml.AppendLine($"    <color>{kmlColor}</color>");
                    kml.AppendLine($"    <fill>0</fill>");
                    kml.AppendLine($"  </PolyStyle>");
                    kml.AppendLine($"  <IconStyle>");
                    kml.AppendLine($"    <color>{kmlColor}</color>");
                    kml.AppendLine($"    <scale>1.2</scale>");
                    kml.AppendLine($"    <Icon><href>http://maps.google.com/mapfiles/kml/shapes/placemark_circle.png</href></Icon>");
                    kml.AppendLine($"  </IconStyle>");
                    kml.AppendLine($"  <LabelStyle><scale>0</scale></LabelStyle>");
                    kml.AppendLine("</Style>");

                    if (ent is DBPoint point)
                    {
                        WritePointToKML(kml, point.Position, layerName, "", styleId);
                    }
                    else if (ent is BlockReference blockRef)
                    {
                        string blockData = GetBlockAttributes(blockRef, tr);
                        WritePointToKML(kml, blockRef.Position, layerName, blockData, styleId);
                    }
                    else if (ent is Polyline poly)
                    {
                        List<Point3d> pts = SamplePolyline(poly);
                        WriteLineToKML(kml, pts, layerName, styleId);
                    }
                    else if (ent is Polyline3d poly3d)
                    {
                        List<Point3d> pts = new List<Point3d>();
                        foreach (ObjectId vtxId in poly3d)
                        {
                            PolylineVertex3d vtx = tr.GetObject(vtxId, OpenMode.ForRead) as PolylineVertex3d;
                            pts.Add(vtx.Position);
                        }
                        WriteLineToKML(kml, pts, layerName, styleId);
                    }
                    else if (ent is Line line)
                    {
                        WriteLineToKML(kml, new List<Point3d> { line.StartPoint, line.EndPoint }, layerName, styleId);
                    }
                    else if (ent is DBText text)
                    {
                        WritePointToKML(kml, text.Position, layerName, text.TextString, styleId);
                    }
                    else if (ent is Circle circle)
                    {
                        List<Point3d> pts = SampleCircle(circle);
                        WriteLineToKML(kml, pts, layerName + " (Circle)", styleId);
                    }
                }

                kml.AppendLine("</Document>");
                kml.AppendLine("</kml>");

                File.WriteAllText(filePath, kml.ToString(), Encoding.UTF8);
                ed.WriteMessage($"\nKML saved to: {filePath}");

                tr.Commit();
            }
        }

        private static void WritePointToKML(StringBuilder kml, Point3d pt, string name, string description, string styleId)
        {
            var (lon, lat) = ConvertITMtoWGS84(pt.X, pt.Y);
            kml.AppendLine("<Placemark>");
            if (!string.IsNullOrEmpty(name)) kml.AppendLine($"  <name>{name}</name>");
            if (!string.IsNullOrEmpty(description)) kml.AppendLine($"  <description><![CDATA[{description}]]></description>");
            kml.AppendLine($"  <styleUrl>#{styleId}</styleUrl>");
            kml.AppendLine("  <Point>");
            kml.AppendLine($"    <coordinates>{lon},{lat},0</coordinates>");
            kml.AppendLine("  </Point>");
            kml.AppendLine("</Placemark>");
        }

        private static void WriteLineToKML(StringBuilder kml, List<Point3d> pts, string name, string styleId)
        {
            kml.AppendLine("<Placemark>");
            kml.AppendLine($"  <name>{name}</name>");
            kml.AppendLine($"  <styleUrl>#{styleId}</styleUrl>");
            kml.AppendLine("  <LineString>");
            kml.AppendLine("    <extrude>0</extrude>");
            kml.AppendLine("    <tessellate>1</tessellate>");
            kml.AppendLine("    <altitudeMode>clampToGround</altitudeMode>");
            kml.AppendLine("    <coordinates>");
            foreach (var pt in pts)
            {
                var (lon, lat) = ConvertITMtoWGS84(pt.X, pt.Y);
                kml.AppendLine($"      {lon},{lat},0");
            }
            kml.AppendLine("    </coordinates>");
            kml.AppendLine("  </LineString>");
            kml.AppendLine("</Placemark>");
        }

        private static List<Point3d> SamplePolyline(Polyline poly)
        {
            List<Point3d> pts = new List<Point3d>();
            double length = poly.Length;
            int segments = Math.Max((int)(length / 1.0), 2);
            for (int i = 0; i <= segments; i++)
            {
                double param = poly.GetParameterAtDistance(length * i / segments);
                pts.Add(poly.GetPointAtParameter(param));
            }
            return pts;
        }

        private static List<Point3d> SampleCircle(Circle circle)
        {
            List<Point3d> pts = new List<Point3d>();
            int segments = 36;
            for (int i = 0; i <= segments; i++)
            {
                double angle = 2 * Math.PI * i / segments;
                Point3d pt = circle.Center + new Vector3d(Math.Cos(angle), Math.Sin(angle), 0) * circle.Radius;
                pts.Add(pt);
            }
            return pts;
        }

        private static string GetBlockAttributes(BlockReference blkRef, Transaction tr)
        {
            StringBuilder desc = new StringBuilder();
            foreach (ObjectId id in blkRef.AttributeCollection)
            {
                if (tr.GetObject(id, OpenMode.ForRead) is AttributeReference attRef)
                {
                    desc.AppendLine($"{attRef.Tag}: {attRef.TextString}<br>");
                }
            }
            return desc.ToString();
        }

        private static (double lon, double lat) ConvertITMtoWGS84(double x, double y)
        {
            double[] result = transform.MathTransform.Transform(new double[] { x, y });
            return (result[0] + ShiftLonDegrees, result[1] + ShiftLatDegrees);
        }
    }
}
0 Likes
Message 6 of 7

ActivistInvestor
Mentor
Mentor
Accepted solution

It was easier and quicker for me to write this, than it would have been to untangle Your ToKmlColor() method. I'm not sure what all of that code is for but it seems to me that the task is fairly simple:

 

public static partial class AcColorExtensions
{
   const string BLACK = "<color>#ff010101</color>";    // goggle earth issue
   const string BYBLOCK = "<color>#ff808080</color>";  // grey

   /// <summary>
   /// Extension method targeting Autodesk.AutoCAD.Colors.Color
   /// that returns a KML Color element specifying the color.
   /// If an entity's color is BYBLOCK, the constant BYBLOCK is
   /// returned. If the entity's color is black, the constant
   /// BLACK is returned.
   /// 
   /// usage:
   /// 
   ///   Color color = someEntity.GetEffectiveColor();
   ///   string tag = color.ToKmlColorElement();
   ///   
   /// </summary>
   public static string ToKmlColorElement(this Color color, byte alpha = 255)
   {
      if(color.ColorMethod == ColorMethod.ByBlock)
         return BYBLOCK;
      var clr = color.ColorValue;
      if(clr.R + clr.G + clr.B == 0)
         return BLACK;
      else
         return $"<color>#{alpha:x2}{clr.B:x2}{clr.G:x2}{clr.R:x2}</color>";
   }

}

Test:

Command: DUMPCOLOR
====[Autodesk.AutoCAD.Colors.Color: 46,209,125]====
DictionaryKey = (null)
DictionaryKeyLength = 0
EntityColor = Autodesk.AutoCAD.Colors.EntityColor
HasBookName = False
HasColorName = False
ColorNameForDisplay = "46,209,125"
BookName = ""
ColorName = ""
PenIndex = -1
ColorIndex = 112
Blue = 125
Green = 209
Red = 46
IsNone = False
IsForeground = False
IsByPen = False
IsByAci = False
IsByBlock = False
IsByLayer = False
IsByColor = True
Explanation = "AutoCAD Color Index"
Description = "AutoCAD Color Index"
ColorMethod = ByColor
ColorValue = Color [A=255, R=46, G=209, B=125]
AutoDelete = True
IsDisposed = False
UnmanagedObject = 3129698973888
<color>#ff7dd12e</color>      <--- ToKmlColorElement()
Command:
Message 7 of 7

yu85.info
Collaborator
Collaborator

Thank you very much for your help. I really appreciate it.

Here is the complete code

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Colors;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ProjNet.CoordinateSystems;
using ProjNet.CoordinateSystems.Transformations;

[assembly: CommandClass(typeof(ExportToKML.Commands))]

namespace ExportToKML
{
    public class Commands
    {
        private static readonly ICoordinateTransformation transform;
        private const double ShiftLonDegrees = -0.000075;
        private const double ShiftLatDegrees = -0.000067;

        private const string PURE_BLACK_KML = "ff010101";
        private const string DEFAULT_COLOR_KML = "ffffffff";

        static Commands()
        {
            CoordinateSystemFactory csFactory = new CoordinateSystemFactory();
            var source = csFactory.CreateFromWkt("PROJCS[\"Israel 1993 / Israeli TM Grid\",GEOGCS[\"GCS_Israel_1993\",DATUM[\"D_Israel_1993\",SPHEROID[\"GRS_1980\",6378137,298.257222101],TOWGS84[-48,55,52,0,0,0,0]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",31.73439361111111],PARAMETER[\"central_meridian\",35.20451694444445],PARAMETER[\"scale_factor\",1.0000067],PARAMETER[\"false_easting\",219529.584],PARAMETER[\"false_northing\",626907.39],UNIT[\"Meter\",1]]");
            var target = GeographicCoordinateSystem.WGS84;
            transform = new CoordinateTransformationFactory().CreateFromCoordinateSystems(source, target);
        }

        [CommandMethod("KML")]
        public void ExportSelectionToKML()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            Database db = doc.Database;

            PromptSelectionResult psr = ed.GetSelection();
            if (psr.Status != PromptStatus.OK)
                return;

            PromptSaveFileOptions saveOpts = new PromptSaveFileOptions("Select KML output path:");
            saveOpts.Filter = "KML Files (*.kml)|*.kml";
            PromptFileNameResult saveResult = ed.GetFileNameForSave(saveOpts);
            if (saveResult.Status != PromptStatus.OK)
                return;

            string filePath = saveResult.StringResult;

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                StringBuilder kml = new StringBuilder();
                kml.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
                kml.AppendLine("<kml xmlns=\"http://www.opengis.net/kml/2.2\">");
                kml.AppendLine("<Document>");
                kml.AppendLine("<name>AutoCAD Export</name>");
                kml.AppendLine("<description>Generated from AutoCAD</description>");

                SelectionSet ss = psr.Value;
                foreach (SelectedObject obj in ss)
                {
                    if (obj != null)
                    {
                        Entity ent = tr.GetObject(obj.ObjectId, OpenMode.ForRead) as Entity;
                        if (ent != null)
                        {
                            ProcessEntity(kml, ent, tr);
                        }
                    }
                }

                kml.AppendLine("</Document>");
                kml.AppendLine("</kml>");

                File.WriteAllText(filePath, kml.ToString(), Encoding.UTF8);
                ed.WriteMessage($"\nKML saved to: {filePath}");

                tr.Commit();
            }
        }

        private static void ProcessEntity(StringBuilder kml, Entity ent, Transaction tr)
        {
            Color entityColor;

            if (ent.Color.ColorMethod == ColorMethod.ByLayer)
            {
                LayerTableRecord layer = tr.GetObject(ent.LayerId, OpenMode.ForRead) as LayerTableRecord;
                entityColor = layer?.Color ?? Color.FromRgb(255, 255, 255);
            }
            else if (ent.Color.ColorMethod == ColorMethod.ByBlock)
            {
                entityColor = Color.FromRgb(200, 200, 200);
            }
            else
            {
                entityColor = ent.Color;
            }

            string kmlColor = GetKmlColorFromAcadColor(entityColor);
            string styleId = $"style_{ent.ObjectId.Handle.Value:X}";
            WriteLineStyleToKML(kml, styleId, kmlColor);

            if (ent is Line line)
            {
                WriteLineToKML(kml, new List<Point3d> { line.StartPoint, line.EndPoint }, ent.Layer, styleId);
            }
            else if (ent is Polyline poly)
            {
                List<Point3d> pts = SamplePolyline(poly);
                WriteLineToKML(kml, pts, ent.Layer, styleId);
            }
            else if (ent is Polyline3d poly3d)
            {
                List<Point3d> pts = new List<Point3d>();
                foreach (ObjectId vtxId in poly3d)
                {
                    PolylineVertex3d vtx = tr.GetObject(vtxId, OpenMode.ForRead) as PolylineVertex3d;
                    pts.Add(vtx.Position);
                }
                WriteLineToKML(kml, pts, ent.Layer, styleId);
            }
            else if (ent is Circle circle)
            {
                List<Point3d> pts = SampleCircle(circle);
                WriteLineToKML(kml, pts, ent.Layer + " (Circle)", styleId);
            }
            else if (ent is BlockReference blockRef)
            {
                string blockData = GetBlockAttributes(blockRef, tr);
                WritePointToKML(kml, blockRef.Position, ent.Layer, blockData, styleId);
            }
            else if (ent is DBText text)
            {
                WritePointToKML(kml, text.Position, ent.Layer, text.TextString, styleId);
            }
            else if (ent is DBPoint point)
            {
                WritePointToKML(kml, point.Position, ent.Layer, "", styleId);
            }
        }

        private static string GetKmlColorFromAcadColor(Color color)
        {
            if (color == null)
                return DEFAULT_COLOR_KML;

            if (color.ColorMethod == ColorMethod.ByBlock)
                return "ff808080";

            var clr = color.ColorValue;

            if (clr.R == 0 && clr.G == 0 && clr.B == 0)
                return PURE_BLACK_KML;

            return $"ff{clr.B:X2}{clr.G:X2}{clr.R:X2}";
        }

        private static void WriteLineStyleToKML(StringBuilder kml, string styleId, string kmlColor)
        {
            kml.AppendLine($"<Style id=\"{styleId}\">");

            kml.AppendLine("  <LineStyle>");
            kml.AppendLine($"    <color>{kmlColor}</color>");
            kml.AppendLine("    <width>2</width>");
            kml.AppendLine("  </LineStyle>");

            kml.AppendLine("  <IconStyle>");
            kml.AppendLine("    <scale>0.5</scale>");
            kml.AppendLine("    <Icon>");
            kml.AppendLine("      <href>http://maps.google.com/mapfiles/kml/shapes/placemark_circle.png</href>");
            kml.AppendLine("    </Icon>");
            kml.AppendLine("  </IconStyle>");

            kml.AppendLine("</Style>");
        }

        private static void WriteLineToKML(StringBuilder kml, List<Point3d> pts, string name, string styleId)
        {
            kml.AppendLine("<Placemark>");
            kml.AppendLine($"  <name>{name}</name>");
            kml.AppendLine($"  <styleUrl>#{styleId}</styleUrl>");
            kml.AppendLine("  <LineString>");
            kml.AppendLine("    <extrude>0</extrude>");
            kml.AppendLine("    <tessellate>1</tessellate>");
            kml.AppendLine("    <altitudeMode>clampToGround</altitudeMode>");
            kml.AppendLine("    <coordinates>");
            foreach (var pt in pts)
            {
                var (lon, lat) = ConvertITMtoWGS84(pt.X, pt.Y);
                kml.AppendLine($"      {lon},{lat},0");
            }
            kml.AppendLine("    </coordinates>");
            kml.AppendLine("  </LineString>");
            kml.AppendLine("</Placemark>");
        }

        private static void WritePointToKML(StringBuilder kml, Point3d pt, string name, string description, string styleId)
        {
            var (lon, lat) = ConvertITMtoWGS84(pt.X, pt.Y);
            kml.AppendLine("<Placemark>");
            // <name> הוסר כדי לא להציג label
            if (!string.IsNullOrEmpty(description))
                kml.AppendLine($"  <description><![CDATA[{description}]]></description>");
            kml.AppendLine($"  <styleUrl>#{styleId}</styleUrl>");
            kml.AppendLine("  <Point>");
            kml.AppendLine($"    <coordinates>{lon},{lat},0</coordinates>");
            kml.AppendLine("  </Point>");
            kml.AppendLine("</Placemark>");
        }

        private static string GetBlockAttributes(BlockReference blkRef, Transaction tr)
        {
            StringBuilder desc = new StringBuilder();
            foreach (ObjectId id in blkRef.AttributeCollection)
            {
                if (tr.GetObject(id, OpenMode.ForRead) is AttributeReference attRef)
                {
                    desc.AppendLine($"{attRef.Tag}: {attRef.TextString}<br>");
                }
            }
            return desc.ToString();
        }

        private static List<Point3d> SamplePolyline(Polyline poly)
        {
            List<Point3d> pts = new List<Point3d>();
            double length = poly.Length;
            int segments = Math.Max((int)(length / 1.0), 2);
            for (int i = 0; i <= segments; i++)
            {
                double param = poly.GetParameterAtDistance(length * i / segments);
                pts.Add(poly.GetPointAtParameter(param));
            }
            return pts;
        }

        private static List<Point3d> SampleCircle(Circle circle)
        {
            List<Point3d> pts = new List<Point3d>();
            int segments = 36;
            for (int i = 0; i <= segments; i++)
            {
                double angle = 2 * Math.PI * i / segments;
                Point3d pt = circle.Center + new Vector3d(Math.Cos(angle), Math.Sin(angle), 0) * circle.Radius;
                pts.Add(pt);
            }
            return pts;
        }

        private static (double lon, double lat) ConvertITMtoWGS84(double x, double y)
        {
            double[] result = transform.MathTransform.Transform(new double[] { x, y });
            return (result[0] + ShiftLonDegrees, result[1] + ShiftLatDegrees);
        }
    }
}
0 Likes