Improve location estimation

This commit is contained in:
Daniel
2022-01-12 16:41:41 +01:00
parent ca4bac3b1b
commit ad21764711
2 changed files with 329 additions and 308 deletions

View File

@@ -8,7 +8,8 @@ import (
)
const (
earthCircumferenceInKm float64 = 40100 // earth circumference in km
earthCircumferenceInKm = 40100 // earth circumference in km
defaultLocationAccuracy = 100
)
// Location holds information regarding the geographical and network location of an IP address.
@@ -30,52 +31,75 @@ type Coordinates struct {
Longitude float64 `maxminddb:"longitude"`
}
/*
Location Estimation
Distance Value
- 0: Other side of the Internet.
- 100: Very near, up to same network / datacenter.
Weighting Goal
- Exposure to different networks shall be limited as much as possible.
- A single network should not see a connection over a large distance.
- Latency should be low.
Weighting Intentions
- Being on the same continent is better than being in the same AS.
- Being in the same country is better than having low coordinate distance.
- Coordinate distance is only a tie breaker, as accuracy varies heavily.
- Same AS with lower coordinate distance beats being on the same continent.
Weighting Configuration
*/
const (
weightContinentMatch = 25
weightCountryMatch = 20
weightASOrgMatch = 15
weightASNMatch = 10
weightCoordinateDistance = 30
)
/*
About the Accuracy Radius
- Range: 1-1000
- Seen values (estimation): 1,5,10,20,50,100,200,500,1000
- The default seems to be 100.
Cxamples
- 1.1.1/24 has 1000: Anycast
- 8.8.0/19 has 1000: Anycast
- 8.8.52/22 has 1: City of Westfield
Conclusion
- Ignore or penalize high accuracy radius.
*/
// EstimateNetworkProximity aims to calculate the distance between two network locations. Returns a proximity value between 0 (far away) and 100 (nearby).
func (l *Location) EstimateNetworkProximity(to *Location) (proximity int) {
/*
Distance Value
- 0: Other side of the Internet.
- 100: Very near, up to same network / datacenter.
Weighting Goal
- Exposure to different networks shall be limited as much as possible.
- A single network should not see a connection over a large distance.
- Latency should be low.
Weighting Intentions
- Being on the same continent is better than being in the same AS.
- Being in the same country is better than having low coordinate distance.
- Coordinate distance is only a tie breaker, as accuracy varies heavily.
- Same AS with lower coordinate distance beats being on the same continent.
Weighting Configuration
- Continent match: 30
- Country match: 25
- ASOrg match: 20
- ASN match: 15
- Coordinate distance: 0-10
*/
if l.Continent.Code != "" &&
l.Continent.Code == to.Continent.Code {
proximity += 30
if l.Country.ISOCode != "" &&
l.Country.ISOCode == to.Country.ISOCode {
proximity += 25
}
func (l *Location) EstimateNetworkProximity(to *Location) (proximity float32) {
switch {
case l.Country.ISOCode != "" && l.Country.ISOCode == to.Country.ISOCode:
// Rely more on the Country Code data, as it is more accurate than the
// Continent Code, especially when combining location data from multiple
// sources.
proximity += weightContinentMatch + weightCountryMatch
case l.Continent.Code != "" && l.Continent.Code == to.Continent.Code:
proximity += weightContinentMatch
}
if l.AutonomousSystemOrganization != "" &&
l.AutonomousSystemOrganization == to.AutonomousSystemOrganization {
proximity += 20
if l.AutonomousSystemNumber != 0 &&
l.AutonomousSystemNumber == to.AutonomousSystemNumber {
proximity += 15
}
switch {
case l.AutonomousSystemNumber != 0 && l.AutonomousSystemNumber == to.AutonomousSystemNumber:
// Rely more on the ASN data, as it is more accurate than the ASOrg data,
// especially when combining location data from multiple sources.
proximity += weightASOrgMatch + weightASNMatch
case l.AutonomousSystemOrganization != "" && l.AutonomousSystemOrganization == to.AutonomousSystemOrganization:
proximity += weightASOrgMatch
}
// Check coordinates and adjust accuracy value.
@@ -91,23 +115,10 @@ func (l *Location) EstimateNetworkProximity(to *Location) (proximity int) {
accuracy = to.Coordinates.AccuracyRadius
}
/*
About the Accuracy Radius
- Range: 1-1000
- Seen values (estimation): 1,5,10,20,50,100,200,500,1000
- The default seems to be 100.
Cxamples
- 1.1.1/24 has 1000: Anycast
- 8.8.0/19 has 1000: Anycast
- 8.8.52/22 has 1: City of Westfield
Conclusion
- Ignore or penalize high accuracy radius.
*/
// Apply the default location accuracy if there is none.
if accuracy == 0 {
accuracy = defaultLocationAccuracy
}
// Calculate coordinate distance in kilometers.
fromCoords := haversine.Coord{Lat: l.Coordinates.Latitude, Lon: l.Coordinates.Longitude}
@@ -116,14 +127,21 @@ func (l *Location) EstimateNetworkProximity(to *Location) (proximity int) {
if km <= 50 && accuracy <= 100 {
// Give a flat out ten for highly accurate coordinates within 50km.
proximity += 10
proximity += weightCoordinateDistance
} else {
// Else, take a percentage.
distanceInPercent := (earthCircumferenceInKm - km) * 100 / earthCircumferenceInKm
proximityInPercent := (earthCircumferenceInKm - km) / earthCircumferenceInKm
// Apply penalty for locations with low accuracy (targeting accuracy radius >100).
// Take away at most 50% of the weight through inaccuracy.
accuracyModifier := 1 - float64(accuracy)/2000
proximity += int(distanceInPercent * 0.10 * accuracyModifier)
// Add proximiy weight.
proximity += float32(
weightCoordinateDistance * // Maxmimum weight for this data point.
proximityInPercent * // Range: 0-1
accuracyModifier, // Range: 0.5-1
)
}
return proximity