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" ;
2
2
import { Channels } from "../channel.js" ;
3
3
import { create } from "../context.js" ;
4
- import { labelof , identity } from "../options.js" ;
4
+ import { labelof , identity , arrayify } from "../options.js" ;
5
5
import { Position } from "../projection.js" ;
6
6
import { applyChannelStyles , applyDirectStyles , applyIndirectStyles , applyTransform , styles } from "../style.js" ;
7
7
import { initializer } from "../transforms/basic.js" ;
@@ -148,23 +148,15 @@ function contourGeometry({thresholds, interval, ...options}) {
148
148
// Blur the raster grid, if desired.
149
149
if ( this . blur > 0 ) for ( const V of VV ) blur2 ( { data : V , width : w , height : h } , this . blur ) ;
150
150
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 } ` ) ;
162
154
163
155
// 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 ) ;
165
157
const contourData = [ ] ;
166
158
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 ) ) ) ) ) ;
168
160
169
161
// Rescale the contour multipolygon from grid to screen coordinates.
170
162
for ( const { coordinates} of contourData ) {
@@ -187,6 +179,22 @@ function contourGeometry({thresholds, interval, ...options}) {
187
179
} ) ;
188
180
}
189
181
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
+
190
198
export function contour ( ) {
191
199
return new Contour ( ...maybeTuples ( "value" , ...arguments ) ) ;
192
200
}
0 commit comments