# Municipal Transit Planner

Municipal Transit Planner is a web-based planning tool for exploring public transit networks, evaluating service changes, and preparing route proposals. It helps municipal planners combine schedules, stops, ridership estimates, and street-network data in one workspace.

> **Status:** Active development. Use results as planning inputs, not as operational or safety-critical guidance.

## Features

- Import routes, stops, trips, and calendars from GTFS feeds
- View existing and proposed services on an interactive map
- Create, edit, duplicate, and compare route scenarios
- Estimate travel times, service frequency, and stop coverage
- Identify underserved areas using configurable walking-distance thresholds
- Compare weekday, weekend, peak, and off-peak service
- Export scenario summaries as CSV, GeoJSON, and PDF
- Validate common data issues, including disconnected shapes and invalid stop sequences
- Support multiple municipalities and role-based access
- Record scenario changes for review and audit

## Requirements

- Node.js 20 or later
- PostgreSQL 15 or later
- PostGIS 3.3 or later
- Redis 7 or later
- A valid GTFS feed
- A Mapbox access token or compatible map-tile provider

## Installation

Clone the repository and install dependencies:

```bash
git clone https://github.com/example/municipal-transit-planner.git
cd municipal-transit-planner
npm install
```

Create a PostgreSQL database and enable PostGIS:

```sql
CREATE DATABASE transit_planner;
\c transit_planner
CREATE EXTENSION postgis;
```

Copy the example environment file:

```bash
cp .env.example .env
```

Update `.env` with your database, Redis, and map-provider credentials. Then initialize the database:

```bash
npm run db:migrate
npm run db:seed
```

Start the development server:

```bash
npm run dev
```

The application will be available at `http://localhost:3000`.

## Usage

### Import a transit feed

Import a local GTFS archive:

```bash
npm run gtfs:import -- ./data/municipal-feed.zip
```

To replace an existing agency feed:

```bash
npm run gtfs:import -- ./data/municipal-feed.zip --replace
```

The importer validates required files, coordinates, stop sequences, service calendars, and route references. Warnings are saved with the import record and displayed in the administration interface.

### Create a planning scenario

1. Sign in and select a municipality.
2. Open **Scenarios** and choose **New scenario**.
3. Select a base service period and effective date.
4. Add or modify routes, stops, frequencies, and operating hours.
5. Run the coverage and travel-time analysis.
6. Compare the scenario with the current network.
7. Export the results or submit the scenario for review.

### Run analysis from the command line

```bash
npm run analyze -- \
  --scenario "fall-service-2026" \
  --date 2026-09-15 \
  --period weekday-am
```

Export results as GeoJSON:

```bash
npm run export -- \
  --scenario "fall-service-2026" \
  --format geojson \
  --output ./exports/fall-service-2026.geojson
```

### Run tests

```bash
npm test
```

Run linting and type checks:

```bash
npm run lint
npm run typecheck
```

## Configuration

Configuration is loaded from environment variables. Local development values belong in `.env`; production secrets should be supplied through the deployment platform.

| Variable | Required | Default | Description |
|---|---:|---|---|
| `APP_URL` | Yes | `http://localhost:3000` | Public application URL |
| `PORT` | No | `3000` | HTTP server port |
| `NODE_ENV` | No | `development` | Runtime environment |
| `DATABASE_URL` | Yes | — | PostgreSQL connection string |
| `REDIS_URL` | Yes | — | Redis connection string |
| `SESSION_SECRET` | Yes | — | Secret used to sign sessions |
| `MAPBOX_ACCESS_TOKEN` | Yes* | — | Mapbox public access token |
| `MAP_TILE_URL` | No | — | Alternative map-tile URL template |
| `DEFAULT_TIMEZONE` | No | `America/Toronto` | Default IANA timezone |
| `DEFAULT_WALK_DISTANCE_METERS` | No | `400` | Stop-coverage walking threshold |
| `GTFS_MAX_UPLOAD_MB` | No | `100` | Maximum GTFS upload size |
| `ANALYSIS_WORKERS` | No | `2` | Number of background analysis workers |
| `LOG_LEVEL` | No | `info` | Logging verbosity |
| `ENABLE_PDF_EXPORT` | No | `true` | Enables PDF report generation |

\* Not required when `MAP_TILE_URL` is configured.

Example configuration:

```dotenv
APP_URL=http://localhost:3000
PORT=3000
NODE_ENV=development

DATABASE_URL=postgresql://transit:transit@localhost:5432/transit_planner
REDIS_URL=redis://localhost:6379
SESSION_SECRET=replace-with-a-long-random-value

MAPBOX_ACCESS_TOKEN=pk.example
DEFAULT_TIMEZONE=America/Toronto
DEFAULT_WALK_DISTANCE_METERS=400
GTFS_MAX_UPLOAD_MB=100
ANALYSIS_WORKERS=2
LOG_LEVEL=info
ENABLE_PDF_EXPORT=true
```

### Analysis settings

Municipality-specific defaults can be stored in `config/municipalities.yml`:

```yaml
municipalities:
  lakeside:
    name: City of Lakeside
    timezone: America/Toronto
    service_day_start: "03:00"
    walking_distance_meters: 400
    minimum_peak_frequency_minutes: 15
    minimum_off_peak_frequency_minutes: 30
    accessibility:
      require_wheelchair_boarding_data: true
```

Restart the application after changing configuration files.

## Production Deployment

Before deploying:

- Use a strong, unique `SESSION_SECRET`.
- Require TLS for application, database, and Redis connections.
- Restrict database privileges to the application schema.
- Back up imported feeds and planning scenarios.
- Run database migrations before starting new application instances.
- Configure monitoring for failed imports and analysis jobs.

Build and start the production server:

```bash
npm run build
npm start
```

## Data and Privacy

Transit feeds may be public, but scenario notes, draft service changes, and ridership datasets can contain sensitive information. Follow municipal retention, privacy, accessibility, and records-management policies. Do not import personally identifiable rider data.

## License

Licensed under the MIT License. See `LICENSE` for details.