作者:yangjl
就在上个星期,有两位客户小伙伴问我如何取得任意随机的最外围的极端点,并使矢量线将其包围。其实这个问题是比较经典的一种计算几何的概念–凸包。用不严谨的话来讲,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形,它能包含点集中所有的点。凸包最常用的凸包算法是Graham扫描法和Jarvis步进法。这次我选取比较容易理解,且网上解释较多的Graham扫描法作为算法,结合iClient for OpenLayers地图框架,实现凸包展示。。
本次功能的实现,需要了解凸包中的Graham扫描法,以及iClient for OpenLayers相关知识
为代码简洁实现利用CDN在线引入iClient for OpenLayer文件,进行单网页的开发。
具体步骤如下:
<link href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.6.5/ol.css" rel="stylesheet" />
<link href='http://iclient.supermap.io/dist/openlayers/iclient-openlayers.min.css' rel='stylesheet' />
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.6.5/ol.js"></script>
<script type="text/javascript" src="http://iclient.supermap.io/dist/openlayers/iclient-openlayers.min.js"></script>
map = new ol.Map({
target: 'map',
controls: ol.control.defaults({attributionOptions: {collapsed: false}})
.extend([new ol.supermap.control.Logo()]),
view: new ol.View({
center: [100, 0],
zoom: 3,
projection: 'EPSG:4326'
})
});
var layer = new ol.layer.Tile({
source: new ol.source.TileSuperMapRest({
url: url,
wrapX: true
}),
projection: 'EPSG:4326'
});
map.addLayer(layer);
var param = new SuperMap.QueryByDistanceParameters({
queryParams: {name: "[email protected]"},
distance: 10,
geometry: point
});
//创建距离查询实例
New ol.supermap.QueryService(url).queryByDistance(param, function (serviceResult) {
Var fes=serviceResult.result.recordsets[0].features.features;
})
var res=new Array();//结果数组坐标点
var p=new Array();//传入数组坐标点
var n=fes.length //传入数组长度
for(var i=0;i<fes.length;i++)
{
p[i]=new Object();
p[i].x=fes[i].geometry.coordinates[0]
p[i].y=fes[i].geometry.coordinates[1]
p[i].tj=false
}
console.log(p,n)
详细代码:
function multiply(p0,p1,p2){
return((p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y));
} //相乘
function distance_no_sqrt(p1,p2)
{
//return(sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)));
return((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
function Graham_scan(pointSet,ch,n){
// 这里会修改pointSet
var i,j,k=0,top=2;
var tmp=new Object();
// 找到一个基点,基本就是保证最下面最左面的点
for(i=1;i<n;i++){
if( (pointSet[i].y<pointSet[k].y) ||
( (pointSet[i].y==pointSet[k].y) && (pointSet[i].x<pointSet[k].x) )
){
k=i;
}
}
//这个点作为基点
tmp=pointSet[0];
pointSet[0]=pointSet[k];
pointSet[k]=tmp;
use=n;
for (i=1;i<use-1;i++){
k=i;
for (j=i+1;j<use;j++){
var direct=multiply(pointSet[0],pointSet[k],pointSet[j]);
if(direct>0){
k=j;
}else if(direct==0){
// k j 同方向
var dis=distance_no_sqrt(pointSet[0],pointSet[j])-distance_no_sqrt(pointSet[0],pointSet[k]);
use--; // 也就是不要了
if(dis>0){
// 保留j
// 把 k 就不要了
pointSet[k]=pointSet[j];
pointSet[j]=pointSet[use];
j--;
}else{
tmp=pointSet[use];
pointSet[use]=pointSet[j];
pointSet[j]=tmp;
}
}
}
tmp=pointSet[i];
pointSet[i]=pointSet[k];
pointSet[k]=tmp;
}
ch.push(pointSet[0]);
ch.push(pointSet[1]);
ch.push(pointSet[2]);
for (i=3;i<use;i++){
while ( !(multiply(pointSet[i],ch[top-1],ch[top]) < 0 ) ){
top--;
ch.pop();
}
top++;
ch.push(pointSet[i]);
}
return ch;
}
var points=Graham_scan(p,res,n) //进行凸包算法
var poin=[];
console.log(points.length,"len")
for(var j=0;j<points.length;j++)
{
var a=new Array();
a[0]=points[j].x;
a[1]=points[j].y;
poin.push(a)
}
poin.push(poin[0])
console.log(poin,"a")
var roadLine = new ol.geom.LineString(poin);
var Source = new ol.source.Vector({
features: [new ol.Feature(roadLine)]
});
var Layer = new ol.layer.Vector({
source: Source,
style: new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'blue',
width: 3
}),
fill: new ol.style.Fill({
color: 'rgba(0, 0, 255, 0.1)'
})
})
});
map.addLayer(Layer);