diff --git a/cmd/serve-rwgps/main.go b/cmd/serve-rwgps/main.go index 99b061f..121feab 100644 --- a/cmd/serve-rwgps/main.go +++ b/cmd/serve-rwgps/main.go @@ -1,19 +1,10 @@ package main import ( - "bytes" - "encoding/json" - "errors" - "fmt" "log" "net/http" "os" - "strconv" - "github.com/dhconnelly/rtreego" - - "github.com/ray1729/gpx-utils/pkg/cafes" - "github.com/ray1729/gpx-utils/pkg/placenames" "github.com/ray1729/gpx-utils/pkg/rwgps" ) @@ -22,75 +13,11 @@ func main() { if listenAddr == "" { listenAddr = ":8000" } - gs, err := placenames.NewGPXSummarizer() + rwgpsHandler, err := rwgps.NewHandler() if err != nil { log.Fatal(err) } - gpxSummarizer = gs - http.HandleFunc("/rwgps", rwgpsHandler) + http.Handle("/rwgps", rwgpsHandler) log.Printf("Listening for requests on %s", listenAddr) log.Fatal(http.ListenAndServe(listenAddr, nil)) } - -var gpxSummarizer *placenames.GPXSummarizer -var stops = cafes.New() - -func rwgpsHandler(w http.ResponseWriter, r *http.Request) { - q := r.URL.Query() - rawRouteId := q.Get("routeId") - stopsName := q.Get("stops") - log.Printf("Handling request for routeId=%s stops=%s", rawRouteId, stopsName) - if rawRouteId == "" { - log.Printf("Missing routeId") - http.Error(w, "routeId is required", http.StatusBadRequest) - return - } - routeId, err := strconv.Atoi(rawRouteId) - if err != nil { - log.Println("Error parsing route id '%s': %v", rawRouteId, err) - http.Error(w, fmt.Sprintf("Invalid routeId: %s", rawRouteId), http.StatusBadRequest) - return - } - var stopsIndex *rtreego.Rtree - if stopsName != "" { - var err error - stopsIndex, err = stops.Get(stopsName) - if err != nil { - log.Println(err) - if errors.Is(err, cafes.ErrInvalidStops) { - http.Error(w, err.Error(), http.StatusBadRequest) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - return - } - } - track, err := rwgps.FetchTrack(routeId) - if err != nil { - log.Println(err.Error()) - switch err.(type) { - case *rwgps.ErrNotFound: - http.Error(w, err.Error(), http.StatusNotFound) - case *rwgps.ErrNotPublic: - http.Error(w, err.Error(), http.StatusForbidden) - default: - http.Error(w, err.Error(), http.StatusInternalServerError) - } - return - } - summary, err := gpxSummarizer.SummarizeTrack(bytes.NewReader(track), stopsIndex) - if err != nil { - log.Printf("Error analyzing route %d: %v", routeId, err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - result, err := json.Marshal(summary) - if err != nil { - log.Printf("Error marshalling JSON for route %d: %v", routeId, err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - w.Write(result) -} diff --git a/pkg/cafes/common.go b/pkg/cafes/common.go index e956e51..e2f21bc 100644 --- a/pkg/cafes/common.go +++ b/pkg/cafes/common.go @@ -35,7 +35,7 @@ func (s *RefreshmentStop) Contains(p rtreego.Point) bool { } // TTL cache based on "9.7 Example: Concurrent Non-Blocking Cache" from -// "The Go Programming Language", Alan A. A. Dovovan and Brian W. Kernighan +// "The Go Programming Language", Alan A. A. Donovan and Brian W. Kernighan type result struct { value *rtreego.Rtree diff --git a/pkg/rwgps/handler.go b/pkg/rwgps/handler.go new file mode 100644 index 0000000..f2fe661 --- /dev/null +++ b/pkg/rwgps/handler.go @@ -0,0 +1,90 @@ +package rwgps + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "log" + "net/http" + "strconv" + + "github.com/dhconnelly/rtreego" + + "github.com/ray1729/gpx-utils/pkg/cafes" + "github.com/ray1729/gpx-utils/pkg/placenames" +) + +type RWGPSHandler struct { + gs *placenames.GPXSummarizer + stops *cafes.Cache +} + +func NewHandler() (*RWGPSHandler, error) { + gs, err := placenames.NewGPXSummarizer() + if err != nil { + return nil, fmt.Errorf("error creating GPX summarizer: %v", err) + } + stops := cafes.New() + return &RWGPSHandler{gs, stops}, nil +} + +func (h *RWGPSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + rawRouteId := q.Get("routeId") + stopsName := q.Get("stops") + log.Printf("Handling request for routeId=%s stops=%s", rawRouteId, stopsName) + if rawRouteId == "" { + log.Printf("Missing routeId") + http.Error(w, "routeId is required", http.StatusBadRequest) + return + } + routeId, err := strconv.Atoi(rawRouteId) + if err != nil { + log.Println("Error parsing route id '%s': %v", rawRouteId, err) + http.Error(w, fmt.Sprintf("Invalid routeId: %s", rawRouteId), http.StatusBadRequest) + return + } + var stopsIndex *rtreego.Rtree + if stopsName != "" { + var err error + stopsIndex, err = h.stops.Get(stopsName) + if err != nil { + log.Println(err) + if errors.Is(err, cafes.ErrInvalidStops) { + http.Error(w, err.Error(), http.StatusBadRequest) + } else { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + return + } + } + track, err := FetchTrack(routeId) + if err != nil { + log.Println(err.Error()) + switch err.(type) { + case *ErrNotFound: + http.Error(w, err.Error(), http.StatusNotFound) + case *ErrNotPublic: + http.Error(w, err.Error(), http.StatusForbidden) + default: + http.Error(w, err.Error(), http.StatusInternalServerError) + } + return + } + summary, err := h.gs.SummarizeTrack(bytes.NewReader(track), stopsIndex) + if err != nil { + log.Printf("Error analyzing route %d: %v", routeId, err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + result, err := json.Marshal(summary) + if err != nil { + log.Printf("Error marshalling JSON for route %d: %v", routeId, err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write(result) +}