【GIS新探索】算法实现在不规则区域内均匀分布点
1 概要
在不规则区域内均匀分布点,这个需求初看可能不好理解。如果设想一下需求场景就比较简单了。
场景1:在某个地区范围内,例如A市区有100W人口,需要将这100W人口在地图上面相对均匀的标识出来。
场景2:某不规则场馆,需要均匀布置展位,快速生成展位示意图。
场景其他:规则的电线杆、移动基站等模拟生成。
2 设计方案
既然是要求相对均匀的分布,我想到了格网法,即将多边形分割成特定边长的正方形格子,每个格子的中心点作为分布点。
好处:得到的点是绝对均匀的。
难点:需要判断格子是否在多边形范围内。
示意图:
其中1 2 3 4 四个点代表了不规则多边形的外接矩形角点。绿色的点用来算出1 2 3 4点的。
3 实现
第一步先看看模拟区域。
第二步画格子。
第三步标注格子中间的点。
第四步取出在区域范围内的格子中心点。
至此,基本满足了要求,部分格子的位置细节稍作调整就好。
4 代码
第一步,绘制区域,使用的是canvas。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | //公共方法,canvas绘制 var drawFunc={ ctx: null , init: function (domId){ //获取canvas容器 var can = document.getElementById(domId); //创建一个画布 var ctx = can.getContext( '2d' ); this .ctx=ctx; }, drawArea: function (pts,background){ this .ctx.beginPath(); var pt=pts[0]; this .ctx.moveTo(pt[0],pt[1]); for ( var i=1;i<pts.length;i++){ var pt=pts[i]; this .ctx.lineTo(pt[0],pt[1]); } this .ctx.fillStyle = background; this .ctx.fill(); this .ctx.closePath(); }, drawPoint: function (point,color,size){ this .ctx.beginPath(); this .ctx.arc(point[0], point[1], size, 0, 2*Math.PI, true ); this .ctx.fillStyle =color; this .ctx.fill(); this .ctx.closePath(); }, drawLine: function (pts,lineWidth,color){ this .ctx.beginPath(); this .ctx.lineWidth=lineWidth; var pt=pts[0]; this .ctx.moveTo(pt[0],pt[1]); for ( var i=1;i<pts.length;i++){ var pt=pts[i]; this .ctx.lineTo(pt[0],pt[1]); } this .ctx.strokeStyle = color; this .ctx.stroke(); } } |
1 2 3 4 5 6 7 8 9 | //01 创建不规则多边形 var pts=[]; pts.push([100,400]); pts.push([800,400]); pts.push([800,100]); pts.push([500,100]); pts.push([500,250]); pts.push([100,250]); drawFunc.drawArea(pts, "#cddc39" ); |
第二步,绘制格子。这里有两个步骤,获取外接矩形和根据特定间距绘制格子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | /** *绘制格网,并返回格网中心点 **/ function buildBox(space,startPt,endPt){ var width=endPt[0]-startPt[0]; var height=endPt[1]-startPt[1]; var y2=endPt[1]; for ( var i=0;i<width;i+=space){ var x=startPt[0]+i; var y1=startPt[1]; drawFunc.drawLine([[x,y1],[x,y2]],1, "#eee" ); } var x2=endPt[0]; for ( var i=0;i<height;i+=space){ var x1=startPt[0]; var y=startPt[1]+i; drawFunc.drawLine([[x1,y],[x2,y]],1, "#eee" ); } var points=[]; for ( var i=space;i<width;i+=space){ var x=startPt[0]+i-space/2; for ( var n=space;n<height;n+=space){ var y=startPt[1]+n-space/2; points.push([x,y]); } } return points; } |
1 2 3 4 | //02 求不规则多边形外接矩形左上右下点 var box=queryMaxMinPt(pts); //03 以一定的间距绘制格网,并返回格网中心点 var points= buildBox(20,box.startPt,box.endPt); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | /* *求多边形外接矩形左上右下点 */ function queryMaxMinPt(points){ var x_min=100000000000000; var x_max=-1; var y_min=100000000000000; var y_max=-1; for ( var i=0;i<points.length;i++){ var pt=points[i]; if (pt[0]<x_min) x_min=pt[0]; if (pt[0]>x_max) x_max=pt[0]; if (pt[1]<y_min) y_min=pt[1]; if (pt[1]>y_max) y_max=pt[1]; } return { startPt:[x_min,y_min], endPt:[x_max,y_max] } } |
第三和四步,查找在区域范围内的格子,并绘制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /** *检查点是否在多边形范围内 **/ function checkInside (point, vs) { var x = point[0], y = point[1]; var inside = false ; for ( var i = 0, j = vs.length - 1; i < vs.length; j = i++) { var xi = vs[i][0], yi = vs[i][1]; var xj = vs[j][0], yj = vs[j][1]; var intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); if (intersect) inside = !inside; } return inside; }; |
注:checkInside方法来源自Git,地址:https://github.com/substack/point-in-polygon/blob/master/index.js
1 2 3 4 5 6 7 8 9 10 11 12 | //04 遍历中心点,判断点是否在范围内 var pointCount=0; for ( var i=0;i<points.length;i++){ var pt=points[i]; if (checkInside(pt,pts)){ drawFunc.drawPoint(pt, "red" ,2); pointCount++; } } console.log( "范围内有:" +pointCount+ "个点" ); |
以上就是核心的实现代码,如果需要下载源码请移步我的博客下载,地址:
http://www.88gis.cn/web/pages/blog/blogInfo.html?id=38d8959a-f348-41df-b507-6c10e517e7a7
查看更多GIS、WPF、JAVA、前端技术分享,请访问我的个人技术网站,查看更多技术分享。网站地址:www.88gis.cn
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步