Skip to content

Lauraponoran/Reflector-Ride-Maps-V2

Β 
Β 

Repository files navigation

🚴 Reflector Ride Maps

A bike sensor data visualization tool that processes GPS and wheel rotation data to display bike trips with speed-colored routes on an interactive map.

πŸ“Š Overview

This project takes raw CSV files from bike sensors and transforms them into:

  • Individual trip visualizations with speed-colored segments
  • Aggregated route maps showing average speeds across multiple trips
  • Interactive web visualization powered by MapLibre GL JS

πŸ—‚οΈ Project Structure

Reflector-Ride-Maps/
β”œβ”€β”€ csv_data/                       # Raw CSV files from sensors (you create this)
β”œβ”€β”€ sensor_data/                    # Cleaned GeoJSON files (generated)
β”œβ”€β”€ processed_sensor_data/          # Speed-calculated trips (generated)
β”œβ”€β”€ public/
β”‚   β”œβ”€β”€ trips.pmtiles               # Compressed trip data for map
β”œβ”€β”€ csv_to_geojson_converter.py     # Step 1: Convert CSVs to GeoJSON
β”œβ”€β”€ combined_processor.py           # Step 2: Calculate speeds from sensor data
β”œβ”€β”€ build_pmtiles.py                # Step 3: Build PMTiles for web
β”œβ”€β”€ index.html                      # Main visualization page
β”œβ”€β”€ app.js                          # Map logic and interactions
β”œβ”€β”€ config.js                       # Configuration (uses .env)
β”œβ”€β”€ styles.css                      # Styling
β”œβ”€β”€ vite.config.js                  # Vite bundler config
β”œβ”€β”€ package.json                    # Node dependencies
└── .env                            # Your Mapbox token (keep secret!)

πŸš€ Quick Start

Prerequisites

  • Python 3.x for data processing
  • Node.js & npm for web development
  • Tippecanoe for PMTiles generation:
    brew install tippecanoe  # macOS
  • MapLibre token (free at mapbox.com)

Installation

  1. Clone the repository:

    git clone https://github.com/tomvanarman/Reflector-Ride-Maps.git
    cd Reflector-Ride-Maps
  2. Install Node dependencies:

    npm install
  3. Set up your environment:

    # Create .env file
    echo "VITE_MAPBOX_TOKEN=pk.YOUR_TOKEN_HERE" > .env

πŸ“‹ Data Processing Workflow

Step 1: Convert Raw CSVs to GeoJSON

Place your CSV files in a csv_data/ folder, then run:

python csv_to_geojson_converter.py

What it does:

  • Reads CSV files with GPS coordinates and sensor data
  • Converts to GeoJSON format with LineString geometries
  • Organizes by sensor ID (e.g., 602B3, 604F0)
  • Extracts metadata from CSV footers
  • Output: sensor_data/{sensor_id}/{sensor_id}_Trip{N}_clean.geojson

Input CSV format:

latitude,longitude,HH:mm:ss,SSS,marker,HRot Count,Samples,Speed
52.3644,4.9130,14:23:45,123,2,100,1000,
52.3645,4.9131,14:23:46,123,3,102,1050,234
...
Bike: Trek 820
Distance: 5.2 km

Step 2: Calculate Speeds from Sensor Data

python combined_processor.py

What it does:

  • Reads cleaned GeoJSON files from sensor_data/
  • Calculates speed using wheel rotation (HRot) data:
    • Uses 660mm (26") wheel diameter
    • Formula: speed = (wheel_rotations Γ— circumference) / time
  • Creates line segments only where the wheel actually moved
  • Filters out stopped periods and anomalies
  • Output: processed_sensor_data/{sensor_id}_Trip{N}_processed.geojson

Key calculations:

  • Wheel circumference: ~2.073 meters
  • Sample rate: 50 Hz (0.02 seconds per sample)
  • Speed cap: 50 km/h (to filter unrealistic values)

Properties added:

  • Speed: km/h calculated from wheel rotation
  • hrot_diff: Wheel rotation difference
  • time_diff_s: Time between points
  • gps_distance_m: GPS distance (for validation)

Step 3: Aggregate Routes Across Trips

python aggregate_routes.py

What it does:

  • Reads all processed trips
  • Groups segments by road location (rounded coordinates)
  • Calculates statistics per road segment:
    • Average speed
    • Min/max speeds
    • Sample count (how many trips)
    • Speed variance
  • Filters out low-quality segments (stopped, large gaps)
  • Output: public/aggregated_routes.geojson

Quality filters:

  • Skip speeds < 3 km/h with GPS distance > 50m
  • Skip time gaps > 5 seconds
  • Require minimum 2 samples per segment

Step 4: Build PMTiles for Web

python build_pmtiles.py

What it does:

  • Uses Tippecanoe to compress processed GeoJSON into PMTiles format
  • PMTiles = efficient vector tiles for web maps
  • Preserves Speed, marker, and trip_id properties
  • Output: public/trips.pmtiles

Why PMTiles?

  • Efficient: ~90% smaller than GeoJSON
  • Fast: Only loads visible tiles
  • Standard: Works with MapLibre/Mapbox

🌐 Running the Web Visualization

Start the development server:

npx vite

The map will open at http://localhost:5173/

Features:

Individual Trips:

  • βœ… View all trips or filter by specific trip
  • 🎨 Color segments by speed (gradient or categories)
  • πŸ–±οΈ Click segments to see speed details

Aggregated Routes:

  • πŸ“Š See average speeds across all trips
  • πŸ”’ Filter by minimum sample count (2-20 trips)
  • πŸ“ˆ View speed statistics per segment

Speed Legend:

  • 🟦 Gray: Stopped (0-2 km/h)
  • πŸ”΄ Red: Very Slow (2-5 km/h)
  • 🟠 Orange: Slow (5-10 km/h)
  • 🟑 Yellow: Moderate (10-15 km/h)
  • 🟒 Green: Fast (15-20 km/h)
  • 🟒 Dark Green: Very Fast (20-25 km/h)
  • 🟒 Darkest Green: Extreme (25-30 km/h)
  • 🟩 Super Fast (30+ km/h)

πŸ› οΈ Why We Use Each Technology

Vite

Purpose: Modern development server and build tool

Benefits:

  • ⚑ Lightning-fast hot module reloading (instant updates)
  • πŸ“¦ Handles ES modules natively (no webpack config)
  • πŸ”„ Injects .env variables into your code
  • πŸ—οΈ Optimized production builds
  • 🎯 Simple setup, zero configuration needed

Alternative: You could use plain HTML + file:// protocol, but:

  • ❌ No .env file support (token visible in code)
  • ❌ No hot reloading (manual refresh needed)
  • ❌ Module imports don't work
  • ❌ No CORS handling for local files

MapLibre GL JS

Purpose: Open-source map rendering library

Why not Mapbox GL JS?

  • βœ… MapLibre is free and open source
  • βœ… Has addProtocol for PMTiles (Mapbox v3 removed it)
  • βœ… 100% API-compatible with Mapbox v2
  • βœ… No usage limits or pricing tiers

PMTiles

Purpose: Efficient vector tile format

Benefits:

  • πŸ“¦ Single file (no tile server needed)
  • πŸš€ 90% smaller than GeoJSON
  • ⚑ Only loads visible map tiles
  • 🌐 Works with static hosting (GitHub Pages, S3, etc.)

πŸ“ File Explanations

Python Scripts

  • csv_to_geojson_converter.py: Converts raw sensor CSVs to GeoJSON LineStrings
  • combined_processor.py: Calculates speeds from wheel rotation data (HRot)
  • aggregate_routes.py: Aggregates speeds across trips to show typical speeds per road
  • build_pmtiles.py: Compresses GeoJSON into PMTiles using Tippecanoe

JavaScript/Web Files

  • index.html: Main webpage with map container and controls
  • app.js: Map initialization, data loading, speed coloring, click handlers
  • config.js: Configuration (Mapbox token, file paths, map style)
  • styles.css: UI styling for controls, legend, and stats panel
  • vite.config.js: Vite configuration for loading .env variables

Configuration Files

  • .env: Your Mapbox token (DO NOT commit to Git!)
  • .gitignore: Prevents committing sensitive files and generated data
  • package.json: Node.js dependencies and scripts
  • package-lock.json: Locked dependency versions

Generated Files (Not in Git)

  • generate-config.js: Alternative script to generate config from .env (not currently used)
  • sensor_data/: Cleaned GeoJSON from CSV conversion
  • processed_sensor_data/: Speed-calculated trips
  • public/trips.pmtiles: Compressed trip data
  • public/aggregated_routes.geojson: Aggregated speed routes

πŸ”§ Configuration

Wheel Settings (in combined_processor.py):

WHEEL_DIAMETER_MM = 660  # 26 inch wheel
WHEEL_CIRCUMFERENCE_M = (660 / 1000) * math.pi  # ~2.073m

Map Settings (in config.js):

MAP_CENTER: [4.9, 52.37],  // Amsterdam area [lon, lat]
MAP_ZOOM: 11,              // Initial zoom level
MAP_STYLE: 'https://...'   // CartoDB Dark Matter style

Speed Colors (in app.js):

Modify the getSpeedColorExpression() function to adjust color thresholds.

πŸ› Troubleshooting

"PMTiles shows all gray/red"

  • Check that Speed property exists in your processed GeoJSON
  • Verify wheel diameter is correct for your bike
  • Run: python build_pmtiles.py to rebuild with -y Speed flag

"Map is blank"

  • Check browser console for errors
  • Verify .env has correct token: VITE_MAPBOX_TOKEN=pk.ey...
  • Restart Vite: Ctrl+C then npx vite

"No data showing"

  • Ensure public/trips.pmtiles exists
  • Check file paths in config.js
  • Verify trips have coordinates in Amsterdam area

"Import errors in browser"

  • Make sure scripts have type="module" in index.html
  • Check that Vite is running (not just opening HTML file)

🀝 Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature-name
  3. Commit changes: git commit -am 'Add feature'
  4. Push to branch: git push origin feature-name
  5. Submit a Pull Request

πŸ“„ License

ISC License - See package.json for details

πŸ™ Acknowledgments

  • MapLibre GL JS: Open-source mapping library
  • Tippecanoe: PMTiles generation by Mapbox
  • CartoDB: Free basemap styles
  • Vite: Modern build tooling

πŸ“§ Contact

For questions or issues, please open a GitHub issue or contact the maintainers.


Happy mapping! πŸš΄β€β™€οΈπŸ—ΊοΈ

About

Data visualization of the Los Aparatos Sensor Paths

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Python 39.8%
  • HTML 27.7%
  • JavaScript 27.1%
  • CSS 5.4%