Locating a point within a GPS polygon

I don't know if this will be relevant for anyone else, but I was challenged to figure out whether a point occurred within a polygon as drawn within a KML (Google Earth/Google Maps) file.

I had originally tried to implement this in pure MySQL using the spatial extensions but frankly didn't have a lot of luck with that.

What I do is extract out the coordinates from the KML, copy and paste them into a PHP string, and then from there it's entirely automated:

<?php

header('Content-Type: text/xml;charset=UTF-8');

print '<?xml version="1.0" encoding="UTF-8"?>

<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">

<Document>

<Style id="unmatchedpin">

<IconStyle id="unmatchedpinicon">

     <Icon>

       <href>http://maps.google.com/mapfiles/kml/shapes/caution.png</href>

       <scale>1.0</scale>

     </Icon>

   </IconStyle>

   <LabelStyle><scale>0</scale></LabelStyle>

</Style>

<Style id="matchedpin">

<IconStyle id="matchedpinicon">

     <Icon>

       <href>http://maps.google.com/mapfiles/kml/shapes/info_circle.png</href>

       <scale>1.0</scale>

     </Icon>

   </IconStyle>

   <LabelStyle><scale>0</scale></LabelStyle>

</Style>

/* Do not service */

$in_dns_txt_1 =

'

-94.987793,49.009052,0.000000

-95.097649,51.124210,0.000000

-94.482422,53.041214,0.000000

-88.945312,56.800869,0.000000

-85.253899,55.528629,0.000000

-82.529289,55.229019,0.000000

-82.001953,53.852520,0.000000

-80.595703,53.041214,0.000000

-80.595703,51.944260,0.000000

-79.562988,51.631657,0.000000

-78.486320,52.268150,0.000000

-79.453117,54.673828,0.000000

-76.640617,55.973789,0.000000

-77.255852,58.170700,0.000000

-78.925781,58.995312,0.000000

-77.607422,59.977001,0.000000

-78.134758,60.973099,0.000000

-78.398430,62.471722,0.000000

-75.410149,62.471722,0.000000

-73.652344,62.552849,0.000000

-71.103508,61.564571,0.000000

-70.048820,61.227951,0.000000

-67.302246,60.392147,0.000000

-69.169922,60.020950,0.000000

-68.378899,58.631210,0.000000

-67.148430,58.447731,0.000000

-64.687500,60.769890,0.000000

-61.699211,57.891491,0.000000

-59.699707,55.899956,0.000000

-57.304688,54.927143,0.000000

-56.074211,53.800652,0.000000

-55.458981,52.268150,0.000000

-55.634762,50.958420,0.000000

-55.722649,50.007729,0.000000

-53.854980,49.823811,0.000000

-53.162842,49.360912,0.000000

-52.470703,46.513515,0.000000

-54.843750,46.860191,0.000000

-56.359863,46.611713,0.000000

-59.567871,47.694973,0.000000

-59.216309,49.353756,0.000000

-57.546387,50.708633,0.000000

-56.601559,51.672550,0.000000

-59.501949,50.345459,0.000000

-64.248039,50.233150,0.000000

-61.699211,49.382370,0.000000

-62.490231,49.037861,0.000000

-60.117180,46.980251,0.000000

-59.677731,46.073231,0.000000

-60.908199,45.274879,0.000000

-61.523430,44.087582,0.000000

-66.621094,44.964790,0.000000

-67.851562,47.338821,0.000000

-69.873039,47.694969,0.000000

-71.279289,45.398449,0.000000

-75.146477,45.089031,0.000000

-76.816399,43.961189,0.000000

-79.453117,44.087582,0.000000

-80.156250,43.004639,0.000000

-82.089844,42.098221,0.000000

-81.738281,43.516682,0.000000

-82.089844,45.151051,0.000000

-83.320312,46.377251,0.000000

-86.132812,47.694969,0.000000

-87.978508,48.574791,0.000000

-89.121094,48.429199,0.000000

-90.878906,48.195389,0.000000

-94.987793,49.009052,0.000000

';

function poly_string_into_coordinate_arrays($instring)

{

$x_array = array();

$y_array = array();


$lines = preg_split('/\n/', $instring);

foreach($lines as $l)

{

$fields = preg_split('/,/', $l);

if(count($fields) == 3)

{

$x = $fields[0];

$y = $fields[1];

$z = $fields[2];


$x_array[] = $x;

$y_array[] = $y;

}

}


return array(

'polyx' => $x_array,

'polyy' => $y_array,

);

}

$in_dns_1 = poly_string_into_coordinate_arrays($in_dns_txt_1);

function pointInPolygon($out,$x,$y) 

{

  $polyX = $out['polyx'];

  $polyY = $out['polyy'];

  $polySides = count($polyX);


  $j = $polySides-1 ;

  $oddNodes = 0;

  for ($i=0; $i<$polySides; $i++) {

    if ($polyY[$i]<$y && $polyY[$j]>=$y 

 ||  $polyY[$j]<$y && $polyY[$i]>=$y) {

    if ($polyX[$i]+($y-$polyY[$i])/($polyY[$j]-$polyY[$i])*($polyX[$j]-$polyX[$i])<$x)    {

    $oddNodes=!$oddNodes; }}

   $j=$i; }

  return $oddNodes; 

}

$mysqli = new mysqli("localhost", "root", "rootpassword", "geolocatedb");

$sql = "SELECT Postal,Latitude,Longitude FROM canada GROUP BY City ORDER BY RAND()*MAX(id) LIMIT 1000";

if($result = $mysqli->query($sql))

{

while($row = $result->fetch_array(MYSQLI_ASSOC))

{

$style = "#unmatchedpin";

if(pointInPolygon($in_dns_1, $row["Longitude"], $row["Latitude"]))

{

$style = "#foundpin";

}

                printf('

<Placemark id="%s">

<name>%s</name>

<Point><coordinates>%f,%f,0</coordinates></Point>

<styleUrl>%s</styleUrl>

</Placemark>' . "\n"

, $row["Postal"], $row["Postal"], $row["Longitude"], $row["Latitude"], $style);

        }

}

?>

</Document>

</kml>