Stage one — scoring
Every candidate place in the city gets a score. Importance comes first: Wikipedia sitelinks, pageview counts and heritage status, computed once at ingest time rather than at request time. Your interests then weight that score per facet, so choosing museums lifts museums without deleting everything else.
Weighting rather than filtering is deliberate. A history-only filter in Istanbul would drop the Bosphorus ferry, and no honest three-day plan drops the ferry.
Stage two — clustering
The scored pool is grouped geographically before anything is scheduled, because travel time is the real constraint. Two stops on opposite banks can cost an hour between them; two stops in Fatih cost eight minutes. Clustering first is what stops a day from zigzagging across a city.
Cluster mass decays with rank, so a day built around one exceptional place does not drag in six mediocre ones just because they are nearby.
- Trip length caps the radius: one day stays within 15 km, three days 25 km, a week 45 km.
- A single stop can claim a whole day when it eats more than 60% of the window and sits near the radius edge.
- Consecutive days are pushed apart geographically so you do not cross the same bridge twice.
Stage three — ordering
Each cluster is ordered into a walking sequence over an injected distance matrix. Travel times are real routing results, not straight-line guesses, and the matrix is not assumed to be symmetric — uphill in Beyoğlu is not the same as downhill.
Stage four — scheduling
Only now does the clock appear. The sequence is scheduled against each place’s recorded opening hours and against your day window, adjusted by tempo and group type. Meal breaks are placed inside their windows as stops of their own, because they cost real time.
- Tempo
- A multiplier on visit duration: relaxed ×1.67, standard ×1.00, intensive ×0.67. Never a cap on the number of stops.
- Group type
- A factor on the day window: solo ×1.1, couple ×0.9, friends ×1.0, family ×0.75.
- Day window
- Your own start and end time, scaled by the group factor.
- Stop count
- An output. The day fills until its time budget is spent, then stops.
Two invariants have dedicated tests and hold without exception: no stop is scheduled while its place is closed, and a day’s planned total — visit time × tempo, plus travel, plus breaks — never exceeds that day’s window.
When the data is thin
A place with no recorded opening hours is scheduled conservatively in the middle of the day and marked unverified. A duration that came from the category default rather than from the venue is shown with an approximate sign. And when a city has fewer worthwhile places than your days need, you get shorter days and a notice — not filler.