szczanieckiej.git

commit cd693c43562bae86e0246a5e0a46a075ed7173d5

Author: Adam <git@apiote.xyz>

query lines

 api/api.go | 79 +++++--------------------------------------
 server/router.go | 2 
 traffic/access.go | 30 ++++++++++++++++
 traffic/convert.go | 53 ++++++++++++++++++++++-------
 traffic/structs_gen.go | 16 +++++---


diff --git a/api/api.go b/api/api.go
index 72333af3ec86c830a0c82e95b5dc7d25c48ae670..743e589baf09e26c0d4a62baf6f40574eb0a2911 100644
--- a/api/api.go
+++ b/api/api.go
@@ -97,13 +97,13 @@ 	}
 	return line, nil*/
 }
 
-func convertTrafficLine(line traffic.Line, context traffic.Context, t *traffic.Traffic) (LineV1, error) {
-	l := LineV1{
+func convertTrafficLine(line traffic.Line, context traffic.Context, t *traffic.Traffic) LineV1 {
+	return LineV1{
 		Name:           line.Name,
 		Colour:         fromColor(line.Colour),
-		Kind:           LineTypeV2(line.Type), // todo(BAF10) types
-		HeadsignsThere: []string{},
-		HeadsignsBack:  []string{},
+		Kind:           makeLineTypeV2(line),
+		HeadsignsThere: line.HeadsignsThere,
+		HeadsignsBack:  line.HeadsignsBack,
 		GraphThere: LineGraphV1{
 			Stops:     []StopStubV1{},
 			NextNodes: line.GraphThere.NextNodes,
@@ -115,62 +115,6 @@ 			NextNodes: line.GraphBack.NextNodes,
 			PrevNodes: line.GraphBack.PrevNodes,
 		},
 	}
-
-	headsigns := map[string]struct{}{}
-
-	for _, lastNode := range line.GraphThere.LastNodes() {
-		code := line.GraphThere.StopCodes[lastNode-1]
-		stop, err := traffic.GetStop(traffic.ID(code), context, t)
-		if err != nil {
-			return l, fmt.Errorf("while getting stop for %s: %w", code, err)
-		}
-		for _, order := range stop.Order {
-			trip, err := traffic.GetTripByOffset(order.TripOffset, context, t) // todo batch trips; open file once
-			if err != nil {
-				return l, fmt.Errorf("while getting trip at %d: %w", order.TripOffset, err)
-			}
-			if trip.LineName == line.Name && trip.Direction == 0 {
-				headsigns[trip.Headsign] = struct{}{}
-			}
-		}
-	}
-
-	l.HeadsignsThere = make([]string, len(headsigns))
-
-	var i = 0
-	for headsign := range headsigns {
-		l.HeadsignsThere[i] = headsign
-		i++
-	}
-
-	headsigns = map[string]struct{}{}
-
-	for _, lastNode := range line.GraphBack.LastNodes() {
-		code := line.GraphBack.StopCodes[lastNode-1]
-		stop, err := traffic.GetStop(traffic.ID(code), context, t)
-		if err != nil {
-			return l, fmt.Errorf("while getting stop for %s: %w", code, err)
-		}
-		for _, order := range stop.Order {
-			trip, err := traffic.GetTripByOffset(order.TripOffset, context, t)
-			if err != nil {
-				return l, fmt.Errorf("while getting trip at %d: %w", order.TripOffset, err)
-			}
-			if trip.LineName == line.Name && trip.Direction == 1 {
-				headsigns[trip.Headsign] = struct{}{}
-			}
-		}
-	}
-
-	l.HeadsignsBack = make([]string, len(headsigns))
-
-	i = 0
-	for headsign := range headsigns {
-		l.HeadsignsBack[i] = headsign
-		i++
-	}
-
-	return l, nil
 }
 
 func convertTrafficVehicle(vehicle traffic.VehicleStatus, context traffic.Context, t *traffic.Traffic) (VehicleV1, error) {
@@ -295,14 +239,11 @@ 	for _, item := range items {
 		if stop, ok := item.(traffic.Stop); ok {
 			s := convertTrafficStopV2(stop, context.FeedName)
 			success.Queryables = append(success.Queryables, QueryableV2(s))
-		} else if _ /*line*/, ok := item.(traffic.Line); ok {
-			/*l, err := convertTrafficLine(line, context, t)
-			if err != nil {
-				return success, err
-			}
-			l.GraphThere = LineGraph{}
-			l.GraphBack = LineGraph{}
-			success.Items = append(success.Items, Item(l))*/
+		} else if line, ok := item.(traffic.Line); ok {
+			l := convertTrafficLine(line, context, t)
+			l.GraphThere = LineGraphV1{}
+			l.GraphBack = LineGraphV1{}
+			success.Queryables = append(success.Queryables, QueryableV2(l))
 		} else {
 			// todo error
 		}




diff --git a/server/router.go b/server/router.go
index da04ad0d535ffcfe19d14104742e7d4c778b5867..91eaf0f910cef201ac284ee2231c2bfc933aa0ab 100644
--- a/server/router.go
+++ b/server/router.go
@@ -375,7 +375,7 @@ 				}
 				lines, err1 := traffic.QueryLines(query, cfg.FeedsPath, feedName, versionCode, t)
 				stops, err2 := traffic.QueryStops(query, context, t)
 				if err1 != nil && err2 != nil {
-					return fmt.Errorf("while querying stops and lines: %w", err)
+					return fmt.Errorf("while querying stops and lines: %w", errors.Join(err1, err2))
 				}
 				items := []traffic.Item{}
 				for _, line := range lines {




diff --git a/traffic/access.go b/traffic/access.go
index 33406a86d15997b3bc43950c83694d54c8e5a7b7..7d8ad43e666814fc12d2cbadea67e40bf254711d 100644
--- a/traffic/access.go
+++ b/traffic/access.go
@@ -873,6 +873,36 @@ 		return r.(_Result).FeedInfo, nil
 	}
 }
 
+func GetTripsByOffset(offsets []uint, context Context, t *Traffic, filter func(Trip) bool) ([]Trip, error) {
+	trips := []Trip{}
+	file, err := os.Open(filepath.Join(context.DataHome, context.FeedName, string(context.Version), "trips.bare"))
+	if err != nil {
+		return trips, fmt.Errorf("while opening file: %w", err)
+	}
+	defer file.Close()
+
+	offsetsSet := map[uint]struct{}{}
+	for _, offset := range offsets {
+		offsetsSet[offset] = struct{}{}
+	}
+
+	for offset := range offsetsSet {
+		_, err = file.Seek(int64(offset), 0)
+		if err != nil {
+			return trips, fmt.Errorf("while seeking to %d: %w", offset, err)
+		}
+		trip := Trip{}
+		err = bare.UnmarshalReader(file, &trip)
+		if err != nil {
+			return trips, fmt.Errorf("while unmarshalling at %d: %w", offset, err)
+		}
+		if filter(trip) {
+			trips = append(trips, trip)
+		}
+	}
+	return trips, nil
+}
+
 func GetTripByOffset(offset uint, context Context, t *Traffic) (Trip, error) {
 	result := _Result{
 		Filename:      "trips.bare",




diff --git a/traffic/convert.go b/traffic/convert.go
index 6c396ee7802c771c58d66051a8b23b2b1cca28c1..f9dc74a1f211e05258595bdc80287de0db83d793 100644
--- a/traffic/convert.go
+++ b/traffic/convert.go
@@ -69,6 +69,7 @@ 	StopsCodeIndex      CodeIndex
 	StopsNameIndex      map[string][]uint
 	Stops               map[string]Stop
 	LineGraphs          map[string]map[uint]LineGraph
+	lineHeadsigns       map[string]map[uint][]string
 	LineIndex           map[string][]uint
 	ValidFrom           time.Time
 	ValidFromError      []error
@@ -482,8 +483,8 @@ 		fmt.Sscanf(record[fields["pickup_type"]], "%d", &departure.Pickup)
 		fmt.Sscanf(record[fields["drop_off_type"]], "%d", &departure.Dropoff)
 
 		tripsThroughStop[stopID] = append(tripsThroughStop[stopID], StopOrder{
-			TripID: tripID,
-			Sequence:  departure.StopSequence,
+			TripID:   tripID,
+			Sequence: departure.StopSequence,
 		})
 
 		if c.Feed.Flags().Headsign == HeadsignTripLastStop {
@@ -644,7 +645,7 @@ 		trip.LineName = lineNames[record[fields["route_id"]]]
 		fmt.Sscanf(record[fields["direction_id"]], "%d", &trip.Direction)
 
 		tripChangeOpts[trip.Id] = ChangeOption{
-			LineName:   lineNames[record[fields["route_id"]]],
+			LineName: lineNames[record[fields["route_id"]]],
 			Headsign: trip.Headsign,
 		}
 
@@ -752,7 +753,7 @@ 		for _, stopTrip := range stopTrips {
 			changeOption := tripChangeOpts[stopTrip.TripID]
 			stopOrder := StopOrder{
 				TripOffset: tripsOffsets[stopTrip.TripID],
-				Sequence:      stopTrip.Sequence,
+				Sequence:   stopTrip.Sequence,
 			}
 			stop.Order[stopTrip.TripID] = stopOrder
 			changeOptionMap[changeOption.LineName+"->"+changeOption.Headsign] = changeOption
@@ -794,15 +795,20 @@ 	}
 
 	c.StopsCodeIndex = stopsOffsetsByCode
 	c.StopsNameIndex = stopsOffsetsByName
-	c.Stops = stops  // todo map[stopID]stopCode
+	c.Stops = stops // todo map[stopID]stopCode
 	return c, nil
 }
 
-func convertLineGraphs(c feedConverter) (feedConverter, error) { // O(n:stop_times) ; (tripsOffsets, stops -- lineGrapsh:map[lineName]map[direction]graph >> )
+func convertLineGraphs(c feedConverter) (feedConverter, error) { // O(n:stop_times) ; (tripsOffsets, stops -- lineGrapsh:map[lineName]map[direction]graph, lineHeadsigns:map[lineName]map[direction][]headsigns >> )
 	path := c.TmpFeedPath
 	tripsOffsets := c.TripsOffsets
 	stops := c.Stops
 
+	//                      lineNa     dire     headsi
+	lineHeadsignsMap := map[string]map[uint]map[string]struct{}{}
+	//                   lineNa     dire   headsi
+	lineHeadsigns := map[string]map[uint][]string{}
+
 	file, err := os.Open(filepath.Join(path, "stop_times.txt"))
 	if err != nil {
 		return c, fmt.Errorf("while opening stop_times: %w", err)
@@ -849,6 +855,16 @@ 			return c, fmt.Errorf("while seeking: %w", err)
 		}
 		trip := unmarshalTripFromFile(trips)
 
+		if _, ok := lineHeadsignsMap[trip.LineName]; !ok {
+			lineHeadsignsMap[trip.LineName] = map[uint]map[string]struct{}{}
+			lineHeadsigns[trip.LineName] = map[uint][]string{}
+		}
+		if _, ok := lineHeadsignsMap[trip.LineName][trip.Direction.Value()]; !ok {
+			lineHeadsignsMap[trip.LineName][trip.Direction.Value()] = map[string]struct{}{}
+			lineHeadsigns[trip.LineName][trip.Direction.Value()] = []string{}
+		}
+		lineHeadsignsMap[trip.LineName][trip.Direction.Value()][trip.Headsign] = struct{}{}
+
 		if _, ok := graphs[trip.LineName]; !ok {
 			graphs[trip.LineName] = map[uint]LineGraph{}
 			graphs[trip.LineName][0] = LineGraph{
@@ -933,11 +949,20 @@ 	if !connectionDone {
 		g.NextNodes[previous] = append(g.NextNodes[previous], -1)
 	}
 
+	for lineName, directions := range lineHeadsignsMap {
+		for direction, headsigns := range directions {
+			for headsign := range headsigns {
+				lineHeadsigns[lineName][direction] = append(lineHeadsigns[lineName][direction], headsign)
+			}
+		}
+	}
+
+	c.lineHeadsigns = lineHeadsigns
 	c.LineGraphs = graphs
 	return c, nil
 }
 
-func convertLines(c feedConverter) (feedConverter, error) { // O(n:routes) ; (lineGraphs -- lineIndex:map[lineName][]offsets >> lines)
+func convertLines(c feedConverter) (feedConverter, error) { // O(n:routes) ; (lineGraphs, lineHeadsigns -- lineIndex:map[lineName][]offsets >> lines)
 	path := c.TmpFeedPath
 	feed := c.Feed
 
@@ -991,12 +1016,14 @@ 			colour = "ffffff"
 		}
 
 		line := Line{
-			Id:         routeID,
-			Name:       lineName,
-			Colour:     hex2colour(colour),
-			Kind:       LineType(kind),
-			GraphThere: c.LineGraphs[lineName][0],
-			GraphBack:  c.LineGraphs[lineName][1],
+			Id:             routeID,
+			Name:           lineName,
+			Colour:         hex2colour(colour),
+			Kind:           LineType(kind),
+			GraphThere:     c.LineGraphs[lineName][0],
+			GraphBack:      c.LineGraphs[lineName][1],
+			HeadsignsThere: c.lineHeadsigns[lineName][0],
+			HeadsignsBack:  c.lineHeadsigns[lineName][1],
 		}
 
 		if field, present := fields["agency_id"]; present {




diff --git a/traffic/structs_gen.go b/traffic/structs_gen.go
index 2a2cbfda15f7555fde3f08e186f79c976416f1e8..fb8859898c4991e4c4fc50ce4f4cb0bb5a13738f 100644
--- a/traffic/structs_gen.go
+++ b/traffic/structs_gen.go
@@ -144,13 +144,15 @@ 	return bare.Marshal(t)
 }
 
 type Line struct {
-	Id         string    `bare:"id"`
-	Name       string    `bare:"name"`
-	Colour     Colour    `bare:"colour"`
-	Kind       LineType  `bare:"kind"`
-	AgencyID   string    `bare:"agencyID"`
-	GraphThere LineGraph `bare:"graphThere"`
-	GraphBack  LineGraph `bare:"graphBack"`
+	Id             string    `bare:"id"`
+	Name           string    `bare:"name"`
+	Colour         Colour    `bare:"colour"`
+	Kind           LineType  `bare:"kind"`
+	AgencyID       string    `bare:"agencyID"`
+	HeadsignsThere []string  `bare:"headsignsThere"`
+	HeadsignsBack  []string  `bare:"headsignsBack"`
+	GraphThere     LineGraph `bare:"graphThere"`
+	GraphBack      LineGraph `bare:"graphBack"`
 }
 
 func (t *Line) Decode(data []byte) error {