Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RouteSunVisualizer

Which side of the bus should I sit on so the sun isn't in my face?

Give it a start, a destination and a departure time. It fetches the real road route, walks along it minute by minute, works out where the sun is at each point, and tells you which side of the vehicle stays in the shade, plus an interactive map showing exactly where and when the sun swaps sides.

routesun Liberec "Hradec Kralove" --depart 07:30

Terminal summary

The report

Interactive HTML report

Hue is the side: blue is your left window, red is your right, grey is no direct sun. Depth of colour is how strong it is. Short rays point at where the sun actually is at the moment you pass each spot. Hovering any stretch of road gives you the time, the sun's altitude and bearing, and the exposure on each window; the panel's timeline does the same over time, and there is a plain table view for anything colour can't carry.

Install

git clone https://github.com/jholaj/RouteSunVisualizer.git
cd RouteSunVisualizer
python -m venv .venv && source .venv/bin/activate
pip install -e .

Routing and place-name lookup use OpenRouteService, which needs a free API key:

cp .env_example .env        # then paste your key into .env

Without a key you can still use --offline, which draws a straight great-circle line at a fixed speed. Good enough for a route that runs roughly straight, useless for one that doesn't.

Usage

routesun ORIGIN DESTINATION [options]

  ORIGIN, DESTINATION   a place name, or "lat,lon"

  -d, --depart TIME     'now', 'HH:MM', or 'YYYY-MM-DD HH:MM'   (default: now)
      --tz ZONE         IANA zone for --depart, e.g. Europe/Prague
  -p, --profile P       driving-car, driving-hgv, cycling-regular,
                        cycling-road, foot-walking, wheelchair
  -o, --output FILE     where to write the report      (default: route_sun.html)
      --open            open the report in a browser when it is ready
      --no-html         print the summary only
      --offline         skip the API, use a great-circle line (needs "lat,lon")
      --speed KMH       average speed for --offline              (default: 70)
      --legs N          how finely to sample the route          (default: 240)
      --api-key KEY     overrides $OPENROUTESERVICE_API_KEY
  -v, --verbose

python main.py ... works identically if you'd rather not install the package.

Examples

# Morning commute, opened straight away
routesun Liberec "Hradec Kralove" --depart 07:30 --open

# A specific day, in a specific timezone
routesun Praha Brno --depart '2026-08-01 06:00' --tz Europe/Prague

# No API key, coordinates only
routesun "50.767,15.056" "50.210,15.825" --offline --depart 17:00

As a library

import datetime as dt
from zoneinfo import ZoneInfo
from routesun import analysis, routing, visualize

provider = routing.OpenRouteServiceProvider(api_key)
route = provider.route(
    provider.geocode("Liberec"), provider.geocode("Hradec Kralove"), "driving-car"
)
trip = analysis.analyse(
    route, dt.datetime(2026, 8, 1, 7, 30, tzinfo=ZoneInfo("Europe/Prague"))
)

print(trip.recommended)        # Side.RIGHT
print(trip.explanation)        # "The sun stays on your left the whole way."
visualize.save_report(trip, "route_sun.html")

How it decides

The route is cut into ~240 legs. Each leg knows its compass heading and, from OpenRouteService's per-step durations, the clock time you actually reach it, so the sun moves across the sky as you travel rather than being frozen at the departure time.

For each leg, PySolar gives the sun's altitude h and azimuth A. With the heading B, the sun's bearing relative to the direction of travel is θ = (A - B) mod 360, and the direct beam landing on a vertical side window is

exposure = I(h) · cos(h) · |sin θ|        sun on the right when sin θ > 0
  • cos(h): a vertical window only catches the horizontal part of the beam. A sun overhead lands on the roof, not on you.
  • |sin θ|: the sun has to be off to the side. Straight ahead or behind, it goes through the windscreen instead.
  • I(h): clear-sky beam strength, from the Kasten-Young air mass with a 0.7 zenith transmittance, square-rooted so a low sun stays perceptually significant rather than rounding to nothing.

Exposure is integrated over each leg's duration; the side with the smaller total is the one to sit on. Below the horizon, exposure is zero.

What it does not know: the weather, tinted or curtained windows, tunnels, cuttings, tree cover, terrain shadowing, or which way the seats face. It assumes a clear sky and a vehicle pointing along the road.

Development

pip install -e '.[dev]'
python -m pytest -q

The map's colours are a validated diverging palette (blue and red poles, neutral grey midpoint) with separately stepped light and dark sets, checked for lightness monotonicity and for separation under simulated protanopia and deuteranopia. If you change them, re-check rather than eyeball.

Layout

File What lives there
routesun/geo.py coordinates, bearings, spherical offsets
routesun/sun.py solar position and the side-exposure model
routesun/routing.py OpenRouteService and offline route providers, trip timing
routesun/analysis.py legs, per-side totals, the recommendation
routesun/chart.py the diverging timeline SVG
routesun/palette.py colour tokens for both themes
routesun/visualize.py the Folium map and the report page
routesun/report.py the terminal summary
routesun/cli.py argument parsing and wiring

Acknowledgments

About

Avoiding the sun on public transport by all means

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages