JavaScript库常见GIS优化算法

文件目录

  • 1、常用算法
    • 1.1、测算两地理坐标点中间的间距
    • 1.2、依据已经知道线条及其到起始点间距,求总体目标点座标
    • 1.3、已经知道点、线条,求垂足
    • 1.4、线条上间距总体目标点近期的点
    • 1.5、点缓存
    • 1.6、点揉面关联
    • 1.7、线条与线条的关联
    • 1.8、线揉面关联
    • 1.9、geojson 面转线
  • 2、线上实例

做为一个GISer,在日常WebGIS开发设计中,会常见到的turf.js,这是一个自然地理数据挖掘技术的JavaScript库,常常配搭各种各样GIS JS API应用,如leafletmapboxglopenlayers等;在后台管理Java开发设计中,也还有一个较为强劲的GIS库,geotools,里边包括搭建一个完全的大数据技术所须要的所有java工具;数据库查询端常见是postgis拓展,必须在postgres库文件引进应用。

殊不知在开发设计某一些信息化系统的情况下,有一些要求只必须读取某一个GIS优化算法,简易的几行编码就可以进行,沒有必需去引入一个GIS类库。

并且有一些优化算法在这常见的GIS类库中沒有相匹配插口,就例如在下文纪录的这几类常用算法中,求垂足、分辨线揉面的关联,在turf.js就沒有相匹配插口。

下边文章内容中就是我汇总的一些常见GIS优化算法,这儿统一用JavaScript语言表达完成,由于JS编码相对性较为简洁明了,便捷了解在其中优化算法逻辑性,也便捷在浏览器下浏览实际效果。在实际运用时可以按照实际要求,译成JavaC#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、点揉面关联

该方式选用X射线法构思完成。(掌握X射线法可参照: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) { // 总体目标点水准X射线与当今线条有相交点          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]  //迅速抵触:  //2个线条为对角构成的矩形框,假如这两个矩形框沒有重合的一部分,那样两根线条是不太可能发生重合的  //这儿确实如此,这一步是判断两矩形框是不是交点  //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  }  //跨立试验:  //假如两根线条交点,那样务必跨立,便是以一条线条为规范,另一条线条的两边点一定在这里条线条的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/),不可用以商业服务目地,根据文中改动后的佳作尽量以同样的批准公布。