@@ -19,6 +19,7 @@ var Drawing = require('../../components/drawing');
1919var Fx = require ( '../../components/fx' ) ;
2020var Plots = require ( '../plots' ) ;
2121var Axes = require ( '../cartesian/axes' ) ;
22+ var getAutoRange = require ( '../cartesian/autorange' ) . getAutoRange ;
2223var dragElement = require ( '../../components/dragelement' ) ;
2324var prepSelect = require ( '../cartesian/select' ) . prepSelect ;
2425var selectOnClick = require ( '../cartesian/select' ) . selectOnClick ;
@@ -143,18 +144,24 @@ proto.fetchTopojson = function() {
143144proto . update = function ( geoCalcData , fullLayout ) {
144145 var geoLayout = fullLayout [ this . id ] ;
145146
146- var hasInvalidBounds = this . updateProjection ( fullLayout , geoLayout ) ;
147- if ( hasInvalidBounds ) return ;
148-
149147 // important: maps with choropleth traces have a different layer order
150148 this . hasChoropleth = false ;
149+
151150 for ( var i = 0 ; i < geoCalcData . length ; i ++ ) {
152- if ( geoCalcData [ i ] [ 0 ] . trace . type === 'choropleth' ) {
151+ var calcTrace = geoCalcData [ i ] ;
152+ var trace = calcTrace [ 0 ] . trace ;
153+
154+ if ( trace . type === 'choropleth' ) {
153155 this . hasChoropleth = true ;
154- break ;
156+ }
157+ if ( trace . visible === true && trace . _length > 0 ) {
158+ trace . _module . calcGeoJSON ( calcTrace , fullLayout ) ;
155159 }
156160 }
157161
162+ var hasInvalidBounds = this . updateProjection ( geoCalcData , fullLayout ) ;
163+ if ( hasInvalidBounds ) return ;
164+
158165 if ( ! this . viewInitial || this . scope !== geoLayout . scope ) {
159166 this . saveViewInitial ( geoLayout ) ;
160167 }
@@ -177,20 +184,19 @@ proto.update = function(geoCalcData, fullLayout) {
177184 this . render ( ) ;
178185} ;
179186
180- proto . updateProjection = function ( fullLayout , geoLayout ) {
187+ proto . updateProjection = function ( geoCalcData , fullLayout ) {
188+ var gd = this . graphDiv ;
189+ var geoLayout = fullLayout [ this . id ] ;
181190 var gs = fullLayout . _size ;
182191 var domain = geoLayout . domain ;
183192 var projLayout = geoLayout . projection ;
184- var rotation = projLayout . rotation || { } ;
185- var center = geoLayout . center || { } ;
186193
187- var projection = this . projection = getProjection ( geoLayout ) ;
194+ var lonaxis = geoLayout . lonaxis ;
195+ var lataxis = geoLayout . lataxis ;
196+ var axLon = lonaxis . _ax ;
197+ var axLat = lataxis . _ax ;
188198
189- // set 'pre-fit' projection
190- projection
191- . center ( [ center . lon - rotation . lon , center . lat - rotation . lat ] )
192- . rotate ( [ - rotation . lon , - rotation . lat , rotation . roll ] )
193- . parallels ( projLayout . parallels ) ;
199+ var projection = this . projection = getProjection ( geoLayout ) ;
194200
195201 // setup subplot extent [[x0,y0], [x1,y1]]
196202 var extent = [ [
@@ -201,11 +207,46 @@ proto.updateProjection = function(fullLayout, geoLayout) {
201207 gs . t + gs . h * ( 1 - domain . y [ 0 ] )
202208 ] ] ;
203209
204- var lonaxis = geoLayout . lonaxis ;
205- var lataxis = geoLayout . lataxis ;
206- var rangeBox = makeRangeBox ( lonaxis . range , lataxis . range ) ;
210+ var center = geoLayout . center || { } ;
211+ var rotation = projLayout . rotation || { } ;
212+ var lonaxisRange = lonaxis . range || [ ] ;
213+ var lataxisRange = lataxis . range || [ ] ;
214+
215+ if ( geoLayout . fitbounds ) {
216+ axLon . _length = extent [ 1 ] [ 0 ] - extent [ 0 ] [ 0 ] ;
217+ axLat . _length = extent [ 1 ] [ 1 ] - extent [ 0 ] [ 1 ] ;
218+ axLon . range = getAutoRange ( gd , axLon ) ;
219+ axLat . range = getAutoRange ( gd , axLat ) ;
220+
221+ var midLon = ( axLon . range [ 0 ] + axLon . range [ 1 ] ) / 2 ;
222+ var midLat = ( axLat . range [ 0 ] + axLat . range [ 1 ] ) / 2 ;
223+
224+ if ( geoLayout . _isScoped ) {
225+ center = { lon : midLon , lat : midLat } ;
226+ } else if ( geoLayout . _isClipped ) {
227+ center = { lon : midLon , lat : midLat } ;
228+ rotation = { lon : midLon , lat : midLat , roll : rotation . roll } ;
229+
230+ var projType = projLayout . type ;
231+ var lonHalfSpan = ( constants . lonaxisSpan [ projType ] / 2 ) || 180 ;
232+ var latHalfSpan = ( constants . lataxisSpan [ projType ] / 2 ) || 180 ;
233+
234+ lonaxisRange = [ midLon - lonHalfSpan , midLon + lonHalfSpan ] ;
235+ lataxisRange = [ midLat - latHalfSpan , midLat + latHalfSpan ] ;
236+ } else {
237+ center = { lon : midLon , lat : midLat } ;
238+ rotation = { lon : midLon , lat : rotation . lat , roll : rotation . roll } ;
239+ }
240+ }
241+
242+ // set 'pre-fit' projection
243+ projection
244+ . center ( [ center . lon - rotation . lon , center . lat - rotation . lat ] )
245+ . rotate ( [ - rotation . lon , - rotation . lat , rotation . roll ] )
246+ . parallels ( projLayout . parallels ) ;
207247
208248 // fit projection 'scale' and 'translate' to set lon/lat ranges
249+ var rangeBox = makeRangeBox ( lonaxisRange , lataxisRange ) ;
209250 projection . fitExtent ( extent , rangeBox ) ;
210251
211252 var b = this . bounds = projection . getBounds ( rangeBox ) ;
@@ -217,12 +258,11 @@ proto.updateProjection = function(fullLayout, geoLayout) {
217258 ! isFinite ( b [ 1 ] [ 0 ] ) || ! isFinite ( b [ 1 ] [ 1 ] ) ||
218259 isNaN ( t [ 0 ] ) || isNaN ( t [ 0 ] )
219260 ) {
220- var gd = this . graphDiv ;
221- var attrToUnset = [ 'projection.rotation' , 'center' , 'lonaxis.range' , 'lataxis.range' ] ;
261+ var attrToUnset = [ 'fitbounds' , 'projection.rotation' , 'center' , 'lonaxis.range' , 'lataxis.range' ] ;
222262 var msg = 'Invalid geo settings, relayout\'ing to default view.' ;
223263 var updateObj = { } ;
224264
225- // clear all attribute that could cause invalid bounds,
265+ // clear all attributes that could cause invalid bounds,
226266 // clear viewInitial to update reset-view behavior
227267
228268 for ( var i = 0 ; i < attrToUnset . length ; i ++ ) {
@@ -236,16 +276,26 @@ proto.updateProjection = function(fullLayout, geoLayout) {
236276 return msg ;
237277 }
238278
279+ if ( geoLayout . fitbounds ) {
280+ var b2 = projection . getBounds ( makeRangeBox ( axLon . range , axLat . range ) ) ;
281+ var k2 = Math . min (
282+ ( b [ 1 ] [ 0 ] - b [ 0 ] [ 0 ] ) / ( b2 [ 1 ] [ 0 ] - b2 [ 0 ] [ 0 ] ) ,
283+ ( b [ 1 ] [ 1 ] - b [ 0 ] [ 1 ] ) / ( b2 [ 1 ] [ 1 ] - b2 [ 0 ] [ 1 ] )
284+ ) ;
285+ projection . scale ( k2 * s ) ;
286+ } else {
287+ // adjust projection to user setting
288+ projection . scale ( projLayout . scale * s ) ;
289+ }
290+
239291 // px coordinates of view mid-point,
240292 // useful to update `geo.center` after interactions
241293 var midPt = this . midPt = [
242294 ( b [ 0 ] [ 0 ] + b [ 1 ] [ 0 ] ) / 2 ,
243295 ( b [ 0 ] [ 1 ] + b [ 1 ] [ 1 ] ) / 2
244296 ] ;
245297
246- // adjust projection to user setting
247298 projection
248- . scale ( projLayout . scale * s )
249299 . translate ( [ t [ 0 ] + ( midPt [ 0 ] - t [ 0 ] ) , t [ 1 ] + ( midPt [ 1 ] - t [ 1 ] ) ] )
250300 . clipExtent ( b ) ;
251301
@@ -540,26 +590,31 @@ proto.saveViewInitial = function(geoLayout) {
540590 var projLayout = geoLayout . projection ;
541591 var rotation = projLayout . rotation || { } ;
542592
593+ this . viewInitial = {
594+ 'fitbounds' : geoLayout . fitbounds ,
595+ 'projection.scale' : projLayout . scale
596+ } ;
597+
598+ var extra ;
543599 if ( geoLayout . _isScoped ) {
544- this . viewInitial = {
600+ extra = {
545601 'center.lon' : center . lon ,
546602 'center.lat' : center . lat ,
547- 'projection.scale' : projLayout . scale
548603 } ;
549604 } else if ( geoLayout . _isClipped ) {
550- this . viewInitial = {
551- 'projection.scale' : projLayout . scale ,
605+ extra = {
552606 'projection.rotation.lon' : rotation . lon ,
553607 'projection.rotation.lat' : rotation . lat
554608 } ;
555609 } else {
556- this . viewInitial = {
610+ extra = {
557611 'center.lon' : center . lon ,
558612 'center.lat' : center . lat ,
559- 'projection.scale' : projLayout . scale ,
560613 'projection.rotation.lon' : rotation . lon
561614 } ;
562615 }
616+
617+ Lib . extendFlat ( this . viewInitial , extra ) ;
563618} ;
564619
565620// [hot code path] (re)draw all paths which depend on the projection
0 commit comments