У меня есть широта и долгота, и я хочу вытащить запись из базы данных, которая имеет ближайшую широту и долготу на расстоянии, если это расстояние больше, чем указанное, и не извлекайте его.
Структура таблицы:
id
latitude
longitude
place name
city
country
state
zip
sealevel
Вам нужно перевести расстояние в градусы долготы и широты, на основе фильтров, чтобы связать записи, которые находятся примерно в ограничивающей рамке, а затем сделать более точный фильтр расстояния. Вот замечательная статья, в которой объясняется, как сделать все это:
http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
SELECT latitude, longitude, SQRT(
POW(69.1 * (latitude - [startlat]), 2) +
POW(69.1 * ([startlng] - longitude) * COS(latitude / 57.3), 2)) AS distance
FROM TableName HAVING distance < 25 ORDER BY distance;
где [starlat] и [startlng] - это позиция, в которой нужно начинать измерять расстояние.
Когда вы создаете таблицу MySQL, вы должны уделять особое внимание атрибутам lat и lng. Благодаря текущим возможностям масштабирования Карт Google, вам нужно будет всего 6 цифр после десятичной дроби. Чтобы сохранить пространство памяти, необходимое для вашей таблицы, как минимум, вы можете указать, что атрибуты lat и lng являются поплавками размера (10,6). Это позволит полям хранить 6 цифр после десятичной, плюс до 4 цифр перед десятичной точкой, например. -123,456789 градусов. Ваша таблица также должна иметь атрибут id в качестве первичного ключа.
CREATE TABLE `markers` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`name` VARCHAR( 60 ) NOT NULL ,
`address` VARCHAR( 80 ) NOT NULL ,
`lat` FLOAT( 10, 6 ) NOT NULL ,
`lng` FLOAT( 10, 6 ) NOT NULL
) ENGINE = MYISAM ;
После создания таблицы время для заполнения данных. Ниже приведены данные о примерах примерно для 180 пиццарий, разбросанных по всей территории Соединенных Штатов. В phpMyAdmin вы можете использовать вкладку IMPORT для импорта различных форматов файлов, включая CSV (значения, разделенные запятыми). Microsoft Excel и Google Spreadsheets экспортируют в формат CSV, поэтому вы можете легко переносить данные из таблиц в таблицы MySQL посредством экспорта/импорта CSV файлов.
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Frankie Johnnie & Luigo Too','939 W El Camino Real, Mountain View, CA','37.386339','-122.085823');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Amici\ East Coast Pizzeria','790 Castro St, Mountain View, CA','37.38714','-122.083235');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Kapp\ Pizza Bar & Grill','191 Castro St, Mountain View, CA','37.393885','-122.078916');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Round Table Pizza: Mountain View','570 N Shoreline Blvd, Mountain View, CA','37.402653','-122.079354');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Tony & Alba\ Pizza & Pasta','619 Escuela Ave, Mountain View, CA','37.394011','-122.095528');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Oregano\ Wood-Fired Pizza','4546 El Camino Real, Los Altos, CA','37.401724','-122.114646');
Чтобы найти местоположения в таблице маркеров, которые находятся на определенном расстоянии от заданной широты/долготы, вы можете использовать инструкцию SELECT на основе формулы Хаверсина. Формула Хаверсина обычно используется для вычисления расстояний между двумя парами координат на сфере. Подробное математическое объяснение дается Википедией и хорошее обсуждение формулы, поскольку она относится к программированию, относится к сайту Movable Type.
Здесь оператор SQL, который найдет ближайшие 20 местоположений, находящихся в радиусе 25 миль от координаты 37, -122. Он вычисляет расстояние, основанное на широте/долготе этой строки и целевой широте/долготе, а затем запрашивает только строки, где значение расстояния меньше 25, заказывает весь запрос по расстоянию и ограничивает его до 20 результатов. Чтобы выполнить поиск километров вместо миль, замените 3959 на 6371.
SELECT
id,
(
3959 *
acos(cos(radians(37)) *
cos(radians(lat)) *
cos(radians(lng) -
radians(-122)) +
sin(radians(37)) *
sin(radians(lat )))
) AS distance
FROM markers
HAVING distance < 25
ORDER BY distance LIMIT 0, 20;
https://developers.google.com/maps/articles/phpsqlsearch_v3#creating-the-map
HAVING distance < 25
когда мы запрашиваем местоположения в радиусе 25 миль?
Вот мое полное решение, реализованное в PHP.
Это решение использует формулу Хаверсина, представленную в http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL.
Следует отметить, что формула Хаверсина испытывает недостатки вокруг полюсов. Этот ответ показывает, как реализовать формулу Vincenty Great Circle Distance, чтобы обойти это, однако я решил использовать Haversine, потому что он достаточно хорош для моих целей.
Я храню широту как DECIMAL (10,8) и долготу как DECIMAL (11,8). Надеюсь, это поможет!
<?PHP
/**
* Use the Haversine Formula to display the 100 closest matches to $origLat, $origLon
* Only search the MySQL table $tableName for matches within a 10 mile ($dist) radius.
*/
include("./assets/db/db.php"); // Include database connection function
$db = new database(); // Initiate a new MySQL connection
$tableName = "db.table";
$origLat = 42.1365;
$origLon = -71.7559;
$dist = 10; // This is the maximum distance (in miles) away from $origLat, $origLon in which to search
$query = "SELECT name, latitude, longitude, 3956 * 2 *
ASIN(SQRT( POWER(SIN(($origLat - latitude)*pi()/180/2),2)
+COS($origLat*pi()/180 )*COS(latitude*pi()/180)
*POWER(SIN(($origLon-longitude)*pi()/180/2),2)))
as distance FROM $tableName WHERE
longitude between ($origLon-$dist/cos(radians($origLat))*69)
and ($origLon+$dist/cos(radians($origLat))*69)
and latitude between ($origLat-($dist/69))
and ($origLat+($dist/69))
having distance < $dist ORDER BY distance limit 100";
$result = mysql_query($query) or die(mysql_error());
while($row = mysql_fetch_assoc($result)) {
echo $row['name']." > ".$row['distance']."<BR>";
}
mysql_close($db);
?>
<?PHP
/**
* Class to initiate a new MySQL connection based on $dbInfo settings found in dbSettings.php
*
* @example $db = new database(); // Initiate a new database connection
* @example mysql_close($db); // close the connection
*/
class database{
protected $databaseLink;
function __construct(){
include "dbSettings.php";
$this->database = $dbInfo['host'];
$this->mysql_user = $dbInfo['user'];
$this->mysql_pass = $dbInfo['pass'];
$this->openConnection();
return $this->get_link();
}
function openConnection(){
$this->databaseLink = mysql_connect($this->database, $this->mysql_user, $this->mysql_pass);
}
function get_link(){
return $this->databaseLink;
}
}
?>
<?php
$dbInfo = array(
'host' => "localhost",
'user' => "root",
'pass' => "password"
);
?>
Возможно, можно увеличить производительность с помощью хранимой процедуры MySQL, как было предложено в статье "Geo-Distance-Search-with-MySQL", опубликованной выше.
У меня есть база данных ~ 17 000 мест, а время выполнения запроса - 0,054 секунды.
На всякий случай, когда вы ленитесь, как я, вот решение, объединенное из этого и других ответов на SO.
set @orig_lat=37.46;
set @orig_long=-122.25;
set @bounding_distance=1;
SELECT
*
,((ACOS(SIN(@orig_lat * PI() / 180) * SIN(`lat` * PI() / 180) + COS(@orig_lat * PI() / 180) * COS(`lat` * PI() / 180) * COS((@orig_long - `long`) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) AS `distance`
FROM `cities`
WHERE
(
`lat` BETWEEN (@orig_lat - @bounding_distance) AND (@orig_lat + @bounding_distance)
AND `long` BETWEEN (@orig_long - @bounding_distance) AND (@orig_long + @bounding_distance)
)
ORDER BY `distance` ASC
limit 25;
bounding_distance
? ограничивает ли это значение результаты милей? так что в этом случае он вернет результаты в течение 1 мили?
Легкий один;)
SELECT * FROM `WAYPOINTS` W ORDER BY
ABS(ABS(W.`LATITUDE`-53.63) +
ABS(W.`LONGITUDE`-9.9)) ASC LIMIT 30;
Просто замените координаты на нужные. Значения должны быть сохранены как двойные. Это пример рабочего MySQL 5.x.
Приветствия
dx+dy
Вы ищете такие вещи, как формула haversine. См. здесь.
Там другие, но это чаще всего цитируется.
Если вы ищете что-то еще более надежное, вы можете посмотреть на свои возможности ГИС базы данных. Они способны на некоторые интересные вещи, такие как рассказывать вам, появляется ли точка (Город) внутри заданного полигона (Регион, Страна, Континент).
Попробуйте это, он покажет ближайшие точки в координатах (в пределах 50 км). Он отлично работает:
SELECT m.name,
m.lat, m.lon,
p.distance_unit
* DEGREES(ACOS(COS(RADIANS(p.latpoint))
* COS(RADIANS(m.lat))
* COS(RADIANS(p.longpoint) - RADIANS(m.lon))
+ SIN(RADIANS(p.latpoint))
* SIN(RADIANS(m.lat)))) AS distance_in_km
FROM <table_name> AS m
JOIN (
SELECT <userLat> AS latpoint, <userLon> AS longpoint,
50.0 AS radius, 111.045 AS distance_unit
) AS p ON 1=1
WHERE m.lat
BETWEEN p.latpoint - (p.radius / p.distance_unit)
AND p.latpoint + (p.radius / p.distance_unit)
AND m.lon BETWEEN p.longpoint - (p.radius / (p.distance_unit * COS(RADIANS(p.latpoint))))
AND p.longpoint + (p.radius / (p.distance_unit * COS(RADIANS(p.latpoint))))
ORDER BY distance_in_km
Просто измените <table_name>
. <userLat>
и <userLon>
Подробнее об этом решении вы можете узнать здесь: http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
Проверить этот код на основе статьи Geo-Distance-Search-with-MySQL:
Пример: найдите 10 ближайших отелей в моем текущем местоположении в радиусе 10 миль:
#Please notice that (lat,lng) values mustn't be negatives to perform all calculations
set @my_lat=34.6087674878572;
set @my_lng=58.3783670308302;
set @dist=10; #10 miles radius
SELECT dest.id, dest.lat, dest.lng, 3956 * 2 * ASIN(SQRT(POWER(SIN((@my_lat -abs(dest.lat)) * pi()/180 / 2),2) + COS(@my_lat * pi()/180 ) * COS(abs(dest.lat) * pi()/180) * POWER(SIN((@my_lng - abs(dest.lng)) * pi()/180 / 2), 2))
) as distance
FROM hotel as dest
having distance < @dist
ORDER BY distance limit 10;
#Also notice that distance are expressed in terms of radius.
simpledb.execSQL("CREATE TABLE IF NOT EXISTS " + tablename + "(id INTEGER PRIMARY KEY AUTOINCREMENT,lat double,lng double,address varchar)");
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2891001','70.780154','craftbox');");
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2901396','70.7782428','kotecha');");//22.2904718 //70.7783906
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2863155','70.772108','kkv Hall');");
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.275993','70.778076','nana mava');");
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2667148','70.7609386','Govani boys hostal');");
double curentlat=22.2667258; //22.2677258
double curentlong=70.76096826;//70.76096826
double curentlat1=curentlat+0.0010000;
double curentlat2=curentlat-0.0010000;
double curentlong1=curentlong+0.0010000;
double curentlong2=curentlong-0.0010000;
try{
Cursor c=simpledb.rawQuery("select * from '"+tablename+"' where (lat BETWEEN '"+curentlat2+"' and '"+curentlat1+"') or (lng BETWEEN '"+curentlong2+"' and '"+curentlong1+"')",null);
Log.d("SQL ", c.toString());
if(c.getCount()>0)
{
while (c.moveToNext())
{
double d=c.getDouble(1);
double d1=c.getDouble(2);
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
Найти ближайших пользователей к моему:
Расстояние в метрах
На основе формула Vincenty
У меня есть таблица пользователя:
+----+-----------------------+---------+--------------+---------------+
| id | email | name | location_lat | location_long |
+----+-----------------------+---------+--------------+---------------+
| 13 | [email protected] | Isaac | 17.2675625 | -97.6802361 |
| 14 | [email protected] | Monse | 19.392702 | -99.172596 |
+----+-----------------------+---------+--------------+---------------+
SQL:
-- my location: lat 19.391124 -99.165660
SELECT
(ATAN(
SQRT(
POW(COS(RADIANS(users.location_lat)) * SIN(RADIANS(users.location_long) - RADIANS(-99.165660)), 2) +
POW(COS(RADIANS(19.391124)) * SIN(RADIANS(users.location_lat)) -
SIN(RADIANS(19.391124)) * cos(RADIANS(users.location_lat)) * cos(RADIANS(users.location_long) - RADIANS(-99.165660)), 2)
)
,
SIN(RADIANS(19.391124)) *
SIN(RADIANS(users.location_lat)) +
COS(RADIANS(19.391124)) *
COS(RADIANS(users.location_lat)) *
COS(RADIANS(users.location_long) - RADIANS(-99.165660))
) * 6371000) as distance,
users.id
FROM users
ORDER BY distance ASC
радиус земли: 6371000 (в метрах)
Первоначальные ответы на вопрос - это хорошие, но более новые версии геоинформационных запросов mysql, поэтому теперь вы можете использовать встроенные функции, а не выполнять сложные запросы.
Теперь вы можете сделать что-то вроде:
select *, ST_Distance_Sphere( point ('input_longitude', 'input_latitude'),
point(longitude, latitude)) * .000621371192
as 'distance_in_miles'
from 'TableName'
having 'distance_in_miles' <= 'input_max_distance'
order by 'distance_in_miles' asc
Результаты возвращаются в meters
поэтому, если вы хотите использовать KM
вместо миль, используйте .0001
вместо .000621371192
MS SQL Edition здесь:
DECLARE @SLAT AS FLOAT
DECLARE @SLON AS FLOAT
SET @SLAT = 38.150785
SET @SLON = 27.360249
SELECT TOP 10 [LATITUDE], [LONGITUDE], SQRT(
POWER(69.1 * ([LATITUDE] - @SLAT), 2) +
POWER(69.1 * (@SLON - [LONGITUDE]) * COS([LATITUDE] / 57.3), 2)) AS distance
FROM [TABLE] ORDER BY 3
В крайних случаях этот подход терпит неудачу, но для производительности я пропустил тригонометрию и просто вычислил квадрат диагонали.
Вы должны попробовать: http://en.wikipedia.org/wiki/Great-circle_distance http://code.google.com/apis/maps/articles/phpsqlsearch.html
Похоже, вы должны просто использовать PostGIS, SpatialLite, SQLServer2008 или Oracle Spatial. Они могут ответить на этот вопрос для вас пространственным SQL.
Эта проблема не очень сложная, но она усложняется, если вам нужно ее оптимизировать.
Что я имею в виду, у вас есть 100 мест в вашей базе данных или 100 миллионов? Это имеет большое значение.
Если количество мест невелико, выведите их из SQL и в код, просто сделав →
Select * from Location
Как только вы получите их в код, вычислите расстояние между каждым лат/лоном и вашим оригиналом с формулой Хаверсина и отсортируйте его.