-
Notifications
You must be signed in to change notification settings - Fork 181
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Q: How do you calculate the distance in km between 2 lat/lng points? #85
Comments
@idc77 the method Point#Distance returns an There's also some additional changes needed from your code to construct a Point from lat-long, see below. package main
import (
"fmt"
"github.com/golang/geo/s2"
)
const earthRadiusKm = 6371.01 // See https://github.com/golang/geo/blob/master/s2/s2_test.go#L46-L51
func main() {
lat1 := 9.1829321
lng1 := 48.7758459
lat2 := 5.03131
lng2 := 39.542448
pg1 := s2.PointFromLatLng(s2.LatLngFromDegrees(lat1, lng1))
pg2 := s2.PointFromLatLng(s2.LatLngFromDegrees(lat2, lng2))
sdist := pg2.Distance(pg1) * earthRadiusKm
fmt.Printf("s2 distance %f\n", sdist)
} |
Hi @doodlesbykumbi thank you very much for your reply. Something is really not adding up. package main
import (
"fmt"
"github.com/golang/geo/s2"
geo2 "github.com/kellydunn/golang-geo"
)
const earthRadiusKm = 6371.01 // See https://github.com/golang/geo/blob/master/s2/s2_test.go#L46-L51
func main() {
lat1 := 9.1829321
lng1 := 48.7758459
// lat2 := 5.03131
// lng2 := 39.542448
lat2 := 0.272091
lng2 := 45.937435
p1 := geo2.NewPoint(9.1829321, 48.7758459)
p2 := geo2.NewPoint(5.03131, 39.542448)
dist := p1.GreatCircleDistance(p2)
fmt.Printf("great circle distance: %f\n", dist)
pg1 := s2.PointFromLatLng(s2.LatLngFromDegrees(lat1, lng1))
pg2 := s2.PointFromLatLng(s2.LatLngFromDegrees(lat2, lng2))
sdist := pg1.Distance(pg2) * earthRadiusKm
odist := pg2.Distance(pg1) * earthRadiusKm
fmt.Printf("s2 distance: %f\n", sdist)
fmt.Printf("s2 r-distance: %f\n", odist)
}
however geoStage := bson.D{{
"$geoNear", bson.D{
{"near", profile.Location},
{"distanceField", "distance"},
{"minDistance", 1},
{"maxDistance", radius},
{"query", bson.D{
{"subject_id", m.TargetID},
}},
{"spherical", true},
},
},
} This is just to get the distance of "my profile" to "target profile". https://www.mongodb.com/docs/manual/reference/operator/aggregation/geoNear/
|
It's definitely something on the mongo side. If I use Google Maps and "Measure Distance", I get close to 1100 km for And ~1036 for Are you sure the points you are using in the mongo query are the same as in the s2 call? |
Sorry I posted the wrong source. package main
import (
"fmt"
"github.com/golang/geo/s2"
geo2 "github.com/kellydunn/golang-geo"
)
const earthRadiusKm = 6371.01 // See https://github.com/golang/geo/blob/master/s2/s2_test.go#L46-L51
func main() {
lat1 := 9.1829321
lng1 := 48.7758459
// lat2 := 5.03131
// lng2 := 39.542448#
lat2 := 15.39967
lng2 := 45.749428
p1 := geo2.NewPoint(lat1, lng1)
p2 := geo2.NewPoint(lat2, lng2)
dist := p1.GreatCircleDistance(p2)
fmt.Printf("great circle distance: %f\n", dist)
pg1 := s2.PointFromLatLng(s2.LatLngFromDegrees(lat1, lng1))
pg2 := s2.PointFromLatLng(s2.LatLngFromDegrees(lat2, lng2))
sdist := pg1.Distance(pg2) * earthRadiusKm
odist := pg2.Distance(pg1) * earthRadiusKm
fmt.Printf("s2 distance: %f\n", sdist)
fmt.Printf("s2 r-distance: %f\n", odist)
}
The non-spherical result in mongodb is 577683.4757611528 aka about 577 km. p1 is a real location Using google maps I couldn't get the exact location of p1 package models
// Location is a GeoJSON type.
type Location struct {
Type string `json:"type" bson:"type"`
Coordinates []float64 `json:"coordinates" bson:"coordinates"`
}
// NewPoint returns a GeoJSON Point with longitude and latitude.
func NewPoint(long, lat float64) Location {
return Location{
"Point",
[]float64{long, lat},
}
} mongodb saves the points in reverse order long, lat. |
Okay, that is indeed the case. package main
import (
"fmt"
"github.com/golang/geo/s2"
geo2 "github.com/kellydunn/golang-geo"
)
const earthRadiusKm = 6371.01 // See https://github.com/golang/geo/blob/master/s2/s2_test.go#L46-L51
func main() {
lat1 := 9.1829321
lng1 := 48.7758459
lat2 := 15.39967
lng2 := 45.749428
// p1 := geo2.NewPoint(lat1, lng1)
p1 := geo2.NewPoint(lng1, lat1)
// p2 := geo2.NewPoint(lat2, lng2)
p2 := geo2.NewPoint(lng2, lat2)
dist := p1.GreatCircleDistance(p2)
fmt.Printf("great circle distance: %f\n", dist)
// pg1 := s2.PointFromLatLng(s2.LatLngFromDegrees(lat1, lng1))
pg1 := s2.PointFromLatLng(s2.LatLngFromDegrees(lng1, lat1))
// pg2 := s2.PointFromLatLng(s2.LatLngFromDegrees(lat2, lng2))
pg2 := s2.PointFromLatLng(s2.LatLngFromDegrees(lng2, lat2))
sdist := pg1.Distance(pg2) * earthRadiusKm
// odist := pg2.Distance(pg1) * earthRadiusKm
fmt.Printf("s2 distance: %f\n", sdist)
// fmt.Printf("s2 r-distance: %f\n", odist)
} I swapped longitude and latitude and the resulsts are drumroll
MongoDB value: 577683.4757611528 So MongoDB requires long, lat format, but does the calculation wrong internall by mistaking lat for long and the other way around. That's if I didn't do anything wrong. Thank you all for you help :) I'll report it to MongoDB. |
It wasn't mongo. It was me. I'm the mongo ;P Self-knowledge is the 1st step to enlightenment great circle distance: 577.040408 package main
import (
"context"
"fmt"
"log"
"time"
"github.com/golang/geo/s2"
geo "github.com/kellydunn/golang-geo"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
mopts "go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
"go.mongodb.org/mongo-driver/x/bsonx"
)
const earthRadiusKm = 6371.01 // See https://github.com/golang/geo/blob/master/s2/s2_test.go#L46-L51
var (
Database = "testgeo"
DefaultTimeout = time.Second * 10
ProfileCollection = "profile"
)
type (
Profile struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id"`
Location Location `bson:"location" json:"location"`
}
ProfileResult struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id"`
Location Location `bson:"location" json:"location"`
Distance float64 `bson:"distance" json:"distance"`
}
Location struct {
Type string `json:"type" bson:"type"`
Coordinates []float64 `json:"coordinates" bson:"coordinates"`
}
)
func main() {
// lat1 := 9.1829321
// lng1 := 48.7758459
// lat2 := 45.749428
//lng2 := 15.39967
// lat1 := 9.653194
// lng1 := 48.709797
// lat2 := 9.182313
// lng2 := 48.783187
lng1 := 9.1829321
lat1 := 48.7758459
lng2 := 15.39967
lat2 := 45.749428
p1 := geo.NewPoint(lat1, lng1)
// p1 := geo2.NewPoint(lng1, lat1)
p2 := geo.NewPoint(lat2, lng2)
// p2 := geo2.NewPoint(lng2, lat2)
dist := p1.GreatCircleDistance(p2)
fmt.Printf("great circle distance: %f\n", dist)
pg1 := s2.PointFromLatLng(s2.LatLngFromDegrees(lat1, lng1))
// pg1 := s2.PointFromLatLng(s2.LatLngFromDegrees(lng1, lat1))
pg2 := s2.PointFromLatLng(s2.LatLngFromDegrees(lat2, lng2))
// pg2 := s2.PointFromLatLng(s2.LatLngFromDegrees(lng2, lat2))
sdist := pg1.Distance(pg2) * earthRadiusKm
// odist := pg2.Distance(pg1) * earthRadiusKm
fmt.Printf("s2 distance: %f\n", sdist)
// fmt.Printf("s2 r-distance: %f\n", odist)
// fmt.Printf("mongo result: %f\n", 5165738.082454552)
mp1 := new(Profile)
mp1.Location = NewPoint(lng1, lat1)
mp2 := new(Profile)
mp2.Location = NewPoint(lng2, lat2)
client, e := mongoInit()
if e != nil {
log.Fatal(e.Error())
}
defer func() {
if e = client.Disconnect(context.Background()); e != nil {
panic(e)
}
}()
res1, e := CreateProfile(client, mp1)
if e != nil {
log.Fatal(e.Error())
}
res2, e := CreateProfile(client, mp2)
if e != nil {
log.Fatal(e.Error())
}
mp1.ID = res1.InsertedID.(primitive.ObjectID)
mp2.ID = res2.InsertedID.(primitive.ObjectID)
var radius int32
radius = int32(6371) * 1000
geoStage := bson.D{{
"$geoNear", bson.D{
{"near", mp1.Location},
{"distanceField", "distance"},
{"minDistance", 1},
{"maxDistance", radius},
{"spherical", true},
},
},
}
result, e := AggregateProfile(client, mongo.Pipeline{geoStage})
if e != nil {
return
}
fmt.Printf("mongodb distance: %f\n", result[0].Distance)
}
func mongoInit() (*mongo.Client, error) {
ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout)
defer cancel()
client, e := mongo.Connect(ctx, mopts.Client().ApplyURI("mongodb://localhost:27017"))
if e != nil {
return nil, e
}
ctx, cancel = context.WithTimeout(context.Background(), DefaultTimeout)
defer cancel()
e = client.Ping(ctx, readpref.Primary())
if e != nil {
return nil, e
}
// drop database
_ = client.Database(Database).Drop(context.Background())
// create geoJSON 2dsphere index
ctx, cancel = context.WithTimeout(context.Background(), DefaultTimeout)
defer cancel()
db := client.Database(Database)
indexOpts := mopts.CreateIndexes().SetMaxTime(DefaultTimeout)
// Index to location 2dsphere type.
pointIndexModel := mongo.IndexModel{
Options: mopts.Index(),
Keys: bsonx.MDoc{
"location": bsonx.String("2dsphere"),
},
}
profileIndexes := db.Collection("profile").Indexes()
// _, e = profileIndexes.CreateOne(ctx, pointIndexModel, indexOpts)
_, e = profileIndexes.CreateOne(ctx, pointIndexModel, indexOpts)
if e != nil {
return nil, e
}
return client, nil
}
// NewPoint returns a GeoJSON Point with longitude and latitude.
func NewPoint(long, lat float64) Location {
return Location{
"Point",
[]float64{long, lat},
}
}
func CreateProfile(client *mongo.Client, m *Profile) (*mongo.InsertOneResult, error) {
ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout)
defer cancel()
c := client.Database(Database).Collection(ProfileCollection)
return c.InsertOne(ctx, m)
}
func AggregateProfile(client *mongo.Client, pipeline interface{}) ([]*ProfileResult, error) {
ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout)
defer cancel()
c := client.Database(Database).Collection(ProfileCollection)
cursor, e := c.Aggregate(ctx, pipeline)
if e != nil {
return nil, e
}
ctx, cancel = context.WithTimeout(context.Background(), DefaultTimeout)
defer cancel()
var m []*ProfileResult
if e := cursor.All(ctx, &m); e != nil {
return nil, e
}
return m, nil
} But it's close enough and the users won't need very high accuracy in their results. |
If anyone is still reading, the postgis 3.2.1 on postgresql 14 result ahaha
4 approaches, 4 different results Somewhat interesting
That's about 200ms faster than MongoDB (but then again I assume MongoDB was going through 100k datasets and comparing the distance to min and max distance). Have a nice day. This was my last post in this issue. |
s2 distance: 1.499294
This can't be true. It's nearly 1080km.
Can you please tell how to do it?
The text was updated successfully, but these errors were encountered: