Skip to content
Snippets Groups Projects
Verified Commit 402dcb5f authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Initial Commit

parents
No related branches found
No related tags found
No related merge requests found
Pipeline #467 failed
*.iml
/.idea/*
!/.idea/copyright/
.DS_Store
/out/*/
\ No newline at end of file
This diff is collapsed.
package main
import (
"encoding/json"
"log"
"os"
"regexp"
"strings"
)
type AutocompleteStation struct {
Id int64 `json:"stop_id"`
Name string `json:"stop_name"`
*Position
Distance float64 `json:"stop_distance,omitempty"`
}
func loadAutocompleteStations() []AutocompleteStation {
var file *os.File
var err error
if file, err = os.Open("assets/stops.json"); err != nil {
log.Fatal(err)
}
var autocompleteStations []AutocompleteStation
if err = json.NewDecoder(file).Decode(&autocompleteStations); err != nil {
log.Fatal(err)
}
if err = file.Close(); err != nil {
log.Fatal(err)
}
return autocompleteStations
}
func canonicalizeName(stationName string) string {
additionalRegex := regexp.MustCompile("\\([^(]*\\)")
spaceRegex := regexp.MustCompile(" +")
stationName = additionalRegex.ReplaceAllString(stationName, "")
stationName = spaceRegex.ReplaceAllString(stationName, " ")
stationName = strings.TrimSpace(stationName)
stationName = strings.TrimSuffix(stationName, " Hbf")
return stationName
}
package main
import (
"errors"
"math"
"strconv"
"strings"
)
type Position struct {
Latitude float64 `json:"stop_lat"`
Longitude float64 `json:"stop_lon"`
}
func PositionFromString(data string) (Position, error) {
var err error
var position Position
split := strings.Split(data, ",")
if len(split) != 2 {
return position, errors.New("incorrect format")
}
if position.Latitude, err = strconv.ParseFloat(split[0], 64); err != nil {
return position, err
}
if position.Longitude, err = strconv.ParseFloat(split[1], 64); err != nil {
return position, err
}
return position, nil
}
// haversin(θ) function
func hsin(theta float64) float64 {
return math.Pow(math.Sin(theta/2), 2)
}
// Distance function returns the distance (in meters) between two points of
// a given longitude and latitude relatively accurately (using a spherical
// approximation of the Earth) through the Haversin Distance Formula for
// great arc distance on a sphere with accuracy for small distances
//
// point coordinates are supplied in degrees and converted into rad. in the func
//
// distance returned is METERS!!!!!!
// http://en.wikipedia.org/wiki/Haversine_formula
func Distance(a Position, b Position) float64 {
// convert to radians
// must cast radius as float to multiply later
var la1, lo1, la2, lo2, r float64
la1 = a.Latitude * math.Pi / 180
lo1 = a.Longitude * math.Pi / 180
la2 = b.Latitude * math.Pi / 180
lo2 = b.Longitude * math.Pi / 180
r = 6378100 // Earth radius in METERS
// calculate
h := hsin(la2-la1) + math.Cos(la1)*math.Cos(la2)*hsin(lo2-lo1)
return 2 * r * math.Asin(math.Sqrt(h))
}
go.mod 0 → 100644
module git.kuschku.de/justjanne/bahn-proxy
go 1.12
require git.kuschku.de/justjanne/bahn-api v0.0.0-20190426122722-3314b1faadb3
go.sum 0 → 100644
main.go 0 → 100644
package main
import (
"encoding/json"
"fmt"
"git.kuschku.de/justjanne/bahn-api"
"log"
"net/http"
"path"
"sort"
"strings"
"time"
)
func returnJson(w http.ResponseWriter, data interface{}) error {
marshalled, err := json.Marshal(data)
if err != nil {
return err
}
w.Header().Add("Content-Type", "application/json")
if _, err := w.Write(marshalled); err != nil {
return err
}
return nil
}
func main() {
autocompleteStations := loadAutocompleteStations()
var netClient = &http.Client{
Timeout: time.Second * 10,
}
MaxResults := 20
http.HandleFunc("/autocomplete/", func(w http.ResponseWriter, r *http.Request) {
if stationName := strings.TrimSpace(r.FormValue("name")); stationName != "" {
var perfectMatch []AutocompleteStation
var prefix []AutocompleteStation
var contains []AutocompleteStation
for _, station := range autocompleteStations {
findableName := canonicalizeName(station.Name)
if strings.EqualFold(station.Name, stationName) {
perfectMatch = append(perfectMatch, station)
} else if strings.EqualFold(findableName, stationName) {
perfectMatch = append(perfectMatch, station)
} else if strings.HasPrefix(station.Name, stationName) {
prefix = append(prefix, station)
} else if strings.Contains(station.Name, stationName) {
contains = append(contains, station)
}
if len(perfectMatch)+len(prefix)+len(contains) >= MaxResults {
break
}
}
result := append(append(perfectMatch, prefix...), contains...)
if err := returnJson(w, result); err != nil {
log.Fatal(err)
return
}
} else if position, err := PositionFromString(strings.TrimSpace(r.FormValue("position"))); err == nil {
var result []AutocompleteStation
for _, station := range autocompleteStations {
result = append(result, AutocompleteStation{
Id: station.Id,
Name: station.Name,
Position: station.Position,
Distance: Distance(*station.Position, position),
})
}
sort.Slice(result, func(i, j int) bool {
return result[i].Distance < result[j].Distance
})
if err := returnJson(w, result[:MaxResults]); err != nil {
log.Fatal(err)
return
}
}
})
http.HandleFunc("/station/", func(w http.ResponseWriter, r *http.Request) {
var err error
_, evaId := path.Split(r.URL.Path)
evaId = strings.TrimSpace(evaId)
url := fmt.Sprintf("http://iris.noncd.db.de/iris-tts/timetable/station/%s", evaId)
var response *http.Response
if response, err = netClient.Get(url); err != nil {
log.Fatal(err)
return
}
var stations []bahn.Station
if stations, err = bahn.StationsFromReader(response.Body); err != nil {
log.Fatal(err)
}
if err = response.Body.Close(); err != nil {
log.Fatal(err)
return
}
if err = returnJson(w, stations); err != nil {
log.Fatal(err)
return
}
})
http.HandleFunc("/timetable/", func(w http.ResponseWriter, r *http.Request) {
var err error
_, evaId := path.Split(r.URL.Path)
evaId = strings.TrimSpace(evaId)
var date time.Time
if date, err = time.Parse(time.RFC3339, strings.TrimSpace(r.FormValue("time"))); err != nil {
date = time.Now()
}
BahnFormat := "060102/15"
url := fmt.Sprintf("http://iris.noncd.db.de/iris-tts/timetable/plan/%s/%s", evaId, date.Format(BahnFormat))
var response *http.Response
if response, err = netClient.Get(url); err != nil {
log.Fatal(err)
return
}
var timetable bahn.Timetable
if timetable, err = bahn.TimetableFromReader(response.Body); err != nil {
log.Fatal(err)
}
if err = response.Body.Close(); err != nil {
log.Fatal(err)
return
}
if err = returnJson(w, timetable); err != nil {
log.Fatal(err)
return
}
})
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment