基于 Highmaps 查找经纬度点所在的区域

基于 Highcharts 的官方教程 我们可以很轻松的通过经纬度定位点,也很轻松的用眼睛看出其所在区域,但是如何用代码实现这个看的过程呢?

如何用代码实现“看出”点所在的区域呢

简单分析一下这个问题,由于 Highmaps 的底图和定位点完全是两个独立的数据列,他们两者的关系不大,地图底图是由多个点组成的不规则点,所以这个问题可以抽象成 “判断一个坐标点是否在不规则多边形内部”,算法是 PNPoly

结合 Highmaps,该算法的实现是:

/**
 - PNPoly 算法查找坐标点是否在不规则多边形(Highmaps 底图区域)中
 - @param  [x, y] point 坐标点
 - @param  Highcharts.Point area  Highmaps 底图数据点对象
 - @return boolean  point is in area
 */
function pointInArea(point, area) {
    var x = point[0],
        y = point[1],
        xAxis = map.xAxis[0],
        yAxis = map.yAxis[0];

    // 如果点不在不规则多边形所在矩形内,则直接返回 false
    if (x < area._minX || x > area._maxX || y > area._maxY || y < area._minY) {
        return false;
    }

    var paths = area.path,   // paths 为 svg 命令数组,形如 ['L', 1212, 11212, 1212, 2323, 'M'...]
        i = 0,
        currentPoint = null,  // 当前点
        lastPoint = null,     // 前一个点
        firstPoint = null,    // 第一个点
        even = false,         // 是否是偶数
        result = false,        // 结果
        length = paths.length;

    for (; i < length; i++) {
        // 只有是数值的时候才是点的坐标值
        if (Highcharts.isNumber(paths[i])) {
            if (even) { // 偶数时才构成一个 x,y 点
                currentPoint = [paths[i - 1], paths[i]];
                even = !even;
            } else {
                even = !even;
                continue;
            }

            if (lastPoint) {
                // 根据 PNPoly 算法来计算点是否在区域内
                // 参考链接:
                // * http://www.cnblogs.com/armyfai/p/3529243.html
                // * http://riyueshi.github.io/2015/10/07/is_point_in_polygon/
                if (
                    (lastPoint[1] > y) !== (currentPoint[1] > y) &&
                    (x < (currentPoint[0] - lastPoint[0]) * (y - lastPoint[1]) / (currentPoint[1] - lastPoint[1]) + currentPoint[0])
                ) {
                    result = !result;
                }
            } else {
                // 保存第一个点
                firstPoint = currentPoint;
            }
            lastPoint = currentPoint;
        }
    }
    // 补充计算最后一个点和第一个点
    if (
        (lastPoint[1] > y) !== (firstPoint[1] > y) &&
        (x < (firstPoint[0] - lastPoint[0]) * (y - lastPoint[1]) / (firstPoint[1] - lastPoint[1]) + firstPoint[0])
    ) {
        result = !result;
    }
    return result
}

具体的代码见 例子,例子截图

经纬度定位点并找出其所在的区域

分享到评论