sha*_*nth 22 php mysql polygon point-in-polygon
我有一个几何数据类型为mysql,polygon的典型问题.
我有纬度和经度数组形式的多边形数据,例如:
[["x":37.628134, "y":-77.458334],
["x":37.629867, "y":-77.449021],
["x":37.62324, "y":-77.445416],
["x":37.622424, "y":-77.457819]]
Run Code Online (Sandbox Code Playgroud)
我有一个坐标为纬度和经度的点(顶点),例如:
$location = new vertex($_GET["longitude"], $_GET["latitude"]);
Run Code Online (Sandbox Code Playgroud)
现在我想找到这个顶点(点)是否在多边形内.我怎么能在PHP中这样做?
Tha*_*ama 43
这是我从另一种语言转换为PHP的函数:
$vertices_x = array(37.628134, 37.629867, 37.62324, 37.622424); // x-coordinates of the vertices of the polygon
$vertices_y = array(-77.458334,-77.449021,-77.445416,-77.457819); // y-coordinates of the vertices of the polygon
$points_polygon = count($vertices_x) - 1; // number vertices - zero-based array
$longitude_x = $_GET["longitude"]; // x-coordinate of the point to test
$latitude_y = $_GET["latitude"]; // y-coordinate of the point to test
if (is_in_polygon($points_polygon, $vertices_x, $vertices_y, $longitude_x, $latitude_y)){
echo "Is in polygon!";
}
else echo "Is not in polygon";
function is_in_polygon($points_polygon, $vertices_x, $vertices_y, $longitude_x, $latitude_y)
{
$i = $j = $c = 0;
for ($i = 0, $j = $points_polygon ; $i < $points_polygon; $j = $i++) {
if ( (($vertices_y[$i] > $latitude_y != ($vertices_y[$j] > $latitude_y)) &&
($longitude_x < ($vertices_x[$j] - $vertices_x[$i]) * ($latitude_y - $vertices_y[$i]) / ($vertices_y[$j] - $vertices_y[$i]) + $vertices_x[$i]) ) )
$c = !$c;
}
return $c;
}
Run Code Online (Sandbox Code Playgroud)
附加:
对于更多功能,我建议您使用此处提供的polygon.php类.使用顶点创建类,并使用isInside测试点作为输入调用函数,以使另一个函数解决您的问题.
amh*_*h15 11
上面流行的答案包含错别字.在其他地方,此代码已被清理.更正后的代码如下:
<?php
/**
From: http://www.daniweb.com/web-development/php/threads/366489
Also see http://en.wikipedia.org/wiki/Point_in_polygon
*/
$vertices_x = array(37.628134, 37.629867, 37.62324, 37.622424); // x-coordinates of the vertices of the polygon
$vertices_y = array(-77.458334,-77.449021,-77.445416,-77.457819); // y-coordinates of the vertices of the polygon
$points_polygon = count($vertices_x); // number vertices
$longitude_x = $_GET["longitude"]; // x-coordinate of the point to test
$latitude_y = $_GET["latitude"]; // y-coordinate of the point to test
//// For testing. This point lies inside the test polygon.
// $longitude_x = 37.62850;
// $latitude_y = -77.4499;
if (is_in_polygon($points_polygon, $vertices_x, $vertices_y, $longitude_x, $latitude_y)){
echo "Is in polygon!";
}
else echo "Is not in polygon";
function is_in_polygon($points_polygon, $vertices_x, $vertices_y, $longitude_x, $latitude_y)
{
$i = $j = $c = 0;
for ($i = 0, $j = $points_polygon-1 ; $i < $points_polygon; $j = $i++) {
if ( (($vertices_y[$i] > $latitude_y != ($vertices_y[$j] > $latitude_y)) &&
($longitude_x < ($vertices_x[$j] - $vertices_x[$i]) * ($latitude_y - $vertices_y[$i]) / ($vertices_y[$j] - $vertices_y[$i]) + $vertices_x[$i]) ) )
$c = !$c;
}
return $c;
}
?>
Run Code Online (Sandbox Code Playgroud)
上面的解决方案不像我期望的那样工作,而不是使用上面的解决方案,你可以更喜欢下面的解决方案
使用 PHP
function pointInPolygon($point, $polygon, $pointOnVertex = true) {
$this->pointOnVertex = $pointOnVertex;
// Transform string coordinates into arrays with x and y values
$point = $this->pointStringToCoordinates($point);
$vertices = array();
foreach ($polygon as $vertex) {
$vertices[] = $this->pointStringToCoordinates($vertex);
}
// Check if the lat lng sits exactly on a vertex
if ($this->pointOnVertex == true and $this->pointOnVertex($point, $vertices) == true) {
return "vertex";
}
// Check if the lat lng is inside the polygon or on the boundary
$intersections = 0;
$vertices_count = count($vertices);
for ($i=1; $i < $vertices_count; $i++) {
$vertex1 = $vertices[$i-1];
$vertex2 = $vertices[$i];
if ($vertex1['y'] == $vertex2['y'] and $vertex1['y'] == $point['y'] and $point['x'] > min($vertex1['x'], $vertex2['x']) and $point['x'] < max($vertex1['x'], $vertex2['x'])) { // Check if point is on an horizontal polygon boundary
return "boundary";
}
if ($point['y'] > min($vertex1['y'], $vertex2['y']) and $point['y'] <= max($vertex1['y'], $vertex2['y']) and $point['x'] <= max($vertex1['x'], $vertex2['x']) and $vertex1['y'] != $vertex2['y']) {
$xinters = ($point['y'] - $vertex1['y']) * ($vertex2['x'] - $vertex1['x']) / ($vertex2['y'] - $vertex1['y']) + $vertex1['x'];
if ($xinters == $point['x']) { // Check if lat lng is on the polygon boundary (other than horizontal)
return "boundary";
}
if ($vertex1['x'] == $vertex2['x'] || $point['x'] <= $xinters) {
$intersections++;
}
}
}
// If the number of edges we passed through is odd, then it's in the polygon.
if ($intersections % 2 != 0) {
return "inside";
} else {
return "outside";
}
}
function pointOnVertex($point, $vertices) {
foreach($vertices as $vertex) {
if ($point == $vertex) {
return true;
}
}
}
function pointStringToCoordinates($pointString) {
$coordinates = explode(" ", $pointString);
return array("x" => $coordinates[0], "y" => $coordinates[1]);
}
// Function to check lat lng
function check(){
$points = array("22.367582 70.711816", "21.43567582 72.5811816","22.367582117085913 70.71181669186944","22.275334996986643 70.88614147123701","22.36934302329968 70.77627818998701"); // Array of latlng which you want to find
$polygon = array(
"22.367582117085913 70.71181669186944",
"22.225161442616514 70.65582486840117",
"22.20736264867434 70.83229276390898",
"22.18701840565626 70.9867880031668",
"22.22452581029355 71.0918447658621",
"22.382709129816103 70.98884793969023",
"22.40112042636022 70.94078275414336",
"22.411912121843205 70.7849142238699",
"22.367582117085913 70.71181669186944"
);
// The last lat lng must be the same as the first one's, to "close the loop"
foreach($points as $key => $point) {
echo "(Lat Lng) " . ($key+1) . " ($point): " . $this->pointInPolygon($point, $polygon) . "<br>";
}
}
Run Code Online (Sandbox Code Playgroud)使用 MySql
Run Code Online (Sandbox Code Playgroud)CREATE TABLE `TestPoly` ( `id` int(11) NOT NULL, `name` varchar(255) NOT NULL, `pol` polygon NOT NULL ) SET @g = 'POLYGON((22.367582117085913 70.71181669186944, 22.225161442616514 70.65582486840117, 22.20736264867434 70.83229276390898, 22.18701840565626 70.9867880031668, 22.22452581029355 71.0918447658621, 22.382709129816103 70.98884793969023, 22.40112042636022 70.94078275414336, 22.411912121843205 70.7849142238699, 22.367582117085913 70.71181669186944))'; INSERT INTO TestPoly (pol) VALUES (ST_GeomFromText(@g)) set @p = GeomFromText('POINT(22.4053386588057 70.86240663480157)'); select * FROM TestPoly where ST_Contains(pol, @p);
If your polygons are self-closing, that is to say that it's final vertex is the line between it's last point and it's first point then you need to add a variable and a condition to your loop to deal with the final vertex. You also need to pass the number of vertices as being equal to the number of points.
Here is the accepted answer modified to deal with self-closing polygons:
$vertices_x = array(37.628134, 37.629867, 37.62324, 37.622424); // x-coordinates of the vertices of the polygon
$vertices_y = array(-77.458334,-77.449021,-77.445416,-77.457819); // y-coordinates of the vertices of the polygon
$points_polygon = count($vertices_x); // number vertices = number of points in a self-closing polygon
$longitude_x = $_GET["longitude"]; // x-coordinate of the point to test
$latitude_y = $_GET["latitude"]; // y-coordinate of the point to test
if (is_in_polygon($points_polygon, $vertices_x, $vertices_y, $longitude_x, $latitude_y)){
echo "Is in polygon!";
}
else echo "Is not in polygon";
function is_in_polygon($points_polygon, $vertices_x, $vertices_y, $longitude_x, $latitude_y)
{
$i = $j = $c = $point = 0;
for ($i = 0, $j = $points_polygon ; $i < $points_polygon; $j = $i++) {
$point = $i;
if( $point == $points_polygon )
$point = 0;
if ( (($vertices_y[$point] > $latitude_y != ($vertices_y[$j] > $latitude_y)) &&
($longitude_x < ($vertices_x[$j] - $vertices_x[$point]) * ($latitude_y - $vertices_y[$point]) / ($vertices_y[$j] - $vertices_y[$point]) + $vertices_x[$point]) ) )
$c = !$c;
}
return $c;
}
Run Code Online (Sandbox Code Playgroud)
Thank you! I found this page and it's accepted answer very helpful and I am proud to offer this variation.
小智 5
我将泰国多边形放入MySQL。并将接受的答案函数与 MySQL 8 中的内置函数进行比较。
CREATE TABLE `polygons` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`polygon` POLYGON NOT NULL,
`country` VARCHAR(50) NULL DEFAULT NULL,
PRIMARY KEY (`id`),
SPATIAL INDEX `polygon` (`polygon`)
)
COLLATE='utf8mb4_0900_ai_ci'
ENGINE=InnoDB
AUTO_INCREMENT=652
;
INSERT INTO `polygons` (`country`, `polygon`) VALUES ('Thailand', ST_GEOMFROMTEXT('POLYGON((102.1728516 6.1842462,101.6894531 5.7253114,101.1401367 5.6815837,101.1181641 6.2497765,100.1074219 6.4899833,96.3281250 6.4244835,96.1083984 9.8822755,98.7670898 10.1419317,99.5800781 11.8243415,98.2177734 15.1569737,98.9868164 16.3201395,97.4267578 18.4587681,98.1079102 19.7253422,99.0087891 19.7460242,100.2612305 20.2828087,100.4809570 19.4769502,101.2060547 19.4147924,100.8544922 17.4135461,102.0849609 17.9996316,102.8320313 17.7696122,103.3593750 18.3545255,104.7875977 17.4554726,104.6337891 16.4676947,105.5126953 15.6018749,105.2270508 14.3069695,102.9858398 14.2643831,102.3486328 13.5819209,103.0297852 11.0059045,103.6669922 8.5592939,102.1728516 6.1842462))'));
Run Code Online (Sandbox Code Playgroud)
这是上面有点的多边形 -红色是第一个,蓝色- 最后一个:
我使用https://www.gpsvisualizer.com/draw/在地图上的泰国多边形外部和内部绘制了一些点,并制作了屏幕来可视化所有点。
我给出了 PHP 函数的点作为坐标 + 使用查询与 MySQL 函数进行了比较结果:
SELECT TRUE FROM `polygons` WHERE `polygons`.`country` = 'Thailand' AND ST_CONTAINS(`polygons`.`polygon`, POINT($long, $lat));
Run Code Online (Sandbox Code Playgroud)
结果:
我尝试更改多边形,但 php 函数总是在这些点上出错,这意味着某个地方存在我找不到的错误。
找到解决方案 assemblysys.com/php-point-in-polygon-algorithm - 该算法与 MySQL 算法相同!
比较 PHP 与 MySQL 的速度(我认为 PHP 应该更快),但事实并非如此。比较 47k 点。
18-06-2020 21:34:45 - PHP Speed Check Start
18-06-2020 21:34:51 - FIN! PHP Check. NOT = 41085 / IN = 5512
18-06-2020 21:34:51 - MYSQL Speed Check Start
18-06-2020 21:34:58 - FIN! MYSQL Check. NOT = 41085 / IN = 5512
Run Code Online (Sandbox Code Playgroud)