多边形注记点搜索算法实现
在上一篇博文中,我讲了多边形注记点搜索算法的基本原理,本篇文章讲一讲如何用JS实现。
建议读者阅读本篇博文之前,一定要先了解上一篇博文中提到的算法原理。
函数接口
我们定义一个函数接口,接受一个多边形,计算其标注点:
1 | function polylabel(polygon) { |
使用时,我们可以这样调用:
1 | var polylabel = require('polylabel') |
对于polygon,我们采用geojson对多边形的坐标定义格式,即多边形由多个环组成,其中第一个环是外环,其他的为内环。所以polygon[0]
表示第一个环,ploygon[0][0]
表示起点坐标。
准备工作
在开始实现polylabel之前,需要一些辅助函数和库。
首先,我们定义网格对象Cell
:
1 | function Cell(x, y, h, polygon) { |
定义网格对象的主要目的是方便求取最大距离this.max
,有了网格对象Cell
之后,每当我们实例化一个cell对象,我们就可以知道这个网格区域的最大距离以及它所代表的区域。
pointToPolygonDist
是计算点到多边形的距离的函数,其基本原理是计算点到多边形每条边的距离,取最短距离即为点到多边形的距离。详细的算法实现我考虑专门用一篇文章来讲。
this.d + this.h * Math.SQRT2
即为我们之前在算法原理中求得的最大距离dist + radius
,由于格网是一个正方形,其外接圆形的半径即为中心点到任意一个顶点的距离,即this.h * Math.SQRT2
。
另一个需要准备的是一个优先级队列的数据结构,它与一般的队列不同在于,优先级队列中的元素都是经过排序的。每当push一个元素,并不是简单地将该元素放入队列末尾,而是会将该元素与队列中的元素进行比较,放到一个合适的位置上。
我们可以定义一个优先级队列,队列里存储的是格网对象。每push入一个格网,根据最大距离max
进行排序,大的在前,小的在后。由此,我们可以分裂格网时,只需从队首pop出一个格网进行操作。
优先级队列的一个JS实现是tinyqueue,同样是Vladimir Agafonkin的作品。
算法实现
1 | var Queue = require('tinyqueue') |
算法优化
上一节描述了多边形注记点搜寻算法的基本实现,我们还需要对其进行优化,提升算法效率。
为了提升效率,我们可以增加一个容差参数,允许最后的注记点有一定的误差,以减少运算次数:
1 | // if (cell.max <= bestCell.d) continue |
另外,我们可以在初始时,用centroid作为初始最优点,因为大多数凸多边形的centroid就是最佳注记点:
1 | // var bestCell = cellQueue.peek() |
当然,多变形的centeroid怎么求,估计又得用一篇博文来说了。
感觉给自己挖了好多坑。
Author: jingsam
Link: https://jingsam.github.io/2016/09/26/polylabel2.html
License: 知识共享署名-非商业性使用 4.0 国际许可协议