About This Project
How we built an interactive intelligence map of Indian cuisine across Greater Melbourne, using open government data, place APIs, and census demographics.
Data Sources
The Australian Bureau of Statistics Census 2021 provides population demographics at the SA2 (Statistical Area Level 2) geographic level. We extract three key datasets from the General Community Profile DataPack for Victoria.
Fields Used
- G09G — Country of Birth:
P_India_Tot — total persons born in India per SA2
- G01 — Selected Person Characteristics:
Tot_P_P — total population (both sexes) per SA2
- G02 — Selected Medians and Averages:
Median_tot_hhd_inc_weekly — median weekly household income, converted to annual (* 52)
SA2 Boundaries
SA2 polygon boundaries are fetched from the ABS ArcGIS REST API at geo.abs.gov.au, filtered to the Greater Melbourne GCCSA region. Geometries are simplified (Douglas-Peucker tolerance ~100m) for web rendering.
The Google Places API (New) provides the most comprehensive restaurant data, including ratings and review counts. We search for "Indian restaurant" across 30 key Melbourne suburbs, covering both inner city and outer growth corridors.
Fields Used
displayName — restaurant name
location — latitude and longitude
rating — average star rating (1–5)
userRatingCount — total number of reviews
formattedAddress — street address
priceLevel — price bracket (0–4)
Suburbs Searched
Dandenong, Clayton, Truganina, Tarneit, Point Cook, Glen Waverley, Craigieburn, Werribee, Springvale, Footscray, Melbourne CBD, Epping, Berwick, St Albans, Caroline Springs, Hoppers Crossing, Deer Park, Sunshine, Narre Warren, Cranbourne, Pakenham, Box Hill, Preston, Reservoir, Melton, Richmond, South Yarra, Roxburgh Park, Wyndham Vale, Williams Landing.
Foursquare's Open Source Places dataset on HuggingFace contains ~100M+ places globally. We download only the Australian Parquet partitions (files 98 & 99, ~240 MB total), filter to the Melbourne bounding box, and identify Indian restaurants by name keywords.
Fields Used
name — place name
latitude / longitude — coordinates
locality — suburb name
postcode — postal code
address — street address
fsq_category_ids — Foursquare taxonomy category IDs
Filtering Method
Indian restaurants are identified by matching name keywords (indian, curry, dosa, tandoori, biryani, masala, sri lanka, nepal, etc.) and then filtered to exclude non-restaurant places (grocery stores, taxis, salons) using both category IDs and name exclusions.
The Census of Land Use and Employment (CLUE) dataset from Melbourne's Open Data portal provides official business registration data for cafes and restaurants within the City of Melbourne municipality (CBD and immediate surrounds only).
Fields Used
trading_name — business trading name
latitude / longitude — coordinates
business_address — street address
number_of_seats — seating capacity
clue_small_area — sub-area within the municipality
census_year — year of survey (latest: 2023)
Methodology
1
Fetch SA2 Boundaries
Download 361 SA2 polygon boundaries for Greater Melbourne from the ABS ArcGIS REST API. Simplify geometries for web display.
2
Join Census Demographics
Download the ABS Census 2021 DataPack for Victoria. Extract Indian-born population (G09G), total population (G01), and median income (G02). Join to SA2 boundaries by SA2 code. Compute india_pct = india_born / total_pop * 100.
3
Collect Restaurant Data
Fetch Indian restaurants from three sources: Foursquare (Parquet download + keyword filter), Google Places API (text search across 30 suburbs), and CLUE (Melbourne Open Data API). Deduplicate across sources by name + location proximity.
4
Cuisine Classification
Each restaurant is classified into a sub-cuisine by keyword matching on its name: South Indian, North Indian, Sri Lankan, Nepalese, Bangladeshi, or General Indian.
5
Spatial Join & Gap Analysis
Restaurants are spatially joined to SA2 polygons to count restaurants per suburb. An opportunity (gap) score is computed for each SA2 area.
Opportunity Score Formula
opportunity_score = (
(india_pct / max_india_pct) * 0.4 +
(median_income / max_income) * 0.2 +
(1 - supply_per_10k / max_supply) * 0.4
)
Where supply_per_10k = number of Indian restaurants per 10,000 Indian-born residents in that SA2. A high score indicates high Indian population, decent income, but few restaurants — a market opportunity.
Gap Classification
- Underserved: fewer than 4 restaurants per 10,000 Indian residents
- Balanced: 4–8 restaurants per 10,000 Indian residents
- Well-served: more than 8 restaurants per 10,000 Indian residents
Demand Score (per restaurant)
demand_score = rating * log10(review_count + 1)
This normalises for both rating quality and volume. A 4.5-star restaurant with 500 reviews scores higher than a 5-star restaurant with 3 reviews.
Deduplication
Restaurants appearing in multiple sources are deduplicated by matching on name (case-insensitive) and coordinates (rounded to 3 decimal places, ~100m). When a duplicate is found, the Google Places version is preferred (it has ratings and reviews), followed by Foursquare, then CLUE.
Limitations
- Census data is from 2021. Melbourne's Indian population has grown significantly since then, particularly in western growth corridors (Tarneit, Truganina, Wyndham Vale). Real 2026 figures are likely 20–40% higher in these areas.
- Google Places returns max 20 results per query. Suburbs with more than 20 Indian restaurants (e.g. Dandenong, Melbourne CBD) are under-counted.
- CLUE data covers CBD only. The City of Melbourne CLUE dataset only includes businesses within the municipality of Melbourne. Outer council areas are not covered.
- Foursquare data has no ratings. Foursquare OSP does not include ratings or review counts, so these restaurants show 0 for both fields.
- Cuisine classification is heuristic. Sub-cuisine (South Indian vs North Indian etc.) is inferred from restaurant names using keyword matching. This is imperfect — a restaurant called "Spice Village" is classified as General even if it serves South Indian food.
- Some false positives may exist. Keyword-based filtering from Foursquare may include non-Indian restaurants that happen to have matching words in their name (e.g. "Spice" in a non-Indian context).
Tech Stack
- Map: MapLibre GL JS — open-source map rendering with dark CartoDB base tiles
- Data pipeline: Python (pandas, geopandas, pyarrow, requests, shapely)
- Dashboard: Single HTML file with vanilla JavaScript — no build step, no framework dependencies
- Hosting: Static files on GitHub Pages
Source Code
The data pipeline (prepare_data.py) and dashboard source are open source. The pipeline fetches live data from all sources and generates the data/dashboard_data.js file that powers the map.
Disclaimer: This is an independent data visualisation project for educational and analytical purposes. It is not affiliated with the Australian Bureau of Statistics, Google, Foursquare, or the City of Melbourne. Restaurant data may be incomplete, outdated, or contain errors. Do not use this data as the sole basis for business decisions.