I\'m making a small application with Google maps that determines if an entered address is part of a predefined service region.
The user enters an address, and a PHP
Assuming that the $points
array contains the corners of a polygon describing the coverage area in clockwise (or counterclockwise order), your code looks correct to me. Basically, it's counting the number of polygon edges that intersect a line drawn due east from the given point to the 180th meridian.
I'd perhaps rewrite it like this, just for clarity:
$p0 = end($points);
foreach ( $points as $p1 ) {
// ignore edges of constant latitude (yes, this is correct!)
if ( $p0->lat != $p1->lat ) {
// scale latitude of $test_point so that $p0 maps to 0 and $p1 to 1:
$interp = ($test_point->lat - $p0->lat) / ($p1->lat - $p0->lat);
// does the edge intersect the latitude of $test_point?
// (note: use >= and < to avoid double-counting exact endpoint hits)
if ( $interp >= 0 && $interp < 1 ) {
// longitude of the edge at the latitude of the test point:
// (could use fancy spherical interpolation here, but for small
// regions linear interpolation should be fine)
$long = $interp * $p1->long + (1 - $interp) * $p0->long;
// is the intersection east of the test point?
if ( $long < $test_point->long ) {
// if so, count it:
$ctr++;
}
}
}
$p0 = $p1;
}
Note that this code will break in all sorts of interesting ways if the region boundary crosses the 180th meridian, so please don't use it if you have any service regions in the middle of the Pacific Ocean.
If you're still having problems, try plotting the polygon described by the $points
array on a map; you may find out that it doesn't look like you thought it would, e.g. if some points are listed in the wrong order.
Well.... your second if() does not compensate for the fact that any of the subtractions may result in a negative number; it would only work if the coordinates are strictly ordered.
Update: On http://rosettacode.org/wiki/Ray-casting_algorithmn there's a whole bunch of algorithms in various languages that describe the process in detail (unfortunately, there is no version for PHP). What seems to be missing from your solution is picking a point that's guaranteed to be outside of the polygon; since you're dealing with longitude/lattitude that should be easy. Second, make sure your polygon is closed (i.e. go from the last point back to the first, if Google Maps doesn't already do so)
There is a bug with this algorithm, when the ray is tangent to the shape. Just add an epsilon to the test point's latitude when it might happen (line 3 of Ilmari's code):
if ($test_point->lat == $p0->lat)
$test_point->lat += 0.0000000001;
Also see http://rosettacode.org/wiki/Ray-casting_algorithm (corrected URL).
Thanks.