Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 26 additions & 10 deletions website/demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,16 @@ <h1 class="logo">gtfs-sqljs</h1>
<main class="container">
<section class="demo-header">
<h1>Interactive Demo</h1>
<p>Exploring Car Jaune GTFS data (La Réunion, France)</p>
<p class="operator-info">Operated by Transdev Outre-Mer and GIE Activ'</p>
<div class="url-input-section">
<label for="gtfs-url-input">GTFS ZIP URL (https only):</label>
<input
type="url"
id="gtfs-url-input"
placeholder="https://www.data.gouv.fr/api/1/datasets/r/..."
value="https://www.data.gouv.fr/api/1/datasets/r/c9c2f609-d0cd-4233-ad1b-cf86b9bf2dc8"
/>
<button id="load-gtfs-btn" class="button button-primary">Load GTFS</button>
</div>
</section>

<div id="loading" class="loading">
Expand All @@ -31,24 +39,32 @@ <h1>Interactive Demo</h1>
</div>

<div id="error" class="error" style="display: none;">
<p>Error loading GTFS data. Please refresh the page.</p>
<p id="error-message">Error loading GTFS data. Please refresh the page.</p>
</div>

<div id="demo-content" style="display: none;">
<section class="demo-section">
<section class="demo-section" id="agencies-section">
<h2>Transit Agencies</h2>
<div id="agencies-list" class="agencies-list">
<p>Loading agencies...</p>
</div>
</section>

<section class="demo-section download-section">
<button id="download-db-btn" class="button button-primary">
📥 Download SQLite Database
</button>
<p class="help-text">Download the processed GTFS data as a SQLite database file</p>
</section>

<section class="demo-section date-section">
<h2>Select Date</h2>
<div class="date-selector">
<input type="date" id="date-input" />
</div>
<div id="active-calendars" class="active-calendars">
<p>Loading...</p>
</div>
<div class="download-section">
<button id="download-db-btn" class="button button-primary">
📥 Download SQLite Database
</button>
<p class="help-text">Download the processed GTFS data as a SQLite database file</p>
</div>
</section>

<section class="demo-section">
Expand Down
103 changes: 94 additions & 9 deletions website/demo.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,46 @@
import initSqlJs from 'sql.js';
import { GtfsSqlJs } from 'gtfs-sqljs';
import type { Route, Trip, StopTime } from 'gtfs-sqljs';
import type { Route, Trip, StopTime, Agency } from 'gtfs-sqljs';

let gtfs: GtfsSqlJs;
let selectedDate: string;
let SQL: any;

// Initialize the demo
async function init() {
// Convert https URL to proxy URL
function getProxyUrl(httpsUrl: string): string {
if (!httpsUrl.startsWith('https://')) {
throw new Error('URL must start with https://');
}
// Remove https:// prefix
const withoutProtocol = httpsUrl.substring(8);
return `https://gtfs-proxy.sys-dev-run.re/proxy/${withoutProtocol}`;
}

// Load GTFS from URL or default
async function loadGTFS(url?: string) {
const loadingEl = document.getElementById('loading')!;
const errorEl = document.getElementById('error')!;
const errorMessageEl = document.getElementById('error-message')!;
const contentEl = document.getElementById('demo-content')!;

try {
// Initialize SQL.js with CDN WASM file
const SQL = await initSqlJs({
locateFile: (filename) => `https://sql.js.org/dist/${filename}`
});
// Show loading, hide error and content
loadingEl.style.display = 'block';
errorEl.style.display = 'none';
contentEl.style.display = 'none';

// Determine source
let source: string;
if (url) {
source = getProxyUrl(url);
} else {
// Load default car-jaune.zip from public folder
source = './car-jaune.zip';
}

// Load GTFS data (Vite will resolve this from public folder)
// Load GTFS data
// Skip shapes.txt to reduce memory usage and improve load time
gtfs = await GtfsSqlJs.fromZip('./car-jaune.zip', {
gtfs = await GtfsSqlJs.fromZip(source, {
SQL,
skipFiles: ['shapes.txt']
});
Expand All @@ -35,12 +56,44 @@ async function init() {
setupDownloadButton();

// Render initial data
renderAgencies();
renderActiveCalendars();
renderRoutes();
} catch (error) {
console.error('Error loading GTFS data:', error);
loadingEl.style.display = 'none';
errorEl.style.display = 'block';
errorMessageEl.textContent = `Error loading GTFS data: ${error instanceof Error ? error.message : String(error)}`;
}
}

// Initialize the demo
async function init() {
try {
// Initialize SQL.js with CDN WASM file
SQL = await initSqlJs({
locateFile: (filename) => `https://sql.js.org/dist/${filename}`
});

// Setup URL input handler
const loadBtn = document.getElementById('load-gtfs-btn')!;
const urlInput = document.getElementById('gtfs-url-input') as HTMLInputElement;

loadBtn.addEventListener('click', () => {
const url = urlInput.value.trim();
if (url) {
loadGTFS(url);
}
});

// Load default GTFS (car-jaune.zip)
await loadGTFS();
} catch (error) {
console.error('Error initializing demo:', error);
const loadingEl = document.getElementById('loading')!;
const errorEl = document.getElementById('error')!;
loadingEl.style.display = 'none';
errorEl.style.display = 'block';
}
}

Expand Down Expand Up @@ -71,6 +124,38 @@ function initDatePicker() {
});
}

// Render agencies
function renderAgencies() {
const agenciesListEl = document.getElementById('agencies-list')!;

try {
const agencies = gtfs.getAgencies();

if (agencies.length === 0) {
agenciesListEl.innerHTML = '<p>No agency information available</p>';
return;
}

const html = agencies.map((agency): string => {
return `
<div class="agency-card">
<h3>${escapeHtml(agency.agency_name)}</h3>
${agency.agency_url ? `<p><a href="${escapeHtml(agency.agency_url)}" target="_blank">${escapeHtml(agency.agency_url)}</a></p>` : ''}
${agency.agency_timezone ? `<p><strong>Timezone:</strong> ${escapeHtml(agency.agency_timezone)}</p>` : ''}
${agency.agency_lang ? `<p><strong>Language:</strong> ${escapeHtml(agency.agency_lang)}</p>` : ''}
${agency.agency_phone ? `<p><strong>Phone:</strong> ${escapeHtml(agency.agency_phone)}</p>` : ''}
${agency.agency_email ? `<p><strong>Email:</strong> ${escapeHtml(agency.agency_email)}</p>` : ''}
</div>
`;
}).join('');

agenciesListEl.innerHTML = html;
} catch (error) {
console.error('Error getting agencies:', error);
agenciesListEl.innerHTML = '<p>Error loading agency information</p>';
}
}

// Render active calendars for selected date
function renderActiveCalendars() {
const activeCalendarsEl = document.getElementById('active-calendars')!;
Expand Down
93 changes: 84 additions & 9 deletions website/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,75 @@ footer {
margin-bottom: 2rem;
}

.operator-info {
color: var(--text-secondary);
.url-input-section {
margin-top: 1.5rem;
display: flex;
flex-direction: column;
gap: 0.75rem;
max-width: 700px;
margin-left: auto;
margin-right: auto;
}

.url-input-section label {
font-weight: 500;
color: var(--text-primary);
text-align: left;
}

.url-input-section input[type="url"] {
padding: 0.75rem 1rem;
border: 2px solid var(--border);
border-radius: 0.5rem;
font-size: 1rem;
font-family: inherit;
width: 100%;
transition: border-color 0.2s;
}

.url-input-section input[type="url"]:focus {
outline: none;
border-color: var(--primary-color);
}

.url-input-section button {
align-self: flex-start;
}

.agencies-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1.5rem;
}

.agency-card {
background: white;
padding: 1.5rem;
border-radius: 0.5rem;
border: 1px solid var(--border);
box-shadow: var(--shadow);
}

.agency-card h3 {
font-size: 1.25rem;
margin: 0 0 1rem 0;
color: var(--primary-color);
}

.agency-card p {
margin: 0.5rem 0;
font-size: 0.95rem;
margin-top: 0.5rem;
color: var(--text-primary);
}

.agency-card a {
color: var(--primary-color);
text-decoration: none;
word-break: break-all;
}

.agency-card a:hover {
text-decoration: underline;
}

.loading {
Expand Down Expand Up @@ -248,24 +313,33 @@ footer {
border: 1px solid var(--border);
}

.date-section {
padding: 1.25rem 2rem;
}

.date-section h2 {
font-size: 1.25rem;
margin-bottom: 1rem;
}

.help-text {
color: var(--text-secondary);
font-style: italic;
margin-bottom: 1rem;
}

.date-selector {
margin-bottom: 1.5rem;
margin-bottom: 1rem;
}

.date-selector input[type="date"] {
padding: 0.75rem 1rem;
padding: 0.6rem 0.875rem;
border: 2px solid var(--border);
border-radius: 0.5rem;
font-size: 1rem;
font-size: 0.95rem;
font-family: inherit;
width: 100%;
max-width: 300px;
max-width: 250px;
transition: border-color 0.2s;
}

Expand All @@ -275,7 +349,7 @@ footer {
}

.active-calendars {
margin-top: 1rem;
margin-top: 0.75rem;
}

.info-text {
Expand All @@ -285,8 +359,8 @@ footer {
}

.download-section {
margin-top: 2rem;
text-align: center;
padding: 1.5rem 2rem;
}

.download-section .button {
Expand All @@ -295,6 +369,7 @@ footer {

.download-section .help-text {
margin-top: 0.5rem;
margin-bottom: 0;
font-size: 0.875rem;
}

Expand Down