Which MRT station has the most bicycle racks?
Pick your station to see its bicycle racks, how many are sheltered, and how it compares with the middle station. LTA's list of 12 Oct 2026 names 35,502 racks at 153 stations.
How many bicycle racks does your station have?
Racks LTA lists under the station's name, such as TAMPINES MRT C3. Sheltered is LTA's yes or no for cover from the weather.
About 2 in 10 are sheltered. Tampines has 13 times the racks of the middle station (158). The average station has 232.
Tampines has 2,057 racks: 510 sheltered and 1,547 in the open (25% sheltered).
Which stations have the most racks?
Each row is a station, biggest first. A longer bar means more racks. The solid part is sheltered and the hatched part is not. Hover or tap a row for details, and click to pick it.
Tampines has the most racks of the 153 stations that have any: 2,057.
How uneven is it?
The 100 dots are all 35,502 racks. The bars are the 153 stations with racks, biggest on the left. The middle station has as many stations above it as below it.
28 of every 100 racks are at the 10 biggest stations. The 76 smallest stations hold 16 of every 100.
Half of the 153 stations have 158 racks or fewer, and Tampines, the biggest, has 2,057: 13 times the middle. Only 52 stations are above the average of 232. Tampines (in red) is above the middle line.
How many racks are sheltered?
Solid is sheltered and hatched is not. Each dot below is one rack in every ten.
Biggest with no shelter
Stations with 100 or more racks and none sheltered
79 stations in all, 73 more in the table.
Biggest with most racks sheltered
Stations with 100 or more racks, 8 in 10 or more sheltered
3 stations in all.
At Tampines, 25% of the 2,057 racks are sheltered. Across all stations 9% are, and across MRT stations as a group 9%.
Where are the racks?
One circle per station, sized by its racks. Dashed rings are stations with none in the list. Hover or tap a circle for details, and click to pick it.
Tampines is the red circle: 2,057 racks, 25% sheltered.
Fun finds
The short version
LTA's list of 12 Oct 2026 names 35,502 bicycle racks at 153 stations, and 9% of them are sheltered. Tampines has the most (2,057 racks), 13 times the middle station (158), and the 10 biggest stations hold 28 of every 100 racks.
What we did. On 12 Oct 2026 we downloaded LTA's bicycle parking list for the area around every MRT and LRT station (19,362 rows). 657 of them are LTA's own racks or yellow-box spaces with a station in the name, such as TAMPINES MRT C3 or SENGKANG MRT YB_1. We added those up by station, with the racks marked sheltered. 153 of the 183 stations have racks, and 30 do not.
What we found. The middle station has 158 racks and the average is 232, because a few stations are far bigger: Tampines has 2,057. 9% of racks are sheltered. 129 stations have none sheltered, including Woodlands, Sembawang and Admiralty. 2 have every rack sheltered. LRT stations have fewer racks each (36, against 253 at MRT stations), and 0% of them are sheltered, against 9%.
What to keep in mind. We count only racks LTA names after a station. Racks that belong to HDB and others, or sit under a bus interchange or street name, are not counted, so a station can have more racks nearby than shown. The list has no date of its own. We count racks, not how many are used, and the list says nothing about why some stations have more. Use the picker to see your own station, then check the numbers and the boring bit below.
By Ben (opens in a new tab). Updated . Data: LTA DataMall bicycle parking (downloaded 12 Oct 2026); LTA MRT and LRT station exits on data.gov.sg.
Questions people ask
How many bicycle racks are there at MRT stations in Singapore?
LTA's list, downloaded on 12 Oct 2026, names 35,502 bicycle racks after 153 stations: 138 MRT stations (34,956 racks) and 15 LRT stations (546). 30 of the 183 stations have none under their name.
Which MRT station has the most bicycle racks?
Tampines, with 2,057. Then Yishun (1,080), Woodlands (1,046), Sembawang (893) and Admiralty (891). The 10 biggest stations hold 28 of every 100 racks.
How many bicycle racks are sheltered?
3,258 of 35,502, or 9%. The other 32,244 are in the open. 2 stations have every rack sheltered, 22 have some, and 129 have none.
Which MRT stations have no sheltered bicycle parking?
129 of the 153 stations with racks have no sheltered rack in the list. Of those with 100 or more racks, the biggest are Woodlands, Sembawang, Admiralty and Lakeside. Woodlands has 1,046 racks and none is sheltered.
Which MRT station has the most sheltered racks?
Yishun, with 662 sheltered racks (of 1,080). Then Tampines (510) and Marine Parade (364).
Do LRT stations have bicycle racks?
Only 15 of the 41 LRT stations have racks listed under their name, 546 in all, and none of them is sheltered. The 138 MRT stations with racks hold 34,956. LRT racks may belong to someone else, such as HDB, and so fall outside this count.
Why does my station show no bicycle racks?
We count the racks LTA names after a station, such as TAMPINES MRT C3. Racks that belong to others (HDB, town councils, parks and so on) are not counted, and racks listed under a bus interchange or a street name are left out. 30 stations show none.
What is the middle station?
List the 153 stations that have racks from fewest to most. The middle one is number 77, with 158 racks. Half of the stations have fewer, half have more. We use it because a few big stations pull the average up to 232.
Does this say whether the racks are full?
No. It counts racks, not how many are used, and says nothing about how busy a station is. The list has no date of its own; we downloaded it on 12 Oct 2026.
How were the racks matched to stations?
LTA's list has 19,362 rows near the stations, each with an owner type, a name, a location, a rack count and a sheltered yes or no. 657 rows are LTA's own racks or yellow-box spaces with a station in the name, such as TAMPINES MRT C3 or SENGKANG MRT YB_1. We added those up by station. All of them are within 271 m of that station's nearest exit.
Why not count every rack in the list?
Within 800 m of the stations the list holds 210,812 racks, but 18,653 of its 19,362 rows belong to HDB, parks, schools and others, or are yellow-box spaces with no station in the name, and many are beside blocks and bus stops, not stations. Counting them would credit those racks to MRT stations.
The numbers behind it
| Station | Type | Racks | Sheltered | Not sheltered | Share sheltered | Place |
|---|---|---|---|---|---|---|
| Tampines | MRT | 2,057 | 510 | 1,547 | 25% | 1 |
| Yishun | MRT | 1,080 | 662 | 418 | 61% | 2 |
| Woodlands | MRT | 1,046 | 0 | 1,046 | 0% | 3 |
| Sembawang | MRT | 893 | 0 | 893 | 0% | 4 |
| Admiralty | MRT | 891 | 0 | 891 | 0% | 5 |
| Lakeside | MRT | 866 | 0 | 866 | 0% | 6 |
| Aljunied | MRT | 840 | 72 | 768 | 9% | 7 |
| Sengkang | MRT | 813 | 0 | 813 | 0% | 8 |
| Boon Lay | MRT | 685 | 208 | 477 | 30% | 9 |
| Tampines East | MRT | 630 | 134 | 496 | 21% | 10 |
| Khatib | MRT | 627 | 0 | 627 | 0% | 11 |
| Paya Lebar | MRT | 564 | 154 | 410 | 27% | 12 |
| Choa Chu Kang | MRT | 559 | 0 | 559 | 0% | 13 |
| Canberra | MRT | 526 | 0 | 526 | 0% | 14 |
| Punggol | MRT | 514 | 194 | 320 | 38% | 15 |
| Bedok North | MRT | 502 | 0 | 502 | 0% | 16 |
| Yew Tee | MRT | 470 | 0 | 470 | 0% | 17 |
| Pasir Ris | MRT | 460 | 0 | 460 | 0% | 18 |
| Macpherson | MRT | 444 | 0 | 444 | 0% | 19 |
| Bukit Gombak | MRT | 442 | 0 | 442 | 0% | 20 |
| Marine Parade | MRT | 436 | 364 | 72 | 83% | 21 |
| Mayflower | MRT | 434 | 0 | 434 | 0% | 22 |
| Ang Mo Kio | MRT | 420 | 0 | 420 | 0% | 23 |
| Simei | MRT | 416 | 10 | 406 | 2% | 24 |
| Outram Park | MRT | 392 | 0 | 392 | 0% | 25 |
| Eunos | MRT | 374 | 0 | 374 | 0% | 26 |
| Bedok Reservoir | MRT | 366 | 0 | 366 | 0% | 27 |
| Caldecott | MRT | 361 | 0 | 361 | 0% | 28 |
| Kranji | MRT | 357 | 60 | 297 | 17% | 29 |
| Newton | MRT | 334 | 30 | 304 | 9% | 30 |
| Lentor | MRT | 330 | 0 | 330 | 0% | 31 |
| Springleaf | MRT | 329 | 0 | 329 | 0% | 32 |
| Tan Kah Kee | MRT | 324 | 18 | 306 | 6% | 33 |
| Jurong East | MRT | 320 | 0 | 320 | 0% | 34 |
| Woodlands South | MRT | 314 | 0 | 314 | 0% | 35 |
| Kallang | MRT | 307 | 0 | 307 | 0% | 36 |
| Expo | MRT | 303 | 24 | 279 | 8% | 37 |
| Kembangan | MRT | 294 | 0 | 294 | 0% | 38 |
| Gardens by the Bay | MRT | 288 | 0 | 288 | 0% | 39 |
| Kaki Bukit | MRT | 287 | 0 | 287 | 0% | 40 |
| Bishan | MRT | 280 | 0 | 280 | 0% | 41 |
| Tanjong Katong | MRT | 280 | 0 | 280 | 0% | 41 |
| Bukit Panjang | MRT | 278 | 76 | 202 | 27% | 43 |
| Upper Changi | MRT | 274 | 0 | 274 | 0% | 44 |
| Tanah Merah | MRT | 266 | 0 | 266 | 0% | 45 |
| Bendemeer | MRT | 259 | 0 | 259 | 0% | 46 |
| Buangkok | MRT | 256 | 0 | 256 | 0% | 47 |
| Tampines West | MRT | 254 | 0 | 254 | 0% | 48 |
| Hougang | MRT | 253 | 0 | 253 | 0% | 49 |
| Stadium | MRT | 249 | 0 | 249 | 0% | 50 |
| Marina Bay | MRT | 244 | 0 | 244 | 0% | 51 |
| Tai Seng | MRT | 240 | 0 | 240 | 0% | 52 |
| Joo Koon | MRT | 230 | 32 | 198 | 14% | 53 |
| Bright Hill | MRT | 226 | 0 | 226 | 0% | 54 |
| Bayshore | MRT | 214 | 204 | 10 | 95% | 55 |
| Chinese Garden | MRT | 213 | 0 | 213 | 0% | 56 |
| Geylang Bahru | MRT | 213 | 0 | 213 | 0% | 56 |
| Woodlands North | MRT | 212 | 0 | 212 | 0% | 58 |
| Mattar | MRT | 211 | 0 | 211 | 0% | 59 |
| Marine Terrace | MRT | 210 | 202 | 8 | 96% | 60 |
| Jalan Besar | MRT | 207 | 0 | 207 | 0% | 61 |
| Botanic Gardens | MRT | 200 | 0 | 200 | 0% | 62 |
| Marina South Pier | MRT | 200 | 0 | 200 | 0% | 62 |
| Hillview | MRT | 196 | 0 | 196 | 0% | 64 |
| Potong Pasir | MRT | 196 | 20 | 176 | 10% | 64 |
| Bedok | MRT | 192 | 0 | 192 | 0% | 66 |
| Cashew | MRT | 192 | 0 | 192 | 0% | 66 |
| King Albert Park | MRT | 192 | 0 | 192 | 0% | 66 |
| Little India | MRT | 182 | 0 | 182 | 0% | 69 |
| Bencoolen | MRT | 180 | 0 | 180 | 0% | 70 |
| Bukit Batok | MRT | 176 | 0 | 176 | 0% | 71 |
| Maxwell | MRT | 172 | 0 | 172 | 0% | 72 |
| Katong Park | MRT | 171 | 0 | 171 | 0% | 73 |
| Mountbatten | MRT | 164 | 0 | 164 | 0% | 74 |
| Telok Ayer | MRT | 164 | 0 | 164 | 0% | 74 |
| Bugis | MRT | 160 | 0 | 160 | 0% | 76 |
| Stevens | MRT | 158 | 60 | 98 | 38% | 77 |
| Ubi | MRT | 151 | 0 | 151 | 0% | 78 |
| Siglap | MRT | 148 | 0 | 148 | 0% | 79 |
| Boon Keng | MRT | 142 | 28 | 114 | 20% | 80 |
| Kovan | MRT | 138 | 0 | 138 | 0% | 81 |
| One-north | MRT | 138 | 0 | 138 | 0% | 81 |
| Tanjong Pagar | MRT | 138 | 0 | 138 | 0% | 81 |
| Lorong Chuan | MRT | 134 | 0 | 134 | 0% | 84 |
| Shenton Way | MRT | 134 | 0 | 134 | 0% | 84 |
| Beauty World | MRT | 133 | 0 | 133 | 0% | 86 |
| Marsiling | MRT | 132 | 0 | 132 | 0% | 87 |
| Havelock | MRT | 130 | 0 | 130 | 0% | 88 |
| Tuas Link | MRT | 130 | 0 | 130 | 0% | 88 |
| Fort Canning | MRT | 126 | 0 | 126 | 0% | 90 |
| Downtown | MRT | 124 | 0 | 124 | 0% | 91 |
| Farrer Park | MRT | 120 | 0 | 120 | 0% | 92 |
| Lavender | MRT | 120 | 0 | 120 | 0% | 92 |
| Yio Chu Kang | MRT | 120 | 0 | 120 | 0% | 92 |
| Serangoon | MRT | 113 | 0 | 113 | 0% | 95 |
| Upper Thomson | MRT | 112 | 0 | 112 | 0% | 96 |
| Sixth Avenue | MRT | 107 | 0 | 107 | 0% | 97 |
| Pioneer | MRT | 102 | 0 | 102 | 0% | 98 |
| Napier | MRT | 100 | 0 | 100 | 0% | 99 |
| Dakota | MRT | 96 | 0 | 96 | 0% | 100 |
| Bartley | MRT | 94 | 0 | 94 | 0% | 101 |
| Great World | MRT | 94 | 26 | 68 | 28% | 101 |
| Marymount | MRT | 93 | 0 | 93 | 0% | 103 |
| Braddell | MRT | 92 | 0 | 92 | 0% | 104 |
| Tuas West Road | MRT | 91 | 0 | 91 | 0% | 105 |
| Pasir Panjang | MRT | 90 | 0 | 90 | 0% | 106 |
| Orchard | MRT | 88 | 88 | 0 | 100% | 107 |
| Farrer Road | MRT | 86 | 0 | 86 | 0% | 108 |
| Holland Village | MRT | 86 | 0 | 86 | 0% | 108 |
| Labrador Park | MRT | 85 | 0 | 85 | 0% | 110 |
| Buona Vista | MRT | 80 | 0 | 80 | 0% | 111 |
| Haw Par Villa | MRT | 80 | 0 | 80 | 0% | 111 |
| Kent Ridge | MRT | 80 | 0 | 80 | 0% | 111 |
| Telok Blangah | MRT | 80 | 0 | 80 | 0% | 111 |
| Phoenix LRT | LRT | 78 | 0 | 78 | 0% | 115 |
| Tanjong Rhu | MRT | 73 | 40 | 33 | 55% | 116 |
| Chinatown | MRT | 70 | 0 | 70 | 0% | 117 |
| Woodleigh | MRT | 70 | 0 | 70 | 0% | 117 |
| Kadaloor LRT | LRT | 60 | 0 | 60 | 0% | 119 |
| Nicoll Highway | MRT | 60 | 0 | 60 | 0% | 119 |
| Senja LRT | LRT | 58 | 0 | 58 | 0% | 121 |
| Rochor | MRT | 56 | 0 | 56 | 0% | 122 |
| Queenstown | MRT | 53 | 0 | 53 | 0% | 123 |
| Dhoby Ghaut | MRT | 52 | 0 | 52 | 0% | 124 |
| Raffles Place | MRT | 52 | 0 | 52 | 0% | 124 |
| Fajar LRT | LRT | 51 | 0 | 51 | 0% | 126 |
| Gul Circle | MRT | 50 | 0 | 50 | 0% | 127 |
| South View LRT | LRT | 50 | 0 | 50 | 0% | 127 |
| Teck Whye LRT | LRT | 50 | 0 | 50 | 0% | 127 |
| Fernvale LRT | LRT | 48 | 0 | 48 | 0% | 130 |
| Dover | MRT | 46 | 0 | 46 | 0% | 131 |
| Commonwealth | MRT | 44 | 0 | 44 | 0% | 132 |
| Orchard Boulevard | MRT | 42 | 42 | 0 | 100% | 133 |
| Harbourfront | MRT | 40 | 0 | 40 | 0% | 134 |
| Somerset | MRT | 40 | 0 | 40 | 0% | 134 |
| Segar LRT | LRT | 38 | 0 | 38 | 0% | 136 |
| Keat Hong LRT | LRT | 36 | 0 | 36 | 0% | 137 |
| Tuas Crescent | MRT | 36 | 0 | 36 | 0% | 137 |
| Clementi | MRT | 34 | 0 | 34 | 0% | 139 |
| Redhill | MRT | 32 | 0 | 32 | 0% | 140 |
| Bras Basah | MRT | 28 | 0 | 28 | 0% | 141 |
| Clarke Quay | MRT | 28 | 0 | 28 | 0% | 141 |
| Toa Payoh | MRT | 27 | 0 | 27 | 0% | 143 |
| Esplanade | MRT | 22 | 0 | 22 | 0% | 144 |
| Jelapang LRT | LRT | 21 | 0 | 21 | 0% | 145 |
| City Hall | MRT | 13 | 0 | 13 | 0% | 146 |
| Nibong LRT | LRT | 13 | 0 | 13 | 0% | 146 |
| Oasis LRT | LRT | 13 | 0 | 13 | 0% | 146 |
| Pending LRT | LRT | 12 | 0 | 12 | 0% | 149 |
| Tiong Bahru | MRT | 12 | 0 | 12 | 0% | 149 |
| Coral Edge LRT | LRT | 10 | 0 | 10 | 0% | 151 |
| Promenade | MRT | 10 | 0 | 10 | 0% | 151 |
| Punggol Point LRT | LRT | 8 | 0 | 8 | 0% | 153 |
| Type | Stations with racks | Racks | Sheltered | Not sheltered | Share sheltered | Stations with none |
|---|---|---|---|---|---|---|
| MRT | 138 | 34,956 | 3,258 | 31,698 | 9% | 4 |
| LRT | 15 | 546 | 0 | 546 | 0% | 26 |
| Station | Type |
|---|---|
| Bakau LRT | LRT |
| Bangkit LRT | LRT |
| Bayfront | MRT |
| Bukit Panjang LRT | LRT |
| Changi Airport | MRT |
| Cheng Lim LRT | LRT |
| Choa Chu Kang LRT | LRT |
| Compassvale LRT | LRT |
| Cove LRT | LRT |
| Damai LRT | LRT |
| Farmway LRT | LRT |
| Kangkar LRT | LRT |
| Kupang LRT | LRT |
| Layar LRT | LRT |
| Marina South | MRT |
| Meridian LRT | LRT |
| Novena | MRT |
| Petir LRT | LRT |
| Punggol LRT | LRT |
| Ranggung LRT | LRT |
| Renjong LRT | LRT |
| Riviera LRT | LRT |
| Rumbia LRT | LRT |
| Sam Kee LRT | LRT |
| Samudera LRT | LRT |
| Soo Teck LRT | LRT |
| Sumang LRT | LRT |
| Teck Lee LRT | LRT |
| Thanggam LRT | LRT |
| Tongkang LRT | LRT |
The boring bit
Where the data comes from
Racks: LTA DataMall's bicycle parking service, asked on 12 Oct 2026 for every rack within 800 m of each of the 183 station points. After removing repeats that gave 19,362 rows. Each row has an owner type (LTA, HDB, a town council, a park and so on), a name, a location, a rack count and a sheltered yes or no. The rows carry no date, so we call the list as downloaded on 12 Oct 2026. Stations: LTA's MRT Station Exit file on data.gov.sg, with every exit of every station. Seven exit records carry only a station code (CC9, DT18, DT4, NE18, CC30, CC31 and CC32) instead of a station name, so we left them out.
An older LTA file on data.gov.sg (stamped July 2019) also counts bicycle racks. It has no owner or name for its racks, so it cannot be compared with this list, and we did not use it.
What is not claimed
- We count only racks that LTA owns and names after a station. A rack beside a station that belongs to HDB or another owner is not counted for that station.
- Racks listed under a bus interchange, a street or a bus stop are left out, even when they are close to a station.
- A station and its neighbour with the same site name, such as an MRT and an LRT station, are counted apart: the MRT or LRT in the rack's name decides.
- Sheltered is LTA's own yes or no for each row. We do not know what kind of cover it is, or how it was decided.
- The count is of racks as the list gives them (its rack count field), not bicycles, and not how many racks are in use.
- The middle station and the average are for the 153 stations with racks. They leave out the 30 stations with none.
- We do not say why some stations have more racks or more cover than others.
Coverage
657 rows are used, for 35,502 racks. 18,653 of the 19,362 rows belong to other owners (HDB, parks, schools and so on), or are yellow-box spaces at bus stops, blocks and streets with no station in the name, and are left out. 52 LTA rows are left out because no station is in the name. Of those, 4 rows (569 racks) name an MRT station that is not in the exit file or is spelt differently: Prince Edward Road MRT 1 (184), Punggol Coast MRT Level 2 (151), Punggol Coast MRT Level 1 (214) and Pending MRT A (20). Every row used is within 271 m of an exit of its station, and 93 of every 100 rows are within 100 m.
Report an error. Corrections are logged publicly with a date.
How we did it
- Read the rack list and kept each row's owner type, name, location, rack count and sheltered yes or no.
- Read the station exit file and grouped the exits by station name.
- Kept LTA's own racks and yellow-box spaces whose name has a station in it (for example "TAMPINES MRT C3", "SENGKANG MRT YB_1" or "KADALOOR LRT A") and gave each to that station.
- Added up racks and sheltered racks for each of the 183 stations.
- Found the middle and the average across the 153 stations with racks.
Show the Python code
"""Bicycle racks at MRT and LRT stations: take the racks LTA names after a station and add them up for each station.
Run: python3 -I prep/prep_racks.py <data folder> <out json>
Reads <data folder>/BicycleParking_raw.json (LTA DataMall bicycle parking, downloaded 12 Oct 2026) and LTAMRTStationExitGEOJSON.geojson."""
import json, re, math, sys, collections
from pathlib import Path
RAW = Path(sys.argv[1]); OUT = Path(sys.argv[2])
STATION_TYPES = ('MRT_RACKS', 'LTA_RACKS', 'LTA_YELLOWBOX') # the types LTA uses for its own racks; the others belong to HDB, parks, schools and so on
YELLOW = 'Yellow Box' # LTA's yellow-box spaces (most are at bus stops or HDB blocks): kept only when the name has a station in it
def read_stations():
ex = collections.defaultdict(list)
for f in json.load(open(RAW / 'LTAMRTStationExitGEOJSON.geojson'))['features']:
n = f['properties']['STATION_NA']
if 'STATION' in n: # 7 exits carry only a station code; they are left out
ex[n].append(tuple(f['geometry']['coordinates'][:2]))
return ex
def metres(a, b):
return math.hypot((a[0] - b[0]) * 111320 * math.cos(math.radians(1.35)), (a[1] - b[1]) * 110570)
def nice(n):
kind = 'LRT' if ' LRT ' in n else 'MRT'
base = re.sub(r' (MRT|LRT) STATION$', '', n)
words = [w.capitalize() if w not in ('OF',) else w.lower() for w in base.split(' ')]
return ' '.join(words), kind, base
def station_of(description, lookup):
"""'TAMPINES MRT C3' -> Tampines MRT; 'KADALOOR LRT A' -> Kadaloor LRT; 'HAVELOCK_MRT_1' -> Havelock; None if no station is named."""
d = re.sub(r'\s+', ' ', description.upper().replace('_', ' ')).strip()
m = re.match(r'^(?:OPP |OPPOSITE )?(.+?) (MRT|LRT|STATION)\b', d)
if not m: return None
base, word = m.group(1), m.group(2)
return lookup.get((base, 'LRT' if word == 'LRT' else 'MRT'))
if __name__ == '__main__':
feed = json.load(open(RAW / 'BicycleParking_raw.json'))
ex = read_stations()
lookup, info = {}, {}
for k in ex:
name, kind, base = nice(k)
lookup[(base, kind)] = k
info[k] = (name, kind)
agg = {k: dict(n=0, sh=0, pts=0) for k in ex}
used, left_out, far, missing = 0, collections.Counter(), [], []
for r in feed:
if r['RackType'] not in STATION_TYPES and r['RackType'] != YELLOW:
left_out['other owners'] += 1; continue
k = station_of(r['Description'], lookup)
if not k and r['RackType'] == YELLOW:
left_out['other owners'] += 1; continue
if not k:
left_out['no station named'] += 1
if re.search(r'\b(MRT|LRT)\b', r['Description'].upper().replace('_', ' ')): missing.append((r['Description'], int(r['RackCount'])))
continue
a = agg[k]; n = int(r['RackCount'])
a['n'] += n; a['pts'] += 1; used += 1
if r['ShelterIndicator'] == 'Y': a['sh'] += n
far.append(min(metres((r['Longitude'], r['Latitude']), p) for p in ex[k]))
rows = []
for k, a in agg.items():
name, kind = info[k]
rows.append(dict(key=k, name=name, kind=kind, n=a['n'], sh=a['sh'], pts=a['pts'],
lon=sum(p[0] for p in ex[k]) / len(ex[k]), lat=sum(p[1] for p in ex[k]) / len(ex[k]), exits=len(ex[k])))
tot = sum(r['n'] for r in rows)
json.dump(dict(rows=rows, total=tot, feed=len(feed), feed_racks=sum(int(r['RackCount']) for r in feed), records=used,
other=left_out['other owners'], unnamed=left_out['no station named'],
unnamed_racks=sum(int(r['RackCount']) for r in feed if r['RackType'] in STATION_TYPES and not station_of(r['Description'], lookup)),
missing=missing, within=dict((m, sum(1 for d in far if d <= m)) for m in (100, 200, 300, 500)), farthest=round(max(far))),
open(OUT, 'w'))
print('stations', len(rows), 'with racks', sum(1 for r in rows if r['n']), 'station rows', used, 'racks', tot, 'left out', dict(left_out), 'farthest m', round(max(far)))
What could make it wrong: a rack name that points to the wrong station, a rack added or moved after 12 Oct 2026, or a sheltered mark that is out of date. As a check, the racks added up by station equal the 35,502 in the 657 rows used, and a second count written separately gives the same.
No machine learning was needed. The question was "how many racks, and how many sheltered", which adding up answers directly.
Sources: LTA DataMall, Bicycle Parking (opens in a new tab) and LTA MRT Station Exit (opens in a new tab), both downloaded 12 Oct 2026. Contains information from LTA DataMall and data.gov.sg, made available under the Singapore Open Data Licence (LTA API terms).