Когда-то решал похожее...
У меня была такая задача:
1. Есть точка на карте;
2. Есть полигоны (многоугольники) на карте;
3. Есть внутри каждого многоугольника точка на случай, если точка из п.1 не попала ни в один многоугольник, то нужно найти ближайшую точку из п.3 для точки из п.1...
Ниже пример как проверять вхождение точки из п.1 в полигоны (многоугольники) из п.2...
class Polygon {
/**
* @var array
*/
var $polygon = [];
/**
* Polygon itself, with basic vector-based structure
* Array: [ [1,1], [2,1], [3,0], [2,-1] ]
*
* @var $polygon array
*/
function setPolygon($polygon) {
if (count($polygon) < 3) {
return false;
}
if (!isset($polygon[0]['x'])) {
foreach ($polygon as &$point) {
//$point = ['x' => round($point[0] + 300, 4), 'y' => round($point[1] + 300, 4)];
//$point = ['x' => round($point[0], 4), 'y' => round($point[1], 4)];
if($point[0] < 0)
$point[0] = 90 + 90 - abs($point[0]);
if($point[1] < 0)
$point[1] = 180 + 180 - abs($point[1]);
$point = ['x' => round($point[0], 4), 'y' => round($point[1], 4)];
}
}
$this->polygon = $polygon;
}
/**
* Check if $polygon contains $test value
*
* @var $test array(x=>decimal, y=>decimal)
*/
function calc($test) {
if($test['x'] < 0)
$test['x'] = 90 + 90 - abs($test['x']);
if($test['y'] < 0)
$test['y'] = 180 + 180 - abs($test['y']);
$q_patt= [[0, 1], [3, 2]];
$end = end($this->polygon);
$pred_pt = end($this->polygon);
$pred_pt['x'] -= $test['x'];
$pred_pt['y'] -= $test['y'];
$pred_q = $q_patt[$pred_pt['y'] < 0][$pred_pt['x'] < 0];
$w = 0;
for ($iter = reset($this->polygon); $iter !== false; $iter = next($this->polygon)) {
$cur_pt = $iter;
$cur_pt['x'] -= $test['x'];
$cur_pt['y'] -= $test['y'];
$q = $q_patt[$cur_pt['y'] < 0][$cur_pt['x'] < 0];
switch ($q - $pred_q) {
case -3:
++$w;
break;
case 3:
--$w;
break;
case -2:
if ($pred_pt['x'] * $cur_pt['y'] >= $pred_pt['y'] * $cur_pt['x']) {
++$w;
}
break;
case 2:
if (!($pred_pt['x'] * $cur_pt['y'] >= $pred_pt['y'] * $cur_pt['x'])) {
--$w;
}
break;
}
$pred_pt = $cur_pt;
$pred_q = $q;
}
return $w != 0;
}
public static function distance($lat1, $lng1, $lat2, $lng2) {
$lat1=deg2rad($lat1);
$lng1=deg2rad($lng1);
$lat2=deg2rad($lat2);
$lng2=deg2rad($lng2);
$delta_lat=($lat2 - $lat1);
$delta_lng=($lng2 - $lng1);
return round( 6378137 * acos( cos( $lat1 ) * cos( $lat2 ) * cos( $lng1 - $lng2 ) + sin( $lat1 ) * sin( $lat2 ) ) );
}
public static function closestPoint($x, $y, $arPoints) {
$RESULT = false;
$x = floatval($x);
$y = floatval($y);
if(!empty($arPoints) && is_array($arPoints) && count($arPoints) > 0) {
$arRes = array();
foreach ($arPoints as $KEY => $VAL) {
$x2 = floatval($VAL[0]);
$y2 = floatval($VAL[1]);
$distance = self::distance($x, $y, $x2, $y2);
$arRes[$KEY] = $distance;
}
if(count($arRes) > 0) {
natsort($arRes);
reset($arRes);
//$RESULT[key($arRes)] = current($arRes);
$RESULT = array(
'ID' => key($arRes),
'DISTANCE' => current($arRes)
);
}
}
return $RESULT;
}
}
/* Ипользование */
// Координаты точки, которую проверяем на вхождение в "полигон"
$X = 33.45;
$Y = 44.25;
// "Полигоны"
$arCheckPoints = [
'КЛЮЧ_МНОГОУГОЛЬНИКА' => [22.45, 44.55, 11.22, 55.66], // Координаты вершин
'КЛЮЧ_МНОГОУГОЛЬНИКА_2' => [33.45, 66.55, 77.22, 99.66], // Координаты вершин
'КЛЮЧ_МНОГОУГОЛЬНИКА_3' => [12.45, 15.55, 17.22, 54.66], // Координаты вершин
];
$arResults = [];
$p = new \App\Helpers\Polygon();
foreach ($arCheckPoints as $KEY_POLYGON => $arPolygon) {
$p->setPolygon($arPolygon);
if($p->calc(array('x' => $X, 'y' => $Y)))
$arResults[$KEY_POLYGON] = $arPolygon;
}
Для решения п.3 используется функция
distance...