3
3
geoAlbersUsa ,
4
4
geoAzimuthalEqualArea ,
5
5
geoAzimuthalEquidistant ,
6
+ geoPath ,
6
7
geoConicConformal ,
7
8
geoConicEqualArea ,
8
9
geoConicEquidistant ,
@@ -17,6 +18,7 @@ import {
17
18
geoTransverseMercator
18
19
} from "d3" ;
19
20
import { isObject } from "./options.js" ;
21
+ import { warn } from "./warnings.js" ;
20
22
21
23
export function Projection (
22
24
{
@@ -32,6 +34,7 @@ export function Projection(
32
34
if ( projection == null ) return ;
33
35
if ( typeof projection . stream === "function" ) return projection ; // d3 projection
34
36
let options ;
37
+ let domain ;
35
38
36
39
// If the projection was specified as an object with additional options,
37
40
// extract those. The order of precedence for insetTop (and other insets) is:
@@ -41,6 +44,7 @@ export function Projection(
41
44
let inset ;
42
45
( {
43
46
type : projection ,
47
+ domain,
44
48
inset,
45
49
insetTop = inset !== undefined ? inset : insetTop ,
46
50
insetRight = inset !== undefined ? inset : insetRight ,
@@ -62,13 +66,31 @@ export function Projection(
62
66
// The projection initializer might decide to not use a projection.
63
67
if ( projection == null ) return ;
64
68
65
- // If there’s no need to translate, return the projection as-is for speed.
66
- // TODO Maybe scale to fit features here?
67
- const tx = marginLeft + insetLeft ;
68
- const ty = marginTop + insetTop ;
69
- if ( tx === 0 && ty === 0 ) return projection ;
69
+ // If there’s no need to translate or scale, return the projection as-is for speed.
70
+ let tx = marginLeft + insetLeft ;
71
+ let ty = marginTop + insetTop ;
72
+ if ( tx === 0 && ty === 0 && domain == null ) return projection ;
73
+
74
+ // Otherwise wrap the projection stream with a suitable transform. If a domain
75
+ // is specified, fit the projection to the frame. Otherwise, translate.
76
+ if ( domain ) {
77
+ const [ [ x0 , y0 ] , [ x1 , y1 ] ] = geoPath ( projection ) . bounds ( domain ) ;
78
+ const kx = dx / ( x1 - x0 ) ;
79
+ const ky = dy / ( y1 - y0 ) ;
80
+ const k = Math . min ( kx , ky ) ;
81
+ if ( k > 0 ) {
82
+ tx -= ( k === kx ? x0 * k : ( ( x0 + x1 ) * k - dx ) / 2 ) ;
83
+ ty -= ( k === ky ? y0 * k : ( ( y0 + y1 ) * k - dy ) / 2 ) ;
84
+ const { stream : affine } = geoTransform ( {
85
+ point ( x , y ) {
86
+ this . stream . point ( x * k + tx , y * k + ty ) ;
87
+ }
88
+ } ) ;
89
+ return { stream : ( s ) => projection . stream ( affine ( s ) ) } ;
90
+ }
91
+ warn ( `The projection could not be fit to the specified domain. Using the default scale.` ) ;
92
+ }
70
93
71
- // Otherwise wrap the projection stream with a translate transform.
72
94
const { stream : translate } = geoTransform ( {
73
95
point ( x , y ) {
74
96
this . stream . point ( x + tx , y + ty ) ;
0 commit comments