V
V
vitovt2013-08-25 20:30:02
PHP
vitovt, 2013-08-25 20:30:02

Determining an occurrence of an address (coordinates) in a polygon?

Hello dear community!
Actually, there was a need to solve a simple problem, but the solution does not come to mind.
So, there is a catalog of online stores, each of which can create its own delivery zone using Yandex. Kart. The administrator creates "polygons", the coordinates of which are stored in the database. (By the way, for now they are stored in one coords field, but I believe that each LAT, LNG coordinate will have to be stored in separate fields)
When entering the catalog, the user enters his address of residence and all online stores that deliver to him are shown to him. Address coordinates are obtained from Yandex.Maps.
The search gave approximate solutions, like this habrahabr.ru/post/127446/but not exactly. It is necessary to determine in php and mysql which polygons include the desired address coordinates. Maybe someone will give you an idea?

Answer the question

In order to leave comments, you need to log in

6 answer(s)
V
vitovt, 2013-08-25
@vitovt

He asked the question himself, he seems to have found the answer) you can google in the direction of Spatial Extensions

C
cat_crash, 2013-08-26
@cat_crash

There is a GEOS library that can be built as a php extension. I think you'll understand in the manual.

D
Dolios, 2013-08-26
@Dolios

Maybe someone will give you an idea?

The problem of a point belonging to a polygon is solved, for example, by a ray tracing algorithm.
In general, here is:
The problem of whether a point belongs to a polygon
If I understand correctly what you want

U
Urvin, 2013-08-26
@Urvin

MySQL has geographic types, and there are corresponding functions for working with them.
However, these functions make mistakes and I had to do the solution in PHP.
This is 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;
}

This is without:
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;
}

You can shift your entire decision to mySQL, for each polygon from some large square, calculate the occurrence of the user's address in the polygon.

A
Anton Pronin, 2013-08-26
@nightw0rk

Look at my solution to a particular case of your problem, namely to get all the points included in the rectangle. Link . Suddenly, what an idea will appear =)

T
Tomarev, 2021-01-30
@Tomarev

You can try this simple class https://github.com/xopbatgh/sb-polygon-pointer
This mini library helped me a lot.

$polygonBox = [
    [55.761515, 37.600375],
    [55.759428, 37.651156],
    [55.737112, 37.649566],
    [55.737649, 37.597301],
];

$sbPolygonEngine = new sbPolygonEngine($polygonBox);

$isCrosses = $sbPolygonEngine->isCrossesWith(55.746768, 37.625605);

// $isCrosses is boolean

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question