d3 / d3-contour

Compute contour polygons using marching squares.

Home Page:https://d3js.org/d3-contour

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Breaking change with contour-density thresholds starting from v3.0.2

HackerNeoAnderson opened this issue · comments

Hi guys,
We are updating our dependency on d3-contour from 3.0.1 to 4.0.2.
But, it looks like it's not backward compatible starting from 3.0.2.
The breaking change was introduced in PR#57 and published as a patch version (3.0.2)

In our application, we provide thresholds as a callback function which returns an array:
contourDensity().thresholds((densityValues) => someArrayOfThresholds)

Could you suggest, how to make it compatible with the latest version (4.0.2) of d3-contour?
Maybe we need to use some built-in function like thresholdFreedmanDiaconis, thresholdScott, thresholdSturges to make the density chart drawn correctly again?

This notebook: https://observablehq.com/@fil/density-thresholds-70

Suppose you wanted to pass the following thresholds in d3-contour 3.0.1:

const contours = d3301
  .contourDensity()
  .thresholds([0, 0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009, 0.01])
    (data)

this would return contours with values:

contours.map((d) => d.value) // [0, 0.0000625, 0.000125, 0.0001875, 0.00025, 0.0003125, 0.000375, 0.0004375, 0.0005, 0.0005625, 0.000625]

This was inconsistent (you ask for 1, you get 1/16). #57 fixes the inconsistency, so starting with 3.0.2, you would pass this instead:

const thresholds = [0, 0.0000625, 0.000125, 0.0001875, 0.00025, 0.0003125, 0.000375, 0.0004375, 0.0005, 0.0005625, 0.000625];
const contours = d3_301
  .contourDensity()
  .thresholds(thresholds)
    (data)

and get the same contours (with the same values).

The relationship between the two is this ratio 2**k with k the cellSize which defaults to 4; in other words you should find the same contours as before if you did:

const thresholds = [0, 0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009, 0.01]; // old style
const contours = d3_302
  .contourDensity()
  .thresholds(thresholds.map(d => d/16)) // convert to new style
    (data)

Hi, @Fil !
Thank you for the response. Faced the same issue.
After diving deep into d3-contour source code, I found that k depends on cellSize provided value:

return k = Math.floor(Math.log(_) / Math.LN2), resize();

So, to get a correct results, seems like we need to use the following formula when using cellSize method:

k = Math.floor(Math.log(cellSizeValue) / Math.LN2)

const thresholds = [0, 0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009, 0.01]; // old style
const contours = d3_302
  .contourDensity()
  .cellSize(cellSizeValue)
  .thresholds(thresholds.map(d => d/ Math.pow(2, 2 * k))) // convert to new style
    (data)