Skip to content

Give IP Intelligence latitude, longitude and WKT points to 3 decimal places - #62

Merged
jwrosewell merged 5 commits into
rcfrom
align/ipi-decimals
Nov 11, 2026
Merged

jwrosewell merged 5 commits into
rcfrom
align/ipi-decimals

Conversation

@jwrosewell

@jwrosewell jwrosewell commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Problem

IP Intelligence gave Latitude and Longitude with every digit the double
carried, so a value stored as a Declination came back as
18.542741172521133 where the specification asks for 18.543. WKT shapes
were worse, because
swigData.js
asked the C library for 16 decimal places on every point of every shape, and
an area can hold many points, so each string was far longer than the data it
carried.

Go and Rust already round, so the same address gave a different number in each
language.

What the specification says

value-types.md, Rounding of Azimuth and Declination values,
at the fixed commit 8aa2de01 on rc.

A value converted from Azimuth or Declination MUST be given to the caller
rounded to 3 decimal places, whether it is given as a number, as a string or
as a point in a WKT shape. For example, a stored Declination of 6751
converts to 18.542741172521133 and is given as 18.543. Where the number is a
binary floating point type, it is the value of that type nearest to the
rounded decimal.

The locations these types hold are rough, and a step of 0.001 degrees of
latitude is about 111 metres. More decimal places would make each string, and
each WKT shape with its many points, longer without making the location any
more accurate.

A package MAY offer a setting that changes the number of decimal places,
provided the default is 3.

data-model.md
at the same commit applies it to the IP Intelligence properties.

Latitude and Longitude, and the points of Areas, are given to 3 decimal
places, as [rounding of Azimuth and Declination values] describes.

Change

swigData.js

  • defaultWktDecimalPlaces, which was 16, becomes defaultDecimalPlaces,
    which is 3, and is both the places a number is rounded to and the places
    asked of getValuesAsWeightedWKTStringList. The WKT points therefore come
    back at 3 places from the C library, which is where they are made.
  • roundLocation formats the double to 3 places and reads it back, which
    gives the binary floating point number nearest to the rounded decimal, as
    the page asks. It adds zero, so a value that rounds to zero from below, such
    as -0.0004, is given as 0 rather than -0. A caller can tell the two
    apart with Object.is, and dividing into them gives Infinity against
    -Infinity.
    51degrees-go#88 made the
    same choice and gives the reason that the C library writes such a value as
    text as 0, so the number agrees with the text. The page does not say which
    zero to give.
  • getInternal rounds each weighted value of a weightedsingle or
    weighteddouble property named latitude or longitude. The weight and
    the raw weight are untouched. getInternalIgnoringCase, which rc added,
    calls getInternal, so a read in any letter case is rounded too.

Why the properties are named rather than found by their stored type

Go and Rust ask the C library which stored type each value came from and round
any Azimuth or Declination, Go with
fiftyoneDegreesPropertyGetStoredTypeByIndex and Rust with a new shim
function. The swig results Node has give no stored type, and adding one would
be a change to the C library and the generated wrappers, so the two properties
the IP Intelligence data model gives as those types are named in
locationProperties instead. The comment above the list says so. A property
added later whose values are stored as an Azimuth or a Declination would
therefore not be rounded in Node until it is named, which is listed under what
remains.

A property declared as text already comes through the C library's string
converter at 3 places, which is the finding
51degrees-go#88 records,
so the text route needed no change here either.

The same change in the other languages

Language Pull request How the value is picked
Go #88 by stored type, isCoordinate
Rust #287 by stored type, through a shim

The Python twin,
51degrees-python#81,
was moved to next week on 10 November.

Tests

swigDataDecimalPlaces.test.js
drives SwigData with a stand in for the swig results, so it needs no data
file and no native build.

  • are given to 3 decimal places uses the page's own example,
    18.542741172521133 for Latitude, and checks it is given as 18.543. Longitude
    is -0.0004 and is given as 0, with Object.is(value, -0) false.
  • are the number nearest to the rounded decimal checks 51.5074567 and
    -0.1277583 give 51.507 and -0.128, and that the weight is unchanged.
  • leave other properties as the engine gives them checks a third
    weighteddouble property named Other keeps all its digits.
  • asks for WKT with 3 decimal places records what
    getValuesAsWeightedWKTStringList was asked for and checks it was
    ['Areas', 3].

Red. Run 43021053852,
on tmp/node-62-red2, which is rc at 8b9fc3d with this test file and nothing
else from the branch. 3 of the 4 failed, the first with
Expected: 18.543, Received: 18.542741172521133. The one that passed is
leave other properties as the engine gives them, which is the test that asks
for no rounding.

An earlier attempt,
run 43020248306,
failed before any test ran because its workflow did not check out the
javascript-templates submodule. It proves nothing and is named here only so
it is not read as a second red.

Green. Run 43026297138,
on tmp/node-62-green2, which is this head. 4 of 4 passed.

Full build. Run 43026212927,
on tmp/pr-matrix-62-g, success. Every one of its seven
Setting 'PullRequestSha' lines reads
6ffa23b68c0f68497567694f47e5edd4997955a9, which is this head. Configure
printed "Left out 2 macOS entries". The branch it built from was rc at
ca51908, and rc has moved on since. Nothing rc has taken since touches the
two files this changes, and the draft merges into the current rc with no
conflict.

macOS was left out of this draft's full build under the 9 November decision.
The rc head is built with macOS before it merges into main.

What remains

  • No setting for the number of places. The page allows one, provided the
    default is 3. Node has a constant and no setting, which is deliberate and
    matches Go and Rust, neither of which offers one either. It is not an
    oversight.
  • Rounding is chosen by property name, not by stored type, for the reason
    above. Closing that gap needs an accessor for the stored type in the C
    library and the generated wrappers, as
    51degrees-go#88 and
    51degrees-rust#287
    have. No issue is open for it yet.

…places

The Rounding of Azimuth and Declination values section of the value types
page says a location is given to 3 decimal places, as a number, as a string
and as a point in a WKT shape. The engine asked for 16 decimal places in WKT
and gave latitude and longitude as the native engine returned them.
…type

Go and Rust ask the C library which stored type a value came from and round
an Azimuth or a Declination. The swig results give no stored type, so the
two properties the IP Intelligence data model gives as those types are
named.
@jwrosewell
jwrosewell marked this pull request as ready for review November 11, 2026 15:11
@jwrosewell
jwrosewell merged commit 5228a9f into rc Nov 11, 2026
2 checks passed
@jwrosewell
jwrosewell deleted the align/ipi-decimals branch November 11, 2026 15:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant