多边形的质心计算

手写地理信息组件系列 第8篇
多边形实体的质心计算
难度指数:★★☆☆☆

前情回顾

OK,第7篇运用了多边形三角剖分+向量叉积的方式计算多边形面积。初步见到了三角形剖分化繁为简的能力,这一篇算是此种算法思想的延伸——质心计算。

质心坐标怎么能和面积计算扯上关系呢?

下面来一一分解。

质心

什么是质心?就是通过该点,区域达到一种质量上的平衡状态。

质量中心简称质心(Centroid),指物质系统上被认为质量集中于此的一个假想点。可能物理学上讲的比较多,简单点说就是规则几何物体的中心,不规则的可以通过挂绳子的方法来寻找。

与重心不同的是,质心不一定要在有重力场的系统中。值得注意的是,除非重力场是均匀的,否则同一物质系统的质心与重心通常不在同一假想点上。

在我们的生活经验里,一个物体,如果想拿起它,我们更倾向于伸手去拿这个物体质量相对更集中的地方,因为这样会使物体保持平稳。往往物体的质心也就存在于此处。

同样地,在一个多边形中,我们假设这个多边形的“材质”是均匀的,那么影响这个多边形质量分布情况的就只有面积。面积大的区域,质量更大,质心就越有可能向这个区域靠近。

那么基于我们对质心的感官理解,计算也自然有了方向。

质心的计算

在【上一篇】内容中我们已经实践过,将多边形分割为多个三角形,通过计算每个三角形的面积得出多边形的总面积。这个方法,同样适用于今天的质心计算。

这里先给出一个公式:


平面多边形X被剖分为i个简单图形X1,X2,▪▪▪,Xi,每个简单图形的重心点为Ci ,面积为Ai,那么这个平面多边形的重心点坐标为(Cx,Cy)。

这里多边形就可被分割为多个三角形。其中每个三角形的重心计算方式:
在这里插入图片描述
所以,以三角形剖分多边形的计算方式可以整理为:

计算每个三角形重心的横(纵)坐标,并与该三角形的面积作乘积,最终将每个乘积加和与总面积作除,即为质心的横(纵)坐标。

质心计算的实现

与上篇类似,将质心计算方法定义在了算法类Algorithm当中,定义了Centroid方法,并接受一个顺序给出的节点对象列表参数,用以计算质心坐标。

 public static Vertex Centroid(List<Vertex> vertexes)
 {
  //多边形总面积
  double area = 0.0;

  //质心
  double Cx = 0.0;
  double Cy = 0.0;

  //取多边形第一个节点为剖分点
  Vertex v0 = vertexes[0];

  for (int i = 1; i < vertexes.Count - 1; i++)
  {
    //三角形两边向量
    Vertex v1 = new Vertex(
      vertexes[i].x - v0.x, vertexes[i].y - v0.y);

    Vertex v2 = new Vertex(
      vertexes[i + 1].x - v0.x, vertexes[i + 1].y - v0.y);

    //面积
    double A = (v1.x * v2.y - v2.x * v1.y) / 2;
    area += A;

    //三角形重心
    double x = 0 + v1.x + v2.x; //x=(x1+x2+x3)/3
    double y = 0 + v1.y + v2.y; //y=(xy+y2+y3)/3

    //加权面积
    Cx += A * x;
    Cy += A * y;
  }

  Cx = Cx / area / 3 + v0.x;
  Cy = Cy / area / 3 + v0.y;

  return new Vertex(Cx, Cy);
 }

这里,为了保证结果精度有两处处理:

1、不取原点为三角形剖分点,取顺序节点的第一点作为剖分点
2、计算三角形重心不在每次循环中除3,在循环结束后统一除以3

两处都是为了减少小数计算的舍入误差,与原算法上没有差别。

更完善的Polygon

现在的Polygon类,继实现面积计算之后,可以再加入质心计算了。下面是加入了质心属性的Polygon:

 public class Polygon : Geometry
 {
  private List<Vertex> vertexes =
    new List<Vertex>();

  public Polygon() { }

  public Polygon(List<Vertex> vertexes)
  {
    this.vertexes = vertexes;
    base.extent = new Extent(vertexes);
    base.centroid = Algorithm.Centroid(vertexes);
  }

  public List<Vertex> Vertexes
  {
    get { return vertexes; }
  }

  public double Area
  {
    get
    {
      //返回面积的绝对值
      return Math.Abs(
        Algorithm.SignedArea(vertexes));
    }
  }

  public override void Draw(Graphics graphics, Map map)
  {
    System.Drawing.Point[] points =
      new System.Drawing.Point[vertexes.Count];

    for (int i = 0; i < points.Length; i++)
    {
      points[i] = map.ToScreenPoint(vertexes[i]);
    }
    graphics.FillPolygon(
      new SolidBrush(Color.LightSeaGreen), points);

    //在界面绘制质心点
    System.Drawing.Point cPoint = map.ToScreenPoint(centroid);
    graphics.FillEllipse(new SolidBrush(Color.Red),
      new Rectangle((int)cPoint.X, (int)cPoint.Y, 4, 4));

    //在界面绘制质心坐标
    graphics.DrawString(
      String.Format("X:{0}\r\nY:{1}", centroid.x, centroid.y),
    new Font("宋体", 17),
    new SolidBrush(Color.Navy),
    new PointF(cPoint.X, cPoint.Y));
  }
 }

做个验证

绘制了两个shp多边形,一个质心在内,一个质心在外。

点击打开,读取shp进入地图程序,计算显示质心坐标:

可以看到两个质点的坐标值,对比一下ArcMap的质点坐标计算结果:

可以再次认为:我们的计算是准确的!

OK,多余的不再多说。基本属性的计算暂时告一段落,之后还会有更深入的算法实践。

在下一篇我会继续回到GIS组件的设计上来,逐步完善GIS组件的功能结构。


看好关注,下期见!

转载于公众号:GIS底层直通

上一篇:面积计算|为什么以节点分割图形没误差


(kt) jia@jia-kt:~/autod1-tmp/ktransformers$ gdb ./ktransformers/local_chat.py gdb: /home/jia/anaconda3/envs/kt/lib/libtinfo.so.6: no version information available (required by gdb) gdb: /home/jia/anaconda3/envs/kt/lib/libncursesw.so.6: no version information available (required by gdb) gdb: /home/jia/anaconda3/envs/kt/lib/libncursesw.so.6: no version information available (required by gdb) gdb: /home/jia/anaconda3/envs/kt/lib/libncursesw.so.6: no version information available (required by gdb) GNU gdb (Ubuntu 12.1-0ubuntu1~22.04.2) 12.1 Copyright (C) 2022 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <https://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... "/home/jia/autod1-tmp/ktransformers/./ktransformers/local_chat.py": not in executable format: file format not recognized (gdb) core-file /home/jia/autod1-tmp/ktransformers/core.144757 warning: Can't open file /dev/zero (deleted) during file-backed mapping note processing [New LWP 145157] [New LWP 144757] [New LWP 144758] [New LWP 144762] [New LWP 144760] [New LWP 144768] [New LWP 144765] [New LWP 144766] [New LWP 144772] [New LWP 144763] [New LWP 144764] [New LWP 144771] [New LWP 144774] [New LWP 144769] [New LWP 144770] [New LWP 144767] [New LWP 144776] [New LWP 144775] [New LWP 144773] [New LWP 144777] [New LWP 145156] [New LWP 144779] [New LWP 144759] [New LWP 145155] [New LWP 145160] [New LWP 145161] [New LWP 144792] [New LWP 144761] [New LWP 145158] [New LWP 145159] [New LWP 144784] [New LWP 144778] [New LWP 145162] [New LWP 145170] [New LWP 145166] [New LWP 144782] [New LWP 145174] [New LWP 144785] [New LWP 145176] [New LWP 144788] [New LWP 144787] [New LWP 145168] [New LWP 145163] [New LWP 144781] [New LWP 144780] [New LWP 145177] [New LWP 145173] [New LWP 145164] [New LWP 144786] [New LWP 145171] [New LWP 145178] [New LWP 145165] [New LWP 145179] [New LWP 145169] [New LWP 145172] [New LWP 145175] [New LWP 144783] [New LWP 145167] [New LWP 145180] warning: Section `.reg-xstate/145157' in core file too small. --Type <RET> for more, q to quit, c to continue without paging--
最新发布
03-19
<think>嗯,用户遇到了在GDB中调试Python脚本的问题,并且出现了一些库版本和核心转储的问题。首先,我需要分析用户提供的日志信息,理解问题的根本原因。 用户尝试用GDB打开一个Python脚本(local_chat.py),但GDB提示文件格式不被识别。这是因为GDB主要用于调试可执行文件,比如编译后的二进制文件,而Python脚本是文本文件,无法直接通过GDB调试。所以错误信息“not in executable format”是正常的,这时候用户可能需要使用专门针对Python的调试工具,比如pdb或者IDE集成的调试器。 接下来,用户加载了一个核心转储文件(core.144757)。GDB在加载核心文件时报告了一些警告,比如无法打开已删除的/dev/zero文件,这可能是因为程序在运行时使用了该设备,但核心转储时该文件已被删除。不过,这个警告通常不会影响主要的调试过程,但需要留意是否有其他更严重的错误。 另外,用户的环境变量中conda环境的库(libtinfo.so.6和libncursesw.so.6)版本信息缺失,导致GDB报错。这可能是因为conda安装的库与系统自带的版本不一致,或者缺少必要的符号信息。这个问题可能影响GDB的正常功能,比如显示某些调试信息,但可能不会完全阻止调试。建议用户检查conda环境中的这些库版本,或者尝试使用系统自带的库,通过设置LD_LIBRARY_PATH来优先使用系统库。 用户可能需要确定程序崩溃的原因,所以需要查看核心转储中的堆栈跟踪。在GDB中,使用bt命令可以查看当前线程的堆栈,而thread apply all bt则能查看所有线程的堆栈,帮助定位死锁或并发问题。此外,检查核心文件生成时的可执行文件是否与当前版本一致,否则符号信息不匹配会导致调试困难。 可能用户在使用某个Python库或扩展模块时发生了崩溃,比如涉及C扩展的部分。这时候需要确保调试时加载了带有调试符号的库。对于Python本身,可能需要安装debug版本的Python或相关包。 总结下来,用户需要逐步确认以下几点: 1. 使用正确的调试工具处理Python脚本,而非GDB直接调试。 2. 检查conda环境中的库版本冲突问题,必要时调整环境变量。 3. 分析核心转储文件,确定崩溃点,检查多线程问题。 4. 确保调试环境与崩溃时的环境一致,符号信息完整。</think>根据您提供的调试信息,这里分步骤分析问题: 1. **GDB调试Python脚本的格式错误** ``` "/home/jia/.../local_chat.py": not in executable format ``` - 根本原因:GDB无法直接调试Python脚本文件 - 解决方案: - 使用`gdb python`启动调试器后执行`run local_chat.py` - 或使用Python专用调试工具如`pdb`:`python -m pdb local_chat.py` 2. **库版本警告分析** ``` gdb: /home/jia/anaconda3/.../libtinfo.so.6: no version information available ``` - 问题定位:Conda环境中的ncurses库与系统gdb不兼容 - 验证方法: ```bash ldd $(which gdb) | grep curses # 查看gdb依赖的curses库路径 ls -l /home/jia/anaconda3/envs/kt/lib/libncurses* # 检查conda库版本 ``` - 临时解决方案: ```bash export LD_LIBRARY_PATH=/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH gdb ./ktransformers/local_chat.py ``` 3. **核心转储分析** ``` [New LWP 145157]... # 出现大量线程 warning: Section `.reg-xstate/145157' in core file too small ``` - 关键诊断步骤: ```gdb (gdb) bt full # 查看当前线程完整堆栈 (gdb) info threads # 列出所有线程状态 (gdb) thread apply all bt # 获取全部线程回溯 ``` - 常见问题原因: - 多线程竞争条件 - Python C扩展模块崩溃 - 内存越界访问(需检查是否有使用C/C++扩展) 4. **后续操作建议** - 确保生成core dump时的执行环境与调试环境一致 - 检查是否加载了带调试符号的Python解释器: ```bash apt install python3-dbg # Ubuntu安装调试版本 ``` - 对于涉及CUDA的操作,建议检查GPU内存状态: ```bash nvidia-smi --query-compute-apps=pid,used_memory --format=csv ``` 建议优先使用`gdb python core.144757`加载核心转储,然后通过`bt`命令查看崩溃时的堆栈轨迹。如果涉及Python/C混合编程,需要确保相关C扩展模块编译时包含调试符号(-g选项)。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值