DEV Community

echoluoluo
echoluoluo

Posted on

How to Find Nearby Places from Latitude and Longitude with JavaScript

When you already have a latitude and longitude pair, you can do much more than simply place a marker on a map.

For example, given:

Latitude:  40.7128
Longitude: -74.0060
Enter fullscreen mode Exit fullscreen mode

you may want to find:

  • restaurants nearby,
  • cafes,
  • hotels,
  • hospitals,
  • gas stations,
  • pharmacies,
  • or other points of interest.

This is a common feature in travel apps, location tools, map interfaces, delivery applications, and local discovery websites.

In this tutorial, we'll build a simple nearby-place search with JavaScript using OpenStreetMap data and the Overpass API.

We'll cover:

  • How radius-based nearby searches work
  • How to query OpenStreetMap POIs
  • How to search multiple place categories
  • How to extract coordinates from results
  • How to calculate the distance to each result
  • A few important production considerations

The Basic Idea

A nearby search usually starts with three pieces of information:

Latitude
Longitude
Search radius
Enter fullscreen mode Exit fullscreen mode

For example:

40.7128
-74.0060
1500 meters
Enter fullscreen mode Exit fullscreen mode

We can then ask a geographic data source:

Find restaurants within
1500 meters of
40.7128, -74.0060
Enter fullscreen mode Exit fullscreen mode

Conceptually:

User selects location
        ↓
Latitude + Longitude
        ↓
Nearby place query
        ↓
POI results
        ↓
Name + category + coordinates
        ↓
Display on map
Enter fullscreen mode Exit fullscreen mode

Once you already have coordinates, the rest becomes a spatial search problem.


Using OpenStreetMap Data

OpenStreetMap contains geographic objects such as:

restaurants
cafes
hotels
schools
hospitals
shops
parks
pharmacies
fuel stations
Enter fullscreen mode Exit fullscreen mode

These locations are usually described using tags.

A restaurant, for example, might contain:

amenity=restaurant
Enter fullscreen mode Exit fullscreen mode

A cafe:

amenity=cafe
Enter fullscreen mode Exit fullscreen mode

A hospital:

amenity=hospital
Enter fullscreen mode Exit fullscreen mode

One way to query this data is the Overpass API.

For a simple experiment or developer tool, it gives us a convenient way to request OpenStreetMap objects near a coordinate.


A Simple Restaurant Query

Suppose we want to find restaurants within 1,500 meters of New York City coordinates:

const latitude = 40.7128;
const longitude = -74.0060;
const radius = 1500;
Enter fullscreen mode Exit fullscreen mode

An Overpass query can look like this:

const query = `
[out:json][timeout:25];

(
  node["amenity"="restaurant"]
    (around:${radius},${latitude},${longitude});

  way["amenity"="restaurant"]
    (around:${radius},${latitude},${longitude});

  relation["amenity"="restaurant"]
    (around:${radius},${latitude},${longitude});
);

out center tags;
`;
Enter fullscreen mode Exit fullscreen mode

There are three object types here:

node
way
relation
Enter fullscreen mode Exit fullscreen mode

A small restaurant may be represented as a node.

A larger building may be represented as a way.

More complex geographic objects can also be relations.

Searching all three gives us more complete results.


Send the Request with JavaScript

We can create a reusable function:

async function findNearbyRestaurants(
  latitude,
  longitude,
  radius = 1500
) {
  const query = `
    [out:json][timeout:25];

    (
      node["amenity"="restaurant"]
        (around:${radius},${latitude},${longitude});

      way["amenity"="restaurant"]
        (around:${radius},${latitude},${longitude});

      relation["amenity"="restaurant"]
        (around:${radius},${latitude},${longitude});
    );

    out center tags;
  `;

  const response = await fetch(
    "https://overpass-api.de/api/interpreter",
    {
      method: "POST",
      body: new URLSearchParams({
        data: query
      })
    }
  );

  if (!response.ok) {
    throw new Error(
      `Nearby search failed: ${response.status}`
    );
  }

  return response.json();
}
Enter fullscreen mode Exit fullscreen mode

Then call it:

findNearbyRestaurants(
  40.7128,
  -74.0060
).then(data => {
  console.log(data.elements);
});
Enter fullscreen mode Exit fullscreen mode

The response contains OpenStreetMap elements matching our query.


Extract Useful Place Information

Raw OpenStreetMap objects contain more information than we usually need.

Let's normalize the response.

function normalizePlace(element) {
  const latitude =
    element.lat ??
    element.center?.lat;

  const longitude =
    element.lon ??
    element.center?.lon;

  return {
    id: element.id,

    name:
      element.tags?.name ??
      "Unnamed place",

    latitude,
    longitude,

    category:
      element.tags?.amenity,

    cuisine:
      element.tags?.cuisine,

    website:
      element.tags?.website,

    openingHours:
      element.tags?.opening_hours
  };
}
Enter fullscreen mode Exit fullscreen mode

Now:

const data =
  await findNearbyRestaurants(
    40.7128,
    -74.0060
  );

const places =
  data.elements
    .map(normalizePlace)
    .filter(
      place =>
        place.latitude != null &&
        place.longitude != null
    );

console.log(places);
Enter fullscreen mode Exit fullscreen mode

The result becomes much easier to work with:

[
  {
    id: 123456,
    name: "Example Restaurant",
    latitude: 40.715,
    longitude: -74.003,
    category: "restaurant",
    cuisine: "italian"
  }
]
Enter fullscreen mode Exit fullscreen mode

Search Multiple Categories

Searching only for restaurants is useful, but most map applications support several categories.

We can use a regular expression in the Overpass query.

For example:

const amenityTypes = [
  "restaurant",
  "cafe",
  "bar",
  "fast_food"
];
Enter fullscreen mode Exit fullscreen mode

Convert them into:

const pattern =
  amenityTypes.join("|");
Enter fullscreen mode Exit fullscreen mode

Then use:

["amenity"~"restaurant|cafe|bar|fast_food"]
Enter fullscreen mode Exit fullscreen mode

Our JavaScript function becomes:

async function findNearbyFood(
  latitude,
  longitude,
  radius = 1500
) {
  const pattern =
    "restaurant|cafe|bar|fast_food";

  const query = `
    [out:json][timeout:25];

    (
      node["amenity"~"${pattern}"]
        (around:${radius},${latitude},${longitude});

      way["amenity"~"${pattern}"]
        (around:${radius},${latitude},${longitude});

      relation["amenity"~"${pattern}"]
        (around:${radius},${latitude},${longitude});
    );

    out center tags;
  `;

  const response = await fetch(
    "https://overpass-api.de/api/interpreter",
    {
      method: "POST",

      body: new URLSearchParams({
        data: query
      })
    }
  );

  if (!response.ok) {
    throw new Error(
      "Unable to search nearby places"
    );
  }

  return response.json();
}
Enter fullscreen mode Exit fullscreen mode

This gives us a simple nearby food search.


Calculate Distance to Each Place

The API tells us which places are nearby, but we may also want to show:

Cafe A       240 m
Restaurant B 480 m
Cafe C       720 m
Enter fullscreen mode Exit fullscreen mode

We can calculate this using the Haversine formula.

function distanceBetween(
  lat1,
  lon1,
  lat2,
  lon2
) {
  const earthRadius = 6371000;

  const toRadians = degrees =>
    degrees * Math.PI / 180;

  const dLat =
    toRadians(lat2 - lat1);

  const dLon =
    toRadians(lon2 - lon1);

  const a =
    Math.sin(dLat / 2) ** 2 +
    Math.cos(toRadians(lat1)) *
    Math.cos(toRadians(lat2)) *
    Math.sin(dLon / 2) ** 2;

  const c =
    2 * Math.atan2(
      Math.sqrt(a),
      Math.sqrt(1 - a)
    );

  return earthRadius * c;
}
Enter fullscreen mode Exit fullscreen mode

Then:

const origin = {
  latitude: 40.7128,
  longitude: -74.0060
};

const placesWithDistance =
  places.map(place => ({
    ...place,

    distance:
      distanceBetween(
        origin.latitude,
        origin.longitude,
        place.latitude,
        place.longitude
      )
  }));
Enter fullscreen mode Exit fullscreen mode

We can sort the results:

placesWithDistance.sort(
  (a, b) =>
    a.distance - b.distance
);
Enter fullscreen mode Exit fullscreen mode

Now the nearest location appears first.


Format the Distance

A small formatting helper improves the UI:

function formatDistance(meters) {
  if (meters < 1000) {
    return `${Math.round(meters)} m`;
  }

  return `${(
    meters / 1000
  ).toFixed(1)} km`;
}
Enter fullscreen mode Exit fullscreen mode

Usage:

for (
  const place
  of placesWithDistance
) {
  console.log(
    place.name,
    formatDistance(place.distance)
  );
}
Enter fullscreen mode Exit fullscreen mode

Example:

Coffee Shop       180 m
Pizza Restaurant  420 m
Cafe              1.2 km
Enter fullscreen mode Exit fullscreen mode

Connect Nearby Search to a Map Click

This becomes especially useful when combined with an interactive map.

For example, with MapLibre:

map.on(
  "click",
  async event => {
    const {
      lat,
      lng
    } = event.lngLat;

    const data =
      await findNearbyFood(
        lat,
        lng,
        1500
      );

    console.log(
      "Nearby places:",
      data.elements
    );
  }
);
Enter fullscreen mode Exit fullscreen mode

Now the workflow becomes:

Click map
   ↓
Get longitude / latitude
   ↓
Search nearby places
   ↓
Display markers
Enter fullscreen mode Exit fullscreen mode

This makes the map itself the search interface.

Instead of forcing users to type an exact address, they can explore visually.


Combine It with Browser Geolocation

We can also start from the user's current position.

navigator.geolocation
  .getCurrentPosition(
    async position => {
      const {
        latitude,
        longitude
      } = position.coords;

      const data =
        await findNearbyFood(
          latitude,
          longitude,
          1500
        );

      console.log(data);
    },
    error => {
      console.error(
        "Unable to get location",
        error
      );
    }
  );
Enter fullscreen mode Exit fullscreen mode

This creates another common workflow:

Use My Location
      ↓
Browser geolocation
      ↓
Latitude + Longitude
      ↓
Nearby search
      ↓
Restaurants / Cafes / POIs
Enter fullscreen mode Exit fullscreen mode

Always remember that browser geolocation requires user permission.

Location access should also be requested only when the feature actually needs it.


Don't Forget Coordinate Validation

Before sending coordinates to any geographic service, validate them.

Latitude must be:

-90 to 90
Enter fullscreen mode Exit fullscreen mode

Longitude must be:

-180 to 180
Enter fullscreen mode Exit fullscreen mode

A simple helper:

function validateCoordinates(
  latitude,
  longitude
) {
  if (
    !Number.isFinite(latitude) ||
    latitude < -90 ||
    latitude > 90
  ) {
    throw new RangeError(
      "Invalid latitude"
    );
  }

  if (
    !Number.isFinite(longitude) ||
    longitude < -180 ||
    longitude > 180
  ) {
    throw new RangeError(
      "Invalid longitude"
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

This prevents many surprisingly common bugs.


The Latitude / Longitude Order Problem

One more thing deserves special attention.

Some APIs expect:

latitude, longitude
Enter fullscreen mode Exit fullscreen mode

Others use:

longitude, latitude
Enter fullscreen mode Exit fullscreen mode

GeoJSON, for example, uses:

[
  longitude,
  latitude
]
Enter fullscreen mode Exit fullscreen mode

MapLibre also commonly works with:

[
  longitude,
  latitude
]
Enter fullscreen mode Exit fullscreen mode

But the Overpass around syntax in our example uses:

latitude, longitude
Enter fullscreen mode Exit fullscreen mode

Mixing these up can move your result thousands of kilometers away.

Naming variables explicitly helps:

const latitude = 40.7128;
const longitude = -74.0060;
Enter fullscreen mode Exit fullscreen mode

instead of:

const x = 40.7128;
const y = -74.0060;
Enter fullscreen mode Exit fullscreen mode

Production Considerations

The public Overpass API is great for learning, prototypes, and relatively light queries.

It should not automatically be treated as the backend for a high-traffic production application.

If you're building a larger application, consider:

  • caching nearby searches,
  • limiting the search radius,
  • limiting requested categories,
  • avoiding unnecessary repeat queries,
  • debouncing map interactions,
  • using your own backend,
  • or using a commercial/local-search provider designed for production traffic.

Also remember that OpenStreetMap coverage varies by location.

Some cities contain extremely detailed POI information, while other areas may contain fewer mapped businesses.


Try Nearby Search with Real Locations

When developing a nearby-search feature, one of the most useful things you can do is test it with coordinates from different types of places.

For example:

New York
40.7128, -74.0060

London
51.5074, -0.1278

Tokyo
35.6762, 139.6503

Sydney
-33.8688, 151.2093
Enter fullscreen mode Exit fullscreen mode

I've been building CoordMap, a browser-based collection of geographic tools for working with coordinates, maps, elevation, location lookup, and nearby places.

You can use CoordMap Nearby Places to select an address or map location and explore restaurants, cafes, hotels, and similar places around it.

The live CoordMap tool uses its own nearby-place provider; the OpenStreetMap/Overpass implementation in this tutorial is an independent example for developers who want to understand how radius-based geographic searches work.

It's also useful when you need real coordinates to test your own mapping application.


Final Thoughts

Once your application has a reliable latitude and longitude pair, nearby search becomes another layer on top of the same location state.

The architecture can stay surprisingly simple:

Address Search ───────┐
                      │
Map Click ────────────┼──→ Latitude + Longitude
                      │
Current Location ─────┘
                              │
                              ▼
                       Nearby Search
                              │
                   ┌──────────┼──────────┐
                   ▼          ▼          ▼
              Restaurants   Cafes      Hotels
                   │
                   ▼
                 Markers
Enter fullscreen mode Exit fullscreen mode

This pattern is reusable across travel applications, mapping tools, local search interfaces, delivery systems, and location-based web apps.

And once the coordinate pair becomes the center of your application state, you can easily add other geographic layers such as:

Address
Elevation
Weather
Distance
Nearby places
Time zone
Enter fullscreen mode Exit fullscreen mode

That's one of the reasons latitude and longitude are such useful building blocks for location-based applications.

Top comments (0)