个性化阅读
专注于IT技术分析

如何使用PHP,MySQL从一组坐标(经度和纬度)中查找最近的位置

本文概述

如今, 许多应用程序提供的最流行的功能之一就是可以在给定自定义位置(你当前的位置, 可能指定纬度和经度)的情况下从数据库中找到最近的寄存器。例如, 在房地产业务中, 这是一个非常有用的实现, 如果你可以简单地将自己放置在城市中心(例如纽约), 该应用程序将允许你在地图上显示最近的可用房屋/公寓。半径为50/100公里:

通过坐标Google Maps过滤地点

这个想法本身很酷, 对吧?显然, 我们没有包括Google Maps中的地点, 这意味着这些标记显然是我们的, 它们存储在我们自己的数据库中。在本文中, 我们将向你解释如何从MySQL的坐标集合中知道最近的位置。

1.了解我们的标记数据库

首先, 你显然需要在数据库中存储位置, 在我们的情况下, 我们将拥有非常简单的数据库结构, 因此每个人都可以轻松理解它。可以使用以下查询创建数据库:

CREATE TABLE `markers` (
  `id` bigint(20) NOT NULL, `lat` decimal(10, 8) NOT NULL, `lng` decimal(11, 8) NOT NULL, `name` varchar(255) CHARACTER SET utf8 NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

它仅存储由名称标识的标记, 并包含纬度和经度的坐标。它包含以下数据:

数据标记坐标

如果我们使用JavaScript在Google地图中放置上述位置的标记:

var markers = [
    {
        "id": "1", "lat": "4.66455174", "lng": "-74.07867091", "name": "Bogot\u00e1"
    }, {
        "id": "2", "lat": "6.24478548", "lng": "-75.57050110", "name": "Medell\u00edn"
    }, {
        "id": "3", "lat": "7.06125013", "lng": "-73.84928550", "name": "Barrancabermeja"
    }, {
        "id": "4", "lat": "7.88475514", "lng": "-72.49432589", "name": "C\u00facuta"
    }, {
        "id": "5", "lat": "3.48835279", "lng": "-76.51532198", "name": "Cali"
    }, {
        "id": "6", "lat": "4.13510880", "lng": "-73.63690401", "name": "Villavicencio"
    }, {
        "id": "7", "lat": "6.55526689", "lng": "-73.13373892", "name": "San Gil"
    }
];

function setMarkers(map) {
    var image = {
        url: 'https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png', size: new google.maps.Size(20, 32), origin: new google.maps.Point(0, 0), anchor: new google.maps.Point(0, 32)
    };

    for (var i = 0; i < markers.length; i++) {
        var marker = markers[i];
        var marker = new google.maps.Marker({
            position: {
                lat: parseFloat(marker.lat), lng: parseFloat(marker.lng)
            }, map: map, icon: image, shape: {
                coords: [1, 1, 1, 20, 18, 20, 18, 1], type: 'poly'
            }, title: marker.name
        });
    }
}

我们将在地图中获得以下输出:

Google Maps Markers示例

现在, 一切正常, 地图正常, 标记在正确的位置, 但是现在, 我们需要根据我们的位置(Bucaramanga)和特定的距离半径对它们进行过滤。我们不会在本文中介绍如何使用JavaScript过滤它们, 而仅在服务器端进行过滤, 因此你可以自行决定如何稍后在前端中显示它们。

2.了解我们需要做什么

首先, 我们需要了解我们将在数据库中进行理论上的操作。以下模式展示了我们需要完成以完成任务的两个问题, 其中包括我们将搜索一个圆形区域, 该圆形区域由我们将其称为搜索半径的某个距离定义:

坐标之间的距离Google Maps MySQL

知道初始位置和其他坐标之间的距离是未知的:

  • 我们如何从起点定义搜索半径?
  • 我们如何知道位置在搜索半径内?

好吧, 基本上, 要定义搜索半径, 你将是一个定义该距离的纯数字值的用户, 例如, 我们将使用150公里!对于我们的初始点, 我们将使用本示例中哥伦比亚布卡拉曼加市的坐标:

  • 拉特:7.08594109039762
  • lng:286.95225338731285

3.准备最近的位置查询

现在, 要回答我们先前提出的第二个问题:”如何知道某个位置何时在搜索半径内?”, 只要初始点(红色标记, 布卡拉曼加)与位置(“某些标志”)小于定义的搜索半径(150)。那么, 我们如何找到位置与初始点之间的距离?为此, 我们将需要找到坐标之间的距离。这是通过大量的数学运算完成的, 在本文中我们将不做解释, 因为否则它会变得很无聊, 因此, 我们可以使用PHP函数来恢复它, 这更有趣:

<?php 

/**
 * Method to find the distance between 2 locations from its coordinates.
 * 
 * @param latitude1 LAT from point A
 * @param longitude1 LNG from point A
 * @param latitude2 LAT from point A
 * @param longitude2 LNG from point A
 * 
 * @return Float Distance in Kilometers.
 */
function getDistanceBetweenPointsNew($latitude1, $longitude1, $latitude2, $longitude2, $unit = 'Mi') {
    $theta = $longitude1 - $longitude2;
    $distance = sin(deg2rad($latitude1)) * sin(deg2rad($latitude2)) + cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * cos(deg2rad($theta));

    $distance = acos($distance); 
    $distance = rad2deg($distance); 
    $distance = $distance * 60 * 1.1515;

    switch($unit) 
    { 
        case 'Mi': break;
        case 'Km' : $distance = $distance * 1.609344; 
    }

    return (round($distance, 2)); 
}

这就是我们基本上要做的, 以便从它们的坐标知道2个位置之间的距离, 例如Bucaramanga之间的距离:

  • 拉特:7.08594109039762
  • lng:286.95225338731285

和库库塔:

  • 拉特:7.88475514
  • lng:-72.49432589

将会:

// Outputs: 107.76, distance in km between the given coordinates
echo getDistanceBetweenPointsNew(
    7.08594109039762, 286.95225338731285, 7.88475514, -72.49432589, 'Km'
);

了解了这一点, 我们将对SQL进行相同的操作, 这将在查询中为我们提供一个新列, 即距离, 该列包含来自给定坐标的值。如果你以公里或英里为单位, 则实现此目的的SQL查询将有所不同:

A.搜索公里

--- Where: 
--- $LATITUDE = the latitude of the start point e.g 7.08594109039762;
--- $LONGITUDE = the longitude of the start point e.g 286.95225338731285;
--- $DISTANCE_KILOMETERS = your radius of search in Kilometers e.g 150

SELECT * FROM (
    SELECT *, (
            (
                (
                    acos(
                        sin(( $LATITUDE * pi() / 180))
                        *
                        sin(( `latitud_fieldname` * pi() / 180)) + cos(( $LATITUDE * pi() /180 ))
                        *
                        cos(( `latitud_fieldname` * pi() / 180)) * cos((( $LONGITUDE - `longitude_fieldname`) * pi()/180)))
                ) * 180/pi()
            ) * 60 * 1.1515 * 1.609344
        )
    as distance FROM `myTable`
) myTable
WHERE distance <= $DISTANCE_KILOMETERS
LIMIT 15;

B.英里搜索

--- Where: 
--- $LATITUDE = the latitude of the start point e.g 7.08594109039762;
--- $LONGITUDE = the longitude of the start point e.g 286.95225338731285;
--- $DISTANCE_MILES = your radius of search in Miles e.g 150

SELECT * FROM (
    SELECT *, (
            (
                (
                    acos(
                        sin(( $LATITUDE * pi() / 180))
                        *
                        sin(( `latitud_fieldname` * pi() / 180)) + cos(( $LATITUDE * pi() /180 ))
                        *
                        cos(( `latitud_fieldname` * pi() / 180)) * cos((( $LONGITUDE - `longitude_fieldname`) * pi()/180)))
                ) * 180/pi()
            ) * 60 * 1.1515
        )
    as distance FROM `myTable`
) myTable
WHERE distance <= $DISTANCE_MILES
LIMIT 15;

4.测试查询

现在我们有了搜索半径距离, 我们最终可以测试查询以替换值并简单地运行它(在屏幕快照中, 我们使用140.85而不是150, 但基本相同):

MySQL查询坐标

现在, 使用给定的值, 我们的查询总共以图形和数学方式完全匹配了3个与搜索匹配的位置。你最终可以使用PHP运行查询, 以根据需要处理结果:

<?php
    $servername = "localhost";
    $username = "root";
    $password = "";
    $dbname = "test";

    // Create connection
    $conn = new mysqli($servername, $username, $password, $dbname);
    mysqli_set_charset($conn, "utf8");
    // Check connection
    if ($conn->connect_error) {
        die("Connection failed: " . $conn->connect_error);
    }

    // Bucaramanga Coordinates
    $lat = 7.08594109039762;
    $lon = 286.95225338731285;
        
    // Only show places within 100km
    $distance = 150;

    $lat = mysqli_real_escape_string($conn, $lat);
    $lon = mysqli_real_escape_string($conn, $lon);
    $distance = mysqli_real_escape_string($conn, $distance);

    $query = <<<EOF
    SELECT * FROM (
        SELECT *, (
                (
                    (
                        acos(
                            sin(( $lat * pi() / 180))
                            *
                            sin(( `lat` * pi() / 180)) + cos(( $lat * pi() /180 ))
                            *
                            cos(( `lat` * pi() / 180)) * cos((( $lon - `lng`) * pi()/180)))
                    ) * 180/pi()
                ) * 60 * 1.1515 * 1.609344
            )
        as distance FROM `markers`
    ) markers
    WHERE distance <= $distance
    LIMIT 15;
EOF;

    $result = $conn->query($query);
    
    if ($result->num_rows > 0) {
        // output data of each row
        while($row = $result->fetch_assoc()) {
            echo $row["name"] . "<br>";
        }
    }

    // Outputs:
    // Barrancabermeja
    // Cúcuta
    // San Gil

编码愉快!

赞(0)
未经允许不得转载:srcmini » 如何使用PHP,MySQL从一组坐标(经度和纬度)中查找最近的位置

评论 抢沙发

评论前必须登录!