Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP][inne]Miasta w odległości do x km od Miasta
Forum PHP.pl > Forum > Przedszkole
Ryder
Witam,

Chciałbym na swojej stronie zaimplementować skrypt wyszukujący miasta w podbliżu podanej miejscowości.
1. W jaki sposób działają takie skrypty, czy potrzebuje wykupić bazę danych miejscowości?
2. Czy znacie gotowe darmowe lub płatne rozwiązania/skrypty?
Ilware
możesz skorzystać z bazy danych terytu ( http://www.stat.gov.pl/broker/access/index.jspa ) która posiada wszystkie miasta, oraz za pomocą goole api pobrać współrzędne potrzebnych lokalizacji , a następnie za pomocą wzoru :

  1. degrees( acos( least( greatest( sin( radians( ' . $szerokosc1 . ' ) ) * sin( radians( ' . $szerokosc2. ' ) ) + cos( radians( ' . $szerokosc1 . ' ) ) * cos( radians( ' . $szerokosc2. ' ) ) * cos( radians( ' . $dlugosc1 . ' - ' . $dlugosc2. ' )), -1), 1) )) * 18957696'


obliczasz potrzebną odległość ( wzor akurat zrealizowany jest w postgress )
thek
ad 2) Rozwiązania są, ale nie spotkałem w pełni darmowych i wydajnych. Sam to kiedyś robiłem i dziś nawet mi przyszło nieco kombinować z aktualizacją jednego z nich.

Najprościej jest użyć google maps i ich API, ale ilość żądań zapewne po jakimś czasie dojdzie do limitów ( http://googlegeodevelopers.blogspot.com/20...ts-to-maps.html ).
Trudniej nieco, ale da sie to obejść, poprzez stworzenie własnego "środowiska" bazując na ogólnodostępnych danych. Jako startowe zassij dane z GUSowskiej bazy miejscowości (słowo kluczowe TERYT). Niestety trwa to troszkę i powstanie Ci (jeśli wybierzesz tylko miasta i wsie) baza około 43 tysięcy rekordów. Niestety bez współrzędnych. Tutaj wchodzi w grę geolokalizacja. Niestety licencja stawia sprawę jasno: limit 2500 żądań na dobę i muszą te dane być wyświetlone na mapie. Każde inne wykorzystanie jest przez licencję zabronione (to uwaga dla Ilware). Co nie oznacza, że nie możemy tego wykorzystać mimo wszystko ;) Pamiętajmy, że skoro czegoś szukamy to spodziewamy się wyników z naszej bazy. Wystarczy więc, że tak napiszemy skrypt wyświetlający, by odwołań do geokodowania było jak najmniej. Jak to osiągnąć? Prosto. Część tego co robi gmaps przerzućmy na nasz serwis i jego bazę. Starajmy się już na starcie uzupełnić bazę danych o tyle wyników ze znanymi współrzędnymi, na ile to możliwe, poprzez sprawdzenie, czy wyświetlana rzecz ma swoje współrzędne w bazie, czy może dopiero ma pociągnąć je z google, jeśli to zrobi, zapiszmy te współrzędne w bazie.

Nie będzie musiało robić tego za każdym razem i nie łamiemy licencji. Dane SĄ bowiem wyświetlane, ale dodatkowo zebrane informacje zapamiętujemy w bazie by nie ciągnąc ich ponownie i tym samym zmniejszamy stopniowo ilość żądań geokodowania dając sobie większy margines na nieoczekiwane skoki ilości żądań.

Dzięki temu budujemy bazę współrzednych miejscowości po stronie naszego serwera. Z czasem bedziemy mieli wysokie pokrycie i geokodowanie stanie się naprawdę sporadyczne. Teraz czas na trudniejszą rzecz... Trzeba zastąpić algorytm google na swój własny. Kwestia najważniejsza to zdefiniowanie odległości. Pamiętajmy, że ziemia to kula, choć na małych (w stosunku do jej promienia) odległościach można to, do pewnego stopnia, pominąć i przyjąć zwykłą definicję odległości. Wciąż jednak obliczanie go na podstawie pierwiastka z sumy kwadratów różnic współrzędnych (skonwertowanych na kilometry) w locie dla każdego miasta z tym znanym to dość duże ilości obliczeń i jednak jedziemy ostro po silniku bazy.

Nawet jeśli nie będzie ich początkowo dużo, to z czasem duże pokrycie sprawi, że liczba ta osiągnie bez problemu 30-40 tysięcy. Zapytanie obliczające dla każdego rekordy w bazie odległośc to nie jest wydajne rozwiązanie.

Można jednak sobie sprawę bardzo uprościć ale, niestety, kosztem dokładności. Wystarczy znać współrzędne miasta głównego i obliczyć współrzędne granic obszaru, tworząc prostokąt lub kwadrat, w którego centrum jest miasto względem którego szukamy. Takie zapytanie, gdzie będziemy znali zakres szerokości i długości geograficznych, powinno już być całkiem wydajne. Bo to tylko prosty warunek WHERE lat IN (latStart, latEnd) AND lng IN (lngStart, lngEnd) ;)
Ryder
Dzięki za rady.

Korzystał ktoś z was z bazy opisanej w poście http://forum.php.pl/index.php?showtopic=123845
Ilware
jak dobrze zaimportujesz teryt do MySql, to chodzi naprawdę szybko + napisz sobie skrypty, które począwszy od największych miast zacznie gromadzić współrzędne z google api to bazę 980 miast będziesz miał po 1 dniu i to za darmo, google api działa bezproblemowo w tym względzie i da się uzyskać odpowiedz zawierająca tylko współrzędne + kilka innych informacji.Nie będziesz potrzebował wtedy kupować jakieś bazy smile.gif
thek
@Ilware: Cały teryt do MySQL? Zbędny wysiłek wink.gif Z niego wystarczy wyłuskać tylko miasta i wsie smile.gif Jeśli ktoś chce to może jeszcze dzielnice i tyle. Bez dzielnic da to w chwili obecnej jakoś 43000 rekordów. Wiem bo niedawno to robiłem i wciąż mam tę tabelę w bazie. Na razie tylko współrzędne muszę podpiąć, ale tym się już zajmie skrypt w oparciu o google maps API.

EDIT: właściwie to mam 4 tabele, ponieważ planuję mieć maksymalnie elastyczna bazę: country(238 rekordów), state(16 - tylko dla Polski), poviat(379 - tylko Polska) i city(43363 tylko wsie i miasta, bez dzielnic). Schemat tabel dla:
country: id, iso2, iso3, name_pl, name_en, continent_pl, continent_en
state: id, country_id, name
poviat: id, state_id, name
city: id, poviat_id, state_id, name, type
Łączę w city zarówno poviat jak i state, ponieważ wiele krajów ma takiej jednostki terytorialnej jak powiat, czy analogicznej (teoretycznie Monako czy Watykan także nie muszą mieć województwa i powinienem od razu do city dodać country_id). Dodanie gmin byłoby według mnie już za dużym rozdrobnieniem informacji, a wysoce nieprawdopodobne jest znaleźć dwie miejscowości o tej samej nazwie w obrębie powiatu. Nam chodzi o unikalność głównie. W ten sposób można w krajach bez powiatu miasto od razu połączyć z województwem, bez użycia rekordu tworzącego fikcyjne, puste, połączenie do województwa.
Ryder
Cześć,

Przegladałem bazę TERC.

Podobnie chce zrezygnować z gmin, natomiast zastanawiam się nad powiatami.
Chciałbym żeby aplikacja była jak najprostsza dla użytkownika.

1. Załóżmy, że użytkownik wybierze wojewódzwtwo Mazowieckie z listy, a następnie wpisze nazwę miejscowosci/wsi - "Adamów".

Baza zwraca 7 rekordów (Kolumna: I - województwo (14 - Mazowieckie), II - Powiat, III - miejscowość)
14 33 Adamów
14 7 Adamów
14 9 Adamów
14 12 Adamów
14 25 Adamów
14 5 Adamów
14 4 Adamów

2. Założmy że użytkownik wpisze w wyszukiwarce wieś Adamów, nie wybierając województwa.
Wtedy baza zwraca 24 wyniki.

Pytanie jak dopasować najtrafniej wynik do zapytania.
Można poprosić użytkownika o doprecyzowanie, ale fajnie by było tego uniknąć.
np. Gdy wpiszemy w Google maps słowo Adamów zwraca na mapie 97-561 Adamów.

W jaki sposób wy dopasowujecie wyniki do zapytania?


thek
A nie pomyślałeś, że google używa geolokalizacji? Sprawdzi Twój IP i zapewne dobierze najbliższy Adamów do lokalizacji powiązanej z tym IP smile.gif Inna sprawa, że zdaje się to psu na budę, bo od czego są proxy? Właśnie najlepszym rozwiązaniem jest zasugerowanie wyniku dając możliwe wyniki poprzez zawężenie. Najpierw do województw, a potem ewentualnie powiatów. Jeśli na jakimś etapie będziesz już miał jednoznaczność - odpuszczasz sobie. Ja ogólnie mam mechanizm ciutkę inny. Mam dwie formy odpytania o miejscowość:
1) przez id - tu brak problemu - zawsze albo jest jedno, albo nie ma wcale
2) przez nazwę - z użyciem LIKE '$nazwa%' - czemu tak? Nazwa jest indeksem i takie zdefiniowanie LIKE (z możliwą "rozszerzoną" nazwą za podaną) używa indeksu (jeśli na kolumnie name założyliśmy takowy), czyli jest szybsze. Sprawdź sobie explain gdy masz z i bez indeksu. Bez indeksu - filesort, temporary i where, a z indeksem tyl where. Dwa - po stronie skryptu wychwytuję nazwy będące idealnym trafieniem oraz podobne. Wydzielam je do dwóch osobnych tablic. Jeśli idealne trafienia mam to korzystam z nich i ewentualnie sugeruję userowi uściślenie. Zawsze jednak mogę odwołać sie do drugiej i zaproponować mechanizm "Może chodziło Ci o ..." i podaję inne nazwy, nie będące już dokładnie nią. Przykład? Wpiszę podany przez Ciebie "Adamów" i znajdzie mi takie (właściwie to nawet 4), ale zasugeruje dodatkowo: Adamów Drwalewski, Adamów Rososki, Adamów-Parcel, Adamów-Wieś, Adamówek, Adamówka. Teraz jeśli jest to wyszukiwarka serwisu, mogę te miejscowości ograniczyć do takich, które mają cokolwiek do zaoferowania, czyli są jakoś połaczone z aktywnymi produktami w ofercie serwisu.

Co do powiatów, to ja tylko uzywam ich w przypadku niejednoznaczności i tak zaimplementowany routing, że niejednoznaczność sprowadzam do jednoznaczności. Jeśli ktoś podal tylko nazwę, podsuwam mu z jakiego województwa są wyniki i koniec. Jeśli trafi w pojedynczy, jego szczęście. Jeśli nadal będzie kilka to zasugeruję miejscowość ale oprócz nazwy dopisuje obok nazwę powiatu. Tak więc linki NIGDY nie rozwiną się do formy wojewodztwo_jakieś/powiat_jakiś/miasto_jakieś, ale powiat będzie olany, gdyz od razu rozwinie się do formy wojewodztwo_jakieś/miasto_jakieś,id_miasta_w_bazie i choćby ktoś cudował z parametrami w url, to nigdy nie zadziała ten parametr... Co zabawniejsze, to router mam tak napisany, że nawet uzycie fałszywych danych poprawia url, ponieważ znając id miasta, znam jego nazwę i województwo, a tym samym mogę cały url poprawić do w pełni poprawnej formy. Przykład z tego mojego routera...
/kategoria/miasto_sdfbjfb,1/ przejdzie przez router i wykona 301 na /kategoria/miasto_lepice,1/wojewodztwo_mazowieckie,7
Pozbywasz się z google od razu double content, gdzie różne adresy prowadza do tej samej treści. Nawet jeśli htaccess mi zawali sprawę, to router go poprawi i przekieruje na jedyny poprawny. Już nawet nie mówię, że przy okazji router oprócz samego adresu generuje mi kanoniczne, które mogę wykorzystać w sekcji head. Jest masę zabawy z tym, ale dużo używam cache i całość powinna być dość wydajna.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.