免费视频|新人指南|投诉删帖|广告合作|地信网APP下载

查看: 4496|回复: 16
收起左侧

[二次开发] CAD图形坐标转换——逐点转换

  [复制链接]

0

主题

1988

铜板

2

好友

技术员

Rank: 3Rank: 3

积分
59

爱心勋章

发表于 2021-10-21 16:10 | 显示全部楼层 |阅读模式
本帖最后由 只怕_不再遇上 于 2021-10-21 18:33 编辑

CAD图形的坐标转换工作的些许思考



一、CAD图形文件的坐标转换需求



1. CAD图形文件简述



        CAD图形文件,通常情况下是指AutoDesk公司发行的AutoCAD系列软件所使用的文件格式——DWG数据库格式的图形文件。据统计,全世界接近80%的图形文件都是CAD(或者说DWG)格式的。地理信息行业的基础信息的获取、处理、交互等都经常使用AutoCAD或者基于CAD软件的工作平台进行,这使得地理信息行业大量使用DWG数据库格式的图形数据。



2. 坐标转换工作



        坐标转换是指对描述地理信息要素的坐标信息的时空参照、投影信息、高程基准等进行的转换、变换的工作。“
就坐标系而言,坐标是数据的骨络”。本文不对坐标转换及坐标系统的基础知识进行详细讲解。

        根据现代大地测量、导航工程等学科的发展,我们所使用的定位、测量技术不断进化,这使得我们所对的新旧数据、不同来源数据日益繁多。现代大地测量使用的坐标系统大致可分为局部拟合的“参心”坐标系统,全局拟合的地心坐标系统。我们常言的“旧”坐标系统诸如54北京坐标系统、西安80坐标系统就是静态的、局部拟合的“参心坐标系统”;而GNSS导航卫星所使用的WGS84、国家当前统一使用的CGCS2000坐标系统就是动态的、全局拟合的地心坐标系统。一下文章内容只涉及坐标的静态转换及方法。

        我们通常所说的坐标转换包含着参照系统的转换、投影坐标系的变换等工作。


1.jpg

(图片来自:刘光明)



3. CAD图形数据的坐标转换需求



        地理信息行业所使用的地形图等数据的成图过程通常包含着DWG数据的编辑,甚至于需要最终输出、提交CAD数据格式的成果。新旧数据的参照统一(坐标系统转换)、成果数据从国家大地测量坐标系统向着地方独立坐标系的转换(投影变换)、规划及国土管理等工作中需要对不同来源的数据上图对比等等,都有可能涉及到需要对CAD图形数据进行坐标转换。



        CAD图形的坐标转换方法,根据需要不同我们可以大致分为:1.平移转换2.逐点转换

                                                                                            (参见西安刘光明老师的系列科普工作)



        (1)、平移转换:

                   1). 从图形上选取2至3个控制点
                   2). 对控制点的坐标进行转换(七参数、四参数、重投影等等),可以使用cass的转换工具、大家常用的COORD工具等等
                   3). 将转换后的控制点展进原图形文件        
                   4). 选中全部图形,按基点平移,选择控制点为基点,使选为基点的控制点与转换后的控制点重合(可以看得出来该方法基本上和平面四参数的原理、效果一致
                   5). 如果需要,则按其他控制点位置进行旋转、放缩。
                   6). 检查数据符合要求则另存转换后的文件
                   7). 完

        该转换方法思路简单,方法易行,相信多数工程师们都能轻松完成不需要过多的讨论,技术细节在此不进行详述。

        该方法特别适用于小范围地形图、无法进行逐点转换只能进行控制点参照系统转换(参照系转换需要坐标具备三维信息即两个坐标系统下的大地高是必须的)的图形、用于地籍等工作的已经经过登记等行政手段确认的需要保证面积等信息不变的图形 等其他多数情况下的图形数据。

        

        (2)、逐点转换

         逐点转换是指,对矢量形式的地理要素,需要对其每一个要素的每一个图元组成的坐标信息进行转换,而不只是对控制点进行转换。

        比如,对于CAD图形文件中的Ployline对象,即多段线表示的要素(如建筑、等高线等),每一个多段线要素都包含许多节点,每一个节点的坐标都使用相同的转换参数进行独立转换,则为逐点转换。

        

2.jpg


上面图片中的这个简单房屋,包含着四个特征点,这是一个多段线形式存储的矢量要素

        PS:在GIS软件中进行重投影往往不需要我们操心这个问题,GIS软件会帮助我们搞定所有矢量要素的转换工作。

        

二、逐点转换思路


1. 总体思路

        

        CAD图形文件的逐点转换工作需要对所有矢量要素的每一个坐标节点进行转换。所以我们自然而然产生以下思路:

        (1)、读取每一个矢量要素的全部图元坐标

        (2)、转换图元坐标

        (3)、将转换后的坐标写回原要素当中去

                                 

2. 读取要素图元坐标的思路

        我们将大致思路分为两大类:1.集成或外挂于CAD类程序、2.脱离CAD类程序

        我们将在下一部分进行详细阐述



3. 坐标转换的思路

        1. 在CAD程序内进行转换
        2. 利用已有的程序在CAD外进转换

4. 坐标写回图元要素的思路
        该部分工作与读取部分的工作完全挂钩


三、从图元中读取坐标的具体实现
1.集成或外挂于CAD类程序
        这两种方法没有本质上的区别,都是依赖于某个版本的CAD程序本身的,通过CAD程序已经实现的功能来完成我们的目的。这里需要拥有程序设计基础。
        有的朋友可能知道,版本较高的CAD 程序中本身就有数据导出的功能,我们为什么需要自己实现这部分功能呢?因为我们这部分功能完全的与坐标写回图元要素的工作相关,我们没有办法知道CAD的坐标导出功能对坐标点的识别的顺序,它基本上与要素无关——也就是说CAD导出坐标的过程很可能不会将同一个要素的多个坐标放在一起,顺序被打乱意味着写回工作将受到阻碍。
        (1)集成于CAD程序的功能设计
         集成于CAD程序内的功能我们往往通过C++、LISP、VBA的二次开发来实现。
         一下通过VBA为例来说明。
        
        要实现从CAD中导出全部的图元坐标就需要遍历图形中全部的要素,并知道每种要素类具有哪些可操作的坐标属性。
        地形图中常见的要素类型大致包括:轻义多段线、样条曲线、椭圆、圆(圆弧)、二维多段线、文字标注(多行文字)、直线、点、三维多段线、块参照、块填充(此处不考虑)等。
         
        步骤
        1)从图纸空间中遍历对象
  1. Dim en As AcadEntity
复制代码
        ThisDrawing即当前打开的并处于活动状态的图纸对象,ModelSpace是当前图纸对象的模型空间,在此空间下包含了全部的矢量要素对象。
        要素对象包含三维类型,二维类型,所以相应的坐标也就包含三维的、二维的。如果不考虑高程基准的问题只对平面坐标进行转换,则可以不对第三个纬度对的坐标进行处理,但程序中必须考虑三维坐标的影响。(因为很多对象的坐标通过一维数组进行存储)
       2)获取坐标
        获取到每个对象之后都应该对其坐标属性进行读取、输出
        一下代码展示对多段线对象的坐标进行读取
  1. Public Sub objectInfGet(en As AcadEntity, ByRef n As Integer, ByRef coords() As Double, ByRef id As Variant)
  2.    
  3.         objName = en.ObjectName
复制代码


        我们通过CAD实体的“ObjectName”属性来判别其类型,“AcDbPolyline”的坐标存储在“Coordinates”属性中。其坐标维度为二维。
         事实上多数的对象都具有“Coordinates”属性,并以一维数组方式存储全部坐标,即使有多个节点的坐标需要存储也会全部存储在同一个数组内,区别只在于二维坐标每两个元素代表一个节点,三维坐标每三个元素代表一个节点。
        但是需要注意的是,并不是所有的对象类型都具有同样的属性
        例如:  直线的坐标被分为起点和端点坐标分别存储在“StartPoint”“EndPoint”属性中。
                  文字、多行文字、块参照等则为“插入点坐标”,存储在“InsertionPoint”属性中。
                  样条曲线为“控制点坐标”存储在"ControlPoint"属性中。
                  圆形为“圆心坐标”,存储在"Center"属性中。
                  圆弧具有圆心和起止点坐标分别存储在"Center"、“StartPoint”、“EndPoint”属性中。
        
        3)坐标存储
        坐标被读取出来之后我们将对其进行转换处理,为了方便在CAD以外的转换工具中进行转换,我们需要用文件形式存储坐标内容
        
  1.     '弹出对话框选择坐标文件
复制代码
        以上使用弹出式对话框选择坐标文件的保存位置,并在程序中打开文件。当未选择文件保存位置时,程序停止。       使用文件写入语句将坐标按行写入到文件中去

       3.jpg

          4.jpg

         5.jpg

         6.jpg


         11.jpg

        以上读取了多段线的四个节点、四个文字插入点坐标存储为:ID,y坐标(横),x坐标(纵)

        PS:坐标是在CAD上随便点的

      

        (2)外挂类程序的思路

        外挂类程序的设计思路完全与以上相同,但是外挂类程序需要启动某个对应版本的CAD程序之后,控制CAD程序内功能来使用,其原理多是通过COM端口来进行的,不一定稳定。

           以下通过C#语言在.net环境下,对应于CAD2019版本及相关的ObjectARX库编写

         

  1. private void 读取对象坐标_Click(object sender, EventArgs e)
  2.         {
  3.             AcadDatabase acadDatabase = AcadApp.ActiveDocument.Database;
  4.             AcadBlocks acadBlocks = acadDatabase.Blocks;


  5.             string vv = "";
  6.             int AA = 0;
  7.             OpenFileDialog of = new OpenFileDialog();
  8.             of.Filter = "坐标文件|*.dat";
  9.             of.ShowDialog();
  10.             string path = of.FileName;
  11.             FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write);
  12.             StreamWriter sw = new StreamWriter(fs);
  13.             sw.WriteLine("NAME         x(m)            y(m)");

  14.             foreach (AcadBlock item in acadBlocks)
  15.             {
  16.                 foreach (AcadEntity acadEntity in item)
  17.                 {
  18.                     switch (acadEntity.ObjectName)
  19.                     {
  20.                         //椭圆
  21.                         case "AcDbEllipse":
  22.                             AcadEllipse acadEllipse = (AcadEllipse)acadEntity;
  23.                             double[] acadEllipseCenter = acadEllipse.Center;
  24.                             if (Panduan(acadEllipseCenter[0]))
  25.                             {
  26.                                 break;
  27.                             }
  28.                             AA++;
  29.                             vv = AA.ToString() + "    " + acadEllipseCenter[1].ToString() + "    " + acadEllipseCenter[0].ToString();
  30.                             //开始写入
  31.                             sw.WriteLine(vv);

  32.                             break;
复制代码


2.脱离CAD类程序


         脱离程序的依赖来进行图形数据处理,可以采用两种方式:
                            1. 操作DWG数据库(困难)
                            2. 使用AutoCAD的交换格式.dxf格式(容易)
        (1)操作DWG数据库
          DWG数据库格式由AutoDesk公司独占,AutoDesk公司极少公开相关资料,互联网上能找到的相对齐全的资料是开放设计联盟对DWG数据库的十年如一日的破解成果,该种方法未进行过尝试,高手请自为之。
        (2)通过dxf交换数据格式
        dxf文件是DWG数据库之外的CAD程序提供的交换数据格式,可通过任意版本的CAD程序进行转换得到。dxf文件的规范主要是由dxf参考手册提供。
         由于时间精力有限,不在此过多赘述,相关思路也仍然是遍历数据中的实体对象,并取出坐标进行存储。


四、坐标转换
        在此通过在CAD外进行转换的方法展示一下数据重投影转换——2000到基于2000的地方独立坐标系
         7.jpg

       找到先前输出的文件
          8.jpg
          9.jpg

        我们对该组坐标进行投影变换,输出坐标设置投影高4**m,中央子午线移动到111.***********(数据过于逼真不宜展示)
          10.jpg
          12.jpg


        输出转换后的坐标。
         13.jpg
        
        对于想在CAD内通过一套软件就解决问题的大佬,可以自行动手实现高斯投影正反算、七参数、四参数等简单的坐标转换算法,此处不再赘述(我也没在CAD内实现过)
        如果在CAD程序内实现了相关算法,则可省去数据在外部软件中的交互这一部分。数据不用离开程序就可以完成全部工作,也不用担心因为坐标数据的顺序问题影响后续工作。


五、坐标写回原要素
        这部分工作的思路严重受到第一部分坐标读取的影响,主要是因为我们需要坐标的顺序能对应到读取和写入的顺序,否则图形将会混乱。CAD程序对图元要素的绘制、存储均由其自己的顺序,猜测由句柄和ID决定,所以在CAD程序中读取和写入坐标的过程中,程序都会遵从同样的顺序遍历图纸空间下的对象,不会造成图面要素的混乱。
        由于前述介绍了读取坐标的思路,此处略微介绍数据的写入,因为这是第一步读取坐标的逆过程,思路上一致。
        (1)VBA演示
        遍历图纸空间的对象

复制代码
               写回坐标
  1. Public Sub coordsWriteBack(ByRef en As AcadEntity, ByRef cds() As Double)

  2.     objName = en.ObjectName
  3.     Select Case objName
  4.         '轻义多段线
  5.         Case Is = "AcDbPolyline"
  6.             
  7.             Dim pl As AcadLWPolyline
  8.             Set pl = en
  9.             pl.Coordinates = cds

  10. <b>……</b>
  11.                
  12.         End Select
  13. End Sub
复制代码


        坐标写回的过程就是找到原对象中存储坐标的属性,如多段线的"Coordinates"属性,并用读取的坐标对他进行修改,即可将图元的位置移动到转换后的坐标位置。
        
         14.jpg

        (2)C# 代码
  1. private void 写回坐标_Click(object sender, EventArgs e)
  2.         {
  3.             AcadDatabase acadDatabase = AcadApp.ActiveDocument.Database;
  4.             AcadBlocks acadBlocks = acadDatabase.Blocks;

  5.             OpenFileDialog of = new OpenFileDialog
  6.             {
  7.                 Filter = "坐标文件|*.dat",
  8.                 Title = "选择坐标文件"
  9.             };
  10.             of.ShowDialog();
  11.             string path = of.FileName;

  12.             //文件流读取
  13.             StreamReader sr = new StreamReader(path, Encoding.Default);

  14.             //获取坐标文件行数,作为临时数组的大小使用
  15.             int xx = 0;
  16.             using (StreamReader read = new StreamReader(path, Encoding.Default))
  17.             {
  18.                 xx = read.ReadToEnd().Split('\n').Length;
  19.             }

  20.             string[] line = new string[xx];
  21.             int count = 0;
  22.             while ((line[count] = sr.ReadLine()) != null)
  23.             {
  24.                 count++;
  25.             }

  26.             //栈顶指示器
  27.             int AA = 1;
  28.             Microsoft.VisualBasic.Interaction.AppActivate(AcadApp.Caption);
  29.             //循环块表获取对象
  30.             foreach (AcadBlock acadBlock in acadBlocks)
  31.             {
  32.                 //vv += item.ObjectID + " " + item.ObjectName + "\n";
  33.                 foreach (AcadEntity acadEntity in acadBlock)
  34.                 {
  35.                     switch (acadEntity.ObjectName)
  36.                     {
  37.                         //椭圆
  38.                         case "AcDbEllipse":
  39.                             AcadEllipse acadEllipse = (AcadEllipse)acadEntity;
  40.                             double[] acadEllipseCenter = StringToCoords(line[AA], acadEllipse.ObjectName);
  41.                             if (((double[])acadEllipse.Center)[0] < 420000)
  42.                             {
  43.                                 break;
  44.                             }

  45. <b>……</b>

  46.                                 acadBlockReference.Move(acadBlockReference.InsertionPoint, StringToCoords(line[AA], acadBlockReference.ObjectName));
  47.                                 AA++;
  48.                                 acadBlockReference.Update();
  49.                             }//end if
  50.                             else
  51.                             { }
  52.                             break;
  53.                         default:
  54.                             break;
  55.                     }//end switch
  56.                 }// end foreach
  57.             }//end foreach
复制代码


六、总结
        发这篇文章的目的主要是为了对此前尝试的CAD逐点转换程序构想及实现的一点总结,这里涉及的编程实现方法都是基础内容。
        逐点转换方法的意义:(纯属个人见解,如有谬误请大侠们指正)
        鄙人认为该种转换方法的意义主要是在于,对图形文件的投影进行变换即重投影时,可以保证图元要素在重投影前后的一致性。即,所有点在重投影之后,图元的几何关系符合该新投影关系下两个地物之间应具有的几何关系(方位、距离等)。
        在实际测量工作中,为了保证图元要素具有几何上的可靠性,我们应当在施测之前对测区成果所需要使用的坐标参照、投影坐标系等进行确认,将控制点转换至相应坐标系后再进行碎部测量,则成图所用的所有碎部点都将处于目标坐标系下,不需要额外进行转换。但是由于当下测量技术的飞速发展,CORS、星站差分等技术的广泛应用(大家都懂的),快速获取CGCS2000国家大地测量坐标系的“标准坐标”成为可能,花费额外成本做地方独立坐标系下的控制测量不被生产公司喜爱。所以产生了行政管理系统接受成果或者生产公司提交成果时的额外的转换需求。此时如果仅仅进行平移转换,则往往图形与规划等成果不能很好吻合。
        “我们明明就是对同一个地块进行的测量啊,界址点怎么会不能完全贴合呢?”
        这是因为平移转换的原理和效果可以认为等同平面四参数,平面四参数是纯线性过程其坐标成果不论是平移还是旋转缩放均具有各向同性。但是高斯投影并不是线性过程(虽然我们的实用公式都是线性的,但那是展开式),所以高斯投影的成果必然具有各向异性,重投影涉及高斯投影的反算——>正算的过程,其成果也必定是各向异性的。
        所以,对CAD要素的逐点转换,可以省去转换工作中利用GIS软件进行交互的工作步骤,有利于数据的保存,减少工作量。

        关于坐标系转换以及坐标系统的相关知识,可以参考刘光明老师的系列讲座以及其所著的《CGCS2000坐标转换》一书。

评分

参与人数 2威望 +20 铜板 +55 收起 理由
yyuanliangg + 5
远方~ + 20 + 50 地信网的成长离不开您的支持!

查看全部评分

324

主题

56万

铜板

1172

好友

管理员

Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20

积分
349368

宣传勋章爱心勋章组织勋章官方团队冰雪节勋章

发表于 2021-10-21 16:29 | 显示全部楼层
感谢分享干货文章
回复 支持 反对

使用道具 举报

0

主题

291

铜板

1

好友

技术员

Rank: 3Rank: 3

积分
34
发表于 2021-10-22 09:38 | 显示全部楼层
感谢分享,干活满满
回复 支持 反对

使用道具 举报

5

主题

4402

铜板

3

好友

高级工程师

Rank: 9Rank: 9Rank: 9

积分
983
发表于 2021-10-22 13:23 | 显示全部楼层
感谢分享思路十分清晰 全是干货
回复 支持 反对

使用道具 举报

11

主题

2万

铜板

8

好友

地信院士

Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15

积分
2620

童话节勋章

发表于 2021-10-22 14:35 | 显示全部楼层
6666666666666
回复 支持 反对

使用道具 举报

0

主题

1988

铜板

2

好友

技术员

Rank: 3Rank: 3

积分
59

爱心勋章

 楼主| 发表于 2021-10-22 19:00 手机频道 | 显示全部楼层
1826658018 发表于 2021-10-22 09:38
感谢分享,干活满满

感谢评价
回复 支持 反对

使用道具 举报

0

主题

1988

铜板

2

好友

技术员

Rank: 3Rank: 3

积分
59

爱心勋章

 楼主| 发表于 2021-10-22 19:02 手机频道 | 显示全部楼层
hunterlcg 发表于 2021-10-22 13:23
感谢分享思路十分清晰 全是干货

感谢同仁们评价,个人拙见,不知是否能有所裨益
BTW,放进代码块的很多代码发出来之后都私密消失了
回复 支持 反对

使用道具 举报

11

主题

1万

铜板

11

好友

高级工程师

Rank: 9Rank: 9Rank: 9

积分
826
QQ
发表于 2021-10-23 16:05 | 显示全部楼层
这个方法好,我用VLISP做了一个
回复 支持 反对

使用道具 举报

11

主题

1万

铜板

11

好友

高级工程师

Rank: 9Rank: 9Rank: 9

积分
826
QQ
发表于 2021-10-23 16:09 | 显示全部楼层

采用逐点转换

C:\Users\Administrator\Desktop\图形转换.png
图形转换.png
回复 支持 反对

使用道具 举报

0

主题

1988

铜板

2

好友

技术员

Rank: 3Rank: 3

积分
59

爱心勋章

 楼主| 发表于 2021-10-23 20:53 手机频道 | 显示全部楼层
PGJ 发表于 2021-10-23 16:09

我一直很恼火程序设计的时候各种转换模型的UI布局,哈哈哈
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

在线客服
快速回复 返回顶部 返回列表