Merge pull request #692 from ahocevar/pnpoly

Point-in-polygon improvements. r=@bartvde
This commit is contained in:
ahocevar
2013-05-08 05:11:16 -07:00
5 changed files with 109 additions and 39 deletions

View File

@@ -37,7 +37,7 @@
<div class="span4">
<h4 id="title">Vector layer example</h4>
<p id="shortdesc">Example of a vector layer.</p>
<p id="shortdesc">Example of a countries vector layer with country information on hover.</p>
<div id="docs">
<p>See the <a href="vector-layer.js" target="_blank">vector-layer.js source</a> to see how this is done.</p>
</div>

View File

@@ -42,31 +42,3 @@ ol.geom.squaredDistanceToSegment = function(coordinate, segment) {
return ol.coordinate.squaredDistance(coordinate,
[v[0] + t * (w[0] - v[0]), v[1] + t * (w[1] - v[1])]);
};
/**
* Calculate whether a point falls inside a polygon.
*
* @param {ol.Coordinate} coordinate Coordinate of the point.
* @param {Array.<ol.Coordinate>} vertices Vertices of the polygon.
* @return {boolean} Whether the point falls inside the polygon.
*/
ol.geom.pointInPolygon = function(coordinate, vertices) {
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
var x = coordinate[0], y = coordinate[1];
var inside = false;
var xi, yi, xj, yj, intersect;
var numVertices = vertices.length;
for (var i = 0, j = numVertices - 1; i < numVertices; j = i++) {
xi = vertices[i][0];
yi = vertices[i][1];
xj = vertices[j][0];
yj = vertices[j][1];
intersect = ((yi > y) != (yj > y)) &&
(x < (xj - xi) * (y - yi) / (yj - yi) + xi);
if (intersect) {
inside = !inside;
}
}
return inside;
};

View File

@@ -33,3 +33,33 @@ goog.inherits(ol.geom.LinearRing, ol.geom.LineString);
ol.geom.LinearRing.prototype.getType = function() {
return ol.geom.GeometryType.LINEARRING;
};
/**
* Check whether a given coordinate is inside this ring. Note that this is a
* fast and simple check - points on an edge or vertex of the ring are either
* classified inside or outside.
*
* @param {ol.Coordinate} coordinate Coordinate.
* @return {boolean} Whether the coordinate is inside the ring.
*/
ol.geom.LinearRing.prototype.containsCoordinate = function(coordinate) {
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
var x = coordinate[0], y = coordinate[1];
var vertices = this.getCoordinates();
var inside = false;
var xi, yi, xj, yj, intersect;
var numVertices = vertices.length;
for (var i = 0, j = numVertices - 1; i < numVertices; j = i++) {
xi = vertices[i][0];
yi = vertices[i][1];
xj = vertices[j][0];
yj = vertices[j][1];
intersect = ((yi > y) != (yj > y)) &&
(x < (xj - xi) * (y - yi) / (yj - yi) + xi);
if (intersect) {
inside = !inside;
}
}
return inside;
};

View File

@@ -91,22 +91,25 @@ ol.geom.Polygon.prototype.getType = function() {
/**
* Check whether a given coordinate is inside this polygon.
* Check whether a given coordinate is inside this polygon. Note that this is a
* fast and simple check - points on an edge or vertex of the polygon or one of
* its inner rings are either classified inside or outside.
*
* @param {ol.Coordinate} coordinate Coordinate.
* @return {boolean} Whether the coordinate is inside the polygon.
*/
ol.geom.Polygon.prototype.containsCoordinate = function(coordinate) {
var rings = this.rings;
var containsCoordinate = ol.geom.pointInPolygon(coordinate,
rings[0].getCoordinates());
if (containsCoordinate) {
// if inner ring contains point, polygon does not contain it
for (var i = 1, ii = rings.length; i < ii; ++i) {
if (ol.geom.pointInPolygon(coordinate, rings[i].getCoordinates())) {
containsCoordinate = false;
break;
}
/** @type {boolean} */
var containsCoordinate;
for (var i = 0, ii = rings.length; i < ii; ++i) {
containsCoordinate = rings[i].containsCoordinate(coordinate);
// if inner ring (i > 0) contains coordinate, polygon does not contain it
if (i > 0) {
containsCoordinate = !containsCoordinate;
}
if (!containsCoordinate) {
break;
}
}
return containsCoordinate;

View File

@@ -40,6 +40,71 @@ describe('ol.geom.LinearRing', function() {
});
describe('#containsCoordinate()', function() {
it('knows when a point coordinate is inside a ring', function() {
/**
* The ring:
* edge 3
* (5, 10) __________ (15, 10)
* / /
* edge 4 / / edge 2
* / /
* (0, 0) /_________/ (10, 0)
* edge 1
*/
var ring = new ol.geom.LinearRing(
[[0, 0], [10, 0], [15, 10], [5, 10]]);
// contains: 1 (touches - not implemented), true (within), false (outside)
var cases = [{
point: [5, 5], contains: true
}, {
point: [20, 20], contains: false
}, {
point: [15, 15], contains: false
}/*, {
point: [0, 0], contains: 1 // lower left corner
}, {
point: [10, 0], contains: 1 // lower right corner
}, {
point: [15, 10], contains: 1 // upper right corner
}, {
point: [5, 10], contains: 1 // upper left corner
}, {
point: [5, 0], contains: 1 // on edge 1
}*/, {
point: [5, -0.1], contains: false // below edge 1
}, {
point: [5, 0.1], contains: true // above edge 1
}/*, {
point: [12.5, 5], contains: 1 // on edge 2
}*/, {
point: [12.4, 5], contains: true // left of edge 2
}, {
point: [12.6, 5], contains: false // right of edge 2
}/*, {
point: [10, 10], contains: 1 // on edge 3
}*/, {
point: [10, 9.9], contains: true // below edge 3
}, {
point: [10, 10.1], contains: false // above edge 3
}/*, {
point: [2.5, 5], contains: 1 // on edge 4
}*/, {
point: [2.4, 5], contains: false // left of edge 4
}, {
point: [2.6, 5], contains: true // right of edge 4
}];
var c;
for (var i = 0, ii = cases.length; i < ii; ++i) {
c = cases[i];
expect(ring.containsCoordinate(c.point)).to.be(c.contains);
}
});
});
});
goog.require('ol.geom.LinearRing');