判断多边形与多边形是否相交的方法,代码来自于OpenLayers。

在做GIS开发时,常常需要用到空间判断的算法。比如:判断地图中的多边形与多边形是否相交。我在项目中具体的需求就是如此,需要过滤某个区域的瓦片地图。先把瓦片地图反向解析成Envolope,然后和该区域进行比对,再做其他处理。

  其实在已经有开源的东西GDAL+GEOS可以使用,由于编译(nmake)GEOS对于C#程序员是一件不容易的事情。因为GEOS是C++实现的,网上虽然有别人已经编译好的DLL和LIB,但是始终不能引入到ASP.NET的项目中。没时间继续研究怎么把GEOS应用到C#项目中了,就选择了开源的JS框架OpenLayers。

  OpenLayers中确实有我需要的几何判断的方法,所以就把它的代码剥离成一个JavaScript的函数。发博,以供有类似需求的童鞋使用。

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <title>Polygon intersect Polygon validate Example</title>
        
        <link rel="stylesheet" href="http://openlayers.org/dev/theme/default/style.css" type="text/css">
        <link rel="stylesheet" href="http://openlayers.org/dev/examples/style.css" type="text/css">
        <script src="http://openlayers.org/dev/OpenLayers.js"></script>
        <script src="intersectPolygon.js"></script>
        <script type="text/javascript">
            var map, vectors, controls;
            function init(){
                map = new OpenLayers.Map('map');
                var wms = new OpenLayers.Layer.WMS( "OpenLayers WMS",
                    "http://vmap0.tiles.osgeo.org/wms/vmap0?", {layers: 'basic'});
    
                // allow testing of specific renderers via "?renderer=Canvas", etc
                var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
                renderer = (renderer) ? [renderer] : OpenLayers.Layer.Vector.prototype.renderers;

                vectors = new OpenLayers.Layer.Vector("Vector Layer", {
                    renderers: renderer
                });

                map.addLayers([wms, vectors]);
                map.addControl(new OpenLayers.Control.LayerSwitcher());
                map.addControl(new OpenLayers.Control.MousePosition());

                controls = {
                    point: new OpenLayers.Control.DrawFeature(vectors,
                                OpenLayers.Handler.Point),
                    line: new OpenLayers.Control.DrawFeature(vectors,
                                OpenLayers.Handler.Path),
                    polygon: new OpenLayers.Control.DrawFeature(vectors,
                                OpenLayers.Handler.Polygon),
                    drag: new OpenLayers.Control.DragFeature(vectors)
                };

                for(var key in controls) {
                    map.addControl(controls[key]);
                }

                map.setCenter(new OpenLayers.LonLat(0, 0), 3); 

                controls.polygon.activate();
            }
 
 

            // get polygon rings
            function getRings (index) {
                var features = map.layers[1].features;
                var polygon1 = features[index];
                var cpts = polygon1.geometry.components;
                var ret=[];
                for (var i = 0; i < cpts.length; i++) {
                    var linearRings = cpts[i].components;
                    for (var j = 0; j < linearRings.length; j++) {
                        var point = linearRings[j];
                        ret.push({x:point.x, y:point.y});
                    };
                };
                return ret;
            }

            function clearPolygon () {
                var features = map.layers[1].features;
                for (var i = features.length - 1; i >= 0; i--) {
                    features[i].destroy();
                };
            }


            function main(){ 
                try{
                    var r1 = getRings(0);
                    var r2 = getRings(1);
                    var flag = intersectsPolygonAndPolygon(r1, r2);
                    alert("两个面的相交情况为:"+flag);
                }catch(ex){
                    alert('请先绘制两个面,点击test按钮,验证是否相交!');
                }
            }
        </script>
    </head>
    <body onload="init()">
        <h1 id="title">Polygon intersect Polygon validate Example</h1>


        <div id="map" class="smallmap"></div>
        
        <div id="controls"> 
            <input type="button" value="clear" onclick="clearPolygon();">
            <input type="button" value="test" onclick="main();">
        </div>

    </body>
</html>
//#region 验证两个面是否相交的算法
function intersectsPolygonAndPolygon (polygon1LinearRings, polygon2LinearRings) {
     
    // polygon1LinearRings : array[LinearRing,...]
    function intersectsByPolygon (polygon1LinearRings, polygon2LinearRings) {
        var intersect = false;

        intersect = intersectsByLinearRings(polygon1LinearRings, polygon2LinearRings);

        if(!intersect) {
            // check if this poly contains points of the ring/linestring
            for(i=0, len=polygon2LinearRings.length; i<len; ++i) {
                var point = polygon2LinearRings[i];
                intersect = containsPointByLinearRing(point, polygon1LinearRings);
                if(intersect) {
                    break;
                }
            }
        }
     
        return intersect;
    }

    // LinearRings
    function containsPointByPolygon (point, LinearRings) {
        var numRings = LinearRings.length;
        var contained = false;
        if(numRings > 0) {
            contained = containsPointByLinearRing(point, LinearRings[0]);
            if( numRings > 1) {
                // check interior rings
                var hole;
                for(var i=1; i<numRings; ++i) {
                    hole = containsPointByLinearRing(point, LinearRings[i]);
                    if(hole) {
                        if(hole === 1) {
                            // on edge
                            contained = 1;
                        } else {
                            // in hole
                            contained = false;
                        }                            
                        break;
                    }
                }
            }
        }
        return contained;
    }

    // LinearRing : array[pt]
    // point : {x:1,y:2}
    function containsPointByLinearRing (point, LinearRing) {

        //limitSigDigs
        function approx(num, sig) {
            var fig = 0;
            if (sig > 0) {
                fig = parseFloat(num.toPrecision(sig));
            }
            return fig;
        } 

        var digs = 14;
        var px = approx(point.x, digs);
        var py = approx(point.y, digs);
        function getX(y, x1, y1, x2, y2) {
            return (y - y2) * ((x2 - x1) / (y2 - y1)) + x2;
        }

        var numSeg = LinearRing.length - 1;
        var start, end, x1, y1, x2, y2, cx, cy;
        var crosses = 0;

        for(var i=0; i<numSeg; ++i) {
            start = LinearRing[i];
            x1 = approx(start.x, digs);
            y1 = approx(start.y, digs);
            end = LinearRing[i + 1];
            x2 = approx(end.x, digs);
            y2 = approx(end.y, digs);
            
            if(y1 == y2) {
                // horizontal edge
                if(py == y1) {
                    // point on horizontal line
                    if(x1 <= x2 && (px >= x1 && px <= x2) || // right or vert
                       x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert
                        // point on edge
                        crosses = -1;
                        break;
                    }
                }
                // ignore other horizontal edges
                continue;
            }
            cx = approx(getX(py, x1, y1, x2, y2), digs);
            if(cx == px) {
                // point on line
                if(y1 < y2 && (py >= y1 && py <= y2) || // upward
                   y1 > y2 && (py <= y1 && py >= y2)) { // downward
                    // point on edge
                    crosses = -1;
                    break;
                }
            }
            if(cx <= px) {
                // no crossing to the right
                continue;
            }
            if(x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) {
                // no crossing
                continue;
            }
            if(y1 < y2 && (py >= y1 && py < y2) || // upward
               y1 > y2 && (py < y1 && py >= y2)) { // downward
                ++crosses;
            }
        }

        var contained = (crosses == -1) ?
            // on edge
            1 :
            // even (out) or odd (in)
            !!(crosses & 1);

        return contained;
    }


    function intersectsByLinearRings (LinearRing1, LinearRings2) {
        var intersect = false;
            var segs1 = getSortedSegments(LinearRing1); 
            var segs2 = getSortedSegments(LinearRings2); 
     
            var seg1, seg1x1, seg1x2, seg1y1, seg1y2,
                seg2, seg2y1, seg2y2;
            // sweep right
            outer: for(var i=0, len=segs1.length; i<len; ++i) {
                seg1 = segs1[i];
                seg1x1 = seg1.x1;
                seg1x2 = seg1.x2;
                seg1y1 = seg1.y1;
                seg1y2 = seg1.y2;
                inner: for(var j=0, jlen=segs2.length; j<jlen; ++j) {
                    seg2 = segs2[j];
                    if(seg2.x1 > seg1x2) {
                        // seg1 still left of seg2
                        break;
                    }
                    if(seg2.x2 < seg1x1) {
                        // seg2 still left of seg1
                        continue;
                    }
                    seg2y1 = seg2.y1;
                    seg2y2 = seg2.y2;
                    if(Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) {
                        // seg2 above seg1
                        continue;
                    }
                    if(Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) {
                        // seg2 below seg1
                        continue;
                    }
                    if(segmentsIntersect(seg1, seg2)) {
                        intersect = true;
                        break outer;
                    }
                }
            } 
        return intersect;
    }

    function getSortedSegments(points) {
        var numSeg = points.length - 1;
        var segments = new Array(numSeg), point1, point2;
        for(var i=0; i<numSeg; ++i) {
            point1 = points[i];
            point2 = points[i + 1];
            if(point1.x < point2.x) {
                segments[i] = {
                    x1: point1.x,
                    y1: point1.y,
                    x2: point2.x,
                    y2: point2.y
                };
            } else {
                segments[i] = {
                    x1: point2.x,
                    y1: point2.y,
                    x2: point1.x,
                    y2: point1.y
                };
            }
        }
        // more efficient to define this somewhere static
        function byX1(seg1, seg2) {
            return seg1.x1 - seg2.x1;
        }
        return segments.sort(byX1);
    }

     function segmentsIntersect(seg1, seg2, options) {
        var point = options && options.point;
        var tolerance = options && options.tolerance;
        var intersection = false;
        var x11_21 = seg1.x1 - seg2.x1;
        var y11_21 = seg1.y1 - seg2.y1;
        var x12_11 = seg1.x2 - seg1.x1;
        var y12_11 = seg1.y2 - seg1.y1;
        var y22_21 = seg2.y2 - seg2.y1;
        var x22_21 = seg2.x2 - seg2.x1;
        var d = (y22_21 * x12_11) - (x22_21 * y12_11);
        var n1 = (x22_21 * y11_21) - (y22_21 * x11_21);
        var n2 = (x12_11 * y11_21) - (y12_11 * x11_21);
        if(d == 0) {
            // parallel
            if(n1 == 0 && n2 == 0) {
                // coincident
                intersection = true;
            }
        } else {
            var along1 = n1 / d;
            var along2 = n2 / d;
            if(along1 >= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) {
                // intersect
                if(!point) {
                    intersection = true;
                } else {
                    // calculate the intersection point
                    var x = seg1.x1 + (along1 * x12_11);
                    var y = seg1.y1 + (along1 * y12_11);
                    intersection = { 'x':x, 'y':y };
                }
            }
        }
        if(tolerance) {
            var dist;
            if(intersection) {
                if(point) {
                    var segs = [seg1, seg2];
                    var seg, x, y;
                    // check segment endpoints for proximity to intersection
                    // set intersection to first endpoint within the tolerance
                    outer: for(var i=0; i<2; ++i) {
                        seg = segs[i];
                        for(var j=1; j<3; ++j) {
                            x = seg["x" + j];
                            y = seg["y" + j];
                            dist = Math.sqrt(
                                Math.pow(x - intersection.x, 2) +
                                Math.pow(y - intersection.y, 2)
                            );
                            if(dist < tolerance) {
                                intersection.x = x;
                                intersection.y = y;
                                break outer;
                            }
                        }
                    }
                    
                }
            } else {
                // no calculated intersection, but segments could be within
                // the tolerance of one another
                var segs = [seg1, seg2];
                var source, target, x, y, p, result;
                // check segment endpoints for proximity to intersection
                // set intersection to first endpoint within the tolerance
                outer: for(var i=0; i<2; ++i) {
                    source = segs[i];
                    target = segs[(i+1)%2];
                    for(var j=1; j<3; ++j) {
                        p = {x: source["x"+j], y: source["y"+j]};
                        result = distanceToSegment(p, target);
                        if(result.distance < tolerance) {
                            if(point) {
                                intersection = { 'x':p.x, 'y':p.y }; 
                            } else {
                                intersection = true;
                            }
                            break outer;
                        }
                    }
                }
            }
        }
        return intersection;
    };

    function distanceToSegment(point, segment) {
        var result = distanceSquaredToSegment(point, segment);
        result.distance = Math.sqrt(result.distance);
        return result;
    };

    function distanceSquaredToSegment(point, segment) {
        var x0 = point.x;
        var y0 = point.y;
        var x1 = segment.x1;
        var y1 = segment.y1;
        var x2 = segment.x2;
        var y2 = segment.y2;
        var dx = x2 - x1;
        var dy = y2 - y1;
        var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) /
                    (Math.pow(dx, 2) + Math.pow(dy, 2));
        var x, y;
        if(along <= 0.0) {
            x = x1;
            y = y1;
        } else if(along >= 1.0) {
            x = x2;
            y = y2;
        } else {
            x = x1 + along * dx;
            y = y1 + along * dy;
        }
        return {
            distance: Math.pow(x - x0, 2) + Math.pow(y - y0, 2),
            x: x, y: y,
            along: along
        };
    }

    return intersectsByPolygon(polygon1LinearRings, polygon2LinearRings);
}
//#endregion

                [图1] 相交的情况。

                [图2] 不相交的情况。

 

本示例采用HTML+JAVASCRIPT编写,如需要源码请点击 例子 下载。

转载自:https://blog.csdn.net/u011001084/article/details/53336106

You may also like...

退出移动版