Guide · Node.js · REST API

GPS & GeoURI Validation in Node.js

Four input formats, coordinate range checks, and automatic DMS conversion — in one API call. Here's why parsing GPS strings yourself is harder than it looks.

1. Why GPS validation is harder than it looks

GPS coordinates look deceptively simple — just two numbers representing latitude and longitude. In practice, users enter coordinates in at least four different formats, each with its own syntax for degrees, minutes, seconds, and direction. Google Maps outputs decimal degrees, marine navigation devices use degrees and decimal minutes, aviation charts prefer degrees-minutes-seconds, and modern QR codes embed geo: URIs defined by RFC 5870.

A naive parseFloat() call handles exactly one of these formats. It silently fails on DMS strings like 52°13'46.92"N, ignores direction letters, cannot distinguish latitude from longitude in a GeoURI, and produces no error when a value exceeds the valid range. The result is subtle bugs that only surface when real users paste coordinates from unfamiliar sources.

On top of format variety, there are Unicode traps. Copy-pasting from Wikipedia or a PDF often introduces prime symbols (U+2032) instead of apostrophes, double primes (U+2033) instead of quotation marks, and ring-above characters (U+02DA) instead of degree signs. These look-alike characters break strict parsers silently.

The IsValid GPS API handles all of this in a single call — four formats, Unicode normalization, direction letters, range validation, and automatic DMS conversion. You send the raw user input; it returns structured, validated coordinates.


2. Four input formats

The API auto-detects which format the input uses. You do not need to tell it — just pass the raw string and the response includes the detected format field.

FormatNameExampleDescription
DDDecimal Degrees52.2297 21.0122Most common in APIs and databases. Plain decimal numbers with optional ° symbol.
DDMDegrees Decimal Minutes52°13.782' 21°0.732'Common in marine navigation and older GPS devices. Degrees plus decimal minutes.
DMSDegrees Minutes Seconds52°13'46.92" 21°0'43.92"Traditional cartographic format. Used in aviation, hiking maps, and government surveys.
GeoURIRFC 5870 URIgeo:52.2297,21.0122Standardized URI scheme. Used in QR codes, vCards, and mobile apps. Supports optional altitude and parameters.

Each format can include direction letters (N/S/E/W) either before or after the number. For example, N52.2297 E21.0122 and 52.2297N 21.0122E are both valid DD inputs.

There is also a dash-separated DMS variant: 52-13-46.92 N 21-0-43.92 E. This format uses hyphens instead of degree, minute, and second symbols. Because hyphens could also indicate negative values, this variant requires an explicit direction letter to avoid ambiguity.


3. Coordinate ranges

Valid GPS coordinates must fall within strict geographic bounds. Anything outside these ranges does not correspond to a real point on Earth's surface.

ComponentRangeNotes
Latitude-90 to +90+90 is the North Pole, -90 is the South Pole
Longitude-180 to +180+180 and -180 both refer to the antimeridian (International Date Line)
Minutes0 to <60Must be less than 60 in DMS and DDM formats
Seconds0 to <60Must be less than 60 in DMS format

The API rounds all coordinates to 6 decimal places in the response. This is intentional — 6 decimal places of latitude or longitude correspond to roughly 11 centimeters of ground distance, which is more precision than most applications need.

ℹ️Six decimal places give you about 11 centimeters of precision — more than enough for address geocoding or asset tracking.

4. Unicode normalization

One of the most common reasons coordinate parsing fails in production is Unicode look-alike characters. When users copy coordinates from Wikipedia articles, PDF documents, or desktop publishing software, the degree, prime, and double-prime symbols are often replaced by visually similar but technically different Unicode code points.

The API normalizes the following characters before parsing, so you never need to pre-process them yourself:

Input characterCode pointNormalized to
(prime)U+2032' (apostrophe, U+0027)
(double prime)U+2033" (double quote, U+0022)
˚ (ring above)U+02DA° (degree sign, U+00B0)
° (degree sign)U+00B0° (normalized, U+00B0)

This is a real problem in practice. Consider the following JavaScript — these three strings look identical but contain different bytes:

// These look the same but use different Unicode code points:
const a = '52°13'46.92"N';   // ASCII quotes — works
const b = '52°13′46.92″N';   // prime + double prime — common in Wikipedia
const c = '52˚13'46.92"N';   // ring above instead of degree — common in PDFs

// Without normalization, b and c would fail to parse.
// The IsValid API normalizes all of them to the same canonical form.

Because the API handles normalization server-side, you can pass raw clipboard content or OCR output directly. No find-and-replace preprocessing needed.


5. Direction letters — N / S / E / W

Direction letters indicate which hemisphere a coordinate falls in. They can appear in several positions depending on the format and regional convention:

Before the number (prefix)

N52.2297 E21.0122 — The direction letter comes first, immediately followed by the numeric value. This is common in aviation and military coordinate systems (ICAO format).

After the number (suffix)

52.2297N 21.0122E — The direction letter follows the numeric value. This is the most common convention in cartographic notation and DMS coordinates: 52°13'46.92"N 21°0'43.92"E.

Negative numbers instead of S / W

-33.8688 151.2093 — Without direction letters, a negative latitude means South and a negative longitude means West. This is the standard in most programming APIs and databases. Sydney, Australia is at approximately -33.87° latitude.

The S and W direction letters produce negative coordinates internally. The API always returns the sign-based lat and lon values (negative for south and west) along with the explicit latDir and lonDir fields.

// All three refer to the same point — Sydney, Australia:
await validateGps('-33.8688 151.2093');       // negative lat = South
await validateGps('S33.8688 E151.2093');       // S prefix = South
await validateGps('33.8688S 151.2093E');       // S suffix = South

// Response for all three:
// { lat: -33.8688, lon: 151.2093, latDir: "S", lonDir: "E", ... }

6. The API response

Valid coordinate — Warsaw, Poland (DD format)

{
  "valid": true,
  "format": "dd",
  "lat": 52.2297,
  "lon": 21.0122,
  "latDir": "N",
  "lonDir": "E",
  "dms": {
    "lat": "52°13'46.92\"N",
    "lon": "21°0'43.92\"E"
  }
}

Invalid coordinate — latitude out of range

{
  "valid": false
}
FieldTypeDescription
validbooleanWhether the input is a valid coordinate pair
formatstringDetected format: "dd", "ddm", "dms", or "geo-uri"(only present when valid: true)
latnumberLatitude in decimal degrees, rounded to 6 decimal places (only present when valid: true)
lonnumberLongitude in decimal degrees, rounded to 6 decimal places (only present when valid: true)
latDirstring"N" or "S"(only present when valid: true)
lonDirstring"E" or "W"(only present when valid: true)
dmsobjectCoordinates converted to DMS notation with lat and lon string fields. Always included when valid: true

7. Node.js code example

Using the built-in fetch API available in Node.js 18+:

// gps-validator.mjs
const API_KEY = process.env.ISVALID_API_KEY;
const BASE_URL = 'https://api.isvalid.dev';

async function validateGps(coords) {
  const url = new URL('/v0/gps', BASE_URL);
  url.searchParams.set('value', coords);

  const response = await fetch(url, {
    headers: { Authorization: `Bearer ${API_KEY}` },
  });

  if (!response.ok) {
    throw new Error(`API error: ${response.status}`);
  }
  return response.json();
}

// ── Example usage ──
const result = await validateGps('52°13\'46.92"N 21°0\'43.92"E');

if (!result.valid) {
  console.log('Invalid coordinates');
} else {
  console.log('Format :', result.format);    // "dms"
  console.log('Lat    :', result.lat);        // 52.2297
  console.log('Lon    :', result.lon);        // 21.0122
  console.log('DMS lat:', result.dms.lat);    // "52°13'46.92\"N"
  console.log('DMS lon:', result.dms.lon);    // "21°0'43.92\"E"
}

Validating multiple formats in a single batch:

const inputs = [
  '52.2297 21.0122',           // DD
  '52°13.782\' 21°0.732\'',    // DDM
  'geo:52.2297,21.0122',       // GeoURI
  '91.0000 21.0122',           // Invalid — lat > 90
];

const results = await Promise.all(inputs.map(validateGps));
results.forEach((r, i) => {
  console.log(inputs[i], '→', r.valid ? `${r.format} (${r.lat}, ${r.lon})` : 'INVALID');
});
The API accepts raw user input — whitespace, Unicode symbols, and mixed formats are handled automatically.

8. cURL examples

Decimal Degrees — Warsaw:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/gps?value=52.2297%2021.0122"

DMS with direction letters — Warsaw:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/gps?value=52%C2%B013%2746.92%22N%2021%C2%B00%2743.92%22E"

GeoURI (RFC 5870) — Warsaw:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/gps?value=geo%3A52.2297%2C21.0122"

9. Edge cases to handle

Poles

Latitude ±90 is valid. Both the North Pole (90°N) and the South Pole (90°S) are legitimate coordinates. Longitude is technically meaningless at the poles — all meridians converge — but the API accepts any longitude value paired with ±90 latitude without error.

// North Pole — all longitudes are equivalent here
await validateGps('90.0 0.0');       // valid, lat: 90, latDir: "N"
await validateGps('90.0 180.0');     // valid, same physical point

// South Pole
await validateGps('-90.0 0.0');      // valid, lat: -90, latDir: "S"

Antimeridian

Longitude ±180 is valid. Both 180° and -180° refer to the same meridian — the International Date Line roughly follows this line through the Pacific Ocean. The API accepts both values without treating them differently.

// Both refer to the same meridian
await validateGps('0.0 180.0');      // valid, lon: 180, lonDir: "E"
await validateGps('0.0 -180.0');     // valid, lon: -180, lonDir: "W"

// But 180.001 is out of range
await validateGps('0.0 180.001');    // { valid: false }

Altitude in GeoURI

The GeoURI scheme (RFC 5870) supports an optional third component for altitude in meters: geo:52.2297,21.0122,123. The API accepts this format and validates the latitude and longitude, but the altitude value is not included in the response — only lat and lon are returned.

// GeoURI with altitude (123 meters)
const result = await validateGps('geo:52.2297,21.0122,123');

// Altitude is accepted but not returned in the response
console.log(result.lat);     // 52.2297
console.log(result.lon);     // 21.0122
console.log(result.format);  // "geo-uri"

DMS with dashes

The dash-separated DMS format uses hyphens as separators instead of degree, minute, and second symbols: 52-13-46.92 N 21-0-43.92 E. This format requires an explicit direction letter. Without it, the parser cannot distinguish a dash-separated DMS value from a negative decimal degree — is -33-51 negative 33 degrees 51 minutes, or just the number -33.51 with a misplaced separator?

// Dash-separated DMS — direction letter required
await validateGps('52-13-46.92 N 21-0-43.92 E');  // valid, format: "dms"

// Without direction letters, dashes are ambiguous
// The API interprets plain negative numbers as DD format:
await validateGps('-33.8688 151.2093');             // valid, format: "dd"

Summary

Do not assume all inputs are decimal degrees — users submit DMS, DDM, and GeoURI
Do not reject Unicode degree/prime symbols — the API normalizes them automatically
Do not parse coordinates with a simple split-on-comma — direction letters and spaces make it ambiguous
Accept all four formats — DD, DDM, DMS, and GeoURI (RFC 5870)
Validate ranges — latitude -90..90, longitude -180..180
Use the dms field for human-readable display of any input format

See also

Try GPS validation instantly

Free tier includes 100 API calls per day. No credit card required. All four coordinate formats supported.