概述
在 Openlayers 中,Polygon
类是SimpleGeometry
的子类,用于表示多边形几何形状。多边形是由一组点组成的封闭图形,通常由一系列的坐标点和多个边界组成. 关于SimpleGeometry
类,可以参考这篇文章源码分析之Openlayers中SimpleGeometry类
本文主要介绍Polygon
类的源码实现和原理。
源码解析
Polygon
类源码实现
Polygon
类的源码实现如下:
class Polygon extends SimpleGeometry {constructor() {super();this.ends_ = [];this.flatInteriorPointRevsion_ = -1;this.flatInteriorPoint_ = null;this.maxDelta_=-1;this.maxDeltaRevision_ = -1;this.orientrevision_ = -1;this.orientedFlatCoordinates_ = null;if (layout !== undefined && ends) {this.setFlatCoordinates(layout, coordinates);this.ends_ = ends;} else {this.setCoordinates(coordinates, layout);}}appendLinearRing(linearRing) {if (!this.flatCoordinates) {this.flatCoordinates = linearRing.getFlatCoordinates().slice();} else {extend(this.flatCoordinates, linearRing.getFlatCoordinates());}this.ends_.push(this.flatCoordinates.length);this.changed();}clone() {const polygon = new Polygon(this.flatCoordinates.slice(),this.layout,this.ends_.slice());polygon.applyProperties(this);return polygon;}closestPointXY(x, y, closestPoint, minSquaredDistance) {if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {return minSquaredDistance;}if (this.maxDeltaRevision_ != this.getRevision()) {this.maxDelta_ = Math.sqrt(arrayMaxSquaredDelta(this.flatCoordinates,0,this.ends_,this.stride,0));this.maxDeltaRevision_ = this.getRevision();}return assignClosestArrayPoint(this.flatCoordinates,0,this.ends_,this.stride,this.maxDelta_,true,x,y,closestPoint,minSquaredDistance);}containsXY(x, y) {return linearRingsContainsXY(this.getOrientedFlatCoordinates(),0,this.ends_,this.stride,x,y);}getArea() {return linearRingsArea(this.getOrientedFlatCoordinates(),0,this.ends_,this.stride);}getCoordinates(right) {let flatCoordinates;if (right !== undefined) {flatCoordinates = this.getOrientedFlatCoordinates().slice();orientLinearRings(flatCoordinates, 0, this.ends_, this.stride, right);} else {flatCoordinates = this.flatCoordinates;}return inflateCoordinatesArray(flatCoordinates, 0, this.ends_, this.stride);}getEnds() {return this.ends_;}getFlatInteriorPoint() {if (this.flatInteriorPointRevision_ != this.getRevision()) {const flatCenter = getCenter(this.getExtent());this.flatInteriorPoint_ = getInteriorPointOfArray(this.getOrientedFlatCoordinates(),0,this.ends_,this.stride,flatCenter,0);this.flatInteriorPointRevision_ = this.getRevision();}return this.flatInteriorPoint_;}getInteriorPoint() {return new Point(this.getFlatInteriorPoint(), "XYM");}getLinearRingCount() {return this.ends_.length;}getLinearRing(index) {if (index < 0 || this.ends_.length <= index) {return null;}return new LinearRing(this.flatCoordinates.slice(index === 0 ? 0 : this.ends_[index - 1],this.ends_[index]),this.layout);}getLinearRings() {const layout = this.layout;const flatCoordinates = this.flatCoordinates;const ends = this.ends_;const linearRings = [];let offset = 0;for (let i = 0, ii = ends.length; i < ii; ++i) {const end = ends[i];const linearRing = new LinearRing(flatCoordinates.slice(offset, end),layout);linearRings.push(linearRing);offset = end;}return linearRings;}getOrientedFlatCoordinates() {if (this.orientedRevision_ != this.getRevision()) {const flatCoordinates = this.flatCoordinates;if (linearRingsAreOriented(flatCoordinates, 0, this.ends_, this.stride)) {this.orientedFlatCoordinates_ = flatCoordinates;} else {this.orientedFlatCoordinates_ = flatCoordinates.slice();this.orientedFlatCoordinates_.length = orientLinearRings(this.orientedFlatCoordinates_,0,this.ends_,this.stride);}this.orientedRevision_ = this.getRevision();}return this.orientedFlatCoordinates_;}getSimplifiedGeometryInternal(squaredTolerance) {const simplifiedFlatCoordinates = [];const simplifiedEnds = [];simplifiedFlatCoordinates.length = quantizeArray(this.flatCoordinates,0,this.ends_,this.stride,Math.sqrt(squaredTolerance),simplifiedFlatCoordinates,0,simplifiedEnds);return new Polygon(simplifiedFlatCoordinates, "XY", simplifiedEnds);}getType() {return "Polygon";}intersectsExtent(extent) {return intersectsLinearRingArray(this.getOrientedFlatCoordinates(),0,this.ends_,this.stride,extent);}setCoordinates(coordinates, layout) {this.setLayout(layout, coordinates, 2);if (!this.flatCoordinates) {this.flatCoordinates = [];}const ends = deflateCoordinatesArray(this.flatCoordinates,0,coordinates,this.stride,this.ends_);this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1];this.changed();}
}
Polygon
类的构造函数
Polygon
类的构造函数接受三个参数:coordinates
坐标点、layout
坐标风格和ends
边界点;另外构造函数中还有如下成员变量:
-
this.ends_
:初始化为空数组[]
,用于存储多边形的边界信息。对于多边形,ends
存储的是每一个环(环形边界)的结束坐标索引。 -
this.flatInteriorPointRevision
:初始化为-1
,用于记录多边形内部点的版本信息,主要是在多边形优化和缓存内部点计算时用到 -
this.flatInteriorPoint_
:默认为null
,用于缓存多边形的内部点或质心 -
this.maxDeltaREvision_
:多边形形状更新的最大变化版本,和LinearRing
类中的同名变量类似,可以参考源码分析之Openlayers中的LinearRing类 -
this.orientedRevision_
:初始为-1
,用于表示多边形的方向版本;如果多边形的坐标顺序发生变化,它将被更新 -
this.orientedFlatCoordiantes_
:初始为null
,用于存储有方向性的平面坐标
Polygon
类的构造函数会判断,若参数layout
坐标布局风格和环信息ends
存在,则调用父类SimpleGeometry
类中的this.setFlatCoordinates
方法设置坐标;否则,调用this.setCoordiantes
方法。
Polygon
类的主要方法
Polygone
类的主要方法如下:
-
setCoordiantes
方法:用于设置坐标this.flatCoordiantes
、坐标布局风格this.layout
和步幅this.stride
,最后会调用this.changed
方法,该方法会修改this.revision_
的值,后面几个方法内部调用this.getRevision()
方法就是获取this.revision_
的值,它们是在Observable
类中定义的,可以参考这篇文章源码分析之Openlayers中的Observable类 -
intersectsExtent
方法:该方法用于判断几何对象是否与extent
交叉 -
getType
方法:返回几何对象多边形的类型,Polygon
-
getSimplifiedGeometryInternal
方法:获取简化后的几何对象,接受一个参数squaredTolerance
容差值平方,内部就是调用quantizeArray
方法简化几何对象,然后获取简化几何对象的坐标,再调用Polygon
进行实例化,并返回 -
getOrientedFlatCoordiantes
方法:返回多边形顶点的有向平面坐标;首先会判断多边形是否发生变化,若未变化,则直接返回this.orientedFlatCoordinates_
;否则调用linearRingsAreOriented
方法判断线性环坐标是否是有向的,若是,则直接返回原始坐标;否则复制一份原始坐标数组,然后调用orientLinearRings
方法重新调整坐标的方向,确保环的方向正确,说白了就是会调整坐标的顺序,然后修改this.orientedFlatCoordintes_
和this.orientedRevision_
-
getLinearRing
方法:根据索引值index
获取某一个具体的线性环,首先会判断索引值index
的合法性,然后实例化LinearRing
类并返回实例对象。 -
getLinearRings
方法:获取多边形的环,内部会遍历this.ends_
,前面提过这个变量存储的是环的结束点坐标索引,即每个环的最后一个坐标在this.flatCoordinates
中的位置,遍历this.ends_
然后截取this.flatCoordiantes
就可以得到每个环的坐标数组,然后调用LinearRing
类实例化线性环,将其值保存到数组linearRings
中,最后返回linearRings
-
getLinearRingCount
方法:获取多边形环的个数 -
getInteriorPoint
方法:方法内部会调用this.getFlatInteriorPoint()
方法获取多边形内部的代表点,然后实例化Point
类并返回实例对象。 -
getFlatInteriorPoint
方法:获取几何对象的内部点,该方法主要用于计算并返回多边形的一个内部点(通常是内部几何中心点),并缓存该结果。它会在多边形发生变更时重新计算,并在未发生变化时直接返回缓存的结果。首先会判断几何对象是否更新变化过,若变化了,则调用getCenter
方法计算几何对象包围盒的中心点,然后调用getInteriorPointOfArray
计算出一个内部点-多边形内部的代表点并返回,将其赋值给this.flatInteriorPoint_
变量,最后修改this.flatInteriorPointRevision_
的值;若未变化,则返回this.flatInteriorPoint_
-
getEnds
方法:获取变量this.ends_
的值,即多边形中环的结束点索引位置数组 -
getCoordinates
方法:获取几何对象的坐标数组 -
getArea
方法:获取多边形面积,内部调用的是linearRingArea
方法计算多边形面积,运用的是高斯面积公式 -
containsXY
方法:用于判断给定点(x,y)
是否在多边形内部或边界上,内部调用的是linearRingsContainsXY
方法 -
closestPointXY
方法:几何对象中很常见的方法了,给定一个目标点(x,y)
、最近点坐标closestPoint
和最小平方距离minSquaredDistance
,然后计算多边形中距离目标点最近的点,并返回多边形距离目标点最近距离的平方;closestPointXY
方法内部会先调用closestSquaredDistanceXY
计算几何对象包围盒距离目标点最近的距离,若参数minSquaredDistance
小于该距离则直接返回它;然后判断this.maxDeltaRevision_
是否与this.getResvision()
方法返回的变量相等,也就是判断几何对象多边形是否发生了改变,若发生了改变,就调用arrayMaxSquaredDelta
方法来计算多边形顶点的最大坐标变化,并将其平方根保存在this.maxDelta
变量中,最后更新this.maxDeltaRevision_
为当前修订版本号;最后调用assignCloestArrayPoint
方法来计算目标点(x,y)
与多边形最近的点,并更新最近点坐标closestPoint
和最小距离平方``minSquaredDistance`并返回最小距离平方. -
clone
方法:该方法内部就是调用Polygon
类实例化一个多边形,然后调用实例的applyProperties
方法应用属性,最后返回实例。 -
appendLinearRing
方法:参数linearRing
是一个LinearRing
类的实例,该方法用于将线性环添加到多边形;首先会判断,若this.flatCoordiantes
不存在,即多边形没有坐标点,则将调用线性环的getFlatCoordiantes
方法获取它的坐标,赋值给this.flatCoordinates
,否则调用extent
方法,将线性环的坐标添加到this.flatCoordiantes
中;更新变量this.ends
,最后调用this.changed()
方法
总结
本文主要介绍Polygon
类的实现原理,LinearRing
类是Polygon
类的基础。