If you have ever told a client "you rank #1 on Google Maps" after searching from your own office, this post is for you.
Local search results are not a single leaderboard. They change with the location of the person searching. A cafe can be first on its own street, fifth a mile away and missing entirely three miles out. If you only check from one spot, you are looking at one pixel of a much bigger picture.
In this post, we will build a small JavaScript helper that models this idea: a grid of coordinates around a business, with a rank recorded at each point. It is the same concept behind the "rank grid" maps you see in local SEO tools, and it is a nice little exercise in coordinates and data visualisation.
The idea
- Take the business location (latitude and longitude).
- Generate an
N x Ngrid of points around it, spaced a fixed distance apart. - Record the business's rank at each point.
- Show the results so weak areas stand out.
Step 3 is the part that needs real data, and we will come back to it. Let's start with the geometry.
Step 1: Generate grid points
One degree of latitude is roughly 111 km everywhere. One degree of longitude shrinks as you move away from the equator, so we scale it by the cosine of the latitude.
const KM_PER_DEG_LAT = 111.32;
function gridPoints(centerLat, centerLng, size = 5, stepKm = 1) {
const kmPerDegLng = KM_PER_DEG_LAT * Math.cos((centerLat * Math.PI) / 180);
const half = (size - 1) / 2;
const points = [];
// row +1 is north of center, col +1 is east of center
for (let row = half; row >= -half; row--) {
for (let col = -half; col <= half; col++) {
points.push({
row,
col,
lat: centerLat + (row * stepKm) / KM_PER_DEG_LAT,
lng: centerLng + (col * stepKm) / kmPerDegLng,
});
}
}
return points;
}
const points = gridPoints(39.7817, -89.6501, 5, 1);
console.log(points.length); // 25
console.log(points[12]); // the center point
Looping the rows from north to south means the array is already in the order you would read a map, top to bottom and left to right.
Use an odd size so there is a true center cell where the business sits.
Step 2: Attach ranks
Now each point needs a rank: the business's position in the local results when searching from that spot. Let's use mock data for now.
// Mock data: rank per grid cell, in the same order as `points`.
// `null` means the business did not appear in the results.
const ranks = [
null, 12, 9, 8, null,
11, 6, 4, 5, 10,
7, 3, 1, 2, 7,
9, 5, 3, 4, 8,
null, 10, 8, 9, null,
];
const grid = points.map((p, i) => ({ ...p, rank: ranks[i] }));
A word on getting real data: collecting rankings from different locations is the hard part. Scraping Google's results yourself usually breaks their terms of service and tends to be fragile, so for production use you will want a proper tool or a data source you are allowed to use. That is why I'm leaving that step abstract here.
Step 3: Turn ranks into something readable
A raw number is fine for a developer. A color band is better for everyone else.
function band(rank) {
if (rank === null) return "none";
if (rank <= 3) return "strong"; // inside the top 3
if (rank <= 10) return "weak"; // visible but easy to miss
return "poor";
}
function summarize(grid) {
const total = grid.length;
const count = (b) => grid.filter((c) => band(c.rank) === b).length;
const strongPct = Math.round((count("strong") / total) * 100);
return {
strong: count("strong"),
weak: count("weak"),
poor: count("poor"),
none: count("none"),
strongPct,
};
}
console.log(summarize(grid));
// { strong: 7, weak: 10, poor: 3, none: 4, strongPct: 28 }
That last number is a useful single metric: what share of the surrounding area do you rank in the top 3? It is much more honest than one lucky search from your front door.
Step 4: Print it in the terminal
You do not need a map library to see the pattern. A quick text render is enough:
function render(grid, size = 5) {
const symbol = { strong: "G", weak: "Y", poor: "R", none: "." };
let out = "";
grid.forEach((cell, i) => {
out += symbol[band(cell.rank)] + " ";
if ((i + 1) % size === 0) out += "\n";
});
return out;
}
console.log(render(grid));
Output:
. R Y Y .
R Y G Y Y
Y G G G Y
Y Y G G Y
. R Y Y .
You can see at a glance that visibility is strongest in the middle and fades toward the edges, exactly what you would expect. If your customers live in the bottom-left corner, you now know where the work is.
Where to take it next
- Plot the points on a real map with Leaflet or MapLibre and color each marker by band.
- Store a snapshot each week and chart how the top-3 share changes after you edit your profile or add reviews.
- Compare two competitors on the same grid to see who owns which neighborhood.
- Adjust
stepKmto match your service area: walking distance for a cafe, much larger for a plumber.
Don't want to build it yourself?
The code above is a learning exercise. If you just want to see how a Google Business Profile ranks across different locations without writing any of this, tools like GMB Ranker are built for that.
Takeaways
- Local rank is a map, not a number.
- A coordinate grid is a simple way to model it.
- Measure the share of the area where you rank in the top 3, and track it over time.
- Change one thing at a time so you know what actually worked.
Have you built something similar, or found a nicer way to visualize local rankings? Tell me in the comments.
Top comments (0)