Vzdálenost dvou zeměpisných bodů

Školení, která pořádám

Nejkratší vzdálenost dvou bodů na Zemi vyjádřených zeměpisnými souřadnicemi se dá vypočítat pomocí jednoduchého vzorečku:

<?php
/** Vzdálenost dvou zeměpisných bodů
* @param float zeměpisná šířka prvního bodu ve stupních
* @param float zeměpisná délka prvního bodu ve stupních
* @param float zeměpisná šířka druhého bodu ve stupních
* @param float zeměpisná délka druhého bodu ve stupních
* @return float nejmenší vzdálenost bodů v kilometrech
* @copyright Jakub Vrána, https://php.vrana.cz/
*/
function gps_distance($lat1, $lng1, $lat2, $lng2) {
    static $great_circle_radius = 6372.795;
    return acos(
        cos(deg2rad($lat1))*cos(deg2rad($lng1))*cos(deg2rad($lat2))*cos(deg2rad($lng2))
        + cos(deg2rad($lat1))*sin(deg2rad($lng1))*cos(deg2rad($lat2))*sin(deg2rad($lng2))
        + sin(deg2rad($lat1))*sin(deg2rad($lat2))
    ) * $great_circle_radius;
}
?>

Číslo 6372.795 není poloměr zeměkoule, nýbrž průměrný poloměr tzv. hlavní kružnice, ale přiznám se, že zbytku vzorce moc nerozumím.

Jakub Vrána, Řešení problému, 8.10.2007, diskuse: 33 (nové: 0)

Diskuse

Tomáš Fejfar:

Pěkné. Ještě by se hodil nějaký komplexní článek o parsování... a tahat to z GM :)

Jarek:

Malé vysvětlení - jedná se o ortodromu - více http://cs.wikipedia.org/wiki/Ortodroma

Jakub Skalický:

Jen pro doplnění, výborně jsou různé modely Země a práce s nimi popsány v publikaci Navigace pro sportovní letce, ke stažení na http://www.aeroweb.cz/download.asp?inc=d2

Messa:

Země ale není kulová plocha :) Na Wikipedii je postup pro výpočet dle referenčního elipsiodu (http://cs.wikipedia.org/wiki/Elipsoid#P.C5.99.C3.ADklad_4) a Google umí najít i další vzorec (prý s přesností pů milimetru, opět asi podle ref. elipsoidu) - http://www.movable-type.co.uk/scripts/latlong-vincenty.html

Martin:

U funkce je "copyright Jakub Vrána", ale autor přiznává, že "zbytku vzorce moc nerozumím". Tak buď vzorec napsal v transu v důsledku božského vnuknutí, anebo ho někde ukradl, což mu ale nebránilo v briskním připsání vlastního copyrightu.

qwe:

J. Vrána je autorem konkrétního zpracování v php. Když v php napíšete funkci na výpočet obsahu čtverce, taky si k ní klidně můžete přidat vlastní copyright.

Martin:

Možná ano. Je to ale takové hulánovské řešení. Někteří programátoři tak ale pracují. Nejde o to, aby to fungovalo, nejde o to, abych to chápal, důležité je mít vyplněné autorství a copyright.

Uvádění zdroje bych u pana Vrány chápal jako povinnost už jen pro jeho pohyb v akademickém prostředí. Ale možná na to nehraje ani tam.

ikona Jakub Vrána OpenID:

Omlouvám se za neuvedení zdroje vzorečku, opravdu si ho už nepamatuji. Kdysi jsem uvedenou hodnotu potřeboval vypočítat, tak jsem pomocí vyhledávání klíčových slov našel stránku, která se problematikou zabývala a uváděla i tento vzoreček. Později jsem z toho udělal PHP funkci a tu zde zveřejnil.

Podobně jako u funkce pro výpočet obvodu čtverce ale myslím není důležité, odkud jsem vzorec získal, ale spíše to, kdo ten vzorec vymyslel. To bohužel netuším.

Jak uvádí qwe, copyright se nevztahuje na vzoreček, ale na implementaci (a ta je má vlastní, odnikud jsem funkci nezkopíroval) – jaké funkce přijímá parametry, jaký vzorec používá a s jakými hodnotami počítá. Samozřejmě kdokoliv může napsat podobnou (nebo i zcela identickou funkci), nemám na postup patent. Pokud však funkci přímo zkopíruje z tohoto blogu, měl by informaci o autorovi ponechat.

aha:

Nepřijde Vám, že obalit nějaký vzoreček funkcí  a nbadeadeklarovat odpovídající parametry je torchu málo tomu říkat _IMPLEMENTACE_? :)))

Michal Illich:

Hezke. Ale pokud to dobre chapu, tak i tenhle vzorecek (ackoliv vypada slozite) je vlastne zjednodusenim. Vychazi z toho, ze zeme je kulata, ale ona neni.

Ale pokud vim, tak na vetsine ceskych webu se pro vypocet vzdalenosti stejne pouziva c2=a2+b2 :). Nespravne, ale pro Ceskou republiku to docela ujde.

Fireman:

Predem se omlouvam ze reaguji na hodne stary topic.

Co se tyka pocitani vzdalenosti pres Vzdalenost = [ (X1-X2)^2 + (Y1-Y2)^2) ]^ (1/2) pak je to vse OK, pokud hodnoty X1-X2 a Y1-Y2 jsou v metrech. Pokud se pouziji souradnice GPS (konkretne uhly, je to spatne protoze rovnobeznik u severniho polu bude mit mnohonasobne mensi vzdalenost nez rovnobeznik nad Saharou. Pro ceskou republiku je ten pomer cca 1,552:1 takze pro GPS souradnice rozhodne Pytagorova veta fungvat nebude. Pytagorova veta bude nejpresnejsi pobliz rovniku... pokud se bude pocitat vzdalenost v radech metru primo pod rovnikem, tak se da na tuto funkci spolehnout, naopak u severniho polu bude totalne nepouzitelna.

anode:

Mohu se zeptat, proč je $great_circle_radius statická?

ikona Honza:

Tipnul bych si, že tím bylo myšleno, že je to vlastně konstanta. A možná to bude trochu rychlejší než obyčejná proměnná bez static, protože proměnná zůstane přiřazena mezi voláním funkcí a nemusí se alokovat při každém volání znova.

webmarcus:

Ahoj Jakube, je to sice OT, ale na jakub.vrana.cz v sekci novinky ti to píše chybu. No teda, že se nestydíš ;)

ikona Jakub Vrána OpenID:

Díky za upozornění, příště prosím e-mailem.

Karel Hrubý:

Jen pro upřesnění - předpokládám, že funkce počítá s parametry  (zeměpisnými souřadnicemi obou bodů) zadanými v radiánech, je tak ?

Karel Hrubý:

Omlouvám se za svůj předchozí nesmyslný příspěvek, jsem slepota. Fakt sorry.

mat.:

http://geocoder.us/blog/2006/04/21/calculating-distances/

ikona Jakub Hejda:

Velice užitečná funkce.
Myslíte, že bude použitelná z hlediska rychlosti když budu mít v databázi cca. 100 000 objektů se souřadnicemi a budu chtít zjistit např. všechny objekty v okruhu 25 km od urcite souradice?

Da se udelat neco s rychlosti? Necim to porovnani urychlit?
Díky moc za radu.

ikona Jakub Vrána OpenID:

Rychlé operace se zeměpisnými souřadnicemi v MySQL zajištuje Spatial Extension, ale to bohužel nepracuje s GPS souřadnicemi.

Rychlé oříznutí se dá zajistit pomocí omezení samotných souřadnic X a Y (tedy jakéhosi čtverce), na což se využije index.

matak:

nemohl by si to rozvést to vyhledávání v dtb?

Milan:

Ahoj všem, mám dotaz, zda se dá někde sehnat soubor se souřadnicemi GPS pro ČR (jen GPS souřadnice žádné jiné údaje), nebo jak matematicky výpočitat-vygenerovat body GPS při zadání počatečních a konečných udajů?

kriplozoik:

"soubor se souřadnicemi GPS pro ČR":
souřadnic "pro ČR" je nekonečně mnoho. ČR leží ve výřezu obdélníku s následujícími souřadnicemi:
levý okraj 12°5'25.985"E
pravý okraj 18°51'32.823"E
horní okraj 51°3'20.894"N
spodní okraj 48°33'7.513"N
pozn.: GPS je spíš způsob, jak tyto souřadnice zjistit, než že by tyto souřadnice byly 'jeho'.

"jak matematicky výpočitat-vygenerovat body GPS při zadání počatečních a konečných udajů"
tomu moc nerozumím. co jsou to ty "počateční" a "konečné udaje"? chceš zjistit geo-souřadnice mezi dvěma body (-přepokládám- definovanými taktéž geo-souřadnicemi)?

též si myslím (a to je jediná on-topic věc v mém příspěvku), že použití souřadnicového systému se souřadnicemi ve formě čísla s plovoucí desetinnou tečkou v intervalu <-180.0;180.0> resp. <-90;90> není specifické pouze a apriori pro GPS, ale i pro jiné systémy, takže by název té funkce mohl být třeba geo_distance(). Nebo, řečeno ještě jinak, použití "gps" v názvu té funkce znamená matení a míchání dvou úplně různých, nezávislých věcí dohromady (a sice: jakým způsobem popsat místo na zemském povrchu a jakým způsobem zjistit místo na zemském povrchu).

ikona v6ak:

"Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons (link)"
Mám si to vysvětlit tak, že to můžu i upravovat, včetně přepsání do jiného jazyka?

ikona Jakub Vrána OpenID:

Ano, beze všeho.

Václav Kusák:

Jakube, skvělá práce, tvůj článek mě zaujal! A děkuji mnohokrát za něj!

---

Pokud by někdo měl zájem o tuto skvělou funkci pro MySQL:

DELIMITER $$

CREATE
    FUNCTION `gps_distance`(lat1 FLOAT,lng1 FLOAT,lat2 FLOAT,lng2 FLOAT)
    RETURNS FLOAT
    BEGIN

    SET lat1 = lat1 * pi() / 180;
    SET lng1 = lng1 * pi() / 180;
    SET lat2 = lat2 * pi() / 180;
    SET lng2 = lng2 * pi() / 180;

    RETURN acos
    (   cos(lat1)*cos(lng1)*cos(lat2)*cos(lng2)
      + cos(lat1)*sin(lng1)*cos(lat2)*sin(lng2)
      + sin(lat1)*sin(lat2)
    ) * 6372.795;

    END$$

DELIMITER ;

---

Informace (další alternativa výpočtu) pro zájemce: Great circle distance
http://www.artfulsoftware.com/infotree/qu…&bw=1280#16

marcela:

vzdalenost v ki8lometrech Usti nd labem a chemnitz

Petr Klimeš:

Napsal jsem si jednoduchou funkci, která využívá Google Maps Api a je velmi přesná:

<?php
/**
* VYUŽITÍ GOOGLE API
* Copy Petr Klimeš (www.petrklimes.eu)
*
* DOKUMENTACE: https://developers.google.com/maps/documentation/distancematrix/?hl=cs
*
* http://maps.googleapis.com/maps/api/distancematrix/json
* ?origins=Vancouver+BC|Seattle
* &destinations=San+Francisco|Victoria+BC
* &mode=bicycling
* &language=fr-FR
* &sensor=false
* ------------------------------------------------------------------------------------
* @param string $od - Startovní adresa
* @param string $do - Cílová adresa
* @param string $stat_od = Stát, kde se nachází startovací adresa
* @param string $stat_do = Stát, kde se nachází cílová adresa
* @param string $mode - Jakym prostredkem se do mista dostaneme (driving = autem, walking = pěšky, bicycling = na kole)
* @param string $language - v jaké řeči vrátit výsledek. Podporované jsou: https://spreadsheets.google.com/pub?key=p9pdwsai2hDMsLkXsoM05KQ&gid=1
* @param string $avoid - cemu se vyhnout (tolls = mýtnému, highways = dálnicím)
* @param string $units - nastaveni jednotek (metric = Metrický systém, imperial = míle, stopy...)
* ------------------------------------------------------------------------------------
* @return array(
*    vzd_text => Vzdálenost textově v nastavené řeči,
*    vzd_val  => Vzálenost v metrech|stopách (dle parametru $units),
*    cas_text => Čas cesty v nastavené řeči,
*    cas_val  => Čas cesty v minutách,
*    ret_od   => Startovní adresa ve správném formátu,
*    ret_do   => Cílová adresa ve správném formátu
*  )
*
*/
function get_vzdalenost($od, $do, $stat_od="Česká republika", $stat_do="Česká republika", $mode="driving", $language="cs", $avoid="tolls", $units="metrics"){
    static
$google_api = "http://maps.googleapis.com/maps/api/distancematrix/";
    static
$type = "json"; // json|xml
   
$res = json_decode(file_get_contents($google_api.$type."?origins=".urlencode($od.",".$stat_od)."&destinations=".urlencode($do.",".$stat_do)."&mode=$mode&language=$language&avoid=$avoid&units=$units&sensor=false"));
   
$ret = array();
   
$ret["vzd_text"] = ($res->rows[0]->elements[0]->distance->text);
   
$ret["vzd_val"] = ($res->rows[0]->elements[0]->distance->value);
   
$ret["cas_text"] = ($res->rows[0]->elements[0]->duration->text);
   
$ret["cas_val"] = ($res->rows[0]->elements[0]->duration->value);
   
$ret["ret_od"] = implode(" | ",$res->origin_addresses);
   
$ret["ret_do"] = implode(" | ",$res->destination_addresses);
    return (
$ret);
}
?>

sagto:

Mám jinou otázku. Co když budu mít chat a ze souřadnic v databázi chci vytáhnout jen ty co jsou v blízkosti 1 km od mých souřadnic. Takže nevytáhne všechny, ale jen v  této vzdálenosti. Nechci zatěžovat celý server, když bych šel všechny položky po jednom. Navíc by to bylo zdlouhavé.

Databáze

id_chat
jmeno
text
lat
lng

ikona Jakub Vrána OpenID:

V MySQL se dají použít spatial indexy. Druhé řešení je si kromě přesných souřadnic uložit i číslo čtverce (o velikosti 1 km²) a nad tímto číslem si udělat index. Pak stačí rychle podle indexu najít devět čtverců (můj a osm okolních) a pak už bez indexu podle přesných souřadnic dohledat ty, které jsou fakt blíž než 1 km.

trestná smradlavice:

Knihovna phpgeo pro měření vzdálenosti dvou míst na Zemi.
https://github.com/mjaschen/phpgeo

SagTo:

A ma nekdo odpoved na to ze mam lng a potrebuji lng2 po pricteni 1000m?

sagto:

Jde nějak přehodit tu funkci na mysql dotaz s podmínkou například vzdalenost<=1? Já to zkoušel, ale nějak to nejde. A nevím proč.

Diskuse je zrušena z důvodu spamu.

avatar © 2005-2024 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.