fix: improve GPS parsing robustness

This commit is contained in:
William Valentin
2026-02-04 15:54:16 -08:00
parent 4180e7866c
commit 5d2054637f

View File

@@ -281,6 +281,18 @@ function parseGpsParts(parts: number[]): number | null {
return sign * value;
}
function parseGpsFraction(input: string): number | null {
const trimmed = input.trim();
if (!trimmed) return null;
const match = trimmed.match(/^(-?\d+(?:\.\d+)?)\s*\/\s*(\d+(?:\.\d+)?)$/);
if (!match) return null;
const numerator = Number(match[1]);
const denominator = Number(match[2]);
if (!Number.isFinite(numerator) || !Number.isFinite(denominator)) return null;
if (denominator === 0) return null;
return numerator / denominator;
}
function parseGpsValue(value: unknown): number | null {
if (typeof value === "number") {
return Number.isFinite(value) ? value : null;
@@ -291,6 +303,8 @@ function parseGpsValue(value: unknown): number | null {
if (!trimmed) return null;
const direct = Number(trimmed);
if (!Number.isNaN(direct)) return direct;
const fraction = parseGpsFraction(trimmed);
if (fraction !== null) return fraction;
const parts = trimmed.match(/-?\d+(?:\.\d+)?/g);
if (!parts) return null;
return parseGpsParts(parts.map((part) => Number(part)).filter(Number.isFinite));
@@ -300,7 +314,19 @@ function parseGpsValue(value: unknown): number | null {
const parts = value
.map((part) => {
if (typeof part === "number") return part;
if (typeof part === "string") return Number(part);
if (typeof part === "string") {
const fraction = parseGpsFraction(part);
if (fraction !== null) return fraction;
return Number(part);
}
if (typeof part === "object" && part !== null) {
const candidate = part as Record<string, unknown>;
const numerator = Number(candidate.numerator);
const denominator = Number(candidate.denominator);
if (Number.isFinite(numerator) && Number.isFinite(denominator) && denominator !== 0) {
return numerator / denominator;
}
}
return NaN;
})
.filter(Number.isFinite);
@@ -310,9 +336,13 @@ function parseGpsValue(value: unknown): number | null {
return null;
}
function applyRefSign(value: number, ref: unknown): number {
if (typeof ref !== "string") return value;
const normalized = ref.trim().toUpperCase();
function applyRefSign(value: number, ref: unknown, valueRaw: unknown): number {
const refChar = typeof ref === "string" ? ref.trim().toUpperCase() : "";
const rawChar =
typeof valueRaw === "string"
? (valueRaw.trim().match(/[NSEW]/i)?.[0]?.toUpperCase() ?? "")
: "";
const normalized = refChar || rawChar;
if (normalized === "S" || normalized === "W") return -Math.abs(value);
if (normalized === "N" || normalized === "E") return Math.abs(value);
return value;
@@ -325,7 +355,7 @@ function parseGpsCoord(
): number | null {
const parsed = parseGpsValue(value);
if (parsed === null) return null;
const signed = applyRefSign(parsed, ref);
const signed = applyRefSign(parsed, ref, value);
if (!Number.isFinite(signed)) return null;
if (kind === "lat") {
return signed >= -90 && signed <= 90 ? signed : null;