booleanEqual check does not properly handle precision
solarissmoke opened this issue · comments
Turf version: 6.5.0/7.0.0a
The way booleanEqual
check handles precision is incorrect. The following example illustrates the problem:
const point1 = {"type": "Point", "coordinates": [100.55719328, 23.0365925]}
const point2 = {"type": "Point", "coordinates": [100.55719327926636, 23.03659249995325]}
booleanEqual(point1, point2, {precision: 7}) // returns true
booleanEqual(point1, point2, {precision: 6}) // returns false !
This is incorrect. If two points are equal to a precision of 7, then by definition they must be equal to a precision of 6 as well. The problem here arises from the underlying implementation of GeojsonEquality
, which uses toFixed to round the coordinates. The problem is that this introduces rounding errors that then cause the equality check to fail:
const a = 23.0365925;
const b = 23.03659249995325;
a.toFixed(6) // returns "23.036593"
b.toFixed(6) // returns "23.036592"
a.toFixed(7) // returns "23.0365925"
b.toFixed(7) // returns "23.0365925"
a.toFixed(6) === b.toFixed(6) // returns false
a.toFixed(7) === b.toFixed(7) // returns true
I think the correct behaviour here is not to use toFixed()
but to check that both values fall within the acceptable precision, e.g., like this:
Math.abs(a - b) < (10**-6) // returns true, as expected
Math.abs(a - b) < (10**-7) // returns true, as expected
Which likely needs to be fixed upstream in GeojsonEquality (I've filed a bug there too), or that logic replaced entirely in turf.
My suggestion is to drop the dependency on geojson-equality
and implement it afresh here, which will allow updating the implementation to use typescript - more than happy to make a PR if the maintainers here agree.
Thanks for raising @solarissmoke. Given geojson-equality hasn't seen any activity for several years, it might be best to re-implement it afresh.
Would you mind sharing a few thoughts about what the PR might look like? Would it be a new module, or do you think it would fit into an existing module? Would it be end user facing, or only for internal use? Thanks!
@smallsaucepan my suggestion would be that we implement this inside the boolean-equal
module, for internal use only, as the existing boolean-equal
API already exposes all the functionality that is required.
I have re-implemented the logic inside geojson-equality
for a project that needed to work around this bug, and think it can be done relatively cleanly - happy to make a PR to port this into turf.
Only thing to consider is geojson-equality is also a dependency of turf-boolean-overlap. So we'd need to put the implementation somewhere we can reuse it internally. Might it fit into turf-helpers or turf-meta?
Putting it in turf-helpers
sounds like a good plan. I'll work on a PR.