penguin/golifehk

@golifehk bot in Telegram

datasources/mtr/bus/busstops.go

raw ยท 2673 bytes

package bus

import (
	"encoding/csv"
	"fmt"
	"io"
	"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.Longitude = 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])
			}
		}

		entryId := fmt.Sprintf("%s:%s", entry.ReferenceId, entry.StationId)

		if busStops[entryId] != nil {
			return nil, fmt.Errorf("Duplicated entry %+v", entry)
		}

		routeDir, hasKey := routeStops[entry.ReferenceId]
		if !hasKey {
			routeStops[entry.ReferenceId] = map[string]map[int]*BusStop{}
			routeDir = routeStops[entry.ReferenceId]
		}

		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[entryId] = &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

}