From 6e230698c7028cdc32954eb36014c6a205cc3a83 Mon Sep 17 00:00:00 2001 From: Yarchik Date: Wed, 24 Jun 2026 02:17:33 +0100 Subject: [PATCH] fix: allow multiple whitespace between coordinate ordinates (OGC WKT) Under the OGC Simple Features WKT grammar the separator between ordinates in a coordinate tuple is one or more whitespace, so "1 2", "1 2" and "1\t2" denote the same point. The parser accepted a single space and a tab but returned null on two or more spaces. The tuples regex hard-coded a single \s between ordinates, and the ordinate splitters in coords() and multicoords() used split(/\s/g), which yields an empty element (parseFloat NaN) on runs of whitespace. Widen both the regex and the four splits to \s+. This is the ordinate-separator whitespace, distinct from issues #33 and #36 which concern whitespace adjacent to the parentheses. --- index.js | 10 +++++----- test/wellknown.test.js | 16 ++++++++++++++++ wellknown.js | 10 +++++----- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/index.js b/index.js index dbac338..5d39ac3 100644 --- a/index.js +++ b/index.js @@ -5,7 +5,7 @@ module.exports.stringify = stringify; var numberRegexp = /[-+]?([0-9]*\.[0-9]+|[0-9]+)([eE][-+]?[0-9]+)?/; // Matches sequences like '100 100' or '100 100 100'. -var tuples = new RegExp('^' + numberRegexp.source + '(\\s' + numberRegexp.source + '){1,}'); +var tuples = new RegExp('^' + numberRegexp.source + '(\\s+' + numberRegexp.source + '){1,}'); /* * Parse WKT and return GeoJSON. @@ -74,8 +74,8 @@ function parse (input) { } else if (elem === ',') { pointer = []; stack[stack.length - 1].push(pointer); - } else if (!elem.split(/\s/g).some(isNaN)) { - Array.prototype.push.apply(pointer, elem.split(/\s/g).map(parseFloat)); + } else if (!elem.split(/\s+/g).some(isNaN)) { + Array.prototype.push.apply(pointer, elem.split(/\s+/g).map(parseFloat)); } else { return null; } @@ -97,9 +97,9 @@ function parse (input) { if (pt === ',') { list.push(item); item = []; - } else if (!pt.split(/\s/g).some(isNaN)) { + } else if (!pt.split(/\s+/g).some(isNaN)) { if (!item) item = []; - Array.prototype.push.apply(item, pt.split(/\s/g).map(parseFloat)); + Array.prototype.push.apply(item, pt.split(/\s+/g).map(parseFloat)); } white(); } diff --git a/test/wellknown.test.js b/test/wellknown.test.js index 751c579..a21fcb9 100644 --- a/test/wellknown.test.js +++ b/test/wellknown.test.js @@ -258,5 +258,21 @@ test('wellknown', function(t) { coordinates: [[[30, 10, 1], [10, 20, 2], [20, 40, 3], [40, 40, 4], [30, 10, 5]]] }); + // OGC SF WKT: the ordinate separator is one or more whitespace, so + // multiple spaces between ordinates denote the same geometry. + t.deepEqual(parse('POINT (1 2)'), parse('POINT (1 2)')); + t.deepEqual(parse('POINT (1 2)'), { + type: 'Point', + coordinates: [1, 2] + }); + t.deepEqual(parse('LINESTRING (1 2, 3 4)'), { + type: 'LineString', + coordinates: [[1, 2], [3, 4]] + }); + t.deepEqual(parse('POLYGON ((0 0, 1 0, 1 1, 0 0))'), { + type: 'Polygon', + coordinates: [[[0, 0], [1, 0], [1, 1], [0, 0]]] + }); + t.end(); }); diff --git a/wellknown.js b/wellknown.js index 0cf84a0..35a1393 100644 --- a/wellknown.js +++ b/wellknown.js @@ -6,7 +6,7 @@ module.exports.stringify = stringify; var numberRegexp = /[-+]?([0-9]*\.[0-9]+|[0-9]+)([eE][-+]?[0-9]+)?/; // Matches sequences like '100 100' or '100 100 100'. -var tuples = new RegExp('^' + numberRegexp.source + '(\\s' + numberRegexp.source + '){1,}'); +var tuples = new RegExp('^' + numberRegexp.source + '(\\s+' + numberRegexp.source + '){1,}'); /* * Parse WKT and return GeoJSON. @@ -75,8 +75,8 @@ function parse (input) { } else if (elem === ',') { pointer = []; stack[stack.length - 1].push(pointer); - } else if (!elem.split(/\s/g).some(isNaN)) { - Array.prototype.push.apply(pointer, elem.split(/\s/g).map(parseFloat)); + } else if (!elem.split(/\s+/g).some(isNaN)) { + Array.prototype.push.apply(pointer, elem.split(/\s+/g).map(parseFloat)); } else { return null; } @@ -98,9 +98,9 @@ function parse (input) { if (pt === ',') { list.push(item); item = []; - } else if (!pt.split(/\s/g).some(isNaN)) { + } else if (!pt.split(/\s+/g).some(isNaN)) { if (!item) item = []; - Array.prototype.push.apply(item, pt.split(/\s/g).map(parseFloat)); + Array.prototype.push.apply(item, pt.split(/\s+/g).map(parseFloat)); } white(); }