# Project export: Foot Traffic Finder

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 12.0
- Tagline: Helping small business in San Francisco optimize staffing, and sharing the best locations for maximized profits, by forecasting foot traffic levels. Utilizes Google Places API!
- Devpost: https://devpost.com/software/foot-traffic-finder
- GitHub: https://github.com/Thinsang/calhacks2025
- Video: https://www.youtube.com/embed/4aWZ7sHxQlg?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — thinsang (9 commits)

## Devpost submission (written by the team)

### Inspiration

San Francisco’s food-truck and small-vendor scene thrives on location, timing, and crowd flow. Yet most operators still rely on gut instinct or last week’s experience to choose spots. We wanted to bring data-driven insight—the kind big retailers use—to small businesses that move every day.

### What it does

Foot Traffic Finder is a live, predictive map that shows how busy each area of San Francisco is now—and how busy it will be in the next few days. Users can search any location to get a future forecast and a Gemini-generated summary explaining events and why it is rated that score. The goal: help small businesses optimize staffing, inventory, and daily placement for maximum profit.

### How we built it

Frontend: Next.js + TypeScript, styled with Tailwind CSS and Radix UI, animated via framer-motion. Map: Mapbox GL JS for a full-screen interactive experience with dynamic data fetching (React Query). Backend: FastAPI (Python) serving endpoints for weather, events, and traffic data. Prediction Model: Multiple Linear Regression (scikit-learn) trained on historical “Popular Times,” weather forecasts (Open-Meteo), and event data (Google Events via SerpApi). AI Summaries: Google Gemini API turns prediction data into human-readable insights.

### Challenges we ran into

Handling API rate limits and combining asynchronous data sources (weather + events + traffic).

### Accomplishments we're proud of

Built a fully interactive, predictive map that is user-friendly! Utilized APIs by debugging over several cruel nights!

### What we learned

How important small UX/UI touches are Best practices for utilizing the API calls without running into rate limits

### What's next

Integrate a more robust statistical prediction for future dates, linear regression is an OK start, but we could utilize machine learning models with confidence intervals to make our data even MORE accurate!

## README (from the GitHub repository)

# Foot Traffic Finder

Created by: Thinsang Tamanag, Michael Zheng, Simon Lamichhane

A modern, data-driven web application that provides a live and predictive foot traffic map of San Francisco, designed to help food truck owners find optimal locations.

## Core Features

-   **Live & Predictive Map:** A full-screen, interactive Mapbox map visualizing real-time and predicted foot traffic across San Francisco.
-   **Dynamic Visualization:** The map displays a color-coded heat map indicating busyness levels from low to high.
-   **Interactive Popups & Legend:** Hover over a location to see its name and busyness score. A clear legend explains the color-coding.
-   **Prediction Engine:** Select a location and a future date to receive a foot traffic prediction based on historical data, weather, and local events.
-   **AI-Powered Summaries:** An integrated Gemini model provides a concise, human-readable summary of the prediction.

## Tech Stack

-   **Frontend:** Next.js (TypeScript), Tailwind CSS, Radix UI, `framer-motion`, Mapbox GL JS, React Query.
-   **Backend:** Python (FastAPI), Scikit-learn, Google Gemini.

## Data Sources

-   **Foot Traffic:** Google Maps "Popular Times" data (via OutScraper API or similar source).
-   **Events:** Google Events API via SerpApi.
-   **Weather:** Open-Meteo API.

## Quickstart

### 1. Backend Setup

First, set up and run the FastAPI server.

```powershell
# 1. Create a virtual environment (only once)
python -m venv .venv

# 2. Activate it (run this every time you open a new terminal)
.\.venv\Scripts\Activate.ps1

# 3. Install dependencies
pip install -r backend/requirements.txt

# 4. Set up environment variables
# Copy `backend/ENV.example` to `backend/.env` and fill in your API keys:
# OUTSCRAPER_API_KEY (foot traffic)
# SERPAPI_API_KEY (events)
# GEMINI_API_KEY (AI summaries)

# 5. Run the server
cd backend
uvicorn app.main:app --reload --port 8000
```

The backend will be running at `http://localhost:8000`.

### 2. Frontend Setup

In a **new terminal**, set up and run the Next.js frontend.

```powershell
# 1. Navigate to the frontend directory
cd frontend

# 2. Install dependencies
npm install

# 3. Set up environment variables
# Create `frontend/.env.local` and add:
# NEXT_PUBLIC_MAPBOX_TOKEN=pk.YOUR_MAPBOX_TOKEN_HERE
# NEXT_PUBLIC_API_BASE=http://localhost:8000

# 4. Run the development server
npm run dev
# or specify the port explicitly
npx next dev -p 3000
```

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



## Detected evidence (automated analysis)

Indexed codebase: 40 recognized source files, 84 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- Next.js (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Google Gemini (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (48 of 48)

```
.gitignore
backend/app/__init__.py
backend/app/api/__init__.py
backend/app/api/routes/__init__.py
backend/app/api/routes/events.py
backend/app/api/routes/foot_traffic.py
backend/app/api/routes/predict_llm.py
backend/app/api/routes/predict.py
backend/app/api/routes/weather.py
backend/app/core/__init__.py
backend/app/core/config.py
backend/app/main.py
backend/app/models/__init__.py
backend/app/models/prediction_model.py
backend/app/services/__init__.py
backend/app/services/events_service.py
backend/app/services/foot_traffic_service.py
backend/app/services/predict_service.py
backend/app/services/weather_service.py
backend/ENV.example
backend/README.md
backend/requirements.txt
frontend/next.config.mjs
frontend/package.json
frontend/postcss.config.mjs
frontend/README.md
frontend/src/app/layout.tsx
frontend/src/app/page.tsx
frontend/src/app/providers.tsx
frontend/src/components/map/map-controls.tsx
frontend/src/components/map/map-legend.tsx
frontend/src/components/map/map-view.tsx
frontend/src/components/ui/button.tsx
frontend/src/components/ui/calendar.tsx
frontend/src/components/ui/popover.tsx
frontend/src/components/ui/scroll-area.tsx
frontend/src/components/ui/select.tsx
frontend/src/components/ui/theme-toggle.tsx
frontend/src/features/filters/floating-controls.tsx
frontend/src/features/filters/widgets/date-picker.tsx
frontend/src/features/filters/widgets/location-input.tsx
frontend/src/features/filters/widgets/prediction-card.tsx
frontend/src/styles/globals.css
frontend/src/types/env.d.ts
frontend/tailwind.config.ts
frontend/tsconfig.json
package.json
README.md
```

### Dependencies

- backend/requirements.txt: fastapi@==0.115.5, google-generativeai@>=0.7.0, httpx@==0.27.2, numpy@==2.1.2, pandas@==2.2.3, pydantic-settings@==2.6.1, scikit-learn@==1.5.2, uvicorn[standard]@==0.32.1
- frontend/package.json: @radix-ui/react-label@^2.0.2, @radix-ui/react-popover@^1.0.7, @tanstack/react-query@^5.56.2, @types/node@^20.14.12, @types/react@^18.2.74, @types/react-dom@^18.2.25, autoprefixer@^10.4.20, clsx@^2.1.1, eslint@^8.57.0, eslint-config-next@^14.2.12, lucide-react@^0.460.0, mapbox-gl@^2.15.0, next@^14.2.12, next-themes@^0.3.0, postcss@^8.4.47, react@^18.3.1, react-day-picker@^8.10.1, react-dom@^18.3.1, react-map-gl@^7.1.6, tailwindcss@^3.4.14, tailwindcss-animate@^1.0.7, typescript@^5.6.3
- package.json: class-variance-authority@^0.7.1, date-fns@^4.1.0, framer-motion@^12.23.24

### Recent commits (newest first)

- feat: refined AI summary (no duplicate score, richer rationale) and tightened UI wrapping; logo badge updates
- chore(frontend): add logo.png to public for top-left badge
- docs: update README with authors and corrected setup (ports, APIs)
- chore: legend cleanup and ignore tweaks
- feat: backend prediction improvements (date-aware weather, ML integration), CORS tightening; frontend map interactivity and API base default; ignore .next artifacts
- ui: bottom-right prediction panel overlay + glass translucency\n- Move panel into map overlay, align with legend\n- Increase spacing, add glass-light sections, refined divider\n- Improve prediction card translucent backgrounds and layout
- chore: switch Popular Times source to OutScraper; remove Google Places/populartimes\n- Add OUTSCRAPER_API_KEY env and examples\n- Implement OutScraper query for single place popular_times\n- Keep bounds mode mocked for now\n- Update requirements (drop populartimes)
- feat: Apple-inspired UI/UX redesign with glassmorphism
- Initial commit

## Key source files (fetched from GitHub, selected and truncated for size)

### package.json

```
{
  "dependencies": {
    "class-variance-authority": "^0.7.1",
    "date-fns": "^4.1.0",
    "framer-motion": "^12.23.24"
  }
}

```

### backend/requirements.txt

```
fastapi==0.115.5
uvicorn[standard]==0.32.1
httpx==0.27.2
pydantic-settings==2.6.1
scikit-learn==1.5.2
numpy==2.1.2
pandas==2.2.3
google-generativeai>=0.7.0


```

### frontend/package.json

```
{
  "name": "frontend",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@radix-ui/react-label": "^2.0.2",
    "@radix-ui/react-popover": "^1.0.7",
    "@tanstack/react-query": "^5.56.2",
    "autoprefixer": "^10.4.20",
    "clsx": "^2.1.1",
    "lucide-react": "^0.460.0",
    "mapbox-gl": "^2.15.0",
    "next": "^14.2.12",
    "next-themes": "^0.3.0",
    "postcss": "^8.4.47",
    "react": "^18.3.1",
    "react-day-picker": "^8.10.1",
    "react-dom": "^18.3.1",
    "react-map-gl": "^7.1.6",
    "tailwindcss": "^3.4.14",
    "tailwindcss-animate": "^1.0.7"
  },
  "devDependencies": {
    "@types/node": "^20.14.12",
    "@types/react": "^18.2.74",
    "@types/react-dom": "^18.2.25",
    "eslint": "^8.57.0",
    "eslint-config-next": "^14.2.12",
    "typescript": "^5.6.3"
  }
}


```

### backend/app/main.py

```python
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from app.core.config import settings
from app.api.routes.weather import router as weather_router
from app.api.routes.events import router as events_router
from app.api.routes.foot_traffic import router as foot_router
from app.api.routes.predict import router as predict_router
from app.api.routes.predict_llm import router as predict_llm_router


app = FastAPI(title="SF Food Truck Spot Finder API")

app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.cors_allow_origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

app.include_router(weather_router, prefix="/api/weather", tags=["weather"])
app.include_router(events_router, prefix="/api/events", tags=["events"])
app.include_router(foot_router, prefix="/api/foot-traffic", tags=["foot-traffic"])
app.include_router(predict_router, prefix="/api/predict", tags=["predict"])
app.include_router(predict_llm_router, prefix="/api/predict-llm", tags=["predict-llm"])


@app.get("/api/health")
def health():
    return {"status": "ok"}



```

### frontend/src/app/page.tsx

```typescript
"use client";

import MapView from "@/components/map/map-view";

export default function Page() {
  return (
    <main className="relative h-screen w-screen overflow-hidden bg-background">
      <MapView />
    </main>
  );
}

```

### frontend/src/app/layout.tsx

```typescript
import "@/styles/globals.css";
import type { Metadata } from "next";
import { ThemeProvider } from "@/lib/theme-provider";
import { Providers } from "./providers";
import { cn } from "@/lib/utils";
import { Inter } from "next/font/google";

const inter = Inter({ 
  subsets: ["latin"], 
  variable: "--font-inter",
  weight: ["400", "500", "600", "700"],
  display: "swap"
});

export const metadata: Metadata = {
  title: "Foot Traffic Finder",
  description: "Live and predicted foot traffic for better food truck spots"
};

export const viewport = {
  width: "device-width",
  initialScale: 1,
  maximumScale: 1
};

export default function RootLayout({
  children
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body className={cn("min-h-screen bg-background antialiased font-sans", inter.variable)}>
        <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
          <Providers>{children}</Providers>
        </ThemeProvider>
      </body>
    </html>
  );
}



```

### frontend/tailwind.config.ts

```typescript
import type { Config } from "tailwindcss";

const config: Config = {
  darkMode: ["class"],
  content: [
    "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}"
  ],
  theme: {
    extend: {
      fontFamily: {
        sans: ["var(--font-inter)", "ui-sans-serif", "system-ui"],
      },
      colors: {
        background: "hsl(var(--background))",
        foreground: "hsl(var(--foreground))",
        primary: {
          DEFAULT: "hsl(var(--primary))",
          foreground: "hsl(var(--primary-foreground))"
        },
        secondary: {
          DEFAULT: "hsl(var(--secondary))",
          foreground: "hsl(var(--secondary-foreground))"
        },
        muted: {
          DEFAULT: "hsl(var(--muted))",
          foreground: "hsl(var(--muted-foreground))"
        },
        accent: {
          DEFAULT: "hsl(var(--accent))",
          foreground: "hsl(var(--accent-foreground))"
        },
        card: {
          DEFAULT: "hsl(var(--card))",
          foreground: "hsl(var(--card-foreground))"
        },
        border: "hsl(var(--border))",
        input: "hsl(var(--input))",
        ring: "hsl(var(--ring))"
      },
      borderRadius: {
        lg: "var(--radius)",
        md: "calc(var(--radius) - 2px)",
        sm: "calc(var(--radius) - 4px)"
      },
      keyframes: {
        "accordion-down": {
          from: { height: "0" },
          to: { height: "var(--radix-accordion-content-height)" }
        },
        "accordion-up": {
          from: { height: "var(--radix-accordion-content-height)" },
          to: { height: "0" }
        }
      },
      animation: {
        "accordion-down": "accordion-down 0.2s ease-out",
        "accordion-up": "accordion-up 0.2s ease-out"
      }
    }
  },
  plugins: [require("tailwindcss-animate")]
};

export default config;



```

### backend/app/__init__.py

```python
# pkg


```

### backend/app/api/__init__.py

```python
# pkg


```

### backend/app/core/__init__.py

```python
# pkg


```

[30 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]