Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Oct 6, 2022. It is now read-only.

Commit 80b8ef4

Browse files
committed
first working (I think) version of reproducible interactions
1 parent 9a7f338 commit 80b8ef4

File tree

4 files changed

+107
-80
lines changed

4 files changed

+107
-80
lines changed

src/plots/geo/geo2.js

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -38,27 +38,25 @@ function Geo(opts) {
3838
this.isStatic = opts.staticPlot;
3939

4040
var geoLayout = this.graphDiv._fullLayout[this.id];
41-
var position = geoLayout.position || {};
41+
var center = geoLayout.center || {};
4242
var projLayout = geoLayout.projection;
4343
var rotation = projLayout.rotation || {};
44-
var center = projLayout.center || {};
4544

4645
this.viewInitial = {
47-
'position.x': position.x,
48-
'position.y': position.y,
46+
'center.lon': center.lon,
47+
'center.lat': center.lat,
4948
'projection.scale': projLayout.scale,
5049
'projection.rotation.lon': rotation.lon,
51-
'projection.rotation.lat': rotation.lat,
52-
'projection.center.lon': center.lon,
53-
'projection.center.lat': center.lat
50+
'projection.rotation.lat': rotation.lat
5451
};
5552

5653
this.topojsonName = null;
5754
this.topojson = null;
5855

5956
this.projection = null;
60-
this.fullScale = null;
57+
this.fitScale = null;
6158
this.bounds = null;
59+
this.midPt = null;
6260

6361
this.hasChoropleth = false;
6462
this.choroplethHoverPt = null;
@@ -135,6 +133,7 @@ proto.fetchTopojson = function() {
135133
proto.update = function(geoCalcData, fullLayout) {
136134
var geoLayout = fullLayout[this.id];
137135

136+
// important: maps with choropleth traces have a different layer order
138137
this.hasChoropleth = false;
139138
for(var i = 0; i < geoCalcData.length; i++) {
140139
if(geoCalcData[i][0].trace.type === 'choropleth') {
@@ -162,55 +161,61 @@ proto.update = function(geoCalcData, fullLayout) {
162161
};
163162

164163
proto.updateProjection = function(fullLayout, geoLayout) {
165-
var projLayout = geoLayout.projection;
166164
var gs = fullLayout._size;
167165
var domain = geoLayout.domain;
166+
var projLayout = geoLayout.projection;
167+
var rotation = projLayout.rotation || {};
168+
var center = geoLayout.center || {};
168169

169170
var projection = this.projection = getProjection(geoLayout);
170171

171-
var rotation = projLayout.rotation || {};
172-
var center = projLayout.center || {};
173-
174172
// set 'pre-fit' projection
175173
projection
174+
.center([center.lon - rotation.lon, center.lat - rotation.lat])
176175
.rotate([-rotation.lon, -rotation.lat, rotation.roll])
177-
.center([center.lon, center.lat])
178176
.parallels(projLayout.parallels);
179177

178+
// setup subplot extent [[x0,y0], [x1,y1]]
180179
var extent = [[
181180
gs.l + gs.w * domain.x[0],
182181
gs.t + gs.h * (1 - domain.y[1])
183182
], [
184183
gs.l + gs.w * domain.x[1],
185184
gs.t + gs.h * (1 - domain.y[0])
186185
]];
187-
var rangeBox = makeRangeBox(
188-
geoLayout.lonaxis.range,
189-
geoLayout.lataxis.range
190-
);
191-
var fullRangeBox = makeRangeBox(
192-
geoLayout.lonaxis._fullRange,
193-
geoLayout.lataxis._fullRange
194-
);
195186

196-
// fit to full lon/lat ranges to find the projection's full scale value
197-
projection.fitExtent(extent, fullRangeBox);
198-
this.fullScale = projection.scale();
187+
var rangeBox = makeRangeBox(geoLayout.lonaxis.range, geoLayout.lataxis.range);
199188

200-
// fit to set lon/lat ranges
189+
// fit projection 'scale' and 'translate' to set lon/lat ranges
201190
projection.fitExtent(extent, rangeBox);
202191

203-
// TODO add logic for 'position'
192+
var b = this.bounds = projection.getBounds(rangeBox);
193+
var s = this.fitScale = projection.scale();
194+
var t = projection.translate();
204195

205-
// TODO test 'center' attribute
196+
if(
197+
!isFinite(b[0][0]) || !isFinite(b[0][1]) ||
198+
!isFinite(b[1][0]) || !isFinite(b[1][1]) ||
199+
isNaN(t[0]) || isNaN(t[0])
200+
) {
201+
Lib.warn('Invalid geo settings');
202+
}
206203

207-
// find bounds
208-
this.bounds = projection.getBounds(rangeBox);
209-
projection.clipExtent(this.bounds);
204+
// px coordinates of view mid-point,
205+
// useful to update `geo.center` after interactions
206+
var midPt = this.midPt = [
207+
b[0][0] + (b[1][0] - b[0][0]) / 2,
208+
b[0][1] + (b[1][1] - b[0][1]) / 2
209+
];
210+
211+
// adjust projection to user setting
212+
projection
213+
.scale(projLayout.scale * s)
214+
.translate([t[0] + (midPt[0] - t[0]), t[1] + (midPt[1] - t[1])])
215+
.clipExtent(b);
210216

211-
// scale to set zoom level
212-
var fitScale = projection.scale();
213-
projection.scale(projLayout.scale * fitScale);
217+
// TODO we'll need to special algo for albersUsa projection
218+
// as this particular projection does not support `.center`
214219
};
215220

216221
proto.updateBaseLayers = function(fullLayout, geoLayout) {

src/plots/geo/layout/defaults.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,11 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
2626
function handleGeoDefaults(geoLayoutIn, geoLayoutOut, coerce) {
2727
var show;
2828

29+
var resolution = coerce('resolution');
2930
var scope = coerce('scope');
3031
var isScoped = (scope !== 'world');
3132
var scopeParams = constants.scopeDefaults[scope];
3233

33-
var resolution = coerce('resolution');
34-
coerce('position.x');
35-
coerce('position.y');
36-
3734
var projType = coerce('projection.type', scopeParams.projType);
3835
var isAlbersUsa = projType === 'albers usa';
3936
var isConic = projType.indexOf('conic') !== -1;
@@ -63,6 +60,14 @@ function handleGeoDefaults(geoLayoutIn, geoLayoutOut, coerce) {
6360

6461
coerce('projection.scale');
6562

63+
geoAxisDefaults(geoLayoutIn, geoLayoutOut);
64+
65+
var lonRange = geoLayoutOut.lonaxis.range;
66+
coerce('center.lon', lonRange[0] + (lonRange[1] - lonRange[0]) / 2);
67+
68+
var latRange = geoLayoutOut.lataxis.range;
69+
coerce('center.lat', latRange[0] + (latRange[1] - latRange[0]) / 2);
70+
6671
show = coerce('showland');
6772
if(show) coerce('landcolor');
6873

@@ -101,9 +106,8 @@ function handleGeoDefaults(geoLayoutIn, geoLayoutOut, coerce) {
101106

102107
coerce('bgcolor');
103108

104-
geoAxisDefaults(geoLayoutIn, geoLayoutOut);
105-
106109
// bind a few helper field that are used downstream
110+
geoLayoutOut._isClipped = !!constants.lonaxisSpan[projType];
107111
geoLayoutOut._isScoped = isScoped;
108112
geoLayoutOut._isConic = isConic;
109113
}

src/plots/geo/layout/layout_attributes.js

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -94,25 +94,6 @@ module.exports = {
9494
].join(' ')
9595
}
9696
},
97-
position: {
98-
x: {
99-
valType: 'number',
100-
role: 'info',
101-
description: [
102-
'Sets the x position of this subplot in the plotting space',
103-
'(in normalized coordinates).',
104-
'Use in tandem with ...'
105-
].join(' ')
106-
},
107-
y: {
108-
valType: 'number',
109-
role: 'info',
110-
description: [
111-
'Sets the y position of this subplot in the plotting space',
112-
'(in normalized coordinates).'
113-
].join(' ')
114-
}
115-
},
11697
resolution: {
11798
valType: 'enumerated',
11899
values: [110, 50],
@@ -185,9 +166,29 @@ module.exports = {
185166
dflt: 1,
186167
description: [
187168
'Zooms in or out on the map view.',
188-
'.. in fractions of ...'
169+
'A scale of *1* corresponds to the largest zoom level',
170+
'that fits the map\'s lon and lat ranges. '
171+
].join(' ')
172+
},
173+
},
174+
center: {
175+
lon: {
176+
valType: 'number',
177+
role: 'info',
178+
description: [
179+
'Sets the longitude of the map\'s center.',
180+
'By default, the map\'s center lies at the middle of the range ',
181+
' ... '
189182
].join(' ')
190183
},
184+
lat: {
185+
valType: 'number',
186+
role: 'info',
187+
description: [
188+
'Sets the latitude of the map\'s center.',
189+
'Use in tandem with ...'
190+
].join(' ')
191+
}
191192
},
192193
showcoastlines: {
193194
valType: 'boolean',

src/plots/geo/zoom.js

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ function createGeoZoom(geo, geoLayout) {
2323

2424
if(geoLayout._isScoped) {
2525
zoomConstructor = zoomScoped;
26-
} else if(projection.clipAngle()) {
26+
} else if(geoLayout._isClipped) {
2727
zoomConstructor = zoomClipped;
2828
} else {
2929
zoomConstructor = zoomNonClipped;
@@ -40,12 +40,11 @@ module.exports = createGeoZoom;
4040
function initZoom(geo, projection) {
4141
return d3.behavior.zoom()
4242
.translate(projection.translate())
43-
.scale(projection.scale())
44-
.scaleExtent([0.5 * geo.fullScale, 100 * geo.fullScale]);
43+
.scale(projection.scale());
4544
}
4645

4746
// sync zoom updates with user & full layout
48-
function sync(geo, projection) {
47+
function sync(geo, projection, cb) {
4948
var id = geo.id;
5049
var gd = geo.graphDiv;
5150
var userOpts = gd.layout[id];
@@ -63,20 +62,8 @@ function sync(geo, projection) {
6362
}
6463
}
6564

66-
var _rotate = projection.rotate();
67-
set('projection.rotation.lon', -_rotate[0]);
68-
set('projection.rotation.lat', -_rotate[1]);
69-
70-
// var _center = projection.center();
71-
// set('projection.center.lon', _center[0]);
72-
// set('projection.center.lat', _center[1]);
73-
74-
set('projection.scale', projection.scale() / geo.fullScale);
75-
76-
var _translate = projection.translate();
77-
set('position.x', _translate[0]);
78-
set('position.y', _translate[1]);
79-
65+
cb(set);
66+
set('projection.scale', projection.scale() / geo.fitScale);
8067
gd.emit('plotly_relayout', eventData);
8168
}
8269

@@ -95,9 +82,20 @@ function zoomScoped(geo, projection) {
9582
geo.render();
9683
}
9784

85+
function syncCb(set) {
86+
var bounds = geo.bounds;
87+
var center = projection.invert([
88+
bounds[0][0] + (bounds[1][0] - bounds[0][0]) / 2,
89+
bounds[0][1] + (bounds[1][1] - bounds[0][1]) / 2
90+
]);
91+
92+
set('center.lon', center[0]);
93+
set('center.lat', center[1]);
94+
}
95+
9896
function handleZoomend() {
9997
d3.select(this).style(zoomendStyle);
100-
sync(geo, projection);
98+
sync(geo, projection, syncCb);
10199
}
102100

103101
zoom
@@ -163,7 +161,20 @@ function zoomNonClipped(geo, projection) {
163161

164162
function handleZoomend() {
165163
d3.select(this).style(zoomendStyle);
166-
sync(geo, projection);
164+
sync(geo, projection, syncCb);
165+
}
166+
167+
function syncCb(set) {
168+
var rotate = projection.rotate();
169+
var bounds = geo.bounds;
170+
var center = projection.invert([
171+
bounds[0][0] + (bounds[1][0] - bounds[0][0]) / 2,
172+
bounds[0][1] + (bounds[1][1] - bounds[0][1]) / 2
173+
]);
174+
175+
set('projection.rotation.lon', -rotate[0]);
176+
set('center.lon', center[0]);
177+
set('center.lat', center[1]);
167178
}
168179

169180
zoom
@@ -243,7 +254,7 @@ function zoomClipped(geo, projection) {
243254
d3.select(this).style(zoomendStyle);
244255
zoomOn.call(zoom, 'zoom', null);
245256
zoomended(event.of(this, arguments));
246-
sync(geo, projection);
257+
sync(geo, projection, syncCb);
247258
})
248259
.on('zoom.redraw', function() {
249260
geo.render();
@@ -261,6 +272,12 @@ function zoomClipped(geo, projection) {
261272
if(!--zooming) dispatch({type: 'zoomend'});
262273
}
263274

275+
function syncCb(set) {
276+
var _rotate = projection.rotate();
277+
set('projection.rotation.lon', -_rotate[0]);
278+
set('projection.rotation.lat', -_rotate[1]);
279+
}
280+
264281
return d3.rebind(zoom, event, 'on');
265282
}
266283

0 commit comments

Comments
 (0)