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.

Updated

The data

Rose City Bikes pickups, September 2025

Synthetic datapickups.csv

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_idstarted_atstation_idstationlatitudelongitude
T000012025-09-01 00:08S16SE Grand & Belmont45.5164-122.6611
T000022025-09-01 00:08S24NE 7th & Holladay45.5287-122.6596
T000032025-09-01 01:39S12Salmon St Springs45.5138-122.6733
T000042025-09-01 03:55S16SE Grand & Belmont45.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

PointLatitudeLongitudeTrips
Average of every row44.82−1211,492
Where the bikes are45.52−1231,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 DESC

Rows

StationLatitudeLongitudeTrips
Union Station45.53−123216
SW 6th & Alder45.52−12375
SW 5th & Oak45.52−12374
Pioneer Square45.52−12373
SW 4th & Stark45.52−12371
SW Broadway & Morrison45.52−12362
SW 3rd & Taylor45.52−12361
NW 13th & Lovejoy45.53−12359
Jamison Square45.53−12358
Salmon St Springs45.51−12355
Hawthorne Bridge West45.51−12353
SW Naito & Clay45.51−12352
OHSU Tram Lower45.5−12350
SW Moody & Gibbs45.5−12350
Riverplace Marina45.51−12349
Eastbank Esplanade45.52−12349
SE Grand & Belmont45.52−12346
Moda Center45.53−12346
Lloyd Center45.53−12343
NW 11th & Johnson45.53−12338
NE 7th & Holladay45.53−12335
NW 6th & Hoyt45.53−12332
NE Killingsworth & 14th45.56−12328
SE MLK & Ash45.52−12327
South Waterfront Park45.5−12326
SE 11th & Morrison45.52−12325
NE Alberta & 15th45.56−12316

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 DESC

Rows

Lat cellLon cellTripsStations
45.52−1234166
45.53−1232482
45.51−1232094
45.53−1231553
45.52−1231474
45.5−1231263
45.53−1231243
45.56−123442

Why isn't the busiest station the busiest place?

Because a dock is a point and demand is an area. Union Station has 216 pickups, almost three times any other dock, so on a station map it looks like the center of the system. Round the pickups into 1 km cells and downtown wins: 6 docks a few blocks apart add up to 416. If you're deciding where to put more bikes, the grid is the map to read.

Grid cell size

In Tableau

The shelves for the finished chart

Marks Circle

  • SizeCOUNT([trip_id])
  • Detail[station]
ColumnsAVG([longitude])
RowsAVG([latitude])

Green pills are continuous, blue are discrete, as in Tableau.

How many decimals should I round to?

Pick the cell size that matches the decision. 3 decimals (about 100 m) is a street corner: good for placing a dock. 2 decimals (about 1 km) is a neighbourhood: good for staffing and rebalancing. 1 decimal (about 11 km) is a whole city and only helps when you compare cities.

Variants

Other ways to draw it

Distance from the depot

Color stations by how far a van drives to rebalance them. The depot sits at Union Station (45.5289, -122.6765).

Point at a circle to read it.
122.7°W122.6°W45.5°N2 km
km from depot: 02.254.5

Common mistake

Summing coordinates

SUM([latitude]) adds a station's latitude once per pickup, so SW 6th & Alder, with 75 rides, lands at latitude 3,414 and falls off the map. Coordinates are positions, not amounts: average them, or use MIN or MAX.

WrongStarts with Dockless bike, Eastbank Esplanade, Hawthorne Bridge West…
Point at the line to read it.
-30K-20K-10K0Stations in this order →

Tableau

Rows: SUM([latitude])

SQL

SELECT
  station,
  SUM(latitude) AS latitude,
  SUM(longitude) AS longitude
FROM input
GROUP BY station
ORDER BY station
RightStarts with Dockless bike, Eastbank Esplanade, Hawthorne Bridge West…
Point at the line to read it.
-30K-20K-10K0Stations in this order →

Tableau

Rows: AVG([latitude])

SQL

SELECT
  station,
  AVG(latitude) AS latitude,
  AVG(longitude) AS longitude
FROM input
GROUP BY station
ORDER BY station

Common mistake

Leaving the (0, 0) rows in

A missing GPS fix often arrives as 0, 0, a spot in the Atlantic off West Africa that people call Null Island. Here 23 rows, 1.5% of the file, move the average point 168 km. Filter them out before you average, and check for empty coordinates too.

Wrong
Point at a bar to read it.
−121Avg(longitude)

SQL

SELECT AVG(latitude), AVG(longitude)
FROM input
Right
Point at a bar to read it.
−123Avg(longitude)

SQL

SELECT AVG(latitude), AVG(longitude)
FROM input
WHERE NOT (latitude = 0 AND longitude = 0) AND latitude IS NOT NULL AND longitude IS NOT NULL

Common mistake

Judging demand by the biggest circle

One big dock catches the eye, but riders don't care which dock is nearest, only that a bike is. Count pickups per grid cell before you decide where to add bikes.

WrongStarts with Union Station…
Point at a bar to read it.
216Trips

SQL

SELECT station, COUNT(*) AS trips
FROM input
GROUP BY station
ORDER BY trips DESC
LIMIT 1
Right
Point at a bar to read it.
416Trips

SQL

SELECT
  ROUND(latitude, 2) AS lat_cell,
  ROUND(longitude, 2) AS lon_cell,
  COUNT(*) AS trips
FROM input
WHERE NOT (latitude = 0 AND longitude = 0)
GROUP BY 1, 2
ORDER BY trips DESC
LIMIT 1

Run it on your own data

Any CSV with latitude and longitude columns works: deliveries, store visits, sensor readings. Drop it here, pick the two coordinate columns and a cell size, and the grid counts itself in your browser. Nothing is uploaded.

Drop a CSV anywhere on this page

Up to 10,000 rows. The sample, pickups.csv, shows the shape.

Questions

Why is my map just one dot?

Latitude and longitude are measures, so Tableau averages them over every row and draws the average. Put the location (station, store, customer) on Detail to get one mark per location.

Why is my map rotated or upside down?

Latitude and longitude are on the wrong shelves. Longitude runs east to west, so it goes on Columns; latitude runs north to south, so it goes on Rows.

How big is a grid cell when I round to 2 decimals?

0.01 degrees of latitude is about 1.1 km everywhere. 0.01 degrees of longitude shrinks toward the poles: about 1.1 km at the equator, about 780 m in Portland, about 560 m in Oslo. Cells are rectangles, not squares, away from the equator.

How do I measure the distance between two points?

In Tableau, DISTANCE(MAKEPOINT([lat1], [lon1]), MAKEPOINT([lat2], [lon2]), 'km'). In SQL, use the haversine formula shown in the distance variant.

Can I plot these points in xmetric?

Point maps in the xmetric app are coming. You can already run the binning and distance steps on your CSV here, in your browser.

Sources