UNPKG

plotly.js

Version:

The open source javascript graphing library that powers plotly

249 lines (208 loc) 9.1 kB
'use strict'; var Lib = require('../../lib'); var handleSubplotDefaults = require('../subplot_defaults'); var getSubplotData = require('../get_data').getSubplotData; const { unwrapLonRange } = require('../../lib/geo_location_utils'); var constants = require('./constants'); var layoutAttributes = require('./layout_attributes'); var axesNames = constants.axesNames; module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { handleSubplotDefaults(layoutIn, layoutOut, fullData, { type: 'geo', attributes: layoutAttributes, handleDefaults: handleGeoDefaults, fullData: fullData, partition: 'y' }); }; function handleGeoDefaults(geoLayoutIn, geoLayoutOut, coerce, opts) { var subplotData = getSubplotData(opts.fullData, 'geo', opts.id); var traceIndices = subplotData.map(function (t) { return t.index; }); var resolution = coerce('resolution'); var scope = coerce('scope'); var scopeParams = constants.scopeDefaults[scope]; var projType = coerce('projection.type', scopeParams.projType); var isAlbersUsa = (geoLayoutOut._isAlbersUsa = projType === 'albers usa'); // no other scopes are allowed for 'albers usa' projection if (isAlbersUsa) scope = geoLayoutOut.scope = 'usa'; var isScoped = (geoLayoutOut._isScoped = scope !== 'world'); var isSatellite = (geoLayoutOut._isSatellite = projType === 'satellite'); var isConic = (geoLayoutOut._isConic = projType.indexOf('conic') !== -1 || projType === 'albers'); var isClipped = (geoLayoutOut._isClipped = !!constants.lonaxisSpan[projType]); if (geoLayoutIn.visible === false) { // should override template.layout.geo.show* - see issue 4482 // make a copy var newTemplate = Lib.extendDeep({}, geoLayoutOut._template); // override show* newTemplate.showcoastlines = false; newTemplate.showcountries = false; newTemplate.showframe = false; newTemplate.showlakes = false; newTemplate.showland = false; newTemplate.showocean = false; newTemplate.showrivers = false; newTemplate.showsubunits = false; if (newTemplate.lonaxis) newTemplate.lonaxis.showgrid = false; if (newTemplate.lataxis) newTemplate.lataxis.showgrid = false; // set ref to copy geoLayoutOut._template = newTemplate; } var visible = coerce('visible'); var show; for (var i = 0; i < axesNames.length; i++) { var axisName = axesNames[i]; var dtickDflt = [30, 10][i]; var rangeDflt; if (isScoped) { rangeDflt = scopeParams[axisName + 'Range']; } else { var dfltSpans = constants[axisName + 'Span']; var hSpan = (dfltSpans[projType] || dfltSpans['*']) / 2; var rot = coerce('projection.rotation.' + axisName.slice(0, 3), scopeParams.projRotate[i]); rangeDflt = [rot - hSpan, rot + hSpan]; } var range = coerce(axisName + '.range', rangeDflt); coerce(axisName + '.tick0'); coerce(axisName + '.dtick', dtickDflt); show = coerce(axisName + '.showgrid', !visible ? false : undefined); if (show) { coerce(axisName + '.gridcolor'); coerce(axisName + '.gridwidth'); coerce(axisName + '.griddash'); } // mock axis for autorange computations geoLayoutOut[axisName]._ax = { type: 'linear', _id: axisName.slice(0, 3), _traceIndices: traceIndices, setScale: Lib.identity, c2l: Lib.identity, r2l: Lib.identity, autorange: true, range: range.slice(), _m: 1, _input: {} }; } var lonRange = geoLayoutOut.lonaxis.range; var latRange = geoLayoutOut.lataxis.range; const [lon0, lon1] = unwrapLonRange(lonRange); var centerLon = (lon0 + lon1) / 2; var projLon; if (!isAlbersUsa) { var dfltProjRotate = isScoped ? scopeParams.projRotate : [centerLon, 0, 0]; projLon = coerce('projection.rotation.lon', dfltProjRotate[0]); coerce('projection.rotation.lat', dfltProjRotate[1]); coerce('projection.rotation.roll', dfltProjRotate[2]); show = coerce('showcoastlines', !isScoped && visible); if (show) { coerce('coastlinecolor'); coerce('coastlinewidth'); } show = coerce('showocean', !visible ? false : undefined); if (show) coerce('oceancolor'); } var centerLonDflt; var centerLatDflt; if (isAlbersUsa) { // 'albers usa' does not have a 'center', // these values were found using via: // projection.invert([geoLayout.center.lon, geoLayoutIn.center.lat]) centerLonDflt = -96.6; centerLatDflt = 38.7; } else { centerLonDflt = isScoped ? centerLon : projLon; centerLatDflt = (latRange[0] + latRange[1]) / 2; } coerce('center.lon', centerLonDflt); coerce('center.lat', centerLatDflt); if (isSatellite) { coerce('projection.tilt'); coerce('projection.distance'); } if (isConic) { var dfltProjParallels = scopeParams.projParallels || [0, 60]; coerce('projection.parallels', dfltProjParallels); } coerce('projection.scale'); coerce('projection.minscale'); coerce('projection.maxscale'); show = coerce('showland', !visible ? false : undefined); if (show) coerce('landcolor'); show = coerce('showlakes', !visible ? false : undefined); if (show) coerce('lakecolor'); show = coerce('showrivers', !visible ? false : undefined); if (show) { coerce('rivercolor'); coerce('riverwidth'); } show = coerce('showcountries', isScoped && scope !== 'usa' && visible); if (show) { coerce('countrycolor'); coerce('countrywidth'); } if (scope === 'usa' || (scope === 'north america' && resolution === 50)) { // Only works for: // USA states at 110m // USA states + Canada provinces at 50m coerce('showsubunits', visible); coerce('subunitcolor'); coerce('subunitwidth'); } if (!isScoped) { // Does not work in non-world scopes show = coerce('showframe', visible); if (show) { coerce('framecolor'); coerce('framewidth'); } } coerce('bgcolor'); // `fitbounds` updates a selection of view attributes, specific to the projection type. // Check to see if a user has set any of these. If they have, skip fitbounds. Otherwise // null out the proper attributes and run the fitting logic. coerce('fitbounds'); if (geoLayoutOut.fitbounds) { const centerIn = geoLayoutIn.center || {}; const projectionIn = geoLayoutIn.projection || {}; const rotationIn = projectionIn.rotation || {}; const lonaxisIn = geoLayoutIn.lonaxis || {}; const lataxisIn = geoLayoutIn.lataxis || {}; // All projection types will set these attributes const viewAttributes = [ { dst: geoLayoutOut.center, key: 'lon', src: centerIn }, { dst: geoLayoutOut.center, key: 'lat', src: centerIn }, { dst: geoLayoutOut.projection, key: 'scale', src: projectionIn } ]; // Branch order is important because scoped projections can also be clipped, // but these should be treated as scoped below if (isScoped) { // Scoped only sets center, so move on } else if (isClipped) { viewAttributes.push( { dst: geoLayoutOut.projection.rotation, key: 'lon', src: rotationIn }, { dst: geoLayoutOut.projection.rotation, key: 'lat', src: rotationIn }, { dst: geoLayoutOut.lonaxis, key: 'range', src: lonaxisIn }, { dst: geoLayoutOut.lataxis, key: 'range', src: lataxisIn } ); } else { viewAttributes.push({ dst: geoLayoutOut.projection.rotation, key: 'lon', src: rotationIn }); } // Add entries for axis ranges with no `dst` key for non-clipped projections. // These ranges signal user view-config intent, but non-clipped projections should // skip the null step because it would break the fit calc. if (!isClipped) { viewAttributes.push({ key: 'range', src: lonaxisIn }, { key: 'range', src: lataxisIn }); } const hasUserView = viewAttributes.some(({ src, key }) => src[key] != null); // Use loose comparison so null/undefined count as unset if (hasUserView || constants.fitboundsIncompatible.has(projType)) { geoLayoutOut.fitbounds = false; } else { // Set auto-filled view attributes to null so updateProjection can // compute the fit from scratch and fullLayout matches user input viewAttributes.forEach(({ dst, key }) => dst && (dst[key] = null)); } } }