Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

69 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Seoul Public Library Dashboard

Live demo: library-final-five.vercel.app

An interactive dashboard for exploring, comparing, and choosing among Seoul's 215 public libraries — from a city-wide map view down to a single library's real-time seat availability, opening hours, and book collection.

The project combines a lightweight data pipeline (collects and cleans public-data.go.kr API responses + reference datasets) with a React/TypeScript single-page app that visualizes the result through a linked map, ranking list, cross-filter panel, scatter-plot explorer, and per-library detail view.


1. Project Goals

Finding a library to study at in Seoul usually means checking several disconnected sources: a map for location, the library's own site for hours, and a separate portal for real-time seat congestion. This project brings all of that into a single, linked view so a user can:

  1. Compare districts/libraries at a glance — see relative congestion, open seats, hours, and collection size across the whole city on a map.
  2. Rank libraries by personal priorities — weight criteria such as congestion, open seats, closing time, and book collection (optionally by genre) to get a personalized ranking.
  3. Drill into a single library — view its weekly occupancy heatmap, today's hours, live reading-room seat counts, and its book collection (including genre breakdown and yearly acquisition trends).

Map exploration, ranking/filtering, and single-library detail are designed as linked views: a selection made in one view (map, list, heatmap, scatter plot) is reflected in the others.


2. Repository Structure

library_visualization/
├── analysis/                 # Data collection & processing scripts
│   ├── scripts/
│   │   ├── fetch-seoul-library-api.mjs   # Pulls live data from data.go.kr
│   │   ├── fetch_prst_data.py            # (reference) operating-status fetch
│   │   ├── fetch_public_data.py          # (reference) public dataset fetch
│   │   └── generate_heatmap.py           # Builds the processed JSON files
│   ├── notebooks/             # Exploratory analysis (placeholder)
│   └── outputs/               # Analysis artifacts (placeholder)
│
├── data/
│   ├── raw/api/                          # Raw API snapshots (JSON/XML)
│   │   ├── info_v2/                      # 자치단체 공공도서관 통합정보
│   │   ├── prst_info_v2/                 # 운영현황 정보
│   │   └── rlt_rdrm_info_v2/             # 열람실 실시간 정보
│   ├── processed/                        # Cleaned, merged output
│   │   ├── enriched_libraries.json       # 215 libraries, merged + enriched
│   │   └── real_time_heatmap.json        # Realtime seat snapshot
│   ├── reference/                        # Reference/lookup data
│   ├── 서울시 공공도서관 현황정보.csv      # Base library registry (215 libs)
│   └── 공공도서관 통계데이터_*.xlsx        # Genre-level collection statistics
│
└── web/                       # React + TypeScript + Vite application
    ├── public/data/           # Static copies of processed data served to the app
    └── src/
        ├── App.tsx                     # Top-level state & layout
        ├── components/                 # UI components (see §6)
        ├── utils/
        │   ├── score.ts                # Recommendation scoring
        │   └── libNameMatch.ts         # Cross-source library name matching
        ├── types/library.ts            # Shared TypeScript types
        └── styles/global.css           # All styling

3. Data Sources

Source Description Used for
data.go.kr B551982/plr_v2info_v2 자치단체 공공도서관 통합정보 (basic info: address, hours, contact, coordinates) Library metadata
data.go.kr B551982/plr_v2prst_info_v2 자치단체 공공도서관 운영현황 정보 (operating status, reservation availability) Filters (reservation, laptop room)
data.go.kr B551982/plr_v2rlt_rdrm_info_v2 자치단체 공공도서관 열람실 실시간 정보 (live reading-room occupancy) Real-time congestion / seat availability, day-of-week heatmap
서울시 공공도서관 현황정보.csv Base registry of all 215 Seoul public libraries Canonical library list & coordinates
공공도서관 통계데이터 (xlsx) Genre-level collection statistics (10 KDC top-level categories) Genre bar/bubble charts, genre-weighted recommendation
newbook-data.json Yearly new-acquisition counts (2019–2024) per library Collection modal trend chart

The web app never calls these APIs directly. All collection and merging happens offline via the scripts in analysis/scripts/, producing static JSON files that the frontend fetches.


4. Data Pipeline

 data.go.kr (B551982/plr_v2)
        │
        │  node analysis/scripts/fetch-seoul-library-api.mjs
        ▼
 data/raw/api/{info_v2, prst_info_v2, rlt_rdrm_info_v2}/latest.{json,xml,meta.json}
        │
        │  python3 analysis/scripts/generate_heatmap.py
        │  ── merges with ──
        │     data/서울시 공공도서관 현황정보.csv  (215 library registry)
        │     data/공공도서관 통계데이터_*.xlsx     (genre statistics)
        ▼
 data/processed/
   ├── enriched_libraries.json     (215 libraries, merged + enriched)
   └── real_time_heatmap.json      (per-library day×hour occupancy)
        │
        │  cp → web/public/data/processed/
        ▼
 React app  ── fetch('/data/processed/enriched_libraries.json') ──▶  UI

To refresh the dataset with the latest live data:

# 1. Fetch the latest snapshot from data.go.kr
node analysis/scripts/fetch-seoul-library-api.mjs

# 2. Merge it with the reference CSV/XLSX into processed JSON
python3 analysis/scripts/generate_heatmap.py

# 3. Copy the processed output into the web app's public folder
cp data/processed/enriched_libraries.json web/public/data/processed/enriched_libraries.json
cp data/processed/real_time_heatmap.json  web/public/data/processed/real_time_heatmap.json

5. Recommendation Algorithm

Each library receives a score between 0 and 1, computed as a weighted sum of up to four criteria. Users adjust each weight (0–10) with sliders in the sidebar; the relative weights determine each criterion's contribution.

Criterion Weight key Value (0–1) Notes
Congestion congestion 1 − congestionRate 0.5 if no real-time data available
Open seats seats (total − used) / total reading-room seats 0.5 if no real-time data
Closing time lateHours Linear scale: 18:00 → 0, 23:00 → 1 0.5 if hours unknown
Book collection bookCount genreCount / 10,000 (if a genre is selected) or bookCount / 100,000 (overall), capped at 1 Genre is selectable in the sidebar
score = Σ ( value_i × weight_i / Σ weight )

If all weights are 0, every library receives a neutral score of 0.5. Scores drive the map's color scale, the library list ranking, and the Tradeoff Explorer scatter plot.


6. Application Architecture (Linked Views)

The app is a single "Live Recommendation" view built around shared state in App.tsx. A selection in any view updates the others:

                         ┌────────────────────────────┐
                         │          App.tsx            │
                         │  weights · filters · district│
                         │  selectedLibrary · compareLibs│
                         └───────────────┬──────────────┘
        ┌───────────────┬────────────────┼───────────────┬─────────────────┐
        ▼                ▼                ▼               ▼                 ▼
 RecommendSidebar   RecommendMap   CrossFilterPanel   DowHeatmap       LibraryList
 (score weights,    (Leaflet map,  + LibraryUnitGrid  (day × hour      (ranked list,
  district/genre/   colored by      + TradeoffChart    occupancy        tags, "compare"
  laptop filters)    score)         (multi-metric      heatmap, click    selection)
                                     filtering &        to select a
                                     scatter plot)      library)
                                                              │                │
                                                              ▼                ▼
                                                       LibraryDetail     RadarCompare
                                                  (hours · seats ·     (5-axis radar for
                                                   collection ·         up to 2 selected
                                                   genre chart)         libraries)
                                                              │
                                                              ▼
                                                      CollectionModal
                                               (book-count comparison,
                                                genre bubble chart,
                                                yearly acquisition trend)

7. Code File Reference (web/src)

Core

File Description
App.tsx Top-level state (libraries, weights, filters, selection), data loading, and layout composition.
main.tsx React entry point.
types/library.ts Shared EnrichedLibrary / ReadingRoom types matching enriched_libraries.json.

Components

File Description
RecommendSidebar.tsx Score-weight sliders, genre selector, district dropdown, and live-filter checkboxes.
RecommendMap.tsx Leaflet map of all libraries, colored by score, with district drill-down and location-based filters.
CrossFilterPanel.tsx Multi-metric range filters (congestion, seats, hours, books, travel time) shared with the scatter plot.
LibraryUnitGrid.tsx Compact grid of library "unit cells" reflecting cross-filter results.
TradeoffChart.tsx Scatter plot ("Tradeoff Explorer") for comparing any two metrics across libraries, highlighting cross-filter matches.
DowHeatmap.tsx Day-of-week × hour occupancy heatmap (city-wide, per-district, or per-library), built with d3.
LibraryList.tsx Ranked list of libraries with quick-glance tags (seats, hours, laptop room, reservation, top genre) and a "compare" toggle.
RadarCompare.tsx 5-axis radar chart comparing up to two selected libraries (Availability, Open Seats, Hours, Collection, Capacity).
LibraryDetail.tsx Detail panel for the selected library: info, hours, seat layout, and collection summary.
HoursViz.tsx Weekly opening-hours bar visualization.
SeatUnitViz.tsx Reading-room seat availability visualization.
GenreChart.tsx Per-library genre bar chart with hover tooltip and Seoul-average marker, used inside the Collection modal.
GenrePieChart.tsx Top-3-genre donut chart (+ "Others") shown at a glance in the Collection card.
CollectionModal.tsx Modal with total-book comparison (library vs. district vs. Seoul average), full genre bar chart, and yearly new-acquisition trend.

Utilities

File Description
utils/score.ts computeScore() — weighted recommendation scoring; scoreToColor() — score → map color; getDistrictGroups() — per-district aggregation.
utils/libNameMatch.ts Resolves library-name discrepancies between the realtime heatmap dataset and enriched_libraries.json (static mapping + normalized fuzzy matching).

8. Getting Started

Prerequisites

  • Node.js 18+
  • Python 3.9+ (for the data pipeline)

Setup

# Install dependencies
npm --prefix web install

# Add your data.go.kr API key
echo "VITE_DATA_GO_KR_SERVICE_KEY=<your-key>" > .env

Run the app

npm run dev       # start the Vite dev server
npm run build     # production build
npm run preview   # preview the production build

Refresh data

npm run fetch:api                              # fetch latest API snapshot
python3 analysis/scripts/generate_heatmap.py   # rebuild processed JSON

9. Tech Stack

  • Frontend: React 18, TypeScript, Vite
  • Mapping: Leaflet / react-leaflet
  • Charts: Recharts (bar/line/radar/scatter), d3 (heatmap, bubble chart)
  • Data pipeline: Node.js (API fetch), Python + pandas (merging/enrichment)
  • Deployment: Vercel

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages