范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

GIS常用算法

  作为一个GISer,在日常WebGIS 开发中,会常用到的turf.js ,这是一个地理空间分析的JavaScript 库,经常搭配各种GIS JS API 使用,如leaflet 、mapboxgl 、openlayers 等;在后台Java 开发中,也有个比较强大的GIS库,geotools ,里面包含构建一个完整的地理信息系统所需要的全部工具类;数据库端常用是postgis 扩展,需要在postgres 库中引入使用。
  然而在开发某一些业务系统的时候,有些需求只需要调用某一个GIS算法,简单的几行代码即可完成,没有必要去引用一个GIS类库。
  而且有些算法在这些常用的GIS类库中没有对应接口,就比如在下文记录的这几种常用算法中,求垂足、判断线和面的关系,在turf.js 就没有对应接口。
  下面文章中是我总结的一些常用GIS算法,这里统一用JavaScript 语言实现,因为JS 代码相对比较简洁,方便理解其中算法逻辑,也方便在浏览器下预览效果。在具体应用时可以根据具体需求,翻译成Java 、C# 、Python 等语言来使用。
  文中代码大部分为之前遇到需求时在网上搜索得到,然后自己根据具体需要做了优化修改,通过这篇文章做个总结收集,也方便后续使用时查找。 1、常用算法
  以下方法中传参的点、线、面都是对应geojson 格式中coordinates ,方便统一调用。geojson 标准参考:https://www.oschina.net/translate/geojson-spec
  1.1、计算两经纬度点之间的距离
  适用场景:测量 /** * 计算两经纬度点之间的距离(单位:米) * @param p1 起点的坐标;[经度,纬度];例:[116.35,40.08] * @param p2 终点的坐标;[经度,纬度];例:[116.72,40.18] * * @return d 返回距离 */ function getDistance(p1, p2) {   var rlat1 = p1[1] * Math.PI / 180.0;   var rlat2 = p2[1] * Math.PI / 180.0;   var a = rlat1 - rlat2;   var b = p1[0] * Math.PI / 180.0 - p2[0] * Math.PI / 180.0;   var d = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(rlat1) * Math.cos(rlat2) * Math.pow(Math.sin(b / 2), 2)));   d = d * 6378.137;   d = Math.round(d * 10000) / 10;   return d } 1.2、根据已知线段以及到起点距离,求目标点坐标
  适用场景:封闭管段定位问题点 /** * 根据已知线段以及到起点距离(单位:米),求目标点坐标 * @param line 线段;[[经度,纬度],[经度,纬度]];例:[[116.01,40.01],[116.52,40.01]] * @param dis 到起点距离(米);Number;例:500 * * @return point 返回坐标 */ function getLinePoint(line, dis) {   var p1 = line[0]   var p2 = line[1]   var d = getDistance(p1, p2) // 计算两经纬度点之间的距离(单位:米)   var dx = p2[0] - p1[0]   var dy = p2[1] - p1[1]   return [p1[0] + dx * (dis / d), p1[1] + dy * (dis / d)] } 1.3、已知点、线段,求垂足
  垂足可能在线段上,也可能在线段延长线上。
  适用场景:求垂足 /** * 已知点、线段,求垂足 * @param line 线段;[[经度,纬度],[经度,纬度]];例:[[116.01,40.01],[116.52,40.01]] * @param p 点;[经度,纬度];例:[116.35,40.08] * * @return point 返回垂足坐标 */ function getFootPoint(line, p) {   var p1 = line[0]   var p2 = line[1]   var dx = p2[0] - p1[0];   var dy = p2[1] - p1[1];   var cross = dx * (p[0] - p1[0]) + dy * (p[1] - p1[1])   var d2 = dx * dx + dy * dy   var u = cross / d2   return [(p1[0] + u * dx), (p1[1] + u * dy)] } 1.4、线段上距离目标点最近的点
  不同于上面求垂足方法,该方法求出的点肯定在线段上。
  如果垂足在线段上,则最近的点就是垂足,如果垂足在线段延长线上,则最近的点就是线段某一个端点。
  适用场景:根据求出最近的点计算点到线段的最短距离 /** * 线段上距离目标点最近的点 * @param line 线段;[[经度,纬度],[经度,纬度]];例:[[116.01,40.01],[116.52,40.01]] * @param p 点;[经度,纬度];例:[116.35,40.08] * * @return point 最近的点坐标 */ function getShortestPointInLine(line, p) {   var p1 = line[0]   var p2 = line[1]   var dx = p2[0] - p1[0];   var dy = p2[1] - p1[1];   var cross = dx * (p[0] - p1[0]) + dy * (p[1] - p1[1])   if (cross <= 0) {     return p1   }   var d2 = dx * dx + dy * dy   if (cross >= d2) {     return p2   }   // 垂足   var u = cross / d2   return [(p1[0] + u * dx), (p1[1] + u * dy)] } 1.5、点缓冲
  这里缓冲属于测地线方法,由于这里并没有严格的投影转换体系,所以与标准的测地线缓冲还有些许误差,不过经测试,半径100KM 内,误差基本可以忽略。具体缓冲类型可看下之前的文章你真的会用PostGIS中的buffer缓冲吗?
  适用场景:根据点和半径画圆 /** * 点缓冲 * @param center 中心点;[经度,纬度];例:[116.35,40.08] * @param radius 半径(米);Number;例:5000 * @param vertices 返回圆面点的个数;默认64;Number;例:32 * * @return coords 面的坐标 */ function bufferPoint(center, radius, vertices) {   if (!vertices) vertices = 64;   var coords = []   // 111319.55:在赤道上1经度差对应的距离,111133.33:在经线上1纬度差对应的距离   var distanceX = radius / (111319.55 * Math.cos(center[1] * Math.PI / 180));   var distanceY = radius / 111133.33;   var theta, x, y;   for (var i = 0; i < vertices; i++) {     theta = (i / vertices) * (2 * Math.PI);     x = distanceX * Math.cos(theta);     y = distanceY * Math.sin(theta);     coords.push([center[0] + x, center[1] + y]);   }   return [coords] } 1.6、点和面关系
  该方法采用射线法思路实现。(了解射线法可参考:https://blog.csdn.net/qq_27161673/article/details/52973866)
  这里已经考虑到环状多边形的情况。
  适用场景:判断点是否在面内 /** * 点和面关系 * @param point 点;[经度,纬度];例:[116.353455, 40.080173] * @param polygon 面;geojson格式中的coordinates;例:[[[116.1,39.5],[116.1,40.5],[116.9,40.5],[116.9,39.5]],[[116.3,39.7],[116.3,40.3],[116.7,40.3],[116.7,39.7]]] * * @return inside 点和面关系;0:多边形外,1:多边形内,2:多边形边上 */ function pointInPolygon(point, polygon) {   var isInNum = 0;   for (var i = 0; i < polygon.length; i++) {     var inside = pointInRing(point, polygon[i])     if (inside === 2) {       return 2;     } else if (inside === 1) {       isInNum++;     }   }   if (isInNum % 2 == 0) {     return 0;   } else if (isInNum % 2 == 1) {     return 1;   } }   /** * 点和面关系 * @param point 点 * @param ring 单个闭合面的坐标 * * @return inside 点和面关系;0:多边形外,1:多边形内,2:多边形边上 */ function pointInRing(point, ring) {   var inside = false,     x = point[0],     y = point[1],     intersects, i, j;    for (i = 0, j = ring.length - 1; i < ring.length; j = i++) {     var xi = ring[i][0],       yi = ring[i][1],       xj = ring[j][0],       yj = ring[j][1];      if (xi == xj && yi == yj) {       continue     }     // 判断点与线段的相对位置,0为在线段上,>0 点在左侧,<0 点在右侧     if (isLeft(point, [ring[i], ring[j]]) === 0) {       return 2; // 点在多边形边上     } else {       if ((yi > y) !== (yj > y)) { // 垂直方向目标点在yi、yj之间         // 求目标点在当前线段上的x坐标。 由于JS小数运算后会转换为精确15位的float,因此需要去一下精度         var xx = Number(((xj - xi) * (y - yi) / (yj - yi) + xi).toFixed(10))         if (x <= xx) { // 目标点水平射线与当前线段有交点           inside = !inside;         }       }     }   }   return Number(inside); }   /** * 判断点与线段的相对位置 * @param point 目标点 * @param line 线段 * * @return isLeft,点与线段的相对位置,0为在线段上,>0 p在左侧,<0 p在右侧 */ function isLeft(point, line) {   var isLeft = ((line[0][0] - point[0]) * (line[1][1] - point[1]) - (line[1][0] - point[0]) * (line[0][1] - point[1]))   // 由于JS小数运算后会转换为精确15位的float,因此需要去一下精度   return Number(isLeft.toFixed(10)) } 1.7、线段与线段的关系
  适用场景:判断线和线的关系 /** * 线段与线段的关系 * @param line1 线段;[[经度,纬度],[经度,纬度]];例:[[116.01,40.01],[116.52,40.01]] * @param line2 线段;[[经度,纬度],[经度,纬度]];例:[[116.33,40.21],[116.36,39.76]] * * @return intersect 线段与线段的关系;0:相离,1:相交,2:相切 */ function intersectLineAndLine(line1, line2) {   var x1 = line1[0][0],     y1 = line1[0][1],     x2 = line1[1][0],     y2 = line1[1][1],     x3 = line2[0][0],     y3 = line2[0][1],     x4 = line2[1][0],     y4 = line2[1][1]    //快速排斥:   //两个线段为对角线组成的矩形,如果这两个矩形没有重叠的部分,那么两条线段是不可能出现重叠的    //这里的确如此,这一步是判定两矩形是否相交   //1.线段ab的低点低于cd的最高点(可能重合)   //2.cd的最左端小于ab的最右端(可能重合)   //3.cd的最低点低于ab的最高点(加上条件1,两线段在竖直方向上重合)   //4.ab的最左端小于cd的最右端(加上条件2,两直线在水平方向上重合)   //综上4个条件,两条线段组成的矩形是重合的   //特别要注意一个矩形含于另一个矩形之内的情况   if (!(Math.min(x1, x2) <= Math.max(x3, x4) && Math.min(y3, y4) <= Math.max(y1, y2) &&       Math.min(x3, x4) <= Math.max(x1, x2) && Math.min(y1, y2) <= Math.max(y3, y4))) {     return 0   }    // 判断点与线段的相对位置,0为在线段上,>0 点在左侧,<0 点在右侧   if (isLeft(line1[0], line2) === 0 || isLeft(line1[1], line2) === 0) {     return 2   }    //跨立实验:   //如果两条线段相交,那么必须跨立,就是以一条线段为标准,另一条线段的两端点一定在这条线段的两段   //也就是说a b两点在线段cd的两端,c d两点在线段ab的两端   var kuaili1 = ((x3 - x1) * (y2 - y1) - (x2 - x1) * (y3 - y1)) * ((x4 - x1) * (y2 - y1) - (x2 - x1) * (y4 - y1))   var kuaili2 = ((x1 - x3) * (y4 - y3) - (x4 - x3) * (y1 - y3)) * ((x2 - x3) * (y4 - y3) - (x4 - x3) * (y2 - y3))   return Number(Number(kuaili1.toFixed(10)) <= 0 && Number(kuaili2.toFixed(10)) <= 0) } 1.8、线和面关系
  适用场景:判断线与面的关系
  该方法考虑到环状多边形的情况,且把相切情况分为了内切和外切。
  参考链接:https://www.cnblogs.com/xiaozhi_5638/p/4165353.html /** * 线和面关系 * @param line 线段;[[经度,纬度],[经度,纬度]];例:[[116.01,40.01],[116.52,40.01]] * @param polygon 面;geojson格式中的coordinates;例:[[[116.1,39.5],[116.1,40.5],[116.9,40.5],[116.9,39.5]],[[116.3,39.7],[116.3,40.3],[116.7,40.3],[116.7,39.7]]] * * @return intersect 线和面关系;0:相离,1:相交,2:包含,3:内切,4:外切 */ function intersectLineAndPolygon(line, polygon) {   var isTangent = false   var isInNum = 0   var intersect = 0   for (var i = 0; i < polygon.length; i++) {     // 线和面关系;0:相离,1:相交,2:包含,3:内切,4:外切     intersect = intersectLineAndRing(line, polygon[i])     if (intersect === 1) {       return 1     } else if (intersect === 2) {       isInNum++     } else if (intersect === 3) {       isInNum++       isTangent = true     } else if (intersect === 4) {       isTangent = true     }   }   if (isInNum % 2 == 0) {     if (isTangent) {       return 4 // 外切     } else {       return 0 // 相离     }   } else if (isInNum % 2 == 1) {     if (isTangent) {       return 3 // 内切     } else {       return 2 // 包含     }   } }   /** * 线和面关系 * @param line 线段 * @param ring 单面 * * @return intersect 线和面关系;0:相离,1:相交,2:包含,3:内切,4:外切 */ function intersectLineAndRing(line, ring) {   var inserset = 0   var isTangent = false   var inserset1 = pointInRing(line[0], ring) // 点和面关系;0:多边形外,1:多边形内,2:多边形边上   var inserset2 = pointInRing(line[1], ring) // 点和面关系;0:多边形外,1:多边形内,2:多边形边上   if (inserset1 === inserset2 === 0) {     inserset = 0   } else if ((inserset1 * inserset2) === 1) {     inserset = 2   } else if ((inserset1 * inserset2) === 2) {     inserset = 3   } else if ((inserset1 === 2 || inserset2 === 2) && (inserset1 === 0 || inserset2 === 0)) {     inserset = 4   } else if ((inserset1 === 1 || inserset2 === 1) && (inserset1 === 0 || inserset2 === 0)) {     return 1 // 相交   }   for (var i = 0, j = ring.length - 1; i < ring.length; j = i++) {     var line2 = [ring[j], ring[i]]     // 目标线段与当前线段的关系;0:相离,1:相交,2:相切     var intersectLine = intersectLineAndLine(line, line2)     if (intersectLine == 1) {       return 1 // 相交     }   }   return inserset } 1.9、geojson 面转线
  适用场景:只有geojson 面数据,获取线的边界 /** * 面转线 * @param geojson 面geojson * * @return geojson 线geojson */ function convertPolygonToPolyline(polygonGeoJson) {   var polylineGeoJson = JSON.parse(JSON.stringify(polygonGeoJson))    for (var i = 0; i < polylineGeoJson.features.length; i++) {     var MultiLineString = []     if (polylineGeoJson.features[i].geometry.type === "Polygon") {       var Polygon = polylineGeoJson.features[i].geometry.coordinates       Polygon.forEach(LinearRing => {         var LineString = LinearRing         MultiLineString.push(LineString)       })     } else if (polylineGeoJson.features[i].geometry.type === "MultiPolygon") {       var MultiPolygon = polylineGeoJson.features[i].geometry.coordinates       MultiPolygon.forEach(Polygon => {         Polygon.forEach(LinearRing => {           var LineString = LinearRing           MultiLineString.push(LineString)         })       })     } else {       console.error("请确认输入参数为geojson格式面数据!")       return null     }     polylineGeoJson.features[i].geometry.type = "MultiLineString" //面转线     polylineGeoJson.features[i].geometry.coordinates = MultiLineString   }    return polylineGeoJson } 2、在线示例
  在线示例:http://gisarmory.xyz/blog/index.html?demo=GISAlgorithm
  代码地址:http://gisarmory.xyz/blog/index.html?source=GISAlgorithm
  原文地址:http://gisarmory.xyz/blog/index.html?blog=GISAlgorithm
  关注《GIS兵器库》, 只给你网上搜不到的GIS知识技能。
  本文章采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名《GIS兵器库》(包含链接: http://gisarmory.xyz/blog/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

一文讲清楚SpringBoot六种读取配置方式欢迎大家关注今日头条号JAVA前线查看更多精彩分享文章,主要包括源码分析实际应用架构思维职场分享产品思考1SpringBoot工程创建1。1maven工程创建使用开发工具创建一个纯运维人员推荐6个服务器监控工具服务器监控工具对系统管理员的重要性不言而喻。推荐6个服务器监控工具给各位运维人。1ConkyConky能在多个的平台上运行,像WindowsLinuxMacOS,大多数BSD都可以快手原副总裁赵丹阳受贿侵占756万余元,一审获刑七年财联社消息,被逮捕一年多后,短视频平台快手原副总裁赵丹阳案新进展披露。4月22日,北京法院审判信息网公开的一审判决书显示,赵丹阳因非国家工作人员受贿罪职务侵占罪被判处有期徒刑七年,Linuxtype命令Linuxtype命令用来显示指定命令的类型,判断给出的指令是内部指令还是外部指令。命令类型alias别名。keyword关键字,shell保留字。function函数,Shell西华大学食品营养与安全团队在FOODS发表封面文章近日,西华大学食品与生物工程学院食品营养与安全团队在国际著名期刊Foods(IF4。35)上发表了题为RegulationMechanismofssDNAAptamerinNano把电脑装进口袋中兴通讯W100D云电脑上手玩疫情的持续反复,让远程办公等云办公方式成为当下新趋势,不少企业持续推进云办公,以提升员工的办公灵活性和工作效率。在此背景下,中兴通讯推出W100D云电脑,意在满足后疫情时代企业云办亚马逊如何打造爆款主图大家好,我是亚马逊知识分享者Simon。最近听到几个行业内的朋友,都在为了怎么做好一套亚马逊主图而苦恼。我相信这也是很多卖家遇到的最常见的问题之一,对于一个精品化运营的Listin院士说地球还能活45亿年,但人类却仅剩一亿年,这是为什么呢只剩一亿年了据媒体报道,在一个节目中,中科院院士汪集旸曾表示目前来看,我们的地球,依旧能够继续存活45亿年的时间,然后事实却并非如此。即使地球还剩下45亿年,而对于人类来说,也只剩3款天玑8100手机,你会选2K屏150W充电还是12512G存储一加Ace天玑8100Max重186g6。7英寸120HzAMOLED直屏居中挖孔LPDDR5UFS3。1后摄主摄5000万OIS防抖800万广角200万微距前摄1600万双扬声器快手原副总裁赵丹阳受贿侵占756万余元一审获刑七年九派财经4月23日讯,被逮捕一年多后,短视频平台快手原副总裁赵丹阳案新进展披露。赵丹阳资料图4月22日,北京法院审判信息网公开的一审判决书显示,赵丹阳因非国家工作人员受贿罪职务侵占UPS暂停中国进口邮包服务信息不实两大转运中心正常服务UPS暂停中国进口邮包服务信息不实两大转运中心正常服务财联社4月23日电,记者从UPS方面获悉,网传近日UPS全面暂停中国进口邮件包裹服务信息不实。UPS方面有关人员表示,UPS持
Java的序列化反序列化一介绍序列化和反序列化几乎是工程师们每天都需要面对的事情,尤其是当前流行的微服务开发。光看定义上,对于初学者来说,可能很难一下子理解序列化的意义,尤其是面对这种特别学术词语的时候,Activity跳转发生TransactionTooLargeException怎么办?发生TransactionTooLargeException是因为Intent的传值太大了,解决方法很简单,activity跳转使用如下方法就可以避免SafeIntent。getI有没有一些好玩的或新奇的app吗?尽管iPhone7新增了防水功能,但是听筒依然是最脆弱的一环。一旦把水溅到手机听筒中,听筒会变得沙哑有杂音。网上流传的吹风机大米棉签等方法通常是没有用的。还记得当初AppleWat办公中,哪些软件可以提高工作效率?不想加班的看过来!这3款小众插件,请务必安装,绝对是告别加班的神器!上班后,你会发现有做不完的PPTExcelWord,简直让人绝望!刚做好的PPT,领导可能突然跟你说颜色风格不行三星LG旗下公司正竞标特斯拉数十亿美元摄像头模组订单鞭牛士1月20日消息,据国外媒体报道,电动汽车厂商特斯拉抛出的价值数十亿美元的摄像头模组订单,将用于他们推出的ModelSModelXModel3和ModelY特斯拉已经推出但尚未佳都科技数字经济已上升为国家战略公司将加速数字化人工智能技术赋能产业升级e公司讯,佳都科技(600728)今日在互动平台表示,公司已在数字孪生计算机视觉大数据等人工智能技术上有多年研发积累,布局智慧交通智慧应急智慧城市治理等数字化应用领域。目前,数字经惠程科技聘任鲁生选为公司财务总监兼副总裁北京商报讯(记者郑蕊周阳洋)1月19日,深圳市惠程信息科技股份有限公司(以下简称惠程科技)发布公告称,经公司总裁王蔚提名董事会提名委员会审核,董事会同意聘任鲁生选为公司财务总监兼副罗永浩要回归科技界,不做手机会做什么?罗永浩相信大家不陌生了,在前些年的时候也就是2012年创办了锤子科技,在2015年左右锤子手机,坚果手机风生水起。罗永浩靠着情怀,和独特的手机设计,独特的手机系统,让自己在科技手机微信更新后支付页面变成了服务,有什么影响吗?微信支付入口更名为了服务,我觉得会产生两大影响,一方面微信更名会给一些用户,特别是老年人制造麻烦,甚至有时候严重了会影响他们找不到微信怎么付钱的方式了。另外一方面也说明微信有了更大电信300m宽带够几个人用?电信300M宽带够几个人用?在不考虑电信服务区主机流畅不拥堵的情况下,电信300M宽带带多少人用取决于光猫出来连接的路由器的性能是否具有上行下行OFDMA技术与路由器高速公路信道的2022年买手机别只盯着旗舰,这十款性价比超高,是最合适你的选择旗舰智能手机性能虽好可是价格也非常昂贵,不是人人都能购买的。好消息是,目前市场上有非常多价格实惠性能超棒的手机,除非你想要一款顶级手机,否则你真的不需要花费大量的资金。下面,为您推