Skip to content

Commit de10ba0

Browse files
TheLawrusMCLiiisbasu107
authored
Google maps (#79)
* google maps wip * deleted maps api key and regenerated * show data on google maps, need to implement selector * Working selector * add range bar and add center dot * added new test drive file * refined DataReplayer with menu and time estimation * made UI improvements for maps * minor changes --------- Co-authored-by: Mingcan Li <mli727@wisc.edu> Co-authored-by: sbasu107 <sbasu107@gmail.com>
1 parent f08204d commit de10ba0

18 files changed

Lines changed: 29233 additions & 89 deletions

File tree

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from fastapi import APIRouter
2-
from . import graph_api, record_data
2+
from . import graph_api, record_data, maps_api
33

44
router = APIRouter()
55

66
router.include_router(graph_api.router)
7-
router.include_router(record_data.router)
7+
router.include_router(record_data.router)
8+
router.include_router(maps_api.router)

Backend/components/maps_api.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from core import db
2+
from fastapi import APIRouter
3+
import time
4+
import numpy as np
5+
import config
6+
import asyncio
7+
import pandas as pd
8+
9+
router = APIRouter()
10+
11+
12+
def hsv_to_rgb(hsv):
13+
h, s, v = hsv
14+
i = int(h * 6)
15+
f = h * 6 - i
16+
p = v * (1 - s)
17+
q = v * (1 - f * s)
18+
t = v * (1 - (1 - f) * s)
19+
switch = {
20+
0: (v, t, p),
21+
1: (q, v, p),
22+
2: (p, v, t),
23+
3: (p, q, v),
24+
4: (t, p, v),
25+
5: (v, p, q)
26+
}
27+
r, g, b = switch[i % 6]
28+
return "#{:02x}{:02x}{:02x}".format(int(r * 255), int(g * 255), int(b * 255))
29+
30+
async def calculate_color(data_name, value):
31+
# Calculate the color value for each segment of the polyline using the value range from dataformat
32+
min_val = config.FORMAT[data_name]["min"]
33+
max_val = config.FORMAT[data_name]["max"]
34+
35+
if max_val - min_val == 0:
36+
if config.FORMAT[data_name]["type"] == bool:
37+
return [hsv_to_rgb((val * 0.375, 1, 1)) for val in value]
38+
else:
39+
return ["#aaaaaa"] * (len(value)-1)
40+
colors = []
41+
for i in range(len(value) - 1):
42+
mean = (value[i] + value[i + 1]) / 2
43+
# Calculate percentage within the value range
44+
percentage = max(min((mean - min_val) / (max_val - min_val), 1), 0)
45+
# Map to HSV from 0 to 120 for transition from green to red
46+
colors.append(hsv_to_rgb((percentage * 0.375, 1, 1)))
47+
return colors
48+
49+
@router.get("/maps")
50+
async def get_maps(data: str, duration: int = 60):
51+
"""pass in a datafield to get and return the coordinates with the value of the data of each timestamp"""
52+
if not data:
53+
return {"coords": [], "data": []}
54+
55+
end_time = int(time.time()*1000)
56+
start_time = end_time - duration*1000
57+
58+
all_data = await db.query_without_aggregation(["lat", "lon", data], start_time, end_time)
59+
60+
# Filter out rows with lat or lon equal to -1000, -1001, or 0
61+
all_data = all_data[~((all_data['lat'] == -1000) |
62+
(all_data['lat'] == -1001) |
63+
(all_data['lat'] == 0) |
64+
(all_data['lon'] == -1000) |
65+
(all_data['lon'] == -1001) |
66+
(all_data['lon'] == 0))]
67+
all_data = all_data.drop_duplicates()
68+
69+
all_data.index = pd.to_datetime(all_data.index, unit='ms')
70+
sample_freq = duration * 5 # get 100 samples max
71+
all_data = all_data.resample(f"{sample_freq}ms").mean().dropna()
72+
color = await calculate_color(data, list(all_data[data]))
73+
74+
return {
75+
"coords": [{"lat": d["lat"], "lng": d["lon"]} for _, d in all_data.iterrows()],
76+
"color": color
77+
}

Backend/core/comms.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from os import getpid
12
import json, select, socket, struct, sys, time
23
import threading
34
import traceback
@@ -31,7 +32,7 @@ def set_format(file_path: str):
3132
format_string += types[data_format[key][1]]
3233
byte_length += data_format[key][0]
3334
properties.append(key)
34-
config.FORMAT[key] = {'type': data_format[key][1]}
35+
config.FORMAT[key] = {'type': data_format[key][1], 'min': data_format[key][3], 'max': data_format[key][4]}
3536

3637
def unpack_data(data):
3738
fields = {}

Backend/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ async def startup():
1313
if __name__ == '__main__':
1414
uvicorn.run(app='main:app', host="0.0.0.0", port=config.HOST_PORT)
1515

16-
16+

DataReplayer/README.md

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ The DataReplayer reads recorded CSV data and replays it by sending UDP packets t
44

55
## Features
66

7-
- Loads CSV data from `raw_data/2024-4-7rawdata.csv`
7+
- **Interactive CSV file selection** with file size and drive duration information
8+
- **Command-line support** for quick file selection
9+
- **Drive duration analysis** - automatically calculates drive time from timestamps
810
- Parses CSV headers and maps them to the solar car data format
911
- **Generates current timestamps** for live graphing compatibility
1012
- Sends data packets via UDP at 31Hz (32ms intervals) to match real-time telemetry
@@ -13,24 +15,57 @@ The DataReplayer reads recorded CSV data and replays it by sending UDP packets t
1315

1416
## Usage
1517

16-
1. Make sure you have the CSV file in `raw_data/2024-4-7rawdata.csv`
17-
2. Navigate to the DataReplayer directory and run:
18-
```bash
19-
cd DataReplayer
20-
npm start
21-
```
22-
3. The replayer will:
23-
- Load all CSV data into memory
24-
- Start sending UDP packets to `localhost:4003`
25-
- Display progress updates every 100 packets
18+
### Interactive Mode (with menu)
19+
Navigate to the DataReplayer directory and run:
20+
```bash
21+
cd DataReplayer
22+
npm start
23+
```
24+
25+
This will show an interactive menu with:
26+
- All available CSV files in the `raw_data` directory
27+
- File sizes (in MB)
28+
- Drive durations (calculated from timestamps)
29+
- Arrow key navigation for selection
30+
31+
Example menu:
32+
```
33+
Data Replayer
34+
=====================================
35+
Select a CSV file to replay:
36+
37+
? Choose CSV file: (Use arrow keys)
38+
❯ 2024-4-7rawdata.csv (71.28 MB, 4h 15m)
39+
coasts2024-04-141530.csv (21.54 MB, 23m)
40+
```
41+
42+
### Command-line Mode (skip menu)
43+
For quick access when you know the filename:
44+
```bash
45+
npm start filename.csv
46+
npm start coasts2024-04-141530.csv
47+
```
48+
49+
This will:
50+
- Skip the interactive menu
51+
- Show file information (size and duration)
52+
- Start replaying immediately
53+
54+
### Getting Help
55+
```bash
56+
npm run help
57+
```
2658

2759
## Installation
2860

29-
No additional dependencies are needed since the DataReplayer uses only Node.js built-in modules:
30-
- `fs` - File system operations
31-
- `readline` - CSV parsing
32-
- `dgram` - UDP socket communication
33-
- `buffer` - Binary data handling
61+
Install dependencies:
62+
```bash
63+
npm install
64+
```
65+
66+
Required dependencies:
67+
- `inquirer` - Interactive CLI menus
68+
- Node.js built-in modules: `fs`, `readline`, `dgram`, `buffer`
3469

3570
## Data Flow
3671

@@ -39,12 +74,23 @@ CSV File → Parse → Binary Buffer → UDP Packet → Backend (port 4003)
3974
```
4075

4176
The replayer:
77+
- **Analyzes CSV files** to determine drive duration and file size
4278
- Reads the CSV headers and maps them to the solar car data format
4379
- **Replaces old timestamps with current time** to enable live graphing
4480
- Converts each row to a binary buffer using the same format as DataGenerator
4581
- Sends packets at the same rate as live telemetry (31Hz)
4682
- Loops through the dataset continuously
4783

84+
## Drive Duration Analysis
85+
86+
The DataReplayer automatically calculates drive duration by:
87+
1. Reading the first timestamp from the `Var1` column (first data row)
88+
2. Reading the last timestamp from the `Var1` column (last data row)
89+
3. Computing the difference and converting to hours and minutes
90+
4. Displaying in format: `4h 15m` or `23m` (if less than 1 hour)
91+
92+
This helps you understand the scope of each dataset before selecting it for replay.
93+
4894
## Timestamp Handling
4995

5096
The DataReplayer automatically replaces timestamp fields from the CSV with current timestamps:

0 commit comments

Comments
 (0)