Plot city-level traffic activity from local GeoIP coordinates
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -26,6 +27,7 @@ var trafficCity struct {
|
||||
db *maxminddb.Reader
|
||||
running bool
|
||||
message string
|
||||
cache map[string]TrafficCityLocation
|
||||
}
|
||||
|
||||
func trafficCityPath() string {
|
||||
@@ -49,14 +51,34 @@ func trafficCityReady() bool {
|
||||
defer trafficCity.Unlock()
|
||||
return trafficCity.db != nil
|
||||
}
|
||||
|
||||
type TrafficCityLocation struct {
|
||||
City string `json:"city"`
|
||||
Region string `json:"region,omitempty"`
|
||||
Latitude float64 `json:"latitude"`
|
||||
Longitude float64 `json:"longitude"`
|
||||
Located bool `json:"located"`
|
||||
}
|
||||
|
||||
func lookupTrafficCity(text string) (string, string) {
|
||||
g := lookupTrafficCityLocation(text)
|
||||
return g.City, g.Region
|
||||
}
|
||||
func lookupTrafficCityLocation(text string) TrafficCityLocation {
|
||||
ip := net.ParseIP(text)
|
||||
if ip == nil || ip.IsPrivate() || ip.IsLoopback() || ip.IsLinkLocalUnicast() || !ip.IsGlobalUnicast() || !trafficCityReady() {
|
||||
return "", ""
|
||||
return TrafficCityLocation{}
|
||||
}
|
||||
trafficCity.Lock()
|
||||
defer trafficCity.Unlock()
|
||||
if g, ok := trafficCity.cache[text]; ok {
|
||||
return g
|
||||
}
|
||||
var r struct {
|
||||
Location struct {
|
||||
Latitude *float64 `maxminddb:"latitude"`
|
||||
Longitude *float64 `maxminddb:"longitude"`
|
||||
} `maxminddb:"location"`
|
||||
City struct {
|
||||
Names map[string]string `maxminddb:"names"`
|
||||
} `maxminddb:"city"`
|
||||
@@ -65,13 +87,24 @@ func lookupTrafficCity(text string) (string, string) {
|
||||
} `maxminddb:"subdivisions"`
|
||||
}
|
||||
if trafficCity.db.Lookup(ip, &r) != nil {
|
||||
return "", ""
|
||||
return TrafficCityLocation{}
|
||||
}
|
||||
region := ""
|
||||
if len(r.Subdivisions) > 0 {
|
||||
region = cityName(r.Subdivisions[0].Names)
|
||||
}
|
||||
return cityName(r.City.Names), region
|
||||
g := TrafficCityLocation{City: cityName(r.City.Names), Region: region}
|
||||
if g.City != "" && r.Location.Latitude != nil && r.Location.Longitude != nil {
|
||||
g.Latitude, g.Longitude = *r.Location.Latitude, *r.Location.Longitude
|
||||
g.Located = !math.IsNaN(g.Latitude) && !math.IsNaN(g.Longitude) && math.Abs(g.Latitude) <= 90 && math.Abs(g.Longitude) <= 180
|
||||
}
|
||||
if trafficCity.cache == nil {
|
||||
trafficCity.cache = map[string]TrafficCityLocation{}
|
||||
}
|
||||
if len(trafficCity.cache) < 32768 {
|
||||
trafficCity.cache[text] = g
|
||||
}
|
||||
return g
|
||||
}
|
||||
func trafficCityRoutes(mux *http.ServeMux) {
|
||||
mux.HandleFunc("/api/settings/traffic/city-database", func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -178,5 +211,6 @@ func installTrafficCityFrom(source, expectedHash string) error {
|
||||
trafficCity.db.Close()
|
||||
}
|
||||
trafficCity.db = db
|
||||
trafficCity.cache = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user