package bus import ( "encoding/csv" "fmt" "io" "log" "net/http" "path/filepath" "strconv" query "github.com/tgckpg/golifehk/query" "github.com/tgckpg/golifehk/utils" ) var CSV_BUSSTOPS string = filepath.Join(utils.WORKDIR, "mtr_bus_stops.csv") func readBusStopData(r io.Reader) (*map[string]*BusStop, error) { reader := csv.NewReader(r) entries, err := reader.ReadAll() if err != nil { return nil, err } busStops := map[string]*BusStop{} routeStops := map[string]map[string]map[int]*BusStop{} var headers []string for i, line := range entries { if i == 0 { headers = line continue } var entry BusStop for j, value := range line { switch headers[j] { case "ROUTE_ID": entry.RouteId = value case "DIRECTION": entry.Direction = value case "STATION_SEQNO": v, _ := strconv.ParseFloat(value, 64) entry.StationSeq = int(v) case "STATION_ID": entry.StationId = value case "STATION_LATITUDE": v, _ := strconv.ParseFloat(value, 64) entry.Latitude = v case "STATION_LONGITUDE": v, _ := strconv.ParseFloat(value, 64) entry.Longtitude = v case "STATION_NAME_CHI": entry.Name_zh = value case "STATION_NAME_ENG": entry.Name_en = value case "REFERENCE_ID": entry.ReferenceId = value default: return nil, fmt.Errorf("Unknown header \"%s\"", headers[j]) } } // Ignoring special route for now as getSchedule does not reflect // which bus is a special route if entry.ReferenceId != entry.RouteId { log.Printf("Ignoring special Route: %s", entry.ReferenceId) continue } if busStops[entry.StationId] != nil { return nil, fmt.Errorf("Duplicated entry %+v", entry) } routeDir, hasKey := routeStops[entry.RouteId] if !hasKey { routeStops[entry.RouteId] = map[string]map[int]*BusStop{} routeDir = routeStops[entry.RouteId] } route, hasKey := routeDir[entry.Direction] if !hasKey { routeDir[entry.Direction] = map[int]*BusStop{} route = routeDir[entry.Direction] } _, hasKey = route[entry.StationSeq] if !hasKey { route[entry.StationSeq] = &entry } entry.RouteStops = &route entry.Reload() busStops[entry.StationId] = &entry } return &busStops, nil } func getBusStops() (*[]query.ISearchable, error) { QUERY_FUNC := func() (io.ReadCloser, error) { resp, err := http.Get("https://opendata.mtr.com.hk/data/mtr_bus_stops.csv") if err != nil { return nil, err } return resp.Body, nil } buff, err := utils.CacheStream(CSV_BUSSTOPS, QUERY_FUNC, 7*24*3600) if err != nil { return nil, err } utils.ReadBOM(buff) busStopMap, err := readBusStopData(buff) if err != nil { return nil, err } searchables := []query.ISearchable{} for _, busStop := range *busStopMap { searchables = append(searchables, busStop) } return &searchables, nil }