fredmo commited on
Commit
b5c2694
·
verified ·
1 Parent(s): 46933c1

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +174 -45
index.html CHANGED
@@ -6,17 +6,18 @@
6
  <title>GCP Accelerator Availability Map</title>
7
  <style>
8
  body { margin: 0; font-family: sans-serif; background-color: #111; color: #eee; }
9
- #globeViz { width: 100vw; height: 100vh; }
10
  #controls {
11
  position: absolute;
12
  top: 10px;
13
  left: 10px;
14
- background: rgba(0, 0, 0, 0.7);
15
  padding: 15px;
16
  border-radius: 5px;
17
- max-height: 90vh;
18
  overflow-y: auto;
19
  z-index: 10;
 
20
  }
21
  #controls h3 { margin-top: 0; border-bottom: 1px solid #555; padding-bottom: 5px; }
22
  #controls label {
@@ -27,22 +28,67 @@
27
  }
28
  #controls input[type="checkbox"] {
29
  margin-right: 5px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  }
31
  #info-panel {
32
  position: absolute;
33
  bottom: 10px;
34
  left: 10px;
35
- background: rgba(0, 0, 0, 0.8);
36
- padding: 10px;
37
  border-radius: 5px;
38
- max-width: 300px;
39
  font-size: 0.85em;
40
  z-index: 10;
41
  display: none; /* Hidden by default */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  }
43
- #info-panel h4 { margin: 0 0 5px 0; }
44
- #info-panel ul { margin: 0; padding-left: 15px; }
45
- #info-panel li { margin-bottom: 3px; }
46
  </style>
47
  <!-- Import globe.gl library -->
48
  <script src="//unpkg.com/three"></script>
@@ -51,32 +97,31 @@
51
  <body>
52
 
53
  <div id="globeViz"></div>
 
54
  <div id="controls">
55
  <h3>Filter by Accelerator:</h3>
56
  <div id="accelerator-filters">
57
  <!-- Filters will be populated by JavaScript -->
58
  </div>
59
- <button id="show-all" style="margin-top: 10px; padding: 5px 10px;">Show All</button>
60
- <button id="hide-all" style="margin-top: 10px; padding: 5px 10px;">Hide All</button>
61
  </div>
 
62
  <div id="info-panel">
63
- <!-- Info about hovered location -->
64
  </div>
65
 
66
  <script>
67
  // --- 1. Data Processing ---
68
 
69
  // Raw data based on the provided table
70
- // Note: Some locations might be slightly approximated. Need exact coordinates for higher precision.
71
- // Note: Handling duplicates and summarizing zones/notes.
72
  const rawData = [
73
  { accel: "TPU v2", region: "us-central1", zones: "b, c, f", location: "Council Bluffs, Iowa, USA" },
74
  { accel: "TPU v2", region: "europe-west4", zones: "a", location: "Eemshaven, Netherlands" },
75
  { accel: "TPU v2", region: "asia-east1", zones: "c", location: "Changhua County, Taiwan" },
76
- // Some TPU v2/v3 entries seem redundant in the input, consolidating them by location
77
  { accel: "TPU v3", region: "us-central1", zones: "a, b, f", location: "Council Bluffs, Iowa, USA" },
78
  { accel: "TPU v3", region: "europe-west4", zones: "a", location: "Eemshaven, Netherlands" },
79
- { accel: "TPU v4", region: "us-central2", zones: "b", location: "Council Bluffs, Iowa, USA", notes: "Location assumed near us-central1" }, // Added note about assumption
80
  { accel: "TPU v5e", region: "us-central1", zones: "a", location: "Council Bluffs, Iowa, USA" },
81
  { accel: "TPU v5e", region: "us-east5", zones: "a, b, c", location: "Columbus, Ohio, USA" },
82
  { accel: "TPU v5e", region: "us-south1", zones: "a", location: "Dallas, Texas, USA" },
@@ -228,7 +273,7 @@
228
  rawData.forEach(item => {
229
  const locName = item.location;
230
  if (!locationsCoords[locName]) {
231
- console.warn(`Coordinates not found for: ${locName}`);
232
  return; // Skip if no coordinates
233
  }
234
 
@@ -241,16 +286,30 @@
241
  };
242
  }
243
 
244
- // Add accelerator details if not already present for this location
245
- // (Simple check based on accel name only for this example)
246
- if (!locationsData[locName].accelerators.some(a => a.name === item.accel)) {
 
 
247
  locationsData[locName].accelerators.push({
248
  name: item.accel,
249
- region: item.region,
250
- zones: item.zones,
251
- notes: item.notes || '' // Include notes if available
252
  });
253
- }
 
 
 
 
 
 
 
 
 
 
 
 
254
  uniqueAccelerators.add(item.accel);
255
  });
256
 
@@ -265,23 +324,69 @@
265
  const globeVizElement = document.getElementById('globeViz');
266
  const infoPanel = document.getElementById('info-panel');
267
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  myGlobe(globeVizElement)
269
  .globeImageUrl('//unpkg.com/three-globe/example/img/earth-night.jpg')
270
  .backgroundImageUrl('//unpkg.com/three-globe/example/img/night-sky.png')
271
- .pointsData(pointsData) // Initial data
272
- .pointColor(() => '#67ff94') // Nice bright green color for markers
273
- .pointAltitude(0.01) // Slightly raise points off the surface
274
- .pointRadius(0.25) // Adjust size as needed
275
- .pointLabel(d => d.name)
276
- .onPointHover(d => { // Show info panel on hover
 
277
  if (d) {
 
278
  infoPanel.style.display = 'block';
 
279
  let accelHtml = '<ul>';
280
- d.accelerators.forEach(accel => {
281
- accelHtml += `<li><b>${accel.name}</b> (${accel.region}: ${accel.zones})${accel.notes ? ` <i>(${accel.notes})</i>` : ''}</li>`;
 
 
 
 
 
 
 
282
  });
283
  accelHtml += '</ul>';
284
- infoPanel.innerHTML = `<h4>${d.name}</h4>${accelHtml}`;
 
 
 
 
 
 
285
  } else {
286
  infoPanel.style.display = 'none'; // Hide if not hovering over a point
287
  }
@@ -292,8 +397,14 @@
292
  myGlobe.controls().autoRotateSpeed = 0.2;
293
 
294
  // Stop auto-rotate on user interaction
295
- myGlobe.controls().addEventListener('start', () => {
296
- myGlobe.controls().autoRotate = false;
 
 
 
 
 
 
297
  });
298
 
299
  // --- 3. Filter Implementation ---
@@ -315,16 +426,30 @@
315
  });
316
 
317
  function updateFilters() {
318
- const selectedAccelerators = filterCheckboxes
319
- .filter(cb => cb.checked)
320
- .map(cb => cb.value);
 
 
321
 
322
- const filteredPoints = pointsData.filter(location => {
323
- // Keep location if it has at least one of the selected accelerators
324
- return location.accelerators.some(accel => selectedAccelerators.includes(accel.name));
325
- });
 
 
 
 
 
 
326
 
327
- myGlobe.pointsData(filteredPoints); // Update the globe
 
 
 
 
 
 
328
  }
329
 
330
  // Show/Hide All buttons
@@ -338,9 +463,10 @@
338
  updateFilters();
339
  });
340
 
341
- // --- 4. Initial Setup ---
 
342
  // Optional: Center the view on a specific point initially
343
- // myGlobe.pointOfView({ lat: 30, lng: -20, altitude: 2.5 });
344
 
345
  // Handle window resize
346
  window.addEventListener('resize', () => {
@@ -348,6 +474,9 @@
348
  myGlobe.height(window.innerHeight);
349
  });
350
 
 
 
 
351
  </script>
352
 
353
  </body>
 
6
  <title>GCP Accelerator Availability Map</title>
7
  <style>
8
  body { margin: 0; font-family: sans-serif; background-color: #111; color: #eee; }
9
+ #globeViz { width: 100vw; height: 100vh; position: absolute; top: 0; left: 0; z-index: 1; }
10
  #controls {
11
  position: absolute;
12
  top: 10px;
13
  left: 10px;
14
+ background: rgba(0, 0, 0, 0.75);
15
  padding: 15px;
16
  border-radius: 5px;
17
+ max-height: calc(100vh - 150px); /* Adjust max-height to leave space for info panel */
18
  overflow-y: auto;
19
  z-index: 10;
20
+ border: 1px solid #444;
21
  }
22
  #controls h3 { margin-top: 0; border-bottom: 1px solid #555; padding-bottom: 5px; }
23
  #controls label {
 
28
  }
29
  #controls input[type="checkbox"] {
30
  margin-right: 5px;
31
+ vertical-align: middle;
32
+ }
33
+ #controls button {
34
+ margin-top: 10px;
35
+ padding: 6px 12px;
36
+ background-color: #444;
37
+ border: 1px solid #666;
38
+ color: #eee;
39
+ cursor: pointer;
40
+ border-radius: 3px;
41
+ font-size: 0.9em;
42
+ margin-right: 5px;
43
+ }
44
+ #controls button:hover {
45
+ background-color: #555;
46
  }
47
  #info-panel {
48
  position: absolute;
49
  bottom: 10px;
50
  left: 10px;
51
+ background: rgba(0, 0, 0, 0.85);
52
+ padding: 12px;
53
  border-radius: 5px;
54
+ max-width: 350px; /* Increased max-width */
55
  font-size: 0.85em;
56
  z-index: 10;
57
  display: none; /* Hidden by default */
58
+ border: 1px solid #555;
59
+ }
60
+ #info-panel h4 {
61
+ margin: 0;
62
+ font-size: 1.1em;
63
+ color: #fff;
64
+ }
65
+ #info-panel ul {
66
+ margin: 8px 0 0 0;
67
+ padding-left: 18px;
68
+ list-style-type: disc; /* Use standard bullets */
69
+ max-height: 150px; /* Limit list height */
70
+ overflow-y: auto;
71
+ }
72
+ #info-panel li {
73
+ margin-bottom: 5px;
74
+ line-height: 1.3;
75
+ }
76
+ #info-panel li b {
77
+ color: #eee; /* Make accelerator name slightly brighter */
78
+ }
79
+ #info-panel li i { /* Style for notes */
80
+ color: #aaa;
81
+ font-size: 0.9em;
82
+ }
83
+ /* Style for the color indicator square */
84
+ .color-indicator {
85
+ display: inline-block;
86
+ width: 12px;
87
+ height: 12px;
88
+ margin-right: 8px;
89
+ border: 1px solid #777; /* Slightly darker border */
90
+ vertical-align: middle; /* Align better with text */
91
  }
 
 
 
92
  </style>
93
  <!-- Import globe.gl library -->
94
  <script src="//unpkg.com/three"></script>
 
97
  <body>
98
 
99
  <div id="globeViz"></div>
100
+
101
  <div id="controls">
102
  <h3>Filter by Accelerator:</h3>
103
  <div id="accelerator-filters">
104
  <!-- Filters will be populated by JavaScript -->
105
  </div>
106
+ <button id="show-all">Show All</button>
107
+ <button id="hide-all">Hide All</button>
108
  </div>
109
+
110
  <div id="info-panel">
111
+ <!-- Info about hovered location will be populated here -->
112
  </div>
113
 
114
  <script>
115
  // --- 1. Data Processing ---
116
 
117
  // Raw data based on the provided table
 
 
118
  const rawData = [
119
  { accel: "TPU v2", region: "us-central1", zones: "b, c, f", location: "Council Bluffs, Iowa, USA" },
120
  { accel: "TPU v2", region: "europe-west4", zones: "a", location: "Eemshaven, Netherlands" },
121
  { accel: "TPU v2", region: "asia-east1", zones: "c", location: "Changhua County, Taiwan" },
 
122
  { accel: "TPU v3", region: "us-central1", zones: "a, b, f", location: "Council Bluffs, Iowa, USA" },
123
  { accel: "TPU v3", region: "europe-west4", zones: "a", location: "Eemshaven, Netherlands" },
124
+ { accel: "TPU v4", region: "us-central2", zones: "b", location: "Council Bluffs, Iowa, USA", notes: "Location assumed near us-central1" },
125
  { accel: "TPU v5e", region: "us-central1", zones: "a", location: "Council Bluffs, Iowa, USA" },
126
  { accel: "TPU v5e", region: "us-east5", zones: "a, b, c", location: "Columbus, Ohio, USA" },
127
  { accel: "TPU v5e", region: "us-south1", zones: "a", location: "Dallas, Texas, USA" },
 
273
  rawData.forEach(item => {
274
  const locName = item.location;
275
  if (!locationsCoords[locName]) {
276
+ console.warn(`Coordinates not found for: ${locName}. Skipping this entry.`);
277
  return; // Skip if no coordinates
278
  }
279
 
 
286
  };
287
  }
288
 
289
+ // Add accelerator details only if it's not a duplicate entry *for this specific location*
290
+ // (We combine regions/zones if the same accelerator appears multiple times for the same location,
291
+ // although the provided data seems mostly unique per location-accelerator pair already)
292
+ const existingAccelIndex = locationsData[locName].accelerators.findIndex(a => a.name === item.accel);
293
+ if (existingAccelIndex === -1) {
294
  locationsData[locName].accelerators.push({
295
  name: item.accel,
296
+ regions: [item.region], // Store regions as an array
297
+ zones: [item.zones], // Store zones as an array
298
+ notes: item.notes ? [item.notes] : [] // Store notes as an array
299
  });
300
+ } else {
301
+ // If accelerator already exists, add region/zone/notes if they differ (simple check)
302
+ const existingAccel = locationsData[locName].accelerators[existingAccelIndex];
303
+ if (!existingAccel.regions.includes(item.region)) {
304
+ existingAccel.regions.push(item.region);
305
+ }
306
+ if (!existingAccel.zones.includes(item.zones)) {
307
+ existingAccel.zones.push(item.zones);
308
+ }
309
+ if (item.notes && !existingAccel.notes.includes(item.notes)) {
310
+ existingAccel.notes.push(item.notes);
311
+ }
312
+ }
313
  uniqueAccelerators.add(item.accel);
314
  });
315
 
 
324
  const globeVizElement = document.getElementById('globeViz');
325
  const infoPanel = document.getElementById('info-panel');
326
 
327
+ // Define colors
328
+ const tpuColor = '#00FFFF'; // Cyan
329
+ const gpuColor = '#67ff94'; // Green
330
+ const mixedColor = '#FFA500'; // Orange
331
+ const defaultColor = '#FFFFFF'; // White (fallback)
332
+
333
+ // List of substrings identifying GPU types in your data
334
+ const gpuIdentifiers = ['H100', 'A100', 'L4', 'T4', 'V100', 'P100', 'P4'];
335
+
336
+ // Function to determine point color based on accelerator types
337
+ function getPointColor(data) {
338
+ let hasTPU = false;
339
+ let hasGPU = false;
340
+
341
+ data.accelerators.forEach(accel => {
342
+ if (accel.name.startsWith('TPU')) {
343
+ hasTPU = true;
344
+ }
345
+ if (gpuIdentifiers.some(gpuId => accel.name.includes(gpuId))) {
346
+ hasGPU = true;
347
+ }
348
+ });
349
+
350
+ if (hasTPU && hasGPU) return mixedColor;
351
+ if (hasTPU) return tpuColor;
352
+ if (hasGPU) return gpuColor;
353
+ return defaultColor;
354
+ }
355
+
356
+
357
  myGlobe(globeVizElement)
358
  .globeImageUrl('//unpkg.com/three-globe/example/img/earth-night.jpg')
359
  .backgroundImageUrl('//unpkg.com/three-globe/example/img/night-sky.png')
360
+ .pointsData(pointsData)
361
+ .pointColor(d => getPointColor(d)) // Use the function for color
362
+ .pointAltitude(0.01)
363
+ .pointRadius(0.4) // Adjusted radius (make bigger or smaller as needed)
364
+ .pointLabel(d => d.name) // Basic label on hover (can be improved)
365
+ .onPointHover(d => { // Show enhanced info panel on hover
366
+ globeVizElement.style.cursor = d ? 'pointer' : 'default'; // Change cursor on hover
367
  if (d) {
368
+ const pointColor = getPointColor(d); // Get color for the hovered point
369
  infoPanel.style.display = 'block';
370
+
371
  let accelHtml = '<ul>';
372
+ // Sort accelerators within the panel for consistency
373
+ const sortedAccels = [...d.accelerators].sort((a, b) => a.name.localeCompare(b.name));
374
+
375
+ sortedAccels.forEach(accel => {
376
+ // Join arrays for display if multiple entries were merged
377
+ const regionStr = accel.regions.join(', ');
378
+ const zoneStr = accel.zones.join('; ');
379
+ const notesStr = accel.notes.length > 0 ? ` <i>(${accel.notes.join(', ')})</i>` : '';
380
+ accelHtml += `<li><b>${accel.name}</b> (${regionStr}: ${zoneStr})${notesStr}</li>`;
381
  });
382
  accelHtml += '</ul>';
383
+
384
+ infoPanel.innerHTML = `
385
+ <div style="display: flex; align-items: center; margin-bottom: 5px;">
386
+ <span class="color-indicator" style="background-color: ${pointColor};"></span>
387
+ <h4>${d.name}</h4>
388
+ </div>
389
+ ${accelHtml}`;
390
  } else {
391
  infoPanel.style.display = 'none'; // Hide if not hovering over a point
392
  }
 
397
  myGlobe.controls().autoRotateSpeed = 0.2;
398
 
399
  // Stop auto-rotate on user interaction
400
+ let userInteracted = false;
401
+ const controls = myGlobe.controls();
402
+ controls.addEventListener('start', () => {
403
+ if (!userInteracted) {
404
+ controls.autoRotate = false;
405
+ userInteracted = true; // Ensure auto-rotate stops permanently after first interaction
406
+ console.log('Globe auto-rotation stopped.');
407
+ }
408
  });
409
 
410
  // --- 3. Filter Implementation ---
 
426
  });
427
 
428
  function updateFilters() {
429
+ const selectedAccelerators = new Set( // Use a Set for faster lookups
430
+ filterCheckboxes
431
+ .filter(cb => cb.checked)
432
+ .map(cb => cb.value)
433
+ );
434
 
435
+ if (selectedAccelerators.size === 0) {
436
+ // If nothing is selected, show nothing
437
+ myGlobe.pointsData([]);
438
+ } else {
439
+ const filteredPoints = pointsData.filter(location => {
440
+ // Keep location if it has at least one of the selected accelerators
441
+ return location.accelerators.some(accel => selectedAccelerators.has(accel.name));
442
+ });
443
+ myGlobe.pointsData(filteredPoints); // Update the globe
444
+ }
445
 
446
+ // Update info panel if it was visible for a point that is now filtered out
447
+ if (infoPanel.style.display === 'block') {
448
+ const currentHoverName = infoPanel.querySelector('h4')?.textContent;
449
+ if (currentHoverName && !myGlobe.pointsData().some(p => p.name === currentHoverName)) {
450
+ infoPanel.style.display = 'none';
451
+ }
452
+ }
453
  }
454
 
455
  // Show/Hide All buttons
 
463
  updateFilters();
464
  });
465
 
466
+ // --- 4. Initial Setup & Resize ---
467
+
468
  // Optional: Center the view on a specific point initially
469
+ // myGlobe.pointOfView({ lat: 20, lng: 0, altitude: 2.5 }, 1000); // Example: Atlantic view
470
 
471
  // Handle window resize
472
  window.addEventListener('resize', () => {
 
474
  myGlobe.height(window.innerHeight);
475
  });
476
 
477
+ // Initial filter application in case some start unchecked (though they don't here)
478
+ updateFilters();
479
+
480
  </script>
481
 
482
  </body>