Hey there! 👋 Diving into my latest project, I'm all about wrangling data to shed some light on the TEC network. We're mixing theory with the thrill of real-time data. In this kickoff blog, we're not jumping straight into analysis. Instead, we'll lay the groundwork with the data itself. Down the road, I'm planning to craft a user-friendly, real-time app and eventually deep-dive into analyzing TEC's performance. Let's roll!
  
Decision Time: Python or Rust? Rust won. It's all about that performance edge. Python's cool, but Rust? It's the speed demon I needed.
Tools of the Trade: Initially tried reqwest, but hit a snag with memory leaks. Switched to ureq and kept rust-protobuf for data parsing.
Ready to decode: Now that we have data and a parser, it's time to decode the protobuf file. Here's the result:
  
Tech Stack: Eyeing an app alongside the analysis, I leaned on nextjs – I'm pretty comfy with React.
Initial Hurdle: The real puzzle was figuring out the best way to visually represent our data. After some brainstorming, I decided on deck.gl for creating a massive, interactive map of Wallonia with WebGL for performance. But that wasn't all – I also realized the need for a robust sharing component on the backend.
  
Breaking Through: Axum to the rescue! Already familiar with it and its websocket support, plus it's a performance powerhouse. I've added a websocket endpoint /ws that sends the data every 5 seconds to the user. A bit of coding (and coffee!) later, and I've got real-time bus positions streaming in. 
First Glimpse: Managed to display a Wallonia-wide map showcasing bus locations. It's a work in progress, but hey, it's a start.
  
Tweaking Data: Initially, the data was chomping up bandwidth (100-200kb every 5 seconds). Not terrible, but could be better. Solution? Shrinking data size with base64, then compressing with gzip. It's a start – reduced the data size by 10-20x.
Client side:
ws.onmessage = (message) => {
    try {
        let ds = new DecompressionStream("gzip");
        let decompressedStream = message.data.stream().pipeThrough(ds);
        new Response(decompressedStream).text().then(e => {
            const data = JSON.parse(atob(e))
            setBuses(data)
        })
    } catch (e) {
        console.log(e)
    }
}Server side:
loop {
    let datas = store.retrieve_json().await;
    let message = general_purpose::STANDARD.encode(&datas);
    let message = match compress_string(&message) {
        Ok(message) => message,
        Err(e) => {
            println!("Error compressing message: {}", e);
            return;
        }
    };
    let message = Message::Binary(message);
    if socket.send(message).await.is_err() {
        println!("Error sending message");
        return;
    }
    sleep(std::time::Duration::from_secs(5)).await
}Future Thoughts: Sending all the data isn't the most efficient. Perhaps a quadtree approach for just viewport data could cut down transmission load. Food for thought, but not the current focus.