diff --git a/main.go b/main.go index 182eb2b..bf8145e 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( "log" "os" "regexp" + "sort" "strconv" ) @@ -72,6 +73,11 @@ type OsmDocument struct { Ways []OsmWay `xml:"way"` } +type CombiPair struct { + Departure *OsmRelation + Return *OsmRelation +} + type CombiData struct { // Number that succeedes the Company name. // E.g.: 7 @@ -83,9 +89,10 @@ type CombiData struct { // E.g.: "Combi B: Villa Santa Rosa -> Terminal Terrestre" Name string // Indentifiable name of the route - Ref string - Color string - Members *[]OsmMember + Ref string + Color string + Members *[]OsmMember + ReturnMembers *[]OsmMember } // Type to be consumed by the client @@ -128,36 +135,13 @@ func main() { nodesMap[node.Id] = &node } - // Get the relation with id 17642638, - // that relation hosts the SIT data - var sitId int64 = 17642638 - var sitRelation *OsmRelation - for _, relation := range osmDocument.Relations { - if relation.Id == sitId { - sitRelation = &relation - } - } - if sitRelation == nil { - panic("SIT relation with id 17642638 not found!") - } - - // (naively) get all the submembers - sitMembers := make([]OsmRelation, 0) - for _, member := range sitRelation.Members { - // search the member - // TODO: determine if this is a performance bottleneck, - // and requires optimization (insertion sort & binary search) - for _, relation := range osmDocument.Relations { - if relation.Id == member.Ref { - sitMembers = append(sitMembers, relation) - } - } - } + // Contains a list of pairs of relations + sitMembers := getSITRelations(&osmDocument) // transform sitMembers into CombiData combis := make([]CombiData, 0) - for _, member := range sitMembers { - combis = append(combis, parseCombiData(member)) + for _, memberPair := range sitMembers { + combis = append(combis, parseCombiData(memberPair)) } // Create a map from string to CombiLine @@ -188,11 +172,10 @@ func main() { // Convert each CombiData into a CombiRoute and store it for _, combi := range combis { log.Printf("Processing %s %s", combi.Company, combi.Ref) - returnCoord := make([][]float64, 0) combiRoute := CombiRoute{ Name: combi.Ref, Departure: getCoordinates(&osmDocument.Ways, nodesMap, combi.Members), - Return: &returnCoord, + Return: getCoordinates(&osmDocument.Ways, nodesMap, combi.ReturnMembers), } combiLineSlice := combiRoutesMap[combi.Id] combiLineSlice.Routes = append(combiLineSlice.Routes, combiRoute) @@ -348,6 +331,10 @@ func writeOutput(lines map[string]*CombiLine, routes map[int]*CombiRouteContaine for _, combiLine := range lines { combiLineSlice = append(combiLineSlice, combiLine) } + // sort the map + sort.Slice(combiLineSlice, func(i, j int) bool { + return combiLineSlice[i].Id < combiLineSlice[j].Id + }) // print JSON jsonBytes, err := json.Marshal(combiLineSlice) @@ -361,6 +348,9 @@ func writeOutput(lines map[string]*CombiLine, routes map[int]*CombiRouteContaine // for lineId, routeContainer := range routes { outFilename := fmt.Sprintf("output/routes_%d.json", lineId) + sort.Slice(routeContainer.Routes, func(i, j int) bool { + return routeContainer.Routes[i].Name < routeContainer.Routes[j].Name + }) jsonBytes, err := json.Marshal(routeContainer.Routes) if err != nil { panic(nil) @@ -375,7 +365,10 @@ func writeOutput(lines map[string]*CombiLine, routes map[int]*CombiRouteContaine log.Print("JSON files written to output/") } -func parseCombiData(member OsmRelation) CombiData { +func parseCombiData(combiPair *CombiPair) CombiData { + member := combiPair.Departure + returnMember := combiPair.Return + var operatorTag *OsmTag var nameTag *OsmTag var refTag *OsmTag @@ -414,15 +407,107 @@ func parseCombiData(member OsmRelation) CombiData { } return CombiData{ - Id: parseLineId(operatorTag.V), - Company: operatorTag.V, - Name: nameTag.V, - Ref: refTag.V, - Color: colorTag.V, - Members: &member.Members, + Id: parseLineId(operatorTag.V), + Company: operatorTag.V, + Name: nameTag.V, + Ref: refTag.V, + Color: colorTag.V, + Members: &member.Members, + ReturnMembers: &returnMember.Members, } } +// Finds all the relations that are related to the SIT +func getSITRelations(osmDocument *OsmDocument) []*CombiPair { + // a route is identified by the pair operator,ref + + // how to know which route is the departure, + // and which is the return? + // each district (route, color) is assigned to a district/area + // in arequipa, + // so to know which way is each route, we compare the first + // coordinate against a coordinate located at that district + // TODO: implement that + + // map of maps + // map of `operator` to (map of `ref` to OsmRelation) + operators := make(map[string]*(map[string]*[]*OsmRelation)) + + // search, filter and store + for _, r := range osmDocument.Relations { + if r.Tag("type", "route") && r.Tag("route", "bus") && r.Tag("network", "SIT") { + operator := r.GetTag("operator") + ref := r.GetTag("ref") + + // check if operator map exists, create if not + operatorMap := operators[operator] + if operatorMap == nil { + newMap := make(map[string]*[]*OsmRelation) + operatorMap = &newMap + operators[operator] = &newMap + } + + // check if ref exists, create if not + refs := (*operatorMap)[ref] + if refs == nil { + refsArr := make([]*OsmRelation, 0) + refs = &refsArr + (*operatorMap)[ref] = &refsArr + } + + // insert + *refs = append(*refs, &r) + } + } + + // map and filter + pairs := make([]*CombiPair, 0) + for operatorKey, operator := range operators { + for refKey, relationSlice := range *operator { + if len(*relationSlice) != 2 { + log.Printf("operator(%s) ref(%s): expected 2 ref elements, found %d", operatorKey, refKey, len(*relationSlice)) + continue + } + + // AYAYA! + newPair := CombiPair{ + Departure: (*relationSlice)[0], + Return: (*relationSlice)[1], + } + pairs = append(pairs, &newPair) + } + } + + return pairs +} + +func (r *OsmRelation) HasTag(tagName string) bool { + for _, tag := range r.Tags { + if tag.K == tagName { + return true + } + } + return false +} + +func (r *OsmRelation) Tag(tagName string, tagValue string) bool { + for _, tag := range r.Tags { + if tag.K == tagName && tag.V == tagValue { + return true + } + } + return false +} + +func (r *OsmRelation) GetTag(tagName string) string { + for _, tag := range r.Tags { + if tag.K == tagName { + return tag.V + } + } + panic(fmt.Sprintf("Tried to get, from relation(%d), tag %s, but it was not found", r.Id, tagName)) +} + // Extracts the id from a line name. // E.g.: "C11 - Cotum" -> 11 func parseLineId(lineName string) int {