Mam takie pytanie czasami w ogłoszeniach o pracę są do wyboru, żeby szukać odległości w iluś tam km i moje pytanie jest takie jak to jest zaprogramowane, czy np. każde miasto ma swoje współrzędne X i Y ?
W najprostszej postaci właśnie tak. Pobierasz przez jakieś API współrzędne obu miast i obliczasz odległość z założeniem że ziemia jest kulą. W niektórych wypadkach możesz też użyć google api do wyliczenia drogi, ale to już chyba płatna opcja więc raczej do specjalnych zastosowań.
- Używasz np. Google Geocode JSON API do pobrania współrzędnych geograficznych tych miast, zapisujesz to w bazie
- Tutaj są przykłady zapytań SQL jeśli chodzi o obliczanie odległości
http://stackoverflow.com/questions/8599200/calculate-distance-given-2-points-latitude-and-longitude
A jak zapisać te dane współrzednych tych miast do bazy czy to działa na takiej zasadzie, że baza jest u mnie na serwerz czy baza jest globalna(google) ?
Ty masz tylko pobrać dane a co z nimi zrobisz potem to tylko twoja sprawa.
To nadal działa bez klucza????
https://maps.googleapis.com/maps/api/geocode/json?address=[ulica%2Bnr_domu],[kod_poczt%2Bmiasto],PL
Zwróci JSON a w nim dane: geometry -> location
lat i lng
i to są dane które należy zapisać w bazie i na bazie tego obliczać
Taki prosty przykład:
Tabela w MySQL:
CREATE TABLE `locations` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`lat` float NOT NULL,
`lng` float NOT NULL,
`city` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Zaciąganie danych
<?php
$cities = array(
'Lublin',
'Warszawa',
'Krakow',
'Gdansk',
'Poznan',
'Zakopane',
'Krasnystaw',
'Zamosc',
);
$url = 'https://maps.googleapis.com/maps/api/geocode/json?address=%s,PL';
$dsn = 'mysql:host=localhost;dbname=test';
$db = new PDO($dsn, 'root', 'root');
foreach ($cities as $city)
{
$json = file_get_contents(sprintf($url, $city));
$location = json_decode($json)->results[0]->geometry->location;
$stmt = $db->prepare('INSERT INTO locations (lat, lng, city) VALUES (:lat, :lng, :city)');
$stmt->execute(array(
':lat' => $location->lat,
':lng' => $location->lng,
':city' => $city,
));
}
I pobieranie wszystkich miast w promieniu 250 km od Warszawy, z nią włącznie:
<?php
// Warszawa, pobrane wspolrzedne (np. z bazy)
// Pobieranie miast w promieniu 250 km od Warszawy
// z Warszawą włącznie
$lat = 52.2297;
$lng = 21.0122;
$distance = 250;
$dsn = 'mysql:host=localhost;dbname=test';
$db = new PDO($dsn, 'root', 'root');
$stmt = $db->prepare("SELECT city, (6371*acos(cos(radians(:lat)) * cos(radians(lat)) * cos(radians(lng)
- radians(:lng)) + sin(radians(:lat)) * sin(radians(lat)))) AS distance FROM locations
HAVING distance < :distance");
$stmt->execute(array(
':lng' => $lng,
':lat' => $lat,
':distance' => $distance,
));
echo '<pre>';
$rows = $stmt->fetchAll(PDO::FETCH_OBJ);
foreach ($rows as $row)
{
echo $row->city . "\n";
}
echo '</pre>';
Wynik:
Lublin
Warszawa
Krasnystaw
Zamosc
Teraz to zapytanie konkretnie to
$long_km = DB::select("SELECT city, (6371*acos(cos(radians($x)) * cos(radians(x)) * cos(radians(y)
- radians($y)) + sin(radians($x)) * sin(radians(x)))) AS distance FROM city
HAVING distance < $km");
zwraca mi błąd
SQLSTATE[42000]: Syntax error or access violation: 1463 Non-grouping field 'distance' is used in HAVING clause (SQL: SELECT city, (6371*acos(cos(radians(51.2465)) * cos(radians(x)) * cos(radians(y)
- radians(22.5684)) + sin(radians(51.2465)) * sin(radians(x)))) AS distance FROM city
HAVING distance < 12)
Nie no ja przyjąłem że tabela nazywa się locations a nie city i taką też utworzyłem, tym masz tam city zamiast locations, podałem to w przykładzie i pewnie dlatego się wywala.
Tylko, że jemu chodzi a paramert distance.
Odziwo ten błąd pojawia się tylko w aplikacji laravel jak to zapytnie wklepię w konsolę to się nic nie pokaże.
Test na Laravelu 4.2, tabela o strukturze takiej jak podałem kilka postów wyżej,
class TestController extends Controller
{
public function getCities()
{
$lat = 52.2297;
$lng = 21.0122;
$distance = 250;
$result = DB::select("SELECT city, (6371 * acos(cos(radians($lat))"
. "* cos(radians(lat)) * cos(radians(lng)"
. " - radians($lng)) + sin(radians($lat)) * sin(radians(lat))))"
. "AS distance FROM locations HAVING distance < $distance"
);
return '<pre>' . print_r($result, true) . '</pre>';
}
}
wynik:
Array
(
[0] => stdClass Object
(
[city] => Lublin
[distance] => 153.08433974689
)
[1] => stdClass Object
(
[city] => Warszawa
[distance] => 0.0033430168099889
)
[2] => stdClass Object
(
[city] => Krasnystaw
[distance] => 203.52231607976
)
[3] => stdClass Object
(
[city] => Zamosc
[distance] => 228.29220173039
)
)
To jest na 100% przez to AS distance bo ja usunę to i to zostanie tak
SELECT city, (6371*acos(cos(radians($x)) * cos(radians(x)) * cos(radians(y)
- radians($y)) + sin(radians($x)) * sin(radians(x)))) FROM city
To się dobrze wykonuje.
Dodam, że jak do innego zapytania wstawiłem as pole_tymczasowe i potem dałem w tym zapytaniu having pole_tymczasowe = 2 to też mi zwróciło taki sam błąd, a przez zwykłego sql jest poprawnie interpretowane
Nie wiem co to jest. To co tutaj podałem to poprawne zapytanie SQL, sprawdzałem działanie na Laravelu 4.2 i wcześniej PDO. Mam MySQL Server 5.4. Nie wiem w jakiej wersji masz MySQL i czy w ogóle korzystasz z MySQL a nie np. SQLite. Gorzej jeśli to problem wewnętrznie w Laravelu. Daj kod SQL (DDL) tej tabeli który masz w bazie. Nie wiem jakiego typu pola masz w tej tabeli.
Tabela city
id int(11) Nie Klucz główny NULL auto_increment
x float Tak Brak NULL
y float Tak Brak NULL
city varchar(100) Tak Brak NULL
Tabela work_offers
id int(10) unsigned Nie Klucz główny NULL auto_increment
name varchar(60) Nie Brak NULL
price int(11) Nie Brak NULL
date_add int(11) Nie Brak NULL
start_date date Nie Brak NULL
city varchar(90) Tak Brak NULL
education varchar(90) Nie Brak NULL
id_user int(11) Nie Brak NULL
remember_token varchar(100) Tak Brak NULL
created_at timestamp Tak Brak NULL
updated_at timestamp Tak Brak NULL
id_city int(11) Tak Brak NULL
Zmieniłem nazwę tabeli z locations na city
CREATE TABLE `city` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`lat` float NOT NULL,
`lng` float NOT NULL,
`city` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
i skrypt:
<?php
$lat = 52.2297;
$lng = 21.0122;
$distance = 250;
$dsn = 'mysql:host=localhost;dbname=test';
$db = new PDO($dsn, 'root', 'root');
$stmt = $db->prepare("SELECT city, (6371*acos(cos(radians(:lat)) * cos(radians(lat)) * cos(radians(lng)
- radians(:lng)) + sin(radians(:lat)) * sin(radians(lat)))) AS distance FROM city
HAVING distance < :distance");
$stmt->execute(array(
':lng' => $lng,
':lat' => $lat,
':distance' => $distance,
));
echo '<pre>';
$rows = $stmt->fetchAll(PDO::FETCH_OBJ);
foreach ($rows as $row)
{
echo $row->city . "\n";
}
echo '</pre>';
I z tym też nie ma problemów, też wypluwa te miasta które są w odległości 250 km od Warszawy łącznie z nią. Nie mam pojęcia z czym się tak naprawdę wiąże Twój problem...
A zamiast tego having
nie powinno być where
? Nie ma tam nigdzie grupowania.
Może trzeba użyć innego sterownika do bazy mysql, a może po prostu trzeba coś zainportować to obsługi mysql np. use ? Bo to się dzieje przy każdej tabeli gdzie próbuje użyć operatora as i having.
A jakiej funkcji używasz w laravelu do drukwoania miast ?
$stmt = $db->prepare("SELECT * FROM (SELECT city, (6371*acos(cos(radians(:lat)) * cos(radians(lat)) * cos(radians(lng)
- radians(:lng)) + sin(radians(:lat)) * sin(radians(lat)))) AS distance FROM city) x
WHERE distance < :distance");
Dzięki teraz działa.