1- import { blur2 , contours , geoPath , map , max , min , range , thresholdSturges } from "d3" ;
1+ import { blur2 , contours , geoPath , map , max , min , nice , range , ticks , thresholdSturges } from "d3" ;
22import { Channels } from "../channel.js" ;
33import { create } from "../context.js" ;
4- import { labelof , identity } from "../options.js" ;
4+ import { labelof , identity , arrayify } from "../options.js" ;
55import { Position } from "../projection.js" ;
66import { applyChannelStyles , applyDirectStyles , applyIndirectStyles , applyTransform , styles } from "../style.js" ;
77import { initializer } from "../transforms/basic.js" ;
@@ -148,23 +148,15 @@ function contourGeometry({thresholds, interval, ...options}) {
148148 // Blur the raster grid, if desired.
149149 if ( this . blur > 0 ) for ( const V of VV ) blur2 ( { data : V , width : w , height : h } , this . blur ) ;
150150
151- // Compute the contour thresholds; d3-contour unlike d3-array doesn’t pass
152- // the min and max automatically, so we do that here to normalize, and also
153- // so we can share consistent thresholds across facets. When an interval is
154- // used, note that the lowest threshold should be below (or equal) to the
155- // lowest value, or else some data will be missing.
156- const T =
157- typeof thresholds ?. range === "function"
158- ? thresholds . range ( ...( ( [ min , max ] ) => [ thresholds . floor ( min ) , max ] ) ( finiteExtent ( VV ) ) )
159- : typeof thresholds === "function"
160- ? thresholds ( V , ...finiteExtent ( VV ) )
161- : thresholds ;
151+ // Compute the contour thresholds.
152+ const T = maybeTicks ( thresholds , V , ...finiteExtent ( VV ) ) ;
153+ if ( T === null ) throw new Error ( `unsupported thresholds: ${ thresholds } ` ) ;
162154
163155 // Compute the (maybe faceted) contours.
164- const contour = contours ( ) . thresholds ( T ) . size ( [ w , h ] ) . smooth ( this . smooth ) ;
156+ const { contour} = contours ( ) . size ( [ w , h ] ) . smooth ( this . smooth ) ;
165157 const contourData = [ ] ;
166158 const contourFacets = [ ] ;
167- for ( const V of VV ) contourFacets . push ( range ( contourData . length , contourData . push ( ...contour ( V ) ) ) ) ;
159+ for ( const V of VV ) contourFacets . push ( range ( contourData . length , contourData . push ( ...T . map ( ( t ) => contour ( V , t ) ) ) ) ) ;
168160
169161 // Rescale the contour multipolygon from grid to screen coordinates.
170162 for ( const { coordinates} of contourData ) {
@@ -187,6 +179,22 @@ function contourGeometry({thresholds, interval, ...options}) {
187179 } ) ;
188180}
189181
182+ // Apply the thresholds interval, function, or count, and return an array of
183+ // ticks. d3-contour unlike d3-array doesn’t pass the min and max automatically,
184+ // so we do that here to normalize, and also so we can share consistent
185+ // thresholds across facets. When an interval is used, note that the lowest
186+ // threshold should be below (or equal) to the lowest value, or else some data
187+ // will be missing.
188+ function maybeTicks ( thresholds , V , min , max ) {
189+ if ( typeof thresholds ?. range === "function" ) return thresholds . range ( thresholds . floor ( min ) , max ) ;
190+ if ( typeof thresholds === "function" ) thresholds = thresholds ( V , min , max ) ;
191+ if ( typeof thresholds !== "number" ) return arrayify ( thresholds , Array ) ;
192+ const tz = ticks ( ...nice ( min , max , thresholds ) , thresholds ) ;
193+ while ( tz [ tz . length - 1 ] >= max ) tz . pop ( ) ;
194+ while ( tz [ 1 ] < min ) tz . shift ( ) ;
195+ return tz ;
196+ }
197+
190198export function contour ( ) {
191199 return new Contour ( ...maybeTuples ( "value" , ...arguments ) ) ;
192200}
0 commit comments