Longitude and latitude finders look simple from the outside:
- Search for an address.
- Get its coordinates.
- Or click somewhere on a map.
- 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
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
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"
};
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
Longitude ranges from:
-180 to 180
Examples:
New York
Latitude: 40.7128
Longitude: -74.0060
Sydney
Latitude: -33.8688
Longitude: 151.2093
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
);
}
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"
);
}
}
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>
Give it a height:
#map {
width: 100%;
height: 500px;
}
Then initialize MapLibre:
const map = new maplibregl.Map({
container: "map",
style: "YOUR_MAP_STYLE_URL",
center: [0, 20],
zoom: 2
});
Notice the coordinate order here:
[longitude, latitude]
Not:
[latitude, longitude]
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
});
That is much easier to read than:
map.flyTo({
center: [-74.006, 40.7128]
});
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);
});
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
});
});
A click might produce:
{
longitude: "-74.006015",
latitude: "40.712728"
}
For calculations and API requests, however, keep them as numbers.
const longitude =
event.lngLat.lng;
const latitude =
event.lngLat.lat;
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);
}
Then:
map.on("click", event => {
const longitude =
event.lngLat.lng;
const latitude =
event.lngLat.lat;
setMarker(
longitude,
latitude
);
});
We can also move the camera:
function focusLocation(
longitude,
latitude
) {
map.flyTo({
center: [
longitude,
latitude
],
zoom: 14
});
}
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
Instead of connecting your frontend directly to a provider containing a private API key, a safer architecture is:
Browser
↓
Your API
↓
Geocoding Provider
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();
}
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"
}
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
);
}
});
The user interaction is now:
Click map
↓
Read longitude / latitude
↓
Move marker
↓
Reverse geocode
↓
Display address
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
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();
}
A normalized response might look like:
{
"longitude": -122.0841,
"latitude": 37.422,
"address": "1600 Amphitheatre Parkway, Mountain View, CA"
}
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);
}
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>
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);
}
}
);
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
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>
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);
}
);
Now we support two-way conversion:
Address → Coordinates
Coordinates → Address
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
);
}
);
}
Connect it to a button:
<button id="locate-me">
Locate me
</button>
document
.querySelector("#locate-me")
.addEventListener(
"click",
locateUser
);
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
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;
}
Now a map click becomes:
map.on("click", event => {
selectLocation({
longitude:
event.lngLat.lng,
latitude:
event.lngLat.lat,
moveMap: false
});
});
Address search becomes:
const place =
await geocodeAddress(query);
await selectLocation(place);
And browser geolocation becomes:
await selectLocation({
longitude:
position.coords.longitude,
latitude:
position.coords.latitude
});
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);
}
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>
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);
}
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
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();
}
Then:
const elevation =
await getElevation(
longitude,
latitude
);
console.log(
`${elevation.meters} m`
);
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
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
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);
}
}
Useful error states include:
Address not found
Invalid longitude
Invalid latitude
Location permission denied
Network unavailable
Geocoding service unavailable
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";
Anything shipped to the browser should be considered visible to users.
A better setup is:
Frontend
↓
/api/geocode
↓
Backend
↓
External geocoding provider
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
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
These help test:
North + West
North + East
South + East
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
and:
DMS
40°42'46.08"N
74°00'21.60"W
Shareable locations
Generate a URL containing:
longitude
latitude
zoom
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
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)