前情回顾
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)/3double 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底层直通
