Beregn eller spør etter stor sirkelavstand mellom bredde- og lengdepunkter ved å bruke Haversine-formelen (PHP, JavaScript, Java, Python, MySQL, MSSQL-eksempler)
Denne måneden har jeg programmert i PHP og MySQL for GIS. Mens jeg undersøkte emnet, hadde jeg problemer med å finne geografiske beregninger for å finne avstanden mellom to steder, så jeg ville dele dem her.
Den enkle måten å beregne en avstand mellom to punkter på er å bruke den pythagoreiske formelen til å beregne hypotenusen til en trekant (A² + B² = C²). Dette er kjent som Euklidisk avstand.
Det er en interessant start, men det gjelder ikke geografi siden avstanden mellom bredde- og lengdegradslinjer er ikke like avstander fra hverandre. Når du kommer nærmere ekvator, blir breddegradslinjene lenger fra hverandre. Hvis du bruker en enkel trianguleringsligning, kan den måle avstanden nøyaktig på ett sted og feil på det andre på grunn av jordens krumning.
Stor sirkelavstand
Rutene som reises lange avstander rundt jorden er kjent som den store sirkelavstanden. Det vil si... den korteste avstanden mellom to punkter på en kule er forskjellig fra punktene på et flatt kart. Kombiner det med det faktum at bredde- og lengdegradslinjer ikke er like langt ... og du har en vanskelig beregning.
Her er en fantastisk videoforklaring på hvordan Great Circles fungerer.
Haversine-formelen
Avstanden ved bruk av jordens krumning er innlemmet i Haversine-formelen, som bruker trigonometri for å tillate jordens krumning. Når du finner avstanden mellom 2 steder på jorden (i luftlinje), er en rett linje faktisk en bue.
Dette gjelder for flyreiser – har du noen gang sett på det faktiske kartet over flyreiser og lagt merke til at de er buede? Det er fordi å fly i en bue mellom to punkter er kortere enn direkte til stedet.
PHP: Beregn avstand mellom 2 punkter i bredde og lengdegrad
Her er PHP-formelen for å beregne avstanden mellom to punkter (sammen med Mile vs. Kilometer-konvertering) avrundet til to desimaler.
function getDistanceBetweenPointsNew($latitude1, $longitude1, $latitude2, $longitude2, $unit = 'miles') {
$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 'miles':
break;
case 'kilometers' :
$distance = $distance * 1.609344;
}
return (round($distance,2));
}
Variablene er:
- $Latitude1 – en variabel for breddegraden til det første stedet.
- $Lengdegrad1 – en variabel for den første plasseringens lengdegrad
- $Latitude2 – en variabel for den andre plasseringens breddegrad.
- $Lengdegrad2 – en variabel for den andre plasseringens lengdegrad.
- $enhet – standard vesen miles. Dette kan oppdateres eller sendes som kilometer.
Java: Beregn avstanden mellom 2 bredde- og lengdepunkter
public static double getDistanceBetweenPointsNew(double latitude1, double longitude1, double latitude2, double longitude2, String unit) {
double theta = longitude1 - longitude2;
double distance = 60 * 1.1515 * (180/Math.PI) * Math.acos(
Math.sin(latitude1 * (Math.PI/180)) * Math.sin(latitude2 * (Math.PI/180)) +
Math.cos(latitude1 * (Math.PI/180)) * Math.cos(latitude2 * (Math.PI/180)) * Math.cos(theta * (Math.PI/180))
);
if (unit.equals("miles")) {
return Math.round(distance, 2);
} else if (unit.equals("kilometers")) {
return Math.round(distance * 1.609344, 2);
} else {
return 0;
}
}
Variablene er:
- breddegrad 1 – en variabel for breddegraden til det første stedet.
- lengdegrad 1 – en variabel for den første plasseringens lengdegrad
- breddegrad 2 – en variabel for den andre plasseringens breddegrad.
- lengdegrad 2 – en variabel for den andre plasseringens lengdegrad.
- enhet – standard vesen miles. Dette kan oppdateres eller sendes som kilometer.
JavaScript: Beregn avstanden mellom 2 bredde- og lengdepunkter
function getDistanceBetweenPoints(latitude1, longitude1, latitude2, longitude2, unit = 'miles') {
let theta = longitude1 - longitude2;
let distance = 60 * 1.1515 * (180/Math.PI) * Math.acos(
Math.sin(latitude1 * (Math.PI/180)) * Math.sin(latitude2 * (Math.PI/180)) +
Math.cos(latitude1 * (Math.PI/180)) * Math.cos(latitude2 * (Math.PI/180)) * Math.cos(theta * (Math.PI/180))
);
if (unit == 'miles') {
return Math.round(distance, 2);
} else if (unit == 'kilometers') {
return Math.round(distance * 1.609344, 2);
}
}
Variablene er:
- breddegrad 1 – en variabel for breddegraden til det første stedet.
- lengdegrad 1 – en variabel for den første plasseringens lengdegrad
- breddegrad 2 – en variabel for den andre plasseringens breddegrad.
- lengdegrad 2 – en variabel for den andre plasseringens lengdegrad.
- enhet – standard vesen miles. Dette kan oppdateres eller sendes som kilometer.
Python: Beregn avstanden mellom 2 bredde- og lengdepunkter
Her er Python-formelen for å beregne avstanden mellom to punkter (sammen med Mile vs. Kilometer-konvertering) avrundet til to desimaler. Takk til sønnen min, Bill Karr, en dataforsker for Åpne INSIGHTS, for koden.
from numpy import sin, cos, arccos, pi, round
def rad2deg(radians):
degrees = radians * 180 / pi
return degrees
def deg2rad(degrees):
radians = degrees * pi / 180
return radians
def getDistanceBetweenPointsNew(latitude1, longitude1, latitude2, longitude2, unit = 'miles'):
theta = longitude1 - longitude2
distance = 60 * 1.1515 * rad2deg(
arccos(
(sin(deg2rad(latitude1)) * sin(deg2rad(latitude2))) +
(cos(deg2rad(latitude1)) * cos(deg2rad(latitude2)) * cos(deg2rad(theta)))
)
)
if unit == 'miles':
return round(distance, 2)
if unit == 'kilometers':
return round(distance * 1.609344, 2)
Variablene er:
- breddegrad 1 – en variabel for din første plassering breddegrad.
- lengdegrad 1 – en variabel for din første plassering lengdegrad
- breddegrad 2 – en variabel for din andre plassering breddegrad.
- lengdegrad 2 – en variabel for din andre plassering lengdegrad.
- enhet – standard vesen miles. Dette kan oppdateres eller sendes som kilometer.
MySQL: Henter alle poster innenfor et område ved å beregne avstand i miles ved bruk av breddegrad og lengdegrad
Bruk av romlige datatyper i MySQL er en mer effektiv og praktisk måte å jobbe med geografiske data på, inkludert beregning av avstander mellom punkter. MySQL støtter romlige datatyper som f.eks POINT
, LINESTRING
og POLYGON
, sammen med romlige funksjoner som ST_Distance
.
Når du bruker ST_Distance
funksjon i MySQL med geografiske data representert som POINT
koordinater, tar den hensyn til krumningen til jordens overflate. Den sfæriske modellen brukt av ST_Distance
bruker Haversine-formelen. Denne tilnærmingen er egnet for de fleste praktiske formål, men kan introdusere små unøyaktigheter for svært lange avstander.
Slik kan du beregne avstander mellom to punkter ved hjelp av romlige datatyper:
- Opprett en tabell med romdatatype: Først lager du en tabell med en
POINT
kolonne for å lagre geografiske punkter. For eksempel:
CREATE TABLE locations (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
coordinates POINT
);
Sett inn dine geografiske punkter i denne tabellen ved å bruke POINT
konstruktør:
INSERT INTO locations (name, coordinates)
VALUES
('Point A', POINT(40.7128, -74.0060)), -- New York City
('Point B', POINT(34.0522, -118.2437)); -- Los Angeles
- Beregn avstand ved å bruke ST_Distance: Du kan beregne avstanden mellom to punkter ved å bruke
ST_Distance
funksjon. Her er et eksempelspørsmål for å beregne avstanden mellom to punkter:
SELECT
id1,
id2,
(ST_Distance(coordinates1, coordinates2) / 1609.344) AS distance_in_miles
FROM (
SELECT
l1.id AS id1,
l2.id AS id2,
l1.coordinates AS coordinates1,
l2.coordinates AS coordinates2
FROM
locations l1,
locations l2
WHERE
l1.id = 1 AND l2.id = 2
) AS distances;
Erstatt 1
og 2
med ID-ene til de to punktene du vil beregne avstanden mellom.
- Resultat: Spørringen vil returnere avstanden mellom de to punktene i miles.
Bruk av romlige datatyper og ST_Distance
funksjon gir en mer effektiv og nøyaktig måte å jobbe med geografiske data i MySQL. Det forenkler også beregning av avstander mellom punkter, noe som gjør det enklere å administrere og søke etter dataene dine.
MySQL: Henter alle poster innenfor et område ved å beregne avstand i kilometer ved å bruke breddegrad og lengdegrad
Som standard ST_Distance
returnerer avstanden i meter, så du trenger bare å oppdatere spørringen for kilometer:
SELECT
id1,
id2,
(ST_Distance(coordinates1, coordinates2) / 1000) AS distance_in_kilometers
FROM (
SELECT
l1.id AS id1,
l2.id AS id2,
l1.coordinates AS coordinates1,
l2.coordinates AS coordinates2
FROM
locations l1,
locations l2
WHERE
l1.id = 1 AND l2.id = 2
) AS distances;
Microsoft SQL Server Geografisk Avstand: STDistance
Hvis du bruker Microsoft SQL Server, tilbyr de sin egen funksjon, STDistance for å beregne avstanden mellom to punkter ved hjelp av datatypen Geografi.
DECLARE @g geography;
DECLARE @h geography;
SET @g = geography::STGeomFromText('LINESTRING(-122.360 47.656, -122.343 47.656)', 4326);
SET @h = geography::STGeomFromText('POINT(-122.34900 47.65100)', 4326);
SELECT @g.STDistance(@h);
Hattip til Manash Sahoo, grunnlegger og seniorarkitekt hos Ion tre.