2024-09-10 15:48:19 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2024-09-10 17:00:28 +00:00
|
|
|
"encoding/json"
|
2024-09-10 15:48:19 +00:00
|
|
|
"encoding/xml"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"regexp"
|
2024-09-10 17:00:28 +00:00
|
|
|
"strconv"
|
2024-09-10 15:48:19 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type OsmBounds struct {
|
|
|
|
MinLat float64 `xml:"minlat,attr"`
|
|
|
|
MinLon float64 `xml:"minlon,attr"`
|
|
|
|
MaxLat float64 `xml:"maxlat,attr"`
|
|
|
|
MaxLon float64 `xml:"maxlon,attr"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type OsmNode struct {
|
|
|
|
Id int64 `xml:"id,attr"`
|
|
|
|
Lat float64 `xml:"lat,attr"`
|
|
|
|
Lon float64 `xml:"lon,attr"`
|
|
|
|
Version int64 `xml:"version,attr"`
|
|
|
|
Timestamp string `xml:"timestamp,attr"`
|
|
|
|
Changeset int64 `xml:"changeset,attr"`
|
|
|
|
Uid int64 `xml:"uid,attr"`
|
|
|
|
User string `xml:"user,attr"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type OsmMember struct {
|
|
|
|
Type string `xml:"type,attr"`
|
|
|
|
Ref int64 `xml:"ref,attr"`
|
|
|
|
Role string `xml:"role,attr"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type OsmTag struct {
|
|
|
|
K string `xml:"k,attr"`
|
|
|
|
V string `xml:"v,attr"`
|
|
|
|
}
|
|
|
|
|
2024-09-10 17:00:28 +00:00
|
|
|
type OsmWay struct {
|
|
|
|
Id int64 `xml:"id,attr"`
|
|
|
|
Version int64 `xml:"version,attr"`
|
|
|
|
Timestamp string `xml:"timestamp,attr"`
|
|
|
|
Changeset int64 `xml:"changeset,attr"`
|
|
|
|
Uid int64 `xml:"uid,attr"`
|
|
|
|
User string `xml:"user,attr"`
|
|
|
|
Nds []OsmNd `xml:"nd"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type OsmNd struct {
|
|
|
|
Ref int64 `xml:"ref,attr"`
|
|
|
|
}
|
|
|
|
|
2024-09-10 15:48:19 +00:00
|
|
|
type OsmRelation struct {
|
|
|
|
Id int64 `xml:"id,attr"`
|
|
|
|
Version int32 `xml:"version,attr"`
|
|
|
|
Timestamp string `xml:"timestamp,attr"`
|
|
|
|
Changeset int64 `xml:"changeset,attr"`
|
|
|
|
Uid int64 `xml:"uid,attr"`
|
|
|
|
User string `xml:"user,attr"`
|
|
|
|
Members []OsmMember `xml:"member"`
|
|
|
|
Tags []OsmTag `xml:"tag"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type OsmDocument struct {
|
|
|
|
Note string `xml:"note"`
|
|
|
|
Bounds OsmBounds `xml:"bounds"`
|
|
|
|
Nodes []OsmNode `xml:"node"`
|
|
|
|
Relations []OsmRelation `xml:"relation"`
|
2024-09-10 17:00:28 +00:00
|
|
|
Ways []OsmWay `xml:"way"`
|
2024-09-10 15:48:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type CombiData struct {
|
2024-09-10 17:00:28 +00:00
|
|
|
// Number that succeedes the Company name.
|
|
|
|
// E.g.: 7
|
|
|
|
Id int
|
|
|
|
// Name of the operator of the route.
|
|
|
|
// E.g.: "C7 - AqpMasivo"
|
2024-09-10 15:48:19 +00:00
|
|
|
Company string
|
2024-09-10 17:00:28 +00:00
|
|
|
// Name of the route
|
|
|
|
// E.g.: "Combi B: Villa Santa Rosa -> Terminal Terrestre"
|
2024-09-10 15:48:19 +00:00
|
|
|
Name string
|
2024-09-10 17:00:28 +00:00
|
|
|
// Indentifiable name of the route
|
|
|
|
Ref string
|
|
|
|
Color string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Type to be consumed by the client
|
|
|
|
// Represents a single company
|
|
|
|
type CombiLine struct {
|
|
|
|
Id int `json:"id"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
District string `json:"district"`
|
|
|
|
Color string `json:"color"`
|
|
|
|
}
|
|
|
|
|
2024-09-10 17:26:44 +00:00
|
|
|
type CombiRouteContainer struct {
|
|
|
|
Routes []CombiRoute
|
|
|
|
}
|
|
|
|
|
2024-09-10 17:00:28 +00:00
|
|
|
type CombiRoute struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
Departure []float64 `json:"departure"`
|
|
|
|
Return []float64 `json:"return"`
|
2024-09-10 15:48:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
log.Println("Begin processing")
|
|
|
|
|
|
|
|
xmlData, err := os.ReadFile("aqp_map.xml")
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var osmDocument OsmDocument
|
|
|
|
err = xml.Unmarshal(xmlData, &osmDocument)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
log.Println("XML unmarshal finished")
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// transform sitMembers into CombiData
|
|
|
|
combis := make([]CombiData, 0)
|
|
|
|
for _, member := range sitMembers {
|
2024-09-10 17:00:28 +00:00
|
|
|
combis = append(combis, parseCombiData(member))
|
|
|
|
}
|
2024-09-10 15:48:19 +00:00
|
|
|
|
2024-09-10 17:00:28 +00:00
|
|
|
// Create a map from string to CombiLine
|
|
|
|
combiLineMap := make(map[string]*CombiLine)
|
2024-09-10 17:26:44 +00:00
|
|
|
// Create a map for CombiRoute, grouped by CombiLine.Id
|
|
|
|
combiRoutesMap := make(map[int]*CombiRouteContainer)
|
2024-09-10 17:00:28 +00:00
|
|
|
|
|
|
|
// Populate the map
|
|
|
|
for _, combi := range combis {
|
|
|
|
combiLine := combiLineMap[combi.Company]
|
|
|
|
if combiLine == nil {
|
|
|
|
// create the company in the map
|
|
|
|
combiLine := CombiLine{
|
|
|
|
Id: combi.Id,
|
|
|
|
Name: combi.Company,
|
|
|
|
District: "",
|
|
|
|
Color: combi.Color,
|
2024-09-10 15:48:19 +00:00
|
|
|
}
|
2024-09-10 17:00:28 +00:00
|
|
|
combiLineMap[combi.Company] = &combiLine
|
2024-09-10 17:26:44 +00:00
|
|
|
|
|
|
|
// create the route container
|
|
|
|
combiRoutesMap[combi.Id] = &CombiRouteContainer{
|
|
|
|
Routes: make([]CombiRoute, 0),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert each CombiData into a CombiRoute and store it
|
|
|
|
for _, combi := range combis {
|
|
|
|
combiRoute := CombiRoute{
|
|
|
|
Name: combi.Ref,
|
|
|
|
Departure: make([]float64, 0),
|
|
|
|
Return: make([]float64, 0),
|
2024-09-10 15:48:19 +00:00
|
|
|
}
|
2024-09-10 17:26:44 +00:00
|
|
|
combiLineSlice := combiRoutesMap[combi.Id]
|
|
|
|
combiLineSlice.Routes = append(combiLineSlice.Routes, combiRoute)
|
2024-09-10 17:00:28 +00:00
|
|
|
}
|
2024-09-10 15:48:19 +00:00
|
|
|
|
2024-09-10 17:26:44 +00:00
|
|
|
writeOutput(combiLineMap, combiRoutesMap)
|
|
|
|
}
|
|
|
|
|
|
|
|
func writeOutput(lines map[string]*CombiLine, routes map[int]*CombiRouteContainer) {
|
|
|
|
// Create output folder
|
|
|
|
os.MkdirAll("output", os.ModePerm)
|
|
|
|
|
|
|
|
//
|
|
|
|
// Write the lines JSON file
|
|
|
|
//
|
|
|
|
|
2024-09-10 17:00:28 +00:00
|
|
|
// convert the map into an array
|
|
|
|
combiLineSlice := make([]*CombiLine, 0)
|
2024-09-10 17:26:44 +00:00
|
|
|
for _, combiLine := range lines {
|
2024-09-10 17:00:28 +00:00
|
|
|
combiLineSlice = append(combiLineSlice, combiLine)
|
|
|
|
}
|
|
|
|
|
|
|
|
// print JSON
|
2024-09-10 17:26:44 +00:00
|
|
|
jsonBytes, err := json.Marshal(combiLineSlice)
|
2024-09-10 17:00:28 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2024-09-10 17:26:44 +00:00
|
|
|
os.WriteFile("output/lines.json", jsonBytes, 0644)
|
|
|
|
|
|
|
|
//
|
|
|
|
// Write each JSON file for the routes
|
|
|
|
//
|
|
|
|
for lineId, routeContainer := range routes {
|
|
|
|
outFilename := fmt.Sprintf("output/routes_%d.json", lineId)
|
|
|
|
jsonBytes, err := json.Marshal(routeContainer.Routes)
|
|
|
|
if err != nil {
|
|
|
|
panic(nil)
|
|
|
|
}
|
|
|
|
// write
|
|
|
|
err = os.WriteFile(outFilename, jsonBytes, 0644)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Print("JSON files written to output/")
|
2024-09-10 17:00:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func parseCombiData(member OsmRelation) CombiData {
|
|
|
|
var operatorTag *OsmTag
|
|
|
|
var nameTag *OsmTag
|
|
|
|
var refTag *OsmTag
|
|
|
|
var colorTag *OsmTag
|
|
|
|
|
|
|
|
for _, tag := range member.Tags {
|
|
|
|
if tag.K == "operator" {
|
|
|
|
operatorTag = &tag
|
|
|
|
continue
|
2024-09-10 15:48:19 +00:00
|
|
|
}
|
2024-09-10 17:00:28 +00:00
|
|
|
if tag.K == "name" {
|
|
|
|
nameTag = &tag
|
|
|
|
continue
|
2024-09-10 15:48:19 +00:00
|
|
|
}
|
2024-09-10 17:00:28 +00:00
|
|
|
if tag.K == "ref" {
|
|
|
|
refTag = &tag
|
|
|
|
continue
|
2024-09-10 15:48:19 +00:00
|
|
|
}
|
2024-09-10 17:00:28 +00:00
|
|
|
if tag.K == "colour" {
|
|
|
|
colorTag = &tag
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
2024-09-10 15:48:19 +00:00
|
|
|
|
2024-09-10 17:00:28 +00:00
|
|
|
if operatorTag == nil {
|
|
|
|
log.Fatalf("Found a SIT member without an operator tag, with id %d\n", member.Id)
|
|
|
|
}
|
|
|
|
if nameTag == nil {
|
|
|
|
log.Fatalf("Found a SIT member without a name tag, with id %d\n", member.Id)
|
|
|
|
}
|
|
|
|
if refTag == nil {
|
|
|
|
log.Fatalf("Found a SIT member without a ref tag, with id %d\n", member.Id)
|
|
|
|
}
|
|
|
|
if colorTag == nil {
|
|
|
|
log.Fatalf("Found a SIT member without a colour tag, with id %d\n", member.Id)
|
2024-09-10 15:48:19 +00:00
|
|
|
}
|
|
|
|
|
2024-09-10 17:00:28 +00:00
|
|
|
return CombiData{
|
|
|
|
Id: parseLineId(operatorTag.V),
|
|
|
|
Company: operatorTag.V,
|
|
|
|
Name: nameTag.V,
|
|
|
|
Ref: refTag.V,
|
|
|
|
Color: colorTag.V,
|
2024-09-10 15:48:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-10 17:00:28 +00:00
|
|
|
// Extracts the id from a line name.
|
|
|
|
// E.g.: "C11 - Cotum" -> 11
|
|
|
|
func parseLineId(lineName string) int {
|
|
|
|
regex := regexp.MustCompile("C(\\d+) - .+")
|
|
|
|
groups := regex.FindStringSubmatch(lineName)
|
|
|
|
if groups == nil {
|
|
|
|
panic(fmt.Sprintf("Found an invalid line name (doesnt match format): %s", lineName))
|
2024-09-10 15:48:19 +00:00
|
|
|
}
|
2024-09-10 17:00:28 +00:00
|
|
|
|
|
|
|
number, err := strconv.Atoi(groups[1])
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
2024-09-10 15:48:19 +00:00
|
|
|
}
|
|
|
|
|
2024-09-10 17:00:28 +00:00
|
|
|
return number
|
2024-09-10 15:48:19 +00:00
|
|
|
}
|