DEV Community

echoluoluo
echoluoluo

Posted on

How to Build a Longitude and Latitude Finder with JavaScript and MapLibre

Longitude and latitude finders look simple from the outside:

  1. Search for an address.
  2. Get its coordinates.
  3. Or click somewhere on a map.
  4. Get the address for that point.

But building a useful version involves several pieces working together:

  • An interactive map
  • Forward geocoding
  • Reverse geocoding
  • Coordinate validation
  • Browser geolocation
  • Map and form state synchronization
  • Error handling

In this article, we'll build the core architecture of a browser-based longitude and latitude finder using JavaScript and MapLibre GL JS.

The goal is a tool that supports three common workflows:

Address → Longitude / Latitude

Longitude / Latitude → Address

Map Click → Longitude / Latitude + Address
Enter fullscreen mode Exit fullscreen mode

Let's build it step by step.


What Are We Building?

Our interface can be kept simple.

┌─────────────────────────────────────┐
│ Search an address                   │
│ [ New York City                ] 🔍 │
└─────────────────────────────────────┘

Longitude
[-74.0060]

Latitude
[40.7128]

[Look up]

┌─────────────────────────────────────┐
│                                     │
│             Interactive Map         │
│                                     │
│          Click any location         │
│                                     │
└─────────────────────────────────────┘

Selected location

New York, NY, United States

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

The important design decision is that all three inputs update the same selected location state.

Whether the user:

  • searches an address,
  • enters coordinates,
  • clicks the map,
  • or uses browser geolocation,

the application should eventually produce the same internal representation.

For example:

const selectedLocation = {
  longitude: -74.006,
  latitude: 40.7128,
  address: "New York, NY, United States"
};
Enter fullscreen mode Exit fullscreen mode

This makes the rest of the application much easier to maintain.


Understanding Longitude and Latitude

Before writing the UI logic, we need to validate coordinates correctly.

Latitude ranges from:

-90 to 90
Enter fullscreen mode Exit fullscreen mode

Longitude ranges from:

-180 to 180
Enter fullscreen mode Exit fullscreen mode

Examples:

New York

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

Latitude:  -33.8688
Longitude: 151.2093
Enter fullscreen mode Exit fullscreen mode

A simple JavaScript validator looks like this:

function isValidLatitude(value) {
  return (
    Number.isFinite(value) &&
    value >= -90 &&
    value <= 90
  );
}

function isValidLongitude(value) {
  return (
    Number.isFinite(value) &&
    value >= -180 &&
    value <= 180
  );
}
Enter fullscreen mode Exit fullscreen mode

Then combine them:

function validateCoordinates(
  longitude,
  latitude
) {
  if (!isValidLongitude(longitude)) {
    throw new RangeError(
      "Longitude must be between -180 and 180"
    );
  }

  if (!isValidLatitude(latitude)) {
    throw new RangeError(
      "Latitude must be between -90 and 90"
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

This should happen before moving the map or sending a reverse-geocoding request.


Create the Map with MapLibre

First, create a container:

<div id="map"></div>
Enter fullscreen mode Exit fullscreen mode

Give it a height:

#map {
  width: 100%;
  height: 500px;
}
Enter fullscreen mode Exit fullscreen mode

Then initialize MapLibre:

const map = new maplibregl.Map({
  container: "map",

  style: "YOUR_MAP_STYLE_URL",

  center: [0, 20],

  zoom: 2
});
Enter fullscreen mode Exit fullscreen mode

Notice the coordinate order here:

[longitude, latitude]
Enter fullscreen mode Exit fullscreen mode

Not:

[latitude, longitude]
Enter fullscreen mode Exit fullscreen mode

This is an easy mistake to make when working with mapping APIs.

I prefer naming variables explicitly:

const longitude = -74.006;
const latitude = 40.7128;

map.flyTo({
  center: [
    longitude,
    latitude
  ],
  zoom: 12
});
Enter fullscreen mode Exit fullscreen mode

That is much easier to read than:

map.flyTo({
  center: [-74.006, 40.7128]
});
Enter fullscreen mode Exit fullscreen mode

Click the Map to Get Coordinates

One of the most useful interactions is allowing users to click anywhere on the map.

MapLibre exposes the clicked geographic position:

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

  console.log("Longitude:", lng);
  console.log("Latitude:", lat);
});
Enter fullscreen mode Exit fullscreen mode

You can round the coordinates for display:

map.on("click", event => {
  const longitude =
    event.lngLat.lng.toFixed(6);

  const latitude =
    event.lngLat.lat.toFixed(6);

  console.log({
    longitude,
    latitude
  });
});
Enter fullscreen mode Exit fullscreen mode

A click might produce:

{
  longitude: "-74.006015",
  latitude: "40.712728"
}
Enter fullscreen mode Exit fullscreen mode

For calculations and API requests, however, keep them as numbers.

const longitude =
  event.lngLat.lng;

const latitude =
  event.lngLat.lat;
Enter fullscreen mode Exit fullscreen mode

Only format the values when displaying them.


Add a Marker to the Selected Location

A visual marker makes it obvious which point is currently selected.

const marker =
  new maplibregl.Marker();

function setMarker(
  longitude,
  latitude
) {
  marker
    .setLngLat([
      longitude,
      latitude
    ])
    .addTo(map);
}
Enter fullscreen mode Exit fullscreen mode

Then:

map.on("click", event => {
  const longitude =
    event.lngLat.lng;

  const latitude =
    event.lngLat.lat;

  setMarker(
    longitude,
    latitude
  );
});
Enter fullscreen mode Exit fullscreen mode

We can also move the camera:

function focusLocation(
  longitude,
  latitude
) {
  map.flyTo({
    center: [
      longitude,
      latitude
    ],
    zoom: 14
  });
}
Enter fullscreen mode Exit fullscreen mode

Reverse Geocoding: Coordinates to Address

Coordinates are useful for software.

Humans usually want an address or place name.

That's where reverse geocoding comes in.

The flow looks like this:

-74.0060, 40.7128
        ↓
Reverse Geocoding API
        ↓
New York, NY, United States
Enter fullscreen mode Exit fullscreen mode

Instead of connecting your frontend directly to a provider containing a private API key, a safer architecture is:

Browser
   ↓
Your API
   ↓
Geocoding Provider
Enter fullscreen mode Exit fullscreen mode

For example, the browser can call:

async function reverseGeocode(
  longitude,
  latitude
) {
  validateCoordinates(
    longitude,
    latitude
  );

  const params =
    new URLSearchParams({
      longitude,
      latitude
    });

  const response = await fetch(
    `/api/reverse-geocode?${params}`
  );

  if (!response.ok) {
    throw new Error(
      "Reverse geocoding failed"
    );
  }

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

The response from your own backend can be normalized.

For example:

{
  "address": "New York, NY, United States",
  "longitude": -74.006,
  "latitude": 40.7128,
  "country": "United States",
  "countryCode": "US"
}
Enter fullscreen mode Exit fullscreen mode

Normalizing the response is useful because different geocoding providers often return different field structures.

Your frontend doesn't need to care which provider was used.


Connect Reverse Geocoding to Map Clicks

Now we can combine the pieces.

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

  const latitude =
    event.lngLat.lat;

  setMarker(
    longitude,
    latitude
  );

  try {
    const place =
      await reverseGeocode(
        longitude,
        latitude
      );

    console.log(place);
  } catch (error) {
    console.error(
      "Unable to find address:",
      error
    );
  }
});
Enter fullscreen mode Exit fullscreen mode

The user interaction is now:

Click map
   ↓
Read longitude / latitude
   ↓
Move marker
   ↓
Reverse geocode
   ↓
Display address
Enter fullscreen mode Exit fullscreen mode

That already gives us a basic longitude and latitude finder.


Forward Geocoding: Address to Coordinates

We also need the opposite direction.

1600 Amphitheatre Parkway
          ↓
      Geocoding API
          ↓
37.4220, -122.0841
Enter fullscreen mode Exit fullscreen mode

Create a search function:

async function geocodeAddress(
  query
) {
  const value = query.trim();

  if (!value) {
    throw new Error(
      "Address is required"
    );
  }

  const params =
    new URLSearchParams({
      q: value
    });

  const response = await fetch(
    `/api/geocode?${params}`
  );

  if (!response.ok) {
    throw new Error(
      "Address search failed"
    );
  }

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

A normalized response might look like:

{
  "longitude": -122.0841,
  "latitude": 37.422,
  "address": "1600 Amphitheatre Parkway, Mountain View, CA"
}
Enter fullscreen mode Exit fullscreen mode

Then update the map:

async function searchAddress(
  query
) {
  const place =
    await geocodeAddress(query);

  setMarker(
    place.longitude,
    place.latitude
  );

  focusLocation(
    place.longitude,
    place.latitude
  );

  updateLocationPanel(place);
}
Enter fullscreen mode Exit fullscreen mode

Build the Address Search Form

The HTML can stay simple:

<form id="address-form">
  <input
    id="address-input"
    type="search"
    placeholder="Enter an address or place"
  />

  <button type="submit">
    Search
  </button>
</form>
Enter fullscreen mode Exit fullscreen mode

And the JavaScript:

const form =
  document.querySelector(
    "#address-form"
  );

const input =
  document.querySelector(
    "#address-input"
  );

form.addEventListener(
  "submit",
  async event => {
    event.preventDefault();

    try {
      await searchAddress(
        input.value
      );
    } catch (error) {
      console.error(error);
    }
  }
);
Enter fullscreen mode Exit fullscreen mode

Now users can search a city, street address, or place name.


Let Users Enter Coordinates Directly

Another important workflow is:

User already has coordinates
           ↓
Enters longitude + latitude
           ↓
Map opens that point
           ↓
Address is resolved
Enter fullscreen mode Exit fullscreen mode

HTML:

<input
  id="longitude"
  type="number"
  step="any"
  min="-180"
  max="180"
  placeholder="Longitude"
/>

<input
  id="latitude"
  type="number"
  step="any"
  min="-90"
  max="90"
  placeholder="Latitude"
/>

<button id="lookup">
  Look up
</button>
Enter fullscreen mode Exit fullscreen mode

JavaScript:

document
  .querySelector("#lookup")
  .addEventListener(
    "click",
    async () => {
      const longitude =
        Number(
          document
            .querySelector(
              "#longitude"
            )
            .value
        );

      const latitude =
        Number(
          document
            .querySelector(
              "#latitude"
            )
            .value
        );

      validateCoordinates(
        longitude,
        latitude
      );

      setMarker(
        longitude,
        latitude
      );

      focusLocation(
        longitude,
        latitude
      );

      const place =
        await reverseGeocode(
          longitude,
          latitude
        );

      updateLocationPanel(place);
    }
  );
Enter fullscreen mode Exit fullscreen mode

Now we support two-way conversion:

Address → Coordinates

Coordinates → Address
Enter fullscreen mode Exit fullscreen mode

Add Browser Geolocation

Users often want one more shortcut:

Where am I right now?

The browser Geolocation API can provide this.

function locateUser() {
  if (!navigator.geolocation) {
    throw new Error(
      "Geolocation is not supported"
    );
  }

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

        setMarker(
          longitude,
          latitude
        );

        focusLocation(
          longitude,
          latitude
        );

        const place =
          await reverseGeocode(
            longitude,
            latitude
          );

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

Connect it to a button:

<button id="locate-me">
  Locate me
</button>
Enter fullscreen mode Exit fullscreen mode
document
  .querySelector("#locate-me")
  .addEventListener(
    "click",
    locateUser
  );
Enter fullscreen mode Exit fullscreen mode

Always request location only after a clear user action.

Users should understand why location permission is being requested.


Use One Location State for Every Input Method

At this point we have several ways to select a location:

Address search
Coordinate input
Map click
Browser geolocation
Enter fullscreen mode Exit fullscreen mode

A common mistake is writing independent UI logic for each workflow.

That quickly becomes difficult to maintain.

Instead, use one function.

let currentLocation = null;

async function selectLocation({
  longitude,
  latitude,
  address = null,
  moveMap = true
}) {
  validateCoordinates(
    longitude,
    latitude
  );

  let location = {
    longitude,
    latitude,
    address
  };

  if (!address) {
    const result =
      await reverseGeocode(
        longitude,
        latitude
      );

    location = {
      ...location,
      ...result
    };
  }

  currentLocation = location;

  setMarker(
    longitude,
    latitude
  );

  if (moveMap) {
    focusLocation(
      longitude,
      latitude
    );
  }

  updateCoordinateInputs(
    longitude,
    latitude
  );

  updateLocationPanel(
    location
  );

  return location;
}
Enter fullscreen mode Exit fullscreen mode

Now a map click becomes:

map.on("click", event => {
  selectLocation({
    longitude:
      event.lngLat.lng,

    latitude:
      event.lngLat.lat,

    moveMap: false
  });
});
Enter fullscreen mode Exit fullscreen mode

Address search becomes:

const place =
  await geocodeAddress(query);

await selectLocation(place);
Enter fullscreen mode Exit fullscreen mode

And browser geolocation becomes:

await selectLocation({
  longitude:
    position.coords.longitude,

  latitude:
    position.coords.latitude
});
Enter fullscreen mode Exit fullscreen mode

This architecture dramatically reduces duplicated code.


Keep Longitude and Latitude Inputs in Sync

Once a location changes, update the input fields:

function updateCoordinateInputs(
  longitude,
  latitude
) {
  document
    .querySelector(
      "#longitude"
    )
    .value =
      longitude.toFixed(6);

  document
    .querySelector(
      "#latitude"
    )
    .value =
      latitude.toFixed(6);
}
Enter fullscreen mode Exit fullscreen mode

Notice again that rounding is done for presentation.

The internal state can keep the full numeric precision.


Build the Location Details Panel

A minimal result panel could contain:

<section id="place-details">
  <h2>Place details</h2>

  <p id="address"></p>

  <dl>
    <dt>Longitude</dt>
    <dd id="result-longitude"></dd>

    <dt>Latitude</dt>
    <dd id="result-latitude"></dd>
  </dl>
</section>
Enter fullscreen mode Exit fullscreen mode

Update it:

function updateLocationPanel(
  location
) {
  document
    .querySelector("#address")
    .textContent =
      location.address ??
      "Address unavailable";

  document
    .querySelector(
      "#result-longitude"
    )
    .textContent =
      location.longitude
        .toFixed(6);

  document
    .querySelector(
      "#result-latitude"
    )
    .textContent =
      location.latitude
        .toFixed(6);
}
Enter fullscreen mode Exit fullscreen mode

The same component works regardless of how the user selected the location.


Add Elevation as a Separate Data Layer

Once we have coordinates, we can query additional geographic data.

For example:

Selected point
     ↓
Longitude / Latitude
     ↓
Elevation API or dataset
     ↓
Estimated elevation
Enter fullscreen mode Exit fullscreen mode

A frontend helper might look like:

async function getElevation(
  longitude,
  latitude
) {
  const params =
    new URLSearchParams({
      longitude,
      latitude
    });

  const response = await fetch(
    `/api/elevation?${params}`
  );

  if (!response.ok) {
    throw new Error(
      "Elevation lookup failed"
    );
  }

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

Then:

const elevation =
  await getElevation(
    longitude,
    latitude
  );

console.log(
  `${elevation.meters} m`
);
Enter fullscreen mode Exit fullscreen mode

One important UI detail:

don't imply that estimated terrain elevation is the same thing as high-precision surveyed altitude.

Always describe the accuracy of your source clearly.


Add Nearby Places as an Optional Extension

Once a user has selected a location, the same coordinates can power other features.

For example:

Selected coordinates
       ↓
Nearby search
       ↓
Restaurants
Hotels
Cafes
Attractions
Enter fullscreen mode Exit fullscreen mode

This doesn't need to be part of the core coordinate finder.

I prefer treating it as a secondary action:

Find location
     ↓
Show coordinates + address
     ↓
Optional: explore nearby places
Enter fullscreen mode Exit fullscreen mode

That keeps the primary workflow focused.


Handle Loading and Errors

Network requests fail.

Addresses can also return no results.

Location permission can be denied.

A real application should explicitly handle these states.

For example:

async function searchAddress(
  query
) {
  setLoading(true);

  try {
    const place =
      await geocodeAddress(query);

    if (!place) {
      showError(
        "No matching place found."
      );

      return;
    }

    await selectLocation(place);
  } catch (error) {
    showError(
      "Unable to search this address."
    );
  } finally {
    setLoading(false);
  }
}
Enter fullscreen mode Exit fullscreen mode

Useful error states include:

Address not found

Invalid longitude

Invalid latitude

Location permission denied

Network unavailable

Geocoding service unavailable
Enter fullscreen mode Exit fullscreen mode

Showing a clear message is better than silently doing nothing.


Avoid Exposing API Keys in the Browser

If your geocoding provider requires a private API key, don't embed that key directly in public frontend JavaScript.

Avoid:

const API_KEY =
  "my-secret-api-key";
Enter fullscreen mode Exit fullscreen mode

Anything shipped to the browser should be considered visible to users.

A better setup is:

Frontend
   ↓
/api/geocode
   ↓
Backend
   ↓
External geocoding provider
Enter fullscreen mode Exit fullscreen mode

Your server can:

  • protect credentials,
  • normalize responses,
  • add caching,
  • add rate limits,
  • switch providers later.

This also keeps the frontend code much cleaner.


A Simple Application Architecture

The final data flow might look like this:

              ┌─────────────────┐
              │ Address Search  │
              └────────┬────────┘
                       │
                       ▼
                   Geocoding

Map Click ─────────────┐
                      │
Coordinate Input ─────┼──→ selectLocation()
                      │
My Location ──────────┘
                            │
                            ▼
                 Longitude + Latitude
                            │
               ┌────────────┼────────────┐
               ▼            ▼            ▼
             Marker       Address     Elevation
               │
               ▼
            Map View
Enter fullscreen mode Exit fullscreen mode

The key idea is simple:

Every interaction eventually becomes a longitude and latitude pair.

Once you have that pair, the rest of the application becomes a collection of optional geographic lookups.


Try a Real Longitude and Latitude Finder

I used this same interaction model while building CoordMap's Longitude and Latitude Finder.

The live tool lets you:

  • search an address for longitude and latitude,
  • enter longitude and latitude to find an address,
  • click directly on the map,
  • use your current location,
  • inspect location details,
  • and continue exploring geographic information around the selected point.

If you're building your own version, using a live map tool can also be useful for testing coordinates from different countries and hemispheres.

For example, try:

Washington, DC
38.9072, -77.0369

London
51.5074, -0.1278

Tokyo
35.6762, 139.6503

Sydney
-33.8688, 151.2093
Enter fullscreen mode Exit fullscreen mode

These help test:

North + West
North + East
South + East
Enter fullscreen mode Exit fullscreen mode

and reveal common latitude/longitude ordering mistakes.


What I Would Add Next

Once the core finder is reliable, several extensions become possible.

Coordinate format conversion

Display the same location as:

Decimal Degrees
40.7128, -74.0060
Enter fullscreen mode Exit fullscreen mode

and:

DMS
40°42'46.08"N
74°00'21.60"W
Enter fullscreen mode Exit fullscreen mode

Shareable locations

Generate a URL containing:

longitude
latitude
zoom
Enter fullscreen mode Exit fullscreen mode

so users can send the selected point to someone else.

Distance measurement

Once you can select one coordinate, selecting two points allows you to calculate distance.

Additional coordinate systems

More advanced GIS applications may also need transformations between WGS84 and other coordinate reference systems.

The important thing is to build these on top of the same clean location state instead of creating separate implementations for every feature.


Final Thoughts

A longitude and latitude finder is a good example of a small application where UI design and data architecture matter just as much as the map itself.

The basic workflow is:

User selects a location
          ↓
Longitude + Latitude
          ↓
Validate coordinates
          ↓
Update marker and map
          ↓
Resolve address
          ↓
Display place details
Enter fullscreen mode Exit fullscreen mode

From there, features such as elevation, nearby places, distance measurement, coordinate conversion, and sharing can all be added without changing the core model.

The most important implementation lessons are:

  • Treat longitude and latitude as the central state.
  • Keep map interactions and form inputs synchronized.
  • Validate coordinate ranges.
  • Keep numeric precision separate from display formatting.
  • Normalize geocoding responses.
  • Keep private API credentials on the server.
  • Handle permission and network errors explicitly.
  • Keep optional map features secondary to the main coordinate workflow.

Once those foundations are in place, building additional mapping tools becomes much easier.

Top comments (0)