Election data belongs to the people. This project is one step toward making it truly accessible.
The Election Commission of India publishes results as PDFs β opaque, unsearchable, and impossible to analyze at scale. We believe that public data should be public: open, structured, visual, and free. This project transforms official ECI Form 20 data into an interactive, village-level analytics platform that anyone can explore β no data science degree required.
If you believe in electoral transparency, open government data, and civic empowerment β this is for you.
An open-source, interactive village-level electoral analytics platform for the 2026 Tamil Nadu Legislative Assembly Election, visualizing voting patterns across 9,809 villages in 234 Assembly Constituencies.
Key question this answers: How did each village in Tamil Nadu vote in the 2026 Assembly Election?
- Open the heatmap β Simply open
site/tn_heatmap_v2.htmlin a modern browser (Chrome recommended) - Wait for load β The file is ~39.5 MB (all data is embedded). First load takes 10β30 seconds
- Explore! β The page has two main areas: the interactive map (left) and the analytics panel (right)
π‘ Tip: For best performance, serve locally:
cd site && python -m http.server 8000, then openhttp://localhost:8000/tn_heatmap_v2.html
Use the π¨ Mode control panel (top-left of the map) to switch between:
| Mode | What It Shows |
|---|---|
| Winner + Margin (default) | Village color = winning party. Intensity shows margin strength (dark = landslide, light = close fight) |
| Vote Blend | Village color = a blend of all parties' vote shares. Shows multi-party competition visually |
| Alliance | Village color = alliance (TVK, SPA, NDA, NTK, Others). Aggregates coalition performance |
| None | No village shading β useful for viewing AC boundaries clearly |
- AC Bounds checkbox β Show/hide constituency boundary lines (dashed)
- AC Labels checkbox β Show/hide constituency number/name labels
- Legend (bottom-left) β Shows party colors with village counts. Click any party to dim it on the map. Click again to restore. Expand/collapse with the βΌ button.
- π±οΈ Click any village β Opens a detail panel on the right showing:
- Village name, district, AC number
- Full candidate-wise vote breakdown with horizontal bar charts
- Winner, runner-up, and margin
- Total valid votes
- π Search (top bar) β Type a village name, district, or AC number to find and zoom to it
- Zoom/Pan β Standard Leaflet map controls (scroll wheel, drag, double-click to zoom)
- βΈ/β Toggle button β Collapse or expand the right analytics panel
- Dashed lines show constituency boundaries (dissolved from village polygons)
- Click any AC label on the map to zoom directly to that constituency
The right panel has 10 tabs with different analytical views:
At-a-glance summary cards:
- Total villages, ACs, booths, and vote counts
- Party wins breakdown (village-level and AC-level)
- Alliance performance summary
- Key stats at a glance
How many villages did each party come in 1st, 2nd, 3rd, or 4th+ place?
- Table showing position breakdown per party
- Filter by position (1st only, 2nd only, etc.)
- Bar visualization of party strength across all position tiers
- Use this to find parties that are strong runner-ups but rarely win
- Table of all districts ranked by number of villages
- Shows top 3 winning parties per district with village counts
- Party-wise breakdown by district
Select any constituency for village-level breakdown:
- Dropdown to pick any AC (1β234)
- Shows: AC winner, margin, total votes, number of villages
- Table of all villages in that AC ranked by winner's vote share
- Each village shows: winner party, votes, margin percentage
Find the razor-thin village-level battles:
- Filter by margin threshold: <5%, <10%, <15%, <20%
- Table of villages where winner and runner-up were separated by tiny margins
- Sorted by margin (smallest first)
- These are the "every vote counts" villages
Aggregate performance by coalition:
- Alliance summary cards (TVK, SPA, NDA, NTK, Others)
- Position breakdown per alliance (1st/2nd/3rd across villages)
- Alliance strength by district β where each coalition dominates
How deep does each party's support run?
- Threshold filter: Show villages where a party crossed 10%, 15%, 20%, 25%, or 30% of votes
- Table showing: for each party, how many villages it crossed the threshold in
- Ranked by number of villages at each penetration level
- Use this to measure a party's grassroots presence beyond just wins
Data quality and completeness:
- Contest classification: Safe / Comfortable / Close / Very Close / Tie
- Booth coverage by AC β how many villages have data vs total
- Identifies ACs with low village coverage
Extreme village-level statistics:
- Closest Margins β Photo finishes (smallest win margins)
- Biggest Blowouts β Most lopsided victories
- Most Booths β Villages with the most polling stations
- Highest Votes β Villages with the highest total vote counts
Side-by-side comparison of two constituencies:
- Select AC 1 and AC 2 from dropdowns
- Shows: winner comparison, margin comparison, total votes
- Party-wise village position breakdown for both ACs
- Great for comparing similar or neighboring constituencies
The repo also includes site/tn_analytics.html β a standalone analytics dashboard (no map) with all the same tab features. Useful for:
- Slower machines that struggle with the 39.5 MB heatmap
- Quick analytics without loading the map
- Embedded analytics in other pages
We believe in radical transparency about what our data covers β and what it doesn't.
| Metric | Value |
|---|---|
| Total ACs in Tamil Nadu | 234 |
| ACs with Form20 data | 218 (93.2%) |
| ACs with station-level votes | 218 |
| ACs with electors data | 206 (88.0%) |
| ACs entirely missing | 16 (6.8%) |
| Metric | Our Data (218 ACs) | Gap | Notes |
|---|---|---|---|
| Registered electors | 5.03 Cr | ~1.83 Cr missing | We cover ~73% of TN's ~6.86 Cr electors |
| Station-level valid votes | 4.27 Cr (42,662,846) | β | This is the figure shown on the heatmap |
| AC-level valid votes | 3.72 Cr (37,190,300) | β | Includes EVM + postal at AC level |
| Polling stations | 71,639 | β | With candidate-wise vote breakdowns |
| Estimated turnout (covered ACs) | ~74% | β | Based on AC-level totals |
| Estimated missing votes | ~1.5β2 Cr | From 16 missing ACs + 12 partial ACs | These votes are invisible on our map |
β οΈ What this means: The heatmap shows 4.27 Cr valid votes from 218 ACs. The official Tamil Nadu total includes all 234 ACs with approximately 6.86 Cr registered electors. Our map does not represent roughly 28 ACs worth of votes (16 entirely missing + 12 with partial data). This is approximately 25β27% of Tamil Nadu's electorate that is invisible on the map.
| Metric | Value |
|---|---|
| Total village polygons | 18,165 (Survey of India Census 2011) |
| Villages with matched data | 9,809 |
| Stations matched to villages | 65,717 / 68,890 (92.0%) |
| Unmatched stations | 3,173 (4.6%) β mostly urban or name-mismatch |
| Region | Missing ACs | Count | Likely District |
|---|---|---|---|
| Perambalur | AC147 | 1 | Perambalur |
| Cuddalore/Ariyalur | AC151βAC159 | 9 | Cuddalore, Ariyalur |
| Thanjavur/Pudukkottai | AC178βAC183 | 6 | Thanjavur, Pudukkottai |
Additionally, 12 ACs (AC027, AC030, AC031, AC034, AC035, AC054, AC229βAC234) have station-level vote data but no electors/turnout data at the AC level.
These constituencies appear as grey/empty on the heatmap.
Form20 PDF β Station-level votes β Name-matching β Village assignment β Heatmap
- Form20 Parsing: Official ECI Form20 PDFs are parsed to extract station-level vote counts for each candidate
- Name Matching: Polling station names are matched to village names from Survey of India boundary data
- Vote Assignment: Station votes are assigned to the matched village
- Aggregation: If multiple stations match a village, their votes are summed
- Visualization: The resulting village-level data is color-coded and rendered on the map
-
Station-to-Village Matching (65.6%): Only 45,193 of 68,890 stations could be matched. Unmatched stations are in urban areas or have naming discrepancies. Their votes are not represented on the map. The map understates actual votes in many villages.
-
Village Boundaries Are Approximate: Survey of India polygons (Census 2011) may not align with actual polling booth service areas. A single station may serve multiple villages β all votes are assigned to one village.
-
No Official Village-Level Data: The ECI does not publish village-level results. Our village vote counts are estimates derived from station-level data. They should not be cited as official figures.
-
Missing 28 ACs: 16 ACs have zero Form20 data. 12 more lack electors/turnout data. Any statewide analysis is incomplete.
-
Candidate Name Variations: Names parsed from PDFs may have minor spelling inconsistencies.
-
AC Boundaries: Dissolved from village polygons, not from official delimitation shapefiles. May have slight inaccuracies.
-
No Postal Votes in Map: Station-level data is EVM only. Postal votes (typically 1β2% of total) are not distributed to villages.
-
Large File Size: ~39.5 MB HTML with embedded GeoJSON. Slow on mobile or weak connections.
| Component | Technology |
|---|---|
| Map Rendering | Leaflet.js 1.9.4 |
| Basemap | CARTO Positron (light tiles) |
| GeoJSON Processing | Shapely (Python) |
| Data Pipeline | Python 3.12 |
| Boundary Data | Survey of India Village Census 2011 |
| Election Data | ECI Form 20 (Official Results) |
| Output Format | Single self-contained HTML file |
electoral-v4/
βββ site/
β βββ tn_heatmap_v2.html # πΊοΈ Main deliverable (39.5 MB, self-contained)
β βββ tn_analytics.html # π Analytics-only dashboard (no map)
βββ scripts/
β βββ 14_build_v2.py # Build pipeline (Python, ~2300 lines)
βββ data/
β βββ form20_raw.json # Parsed Form20 election data (218 ACs) [gitignored]
β βββ wiki_results.json # Wikipedia-sourced AC results (234 ACs)
β βββ wiki_candidates.json # Wikipedia-sourced candidate lists
βββ booth/ # Booth structure data (234 AC JSONs) [gitignored]
βββ form20-pdf/ # Original Form20 PDFs [gitignored]
βββ TAMIL NADU_VILLAGES.geojson # Village boundary data (93 MB) [gitignored]
βββ README.md
# Simply open in a browser (Chrome recommended)
start site/tn_heatmap_v2.html # Windows
open site/tn_heatmap_v2.html # macOS
xdg-open site/tn_heatmap_v2.html # Linux
β οΈ The file is ~39.5 MB. First load may take 10β30 seconds.
cd site
python -m http.server 8000
# Open http://localhost:8000/tn_heatmap_v2.html# Prerequisites: Python 3.12+, shapely
pip install shapely
# Run the build pipeline
python scripts/14_build_v2.py
# Output: site/tn_heatmap_v2.html| Party | Abbrev | Color | Alliance |
|---|---|---|---|
| Tamilaga Vettri Kazhagam | TVK | π’ Dark Green | TVK (Standalone) |
| Dravida Munnetra Kazhagam | DMK | π΄ Red | Secular Progressive Alliance (SPA) |
| Indian National Congress | INC | π΅ Sky Blue | SPA |
| Desiya Murpokku Dravida Kazhagam | DMDK | οΏ½ Medium Purple | SPA (Secular Progressive Alliance) |
| Viduthalai Chiruthaigal Katchi | VCK | π£ Purple | SPA |
| Communist Party of India | CPI | π₯ Dark Red | SPA |
| Communist Party of India (Marxist) | CPI(M) | π₯ Crimson | SPA |
| Indian Union Muslim League | IUML | π© Forest Green | SPA |
| All India Anna Dravida Munnetra Kazhagam | AIADMK | π Orange | NDA |
| Bharatiya Janata Party | BJP | π€ Dark Orange | NDA |
| Pattali Makkal Katchi | PMK | π‘ Gold | NDA |
| Amma Makkal Munnetra Kazhagam | AMMK | π« Chocolate | NDA |
| Naam Tamilar Katchi | NTK | π· Royal Blue | NTK (Standalone) |
| Other parties / Independents | IND/Others | βͺ Grey | β |
| Source | Description | License |
|---|---|---|
| Election Commission of India | Form 20 (official constituency-wise results) | Public government data |
| Survey of India | Village boundary polygons (Census 2011) | Government open data |
| Wikipedia | AC-level results, candidate lists | CC-BY-SA |
| Booth-level mapping | Polling station to village name mapping | Crowd-sourced |
The build pipeline (scripts/14_build_v2.py, ~2300 lines) performs:
- Load booth data (234 AC files with polling station structure)
- Load Form20 data (218 ACs with station-level vote counts)
- Load village GeoJSON (93 MB boundary file, 18,165 villages)
- Name-match stations to villages (exact string matching on village names)
- Distribute votes from stations to matched villages
- Dissolve village polygons into AC boundaries (using Shapely)
- Generate analytics (party positions, alliance data, district summaries)
- Optimize GeoJSON (round coordinates to 5 decimal places β 40% size reduction)
- Output single HTML with all data embedded (CSS + JS + GeoJSON inline)
Build time: ~3β5 minutes (primarily I/O bound on the 93 MB GeoJSON)
Contributions are welcome! Areas of particular interest:
- Missing Form20 data: If you have Form20 PDFs for the 16 missing ACs (AC147, AC151β159, AC178β183), please open an issue or PR
- Better village matching: Improved station-to-village name matching algorithms (fuzzy matching, geospatial proximity)
- Performance optimization: Reduce the 39.5 MB output size (tiling, lazy loading, compression)
- Additional visualizations: New map layers, chart types, or analytical views
- Mobile responsiveness: The current layout is optimized for desktop
- Accessibility: Screen reader support, keyboard navigation, high-contrast mode
This project is for educational and research purposes only.
- Election data: Sourced from Election Commission of India (Form 20), which is public government data
- Boundary data: Survey of India village boundaries, Government of India open data
- Code: MIT License
This is an independent, non-official visualization of publicly available election data. It is NOT affiliated with or endorsed by the Election Commission of India (ECI).
1. Indicative, not definitive. Village-level vote estimates are derived by mapping polling station data to village boundaries using name-matching algorithms. These estimates are indicative only and should NOT be treated as official ECI figures. Official results are published only at the constituency (AC) level by the ECI.
2. Booth clubbing affects accuracy. Multiple polling stations are often grouped (clubbed) under a single station name or mapped to the same village. Where clubbing occurs, votes are attributed to the matched village but may not precisely reflect that village's actual voting pattern. Direct village-to-village comparisons may not be accurate.
3. Name-matching is imperfect. Station-to-village matching uses string-based name comparison. Variations in spelling (English transliteration of Tamil names), abbreviated names, and urban station naming conventions cause ~4.6% of stations to remain unmatched. Matched results may also contain errors where similar village names exist in the same AC.
4. Candidate coverage is limited. Candidate-level vote data is sourced from Wikipedia, which typically lists only the top 3 candidates per AC (out of an average of ~18 candidates). All remaining candidates' votes are aggregated under "Others." This means:
- Naam Tamilar Katchi (NTK) votes are largely invisible at the village level, as NTK candidates rarely finish in the top 3 and are not individually tracked in our source data. NTK is a significant party that consistently polls in the range of 3β8% across Tamil Nadu β their votes are absorbed into "Others" at the booth/village level.
- Other smaller parties and most independents similarly have their votes aggregated into "Others."
5. Form20 data gaps. Form20 (station-level) data is available for only 218 of 234 ACs. The remaining ~16 ACs (primarily in Krishnagiri and Dharmapuri districts) have no station-level breakdown β only AC-level totals from Wikipedia.
6. Known data quality issues. Approximately 12 ACs have Form20 data that appears incomplete or inconsistent (near-zero vote totals), likely due to PDF parsing errors. These ACs may show misleadingly low vote counts at the village level. Affected ACs include: AC027, AC030β035, AC057β061.
7. Boundary data is from Census 2011. Village boundaries are based on Survey of India Census 2011 data. Boundary changes, new villages, merged villages, or urban reclassification since 2011 are not reflected.
8. Alliance assignments. Alliance groupings (TVK, SPA, NDA, NTK) are based on publicly reported seat-sharing agreements and may not reflect every individual party's formal alliance declaration. Independent candidates (IND) are classified under "Others."
In summary: treat this visualization as an exploratory tool for understanding broad electoral patterns, not as a source of precise vote counts. For official results, always refer to the Election Commission of India.
- Election Commission of India for making Form 20 data publicly available
- Survey of India for village boundary data
- Leaflet.js and the open-source mapping community
- All contributors and data validators
- Every citizen who believes that public data should be public
Built with β€οΈ for electoral transparency and civic empowerment
Because democracy deserves better than PDFs.