В mySQL есть географические типы, есть соответствующие функции для работы с ними.
Однако, эти функции совершают ошибки и мне пришлось делать решение на PHP.
Это SQL
/**
* Calculates whether Point is in Polygon or not. Uses mySql
* @param array $aPoint
* @param array $aCityBounds
* @return boolean
*/
protected function isPointInCity(array $aPoint,array &$aCityBounds)
{
$lPointGeomText = 'Point(' . $aPoint[0] . ' ' . $aPoint[1] . ')';
$lPolyGeomText = '';
foreach($aCityBounds as &$lBoundPoint)
$lPolyGeomText .= (empty($lPolyGeomText) ? '' : ',') . $lBoundPoint[0] . ' ' . $lBoundPoint[1];
// include first point to finish polygon
$lPolyGeomText = 'Polygon((' . $lPolyGeomText . ',' . $aCityBounds[0][0] . ' ' . $aCityBounds[0][1] . '))';
$lQuery = "SELECT MBRIntersects(GeomFromText('" . $lPolyGeomText . "'),GeomFromText('" . $lPointGeomText . "'))";
return intval(DbWrapper::dbGetValue($lQuery)) > 0 ? true : false;
}
Это — без:
protected function isPointInCityBoundingBox(array $aPoint, array &$aCityBoundBox)
{
return $aPoint[0] >= $aCityBoundBox['minx'] && $aPoint[0] <= $aCityBoundBox['maxx'] &&
$aPoint[1] >= $aCityBoundBox['miny'] && $aPoint[1] <= $aCityBoundBox['maxy'];
}
/**
* Calculate if point is inside polygon, more precisely than mySql now
* @param array $aPoint
* @param array $aCityBounds
* @param array $aCityBoundBox
* @return boolean
*/
protected function isPointInCityNoSql(array $aPoint, array &$aCityBounds, array &$aCityBoundBox)
{
if(!$this->isPointInCityBoundingBox($aPoint, $aCityBoundBox))
return false;
$pj = 0;
$pk = 0;
$wrkx = 0;
$yu = 0;
$yl = 0;
$lPointsCount = count($aCityBounds);
for($pj = 0; $pj < $lPointsCount; $pj++)
{
$yu = $aCityBounds[$pj][1] > $aCityBounds[($pj + 1) % $lPointsCount][1] ? $aCityBounds[$pj][1] : $aCityBounds[($pj + 1) % $lPointsCount][1];
$yl = $aCityBounds[$pj][1] < $aCityBounds[($pj + 1) % $lPointsCount][1] ? $aCityBounds[$pj][1] : $aCityBounds[($pj + 1) % $lPointsCount][1];
if($aCityBounds[($pj + 1) % $lPointsCount][1] - $aCityBounds[$pj][1])
$wrkx = $aCityBounds[$pj][0] + ($aCityBounds[($pj + 1) % $lPointsCount][0] - $aCityBounds[$pj][0]) * ($aPoint[1] - $aCityBounds[$pj][1]) / ($aCityBounds[($pj + 1) % $lPointsCount][1] - $aCityBounds[$pj][1]);
else
$wrkx = $aCityBounds[$pj][0];
if($yu >= $aPoint[1])
if($yl < $aPoint[1])
{
if($aPoint[0] > $wrkx)
$pk++;
if(abs($aPoint[0] - $wrkx) < 0.00001)
return true;
}
if((abs($aPoint[1] - $yl) < 0.00001) && (abs($yu - $yl) < 0.00001) && (abs(abs($wrkx - $aCityBounds[$pj][0]) + abs($wrkx - $aCityBounds[($pj + 1) % $lPointsCount][0]) - abs($aCityBounds[$pj][0] - $aCityBounds[($pj + 1) % $lPointsCount][0])) < 0.0001))
return true;
}
return ($pk % 2) ? true : false;
}
Вы можете все ваше решение переложить целиком на mySQL, по каждому полигону из какого-то большого квадрата рассчитывать вхождение адреса пользователя в полигон.