find-stone-beer / commit
Added Bing Maps route distance calculation as a second dist check, including the driving distance with the results.
bing-maps.go | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 15 ++++++- 2 files changed, 158 insertions(+), 2 deletions(-) diff --git a/bing-maps.go b/bing-maps.go new file mode 100644 index 0000000..ab7b403 --- /dev/null +++ b/bing-maps.go @@ -0,0 +1,145 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/jftuga/geodist" +) + +const ( + BingMapsKeyEnvVar = "BING_MAPS_KEY" +) + +func GetDistBetweenPoints(a, b geodist.Coord) (float64, error) { + dist, err := _getDistBetweenPoints(a.Lat, a.Lon, b.Lat, b.Lon) + return dist, err +} + +func _getDistBetweenPoints(lat1, lon1, lat2, lon2 float64) (float64, error) { + // http://dev.virtualearth.net/REST/v1/Routes?wayPoint.1={wayPoint1}&viaWaypoint.2={viaWaypoint2}&waypoint.3={waypoint3}&wayPoint.n={waypointN}&heading={heading}&optimize={optimize}&avoid={avoid}&distanceBeforeFirstTurn={distanceBeforeFirstTurn}&routeAttributes={routeAttributes}&timeType={timeType}&dateTime={dateTime}&maxSolutions={maxSolutions}&tolerances={tolerances}&distanceUnit={distanceUnit}&key={BingMapsKey} + + bingMapsKey, found := os.LookupEnv(BingMapsKeyEnvVar) + if !found { + return 0, fmt.Errorf("missing environment variable: %s", BingMapsKeyEnvVar) + } + + url := fmt.Sprintf("http://dev.virtualearth.net/REST/v1/Routes?wayPoint.1=%f,%f&wayPoint.2=%f,%f&distanceUnit=mi&key=%s", lat1, lon1, lat2, lon2, bingMapsKey) + data, err := getCachedUrl( + url, + fmt.Sprintf("bing-maps-%f,%f,%f,%f", lat1, lon1, lat2, lon2), + "2006-01", // cache this data for 1 month + ) + if err != nil { + return 0, err + } + + autoGenerated := autoGenerated{} + err = json.Unmarshal(data, &autoGenerated) + if err != nil { + return 0, err + } + dist := autoGenerated.ResourceSets[0].Resources[0].RouteLegs[0].TravelDistance + return dist, nil +} + +// https://mholt.github.io/json-to-go/ +type autoGenerated struct { + AuthenticationResultCode string `json:"authenticationResultCode"` + BrandLogoURI string `json:"brandLogoUri"` + Copyright string `json:"copyright"` + ResourceSets []struct { + EstimatedTotal int `json:"estimatedTotal"` + Resources []struct { + Type string `json:"__type"` + Bbox []float64 `json:"bbox"` + ID string `json:"id"` + DistanceUnit string `json:"distanceUnit"` + DurationUnit string `json:"durationUnit"` + RouteLegs []struct { + ActualEnd struct { + Type string `json:"type"` + Coordinates []float64 `json:"coordinates"` + } `json:"actualEnd"` + ActualStart struct { + Type string `json:"type"` + Coordinates []float64 `json:"coordinates"` + } `json:"actualStart"` + AlternateVias []any `json:"alternateVias"` + Description string `json:"description"` + EndTime string `json:"endTime"` + ItineraryItems []struct { + CompassDirection string `json:"compassDirection"` + Details []struct { + CompassDegrees int `json:"compassDegrees"` + EndPathIndices []int `json:"endPathIndices"` + LocationCodes []string `json:"locationCodes"` + ManeuverType string `json:"maneuverType"` + Mode string `json:"mode"` + Names []string `json:"names"` + RoadType string `json:"roadType"` + StartPathIndices []int `json:"startPathIndices"` + } `json:"details"` + Exit string `json:"exit"` + IconType string `json:"iconType"` + Instruction struct { + FormattedText any `json:"formattedText"` + ManeuverType string `json:"maneuverType"` + Text string `json:"text"` + } `json:"instruction"` + IsRealTimeTransit bool `json:"isRealTimeTransit"` + ManeuverPoint struct { + Type string `json:"type"` + Coordinates []float64 `json:"coordinates"` + } `json:"maneuverPoint"` + RealTimeTransitDelay int `json:"realTimeTransitDelay"` + SideOfStreet string `json:"sideOfStreet"` + TollZone string `json:"tollZone"` + TransitTerminus string `json:"transitTerminus"` + TravelDistance float64 `json:"travelDistance"` + TravelDuration int `json:"travelDuration"` + TravelMode string `json:"travelMode"` + Hints []struct { + HintType string `json:"hintType"` + Text string `json:"text"` + } `json:"hints,omitempty"` + } `json:"itineraryItems"` + RouteRegion string `json:"routeRegion"` + RouteSubLegs []struct { + EndWaypoint struct { + Type string `json:"type"` + Coordinates []float64 `json:"coordinates"` + Description string `json:"description"` + IsVia bool `json:"isVia"` + LocationIdentifier string `json:"locationIdentifier"` + RoutePathIndex int `json:"routePathIndex"` + } `json:"endWaypoint"` + StartWaypoint struct { + Type string `json:"type"` + Coordinates []float64 `json:"coordinates"` + Description string `json:"description"` + IsVia bool `json:"isVia"` + LocationIdentifier string `json:"locationIdentifier"` + RoutePathIndex int `json:"routePathIndex"` + } `json:"startWaypoint"` + TravelDistance float64 `json:"travelDistance"` + TravelDuration int `json:"travelDuration"` + } `json:"routeSubLegs"` + StartTime string `json:"startTime"` + TravelDistance float64 `json:"travelDistance"` + TravelDuration int `json:"travelDuration"` + TravelMode string `json:"travelMode"` + } `json:"routeLegs"` + TrafficCongestion string `json:"trafficCongestion"` + TrafficDataUsed string `json:"trafficDataUsed"` + TravelDistance float64 `json:"travelDistance"` + TravelDuration int `json:"travelDuration"` + TravelDurationTraffic int `json:"travelDurationTraffic"` + TravelMode string `json:"travelMode"` + } `json:"resources"` + } `json:"resourceSets"` + StatusCode int `json:"statusCode"` + StatusDescription string `json:"statusDescription"` + TraceID string `json:"traceId"` +} diff --git a/main.go b/main.go index 848f128..30b9351 100644 --- a/main.go +++ b/main.go @@ -40,7 +40,7 @@ func main() { } // load other settings from env vars - var rangeMiles = getEnvVarInteger(RANGE_MILES_ENV_VAR, 10) + var rangeMiles = getEnvVarInteger(RANGE_MILES_ENV_VAR, 15) var maxDeliveryWindowDays = getEnvVarInteger(MAX_DELIVERY_DAYS_ENV_VAR, 14) // descriptions of the beers and packaging @@ -74,6 +74,7 @@ func main() { relevantSellers := []relevantSeller{} for _, seller := range arrogantBastardSellers { + // quick distance check, gives us the crowd-sourced distance, which is the shortest distance between two points, so using this as a range check is a good and cheap first pass dist, _, err := geodist.VincentyDistance(coords, seller.Pos) if err != nil { panic(err) @@ -82,6 +83,16 @@ func main() { continue } + // calculate route distance, which is the actual distance you would drive, using Bing Maps API + // the results are cached for the month, for each set of coordinates, so this is a stable cached operation + dist, err = GetDistBetweenPoints(coords, seller.Pos) + if err != nil { + panic(err) + } + if dist > float64(rangeMiles) { + continue + } + sellerInfo, err := getSellerInfo(seller.Nid) if err != nil { panic(err) @@ -100,7 +111,7 @@ func main() { continue } mapsLink := getGoogleMapsLink(seller.Pos) - message := fmt.Sprintf("<a href='%s'>%s</a> - delivered %d days ago\n", mapsLink, seller.Title, daysSinceDelivery) + message := fmt.Sprintf("<a href='%s'>%s (%.1fm)</a> - delivered %d days ago\n", mapsLink, seller.Title, dist, daysSinceDelivery) relevantSellers = append(relevantSellers, relevantSeller{ Name: seller.Title,