Chart guide Tableau and SQL
How do I plot latitude and longitude from a CSV on a map?
Put AVG([longitude]) on Columns and AVG([latitude]) on Rows, then add the location to Detail so you get one mark per place, not one average dot. To see density, round both coordinates into grid cells. In this bike-share, the busiest dock (Union Station) is not in the busiest part of town.
The data
Rose City Bikes pickups, September 2025
A docked bike-share in central Portland, Oregon: 27 stations, one row per pickup, with the bike's GPS position at the start of the ride. A few dockless bikes report no GPS fix.
trip_id | started_at | station_id | station | latitude | longitude |
|---|---|---|---|---|---|
| T00001 | 2025-09-01 00:08 | S16 | SE Grand & Belmont | 45.5164 | -122.6611 |
| T00002 | 2025-09-01 00:08 | S24 | NE 7th & Holladay | 45.5287 | -122.6596 |
| T00003 | 2025-09-01 01:39 | S12 | Salmon St Springs | 45.5138 | -122.6733 |
| T00004 | 2025-09-01 03:55 | S16 | SE Grand & Belmont | 45.5164 | -122.6611 |
Step 1
Drop the coordinates on the shelves: one dot
Every pickup averaged into one point, and it isn't even in Portland: 23 rows with no GPS fix sit at (0, 0) and drag the average 168 km away, into the middle of the state. The second dot is the same average without them.
Tableau
Columns: AVG([longitude]) Rows: AVG([latitude])SQL
SELECT
'Average of every row' AS point,
AVG(latitude) AS latitude,
AVG(longitude) AS longitude,
COUNT(*) AS trips
FROM input
UNION ALL
SELECT
'Where the bikes are',
AVG(latitude),
AVG(longitude),
COUNT(*)
FROM input
WHERE NOT (latitude = 0 AND longitude = 0)Rows
| Point | Latitude | Longitude | Trips |
|---|---|---|---|
| Average of every row | 44.82 | −121 | 1,492 |
| Where the bikes are | 45.52 | −123 | 1,469 |
Step 2
One mark per station, without the (0, 0) rows
Now each station is a circle sized by pickups. One circle dominates: Union Station, with 216 pickups, nearly three times the next station.
Tableau
// Detail: [station] Size: COUNT([trip_id])
// Filter: NOT ([latitude] = 0 AND [longitude] = 0)SQL
SELECT
station,
AVG(latitude) AS latitude,
AVG(longitude) AS longitude,
COUNT(*) AS trips
FROM input
WHERE NOT (latitude = 0 AND longitude = 0)
GROUP BY station
ORDER BY trips DESCRows
| Station | Latitude | Longitude | Trips |
|---|---|---|---|
| Union Station | 45.53 | −123 | 216 |
| SW 6th & Alder | 45.52 | −123 | 75 |
| SW 5th & Oak | 45.52 | −123 | 74 |
| Pioneer Square | 45.52 | −123 | 73 |
| SW 4th & Stark | 45.52 | −123 | 71 |
| SW Broadway & Morrison | 45.52 | −123 | 62 |
| SW 3rd & Taylor | 45.52 | −123 | 61 |
| NW 13th & Lovejoy | 45.53 | −123 | 59 |
| Jamison Square | 45.53 | −123 | 58 |
| Salmon St Springs | 45.51 | −123 | 55 |
| Hawthorne Bridge West | 45.51 | −123 | 53 |
| SW Naito & Clay | 45.51 | −123 | 52 |
| OHSU Tram Lower | 45.5 | −123 | 50 |
| SW Moody & Gibbs | 45.5 | −123 | 50 |
| Riverplace Marina | 45.51 | −123 | 49 |
| Eastbank Esplanade | 45.52 | −123 | 49 |
| SE Grand & Belmont | 45.52 | −123 | 46 |
| Moda Center | 45.53 | −123 | 46 |
| Lloyd Center | 45.53 | −123 | 43 |
| NW 11th & Johnson | 45.53 | −123 | 38 |
| NE 7th & Holladay | 45.53 | −123 | 35 |
| NW 6th & Hoyt | 45.53 | −123 | 32 |
| NE Killingsworth & 14th | 45.56 | −123 | 28 |
| SE MLK & Ash | 45.52 | −123 | 27 |
| South Waterfront Park | 45.5 | −123 | 26 |
| SE 11th & Morrison | 45.52 | −123 | 25 |
| NE Alberta & 15th | 45.56 | −123 | 16 |
Step 3
Round the coordinates into a grid
Each square counts every pickup inside it, whichever station it came from. The hottest square is downtown, where 6 ordinary docks add up to 416 pickups, well ahead of the square around Union Station. Try 3 decimals and the grid shrinks to single docks; try 1 and nearly the whole city falls in one cell.
Tableau
// [Lat cell]: ROUND([latitude], {knob.decimals})
// [Lon cell]: ROUND([longitude], {knob.decimals})
// Columns: [Lon cell] Rows: [Lat cell] (both as continuous dimensions)
// Marks: Square Color and Size: COUNT([trip_id])SQL
SELECT
ROUND(latitude, 2) AS lat_cell,
ROUND(longitude, 2) AS lon_cell,
COUNT(*) AS trips,
COUNT(DISTINCT station) AS stations
FROM input
WHERE NOT (latitude = 0 AND longitude = 0)
GROUP BY 1, 2
ORDER BY trips DESCRows
| Lat cell | Lon cell | Trips | Stations |
|---|---|---|---|
| 45.52 | −123 | 416 | 6 |
| 45.53 | −123 | 248 | 2 |
| 45.51 | −123 | 209 | 4 |
| 45.53 | −123 | 155 | 3 |
| 45.52 | −123 | 147 | 4 |
| 45.5 | −123 | 126 | 3 |
| 45.53 | −123 | 124 | 3 |
| 45.56 | −123 | 44 | 2 |