-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path3d.html
More file actions
295 lines (280 loc) · 14.7 KB
/
Copy path3d.html
File metadata and controls
295 lines (280 loc) · 14.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>NorCal Touge Spots — 3D — by Justin Suo</title>
<meta name="description" content="3D interactive map of Northern California's best driving roads. Real terrain, satellite imagery, and an entire golf course rendered hole by hole." />
<meta property="og:title" content="NorCal Touge Spots — 3D" />
<meta property="og:description" content="3D interactive driving-roads map with real terrain and satellite imagery." />
<meta property="og:type" content="website" />
<!--
Cesium is loaded from unpkg (npm mirror) by default. To use a local copy:
1. Download Cesium 1.123 from cesium.com or `npm pack cesium@1.123`
2. Unpack into ./vendor/cesium/Build/Cesium/
3. Set window.CESIUM_LOCAL = true at the top of <body>
The two link/script tags below will then resolve to ./vendor/cesium/...
-->
<link rel="stylesheet" href="https://unpkg.com/cesium@1.123/Build/Cesium/Widgets/widgets.css" id="cesium-css" />
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="" />
<link rel="stylesheet" href="styles.css" />
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ctext y='14' font-size='14'%3E%F0%9F%8C%90%3C/text%3E%3C/svg%3E" />
<link rel="manifest" href="manifest.webmanifest" />
<meta name="theme-color" content="#f97316" />
<link rel="prefetch" href="index.html" />
<link rel="prefetch" href="routes.json" />
<link rel="prefetch" href="tilden_holes.json" />
</head>
<body>
<header id="app-tabs" role="tablist" aria-label="Section tabs">
<a href="index.html" class="app-tab" role="tab" aria-selected="false">
<span class="app-tab-glyph">🗻</span>
<span class="app-tab-label">Roads (2D)</span>
</a>
<a href="food.html" class="app-tab" role="tab" aria-selected="false">
<span class="app-tab-glyph">🥢</span>
<span class="app-tab-label">Chinese Food</span>
</a>
<a href="3d.html" class="app-tab active" role="tab" aria-current="page" aria-selected="true">
<span class="app-tab-glyph">🌐</span>
<span class="app-tab-label">3D (beta)</span>
</a>
</header>
<aside id="sidebar" aria-label="Route list and filters">
<header class="sidebar-header">
<h1>NorCal Touge Spots <span style="font-size:11px;color:var(--accent);font-weight:600;letter-spacing:0.5px;">· 3D</span></h1>
<p class="subtitle">Real terrain · satellite imagery · click a route to fly there</p>
</header>
<section class="filters" aria-label="Filters">
<div class="filter-group">
<input type="search" id="route-search" class="route-search" placeholder="Search routes…" />
</div>
<div class="filter-group">
<label class="filter-label">Min rating</label>
<div class="chip-row" id="rating-chips">
<button class="chip active" data-rating="0">All</button>
<button class="chip" data-rating="3.5">3.5+</button>
<button class="chip" data-rating="4">4+</button>
<button class="chip" data-rating="4.5">4.5+</button>
</div>
</div>
<div class="filter-group">
<label class="filter-label">Region</label>
<div class="chip-row" id="region-chips">
<button class="chip active" data-region="all">All</button>
<button class="chip" data-region="East Bay">East Bay</button>
<button class="chip" data-region="Peninsula">Peninsula</button>
<button class="chip" data-region="South Bay">South Bay</button>
<button class="chip" data-region="North Bay">North Bay</button>
<button class="chip" data-region="Sacramento Valley">Sacramento Valley</button>
<button class="chip" data-region="Modoc">Modoc</button>
</div>
</div>
<div class="filter-group">
<label class="filter-label">Type</label>
<div class="chip-row" id="type-chips">
<button class="chip active" data-type="all">All</button>
<button class="chip" data-type="touge">Touge</button>
<button class="chip" data-type="roll-racing">🏁 Roll Racing</button>
<button class="chip" data-type="dig-racing">🏁 Dig Racing</button>
</div>
</div>
<div class="filter-group">
<label class="filter-label">Max length</label>
<div class="range-row">
<input type="range" id="max-length" min="5" max="100" step="5" value="100" />
<span class="range-val" id="max-length-val">∞</span>
</div>
</div>
<div class="filter-group">
<label class="filter-label">Max time from Berkeley</label>
<div class="range-row">
<input type="range" id="max-from-berkeley" min="15" max="240" step="15" value="240" />
<span class="range-val" id="max-from-berkeley-val">∞</span>
</div>
</div>
<div class="filter-group">
<label class="filter-label">Basemap</label>
<div class="chip-row" id="basemap-chips">
<button class="chip" data-basemap="gray">B&W light</button>
<button class="chip" data-basemap="dark">B&W dark</button>
<button class="chip active" data-basemap="satellite">Satellite</button>
</div>
</div>
<div class="filter-group">
<label class="filter-label">Layers</label>
<div class="chip-row">
<button class="chip toggle active" id="toggle-pois" title="Show or hide POI pins">POIs</button>
<button class="chip toggle" id="toggle-buildings" title="Extruded OSM buildings (Bay Area)">Buildings</button>
<button class="chip toggle" id="toggle-trees" title="OSM forest / wood polygons extruded as canopy">Trees</button>
<button class="chip toggle" id="toggle-rings" title="Drive-time rings around UC Berkeley">Drive rings</button>
<button class="chip toggle" id="toggle-heatmap" title="Heatmap is currently 2D-only">My runs heatmap</button>
</div>
<div class="chip-row" style="margin-top: 6px;">
<button class="chip toggle" id="toggle-favorites" title="Show only routes you've ♥-favorited">♥ Favorites only</button>
</div>
</div>
<div class="filter-group">
<label class="filter-label">Performance</label>
<div class="chip-row" id="perf-chips">
<button class="chip active" data-perf="high">High quality</button>
<button class="chip" data-perf="fast">Fast</button>
</div>
</div>
<div class="filter-group">
<label class="filter-label">Visual</label>
<div class="range-row">
<label for="exaggeration">Hill exag.</label>
<input type="range" id="exaggeration" min="1.0" max="3.0" step="0.1" value="1.5" />
<span class="range-val" id="exaggeration-val">1.5×</span>
</div>
<div class="range-row">
<label for="time-of-day">Sun position</label>
<input type="range" id="time-of-day" min="0" max="24" step="0.25" value="14" />
<span class="range-val" id="time-of-day-val">2pm</span>
</div>
<div class="chip-row" style="margin-top: 6px;">
<button class="chip toggle active" id="toggle-atmosphere" title="Sky/atmosphere haze">Sky haze</button>
<button class="chip toggle" id="toggle-fog" title="Distance fog">Fog</button>
</div>
</div>
<div class="filter-group">
<label class="filter-label">Tools</label>
<div class="chip-row">
<button class="chip toggle" id="tool-measure" title="Click two points on the map to measure distance">📏 Measure</button>
<button class="chip" id="tool-share" title="Copy a link to your current view">🔗 Share view</button>
</div>
<div class="chip-row" style="margin-top: 6px;">
<button class="chip" id="tool-tour" title="Auto-fly through every visible route">🎬 Tour all routes</button>
<button class="chip" id="tool-locate" title="Fly to your current GPS location">📍 Locate me</button>
</div>
<div class="chip-row" style="margin-top: 6px;">
<button class="chip" id="tool-cinematic" title="Hide all UI for a clean cinematic view (H)">🎥 Cinematic</button>
<button class="chip" id="tool-day-cycle" title="Animate sun position through 24 hours">🌗 Day cycle</button>
<button class="chip" id="tool-snapshot" title="Download a PNG of the current view">📷 Snapshot</button>
</div>
</div>
</section>
<div id="route-stats" class="route-stats" aria-live="polite"></div>
<section class="route-list-wrap" aria-label="Routes">
<div id="route-list" class="route-list" role="listbox" aria-label="Routes"></div>
</section>
<footer class="sidebar-footer">
<a href="index.html">← Back to 2D</a>
<span aria-hidden="true">·</span>
<a href="https://github.com/justinsuo/tougespot-in-norcal" target="_blank" rel="noopener">GitHub</a>
</footer>
</aside>
<main id="map" role="application" aria-label="3D map of NorCal touge spots"></main>
<button id="sidebar-toggle" aria-label="Toggle sidebar" aria-expanded="true">
<span class="bars" aria-hidden="true"></span>
</button>
<aside id="detail-panel" aria-hidden="true" aria-label="Route details">
<button class="detail-close" id="detail-close" aria-label="Close details">×</button>
<div id="detail-body"></div>
</aside>
<div id="loading" class="loading" aria-live="polite">Loading 3D map and routes…</div>
<!-- First-visit welcome overlay -->
<div id="welcome-overlay" class="help-overlay" aria-hidden="true" style="display:none;">
<div class="help-card">
<button class="help-close" id="welcome-close" aria-label="Close">×</button>
<h3>Welcome to NorCal Touge Spots — 3D 🌐</h3>
<p style="font-size:13px;line-height:1.6;color:var(--text-dim);">
21 hand-picked driving roads across the Bay Area, rendered on a
live 3D globe with real terrain, satellite imagery, and an entire
golf course mapped hole-by-hole.
</p>
<ul style="font-size:12px;line-height:1.7;color:var(--text);padding-left:18px;margin:12px 0;">
<li><strong>Pick a road</strong> from the sidebar (or search ↗) to fly there.</li>
<li><strong>▶ Fly this road</strong> takes the camera on a tour.</li>
<li><strong>Right-click</strong> to drop a custom pin anywhere.</li>
<li><strong>♥</strong> a route to save it. <strong>Shift-click</strong> to compare 2+.</li>
<li><strong>H</strong> for cinematic mode. <strong>?</strong> for shortcuts.</li>
</ul>
<button class="btn fly-route" id="welcome-start" style="width:100%;margin-top:6px;">Got it — explore the map</button>
</div>
</div>
<!-- 3D-only HUD: live coords + reset/help buttons -->
<div id="hud-3d" aria-hidden="false">
<button id="reset-view" class="hud-btn" title="Fly back to the Bay Area overview (R)">⌖ Reset view</button>
<button id="help-3d-btn" class="hud-btn" title="Show keyboard shortcuts (?)">? Shortcuts</button>
<div id="hud-coords" class="hud-coords" aria-live="off">—</div>
<div id="hud-measure" class="hud-coords" style="display:none;"></div>
</div>
<!-- Compass — top-right of viewport, points to north -->
<div id="compass-3d" title="Compass — click to face north">
<div class="compass-ring">
<div class="compass-needle" id="compass-needle">
<span class="compass-n">N</span>
</div>
<div class="compass-sun" id="compass-sun" title="Sun position">☀</div>
</div>
</div>
<!-- Mini-map — small 2D Leaflet map showing where the 3D camera looks -->
<div id="minimap-wrap">
<div id="minimap"></div>
<button id="minimap-toggle" class="hud-btn" title="Show / hide mini-map">⌖</button>
</div>
<!-- Tour mode controls -->
<div id="tour-controls" class="tour-controls" aria-hidden="true">
<span id="tour-label">Touring…</span>
<button id="tour-stop" class="hud-btn">■ Stop tour</button>
</div>
<div id="help-overlay" class="help-overlay" aria-hidden="true">
<div class="help-card">
<button class="help-close" id="help-close" aria-label="Close shortcuts">×</button>
<h3>3D viewer controls</h3>
<table class="help-table">
<tr><td><kbd>Drag</kbd></td><td>Pan the camera</td></tr>
<tr><td><kbd>Right-drag</kbd></td><td>Tilt & rotate</td></tr>
<tr><td><kbd>Scroll</kbd></td><td>Zoom in / out</td></tr>
<tr><td><kbd>Right-click</kbd></td><td>Drop / remove a custom pin</td></tr>
<tr><td><kbd>R</kbd></td><td>Reset to Bay Area overview</td></tr>
<tr><td><kbd>G</kbd></td><td>Cycle basemap (B&W → dark → satellite)</td></tr>
<tr><td><kbd>P</kbd></td><td>Toggle POI pins</td></tr>
<tr><td><kbd>?</kbd></td><td>Open this help</td></tr>
<tr><td><kbd>Esc</kbd></td><td>Close detail panel</td></tr>
</table>
<p class="help-tip">
Open a route from the sidebar, then hit <strong>▶ Fly this road</strong>
for a driver's-seat tour. Toggle <strong>Buildings</strong> +
<strong>Trees</strong> for a Google-Earth-style cityscape.
Use <strong>📏 Measure</strong> to get distance & bearing
between any two points, or <strong>🔗 Share view</strong> to copy a
link that opens this exact camera view.
</p>
</div>
</div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
<script>
// If a local Cesium build is present at ./vendor/cesium, use it. Otherwise
// fall back to unpkg's npm mirror (more reliable than cesium.com's CDN).
(function () {
const useLocal = window.CESIUM_LOCAL === true;
const base = useLocal
? "vendor/cesium/Build/Cesium/"
: "https://unpkg.com/cesium@1.123/Build/Cesium/";
window.CESIUM_BASE_URL = base;
if (useLocal) {
const link = document.getElementById("cesium-css");
if (link) link.href = base + "Widgets/widgets.css";
}
const s = document.createElement("script");
s.src = base + "Cesium.js";
s.onload = () => {
const app = document.createElement("script");
const q = new URL(window.location.href).searchParams.get("v");
app.src = q ? `app-3d.js?v=${q}` : "app-3d.js";
document.body.appendChild(app);
};
s.onerror = () => {
document.getElementById("loading").textContent =
"Cesium failed to load. Check your network connection.";
};
document.body.appendChild(s);
})();
</script>
</body>
</html>