diff --git a/src/lib/geojson_utils.js b/src/lib/geojson_utils.js index 21ddaaacd6a..b123c1c68ba 100644 --- a/src/lib/geojson_utils.js +++ b/src/lib/geojson_utils.js @@ -9,6 +9,8 @@ 'use strict'; +var BADNUM = require('../constants/numerical').BADNUM; + /** * Convert calcTrace to GeoJSON 'MultiLineString' coordinate arrays * @@ -21,18 +23,19 @@ * */ exports.calcTraceToLineCoords = function(calcTrace) { - var trace = calcTrace[0].trace, - connectgaps = trace.connectgaps; + var trace = calcTrace[0].trace; + var connectgaps = trace.connectgaps; - var coords = [], - lineString = []; + var coords = []; + var lineString = []; for(var i = 0; i < calcTrace.length; i++) { var calcPt = calcTrace[i]; + var lonlat = calcPt.lonlat; - lineString.push(calcPt.lonlat); - - if(!connectgaps && calcPt.gapAfter && lineString.length > 0) { + if(lonlat[0] !== BADNUM) { + lineString.push(lonlat); + } else if(!connectgaps && lineString.length > 0) { coords.push(lineString); lineString = []; } diff --git a/src/plots/mapbox/mapbox.js b/src/plots/mapbox/mapbox.js index 3c3185b1c44..6062376404b 100644 --- a/src/plots/mapbox/mapbox.js +++ b/src/plots/mapbox/mapbox.js @@ -111,10 +111,14 @@ proto.createMap = function(calcData, fullLayout, resolve, reject) { }); // clear navigation container - var className = constants.controlContainerClassName, - controlContainer = self.div.getElementsByClassName(className)[0]; + var className = constants.controlContainerClassName; + var controlContainer = self.div.getElementsByClassName(className)[0]; self.div.removeChild(controlContainer); + // make sure canvas does not inherit left and top css + map._canvas.canvas.style.left = '0px'; + map._canvas.canvas.style.top = '0px'; + self.rejectOnError(reject); map.once('load', function() { @@ -176,7 +180,6 @@ proto.createMap = function(calcData, fullLayout, resolve, reject) { map.on('dragstart', unhover); map.on('zoomstart', unhover); - }; proto.updateMap = function(calcData, fullLayout, resolve, reject) { diff --git a/src/plots/plots.js b/src/plots/plots.js index 6f4dfcf913e..51757322917 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -16,6 +16,7 @@ var Plotly = require('../plotly'); var Registry = require('../registry'); var Lib = require('../lib'); var Color = require('../components/color'); +var BADNUM = require('../constants/numerical').BADNUM; var plots = module.exports = {}; @@ -2012,11 +2013,8 @@ plots.doCalcdata = function(gd, traces) { // // This ensures there is a calcdata item for every trace, // even if cartesian logic doesn't handle it (for things like legends). - // - // Tag this artificial calc point with 'placeholder: true', - // to make it easier to skip over them in during the plot and hover step. if(!Array.isArray(cd) || !cd[0]) { - cd = [{x: false, y: false, placeholder: true}]; + cd = [{x: BADNUM, y: BADNUM}]; } // add the trace-wide properties to the first point, diff --git a/src/traces/scattergeo/calc.js b/src/traces/scattergeo/calc.js index e124cf9993c..8f2fcdee80f 100644 --- a/src/traces/scattergeo/calc.js +++ b/src/traces/scattergeo/calc.js @@ -10,45 +10,32 @@ 'use strict'; var isNumeric = require('fast-isnumeric'); +var BADNUM = require('../../constants/numerical').BADNUM; var calcMarkerColorscale = require('../scatter/colorscale_calc'); - +var arraysToCalcdata = require('../scatter/arrays_to_calcdata'); module.exports = function calc(gd, trace) { - var hasLocationData = Array.isArray(trace.locations), - len = hasLocationData ? trace.locations.length : trace.lon.length; - - var calcTrace = [], - cnt = 0; + var hasLocationData = Array.isArray(trace.locations); + var len = hasLocationData ? trace.locations.length : trace.lon.length; + var calcTrace = new Array(len); for(var i = 0; i < len; i++) { - var calcPt = {}, - skip; + var calcPt = calcTrace[i] = {}; if(hasLocationData) { var loc = trace.locations[i]; + calcPt.loc = typeof loc === 'string' ? loc : null; + } else { + var lon = trace.lon[i]; + var lat = trace.lat[i]; - calcPt.loc = loc; - skip = (typeof loc !== 'string'); - } - else { - var lon = trace.lon[i], - lat = trace.lat[i]; - - calcPt.lonlat = [+lon, +lat]; - skip = (!isNumeric(lon) || !isNumeric(lat)); + if(isNumeric(lon) && isNumeric(lat)) calcPt.lonlat = [+lon, +lat]; + else calcPt.lonlat = [BADNUM, BADNUM]; } - - if(skip) { - if(cnt > 0) calcTrace[cnt - 1].gapAfter = true; - continue; - } - - cnt++; - - calcTrace.push(calcPt); } + arraysToCalcdata(calcTrace, trace); calcMarkerColorscale(trace); return calcTrace; diff --git a/src/traces/scattergeo/hover.js b/src/traces/scattergeo/hover.js index 3e3c54f5bd7..dd609047e99 100644 --- a/src/traces/scattergeo/hover.js +++ b/src/traces/scattergeo/hover.js @@ -11,6 +11,7 @@ var Fx = require('../../plots/cartesian/graph_interact'); var Axes = require('../../plots/cartesian/axes'); +var BADNUM = require('../../constants/numerical').BADNUM; var getTraceColor = require('../scatter/get_trace_color'); var attributes = require('./attributes'); @@ -23,8 +24,6 @@ module.exports = function hoverPoints(pointData) { ya = pointData.ya, geo = pointData.subplot; - if(cd[0].placeholder) return; - function c2p(lonlat) { return geo.projection(lonlat); } @@ -32,8 +31,7 @@ module.exports = function hoverPoints(pointData) { function distFn(d) { var lonlat = d.lonlat; - // this handles the not-found location feature case - if(lonlat[0] === null || lonlat[1] === null) return Infinity; + if(lonlat[0] === BADNUM) return Infinity; if(geo.isLonLatOverEdges(lonlat)) return Infinity; diff --git a/src/traces/scattergeo/plot.js b/src/traces/scattergeo/plot.js index b0c8b278dbc..10451a247be 100644 --- a/src/traces/scattergeo/plot.js +++ b/src/traces/scattergeo/plot.js @@ -15,10 +15,10 @@ var Drawing = require('../../components/drawing'); var Color = require('../../components/color'); var Lib = require('../../lib'); +var BADNUM = require('../../constants/numerical').BADNUM; var getTopojsonFeatures = require('../../lib/topojson_utils').getTopojsonFeatures; var locationToFeature = require('../../lib/geo_location_utils').locationToFeature; var geoJsonUtils = require('../../lib/geojson_utils'); -var arrayToCalcItem = require('../../lib/array_to_calc_item'); var subTypes = require('../scatter/subtypes'); @@ -26,6 +26,16 @@ module.exports = function plot(geo, calcData) { function keyFunc(d) { return d[0].trace.uid; } + function removeBADNUM(d, node) { + if(d.lonlat[0] === BADNUM) { + d3.select(node).remove(); + } + } + + for(var i = 0; i < calcData.length; i++) { + fillLocationLonLat(calcData[i], geo.topojson); + } + var gScatterGeoTraces = geo.framework.select('.scattergeolayer') .selectAll('g.trace.scattergeo') .data(calcData, keyFunc); @@ -39,27 +49,11 @@ module.exports = function plot(geo, calcData) { gScatterGeoTraces.selectAll('*').remove(); gScatterGeoTraces.each(function(calcTrace) { - var s = d3.select(this), - trace = calcTrace[0].trace, - convertToLonLatFn = makeConvertToLonLatFn(trace, geo.topojson); - - // skip over placeholder traces - if(calcTrace[0].placeholder) s.remove(); - - // just like calcTrace but w/o not-found location datum - var _calcTrace = []; - - for(var i = 0; i < calcTrace.length; i++) { - var _calcPt = convertToLonLatFn(calcTrace[i]); - - if(_calcPt) { - arrayItemToCalcdata(trace, calcTrace[i], i); - _calcTrace.push(_calcPt); - } - } + var s = d3.select(this); + var trace = calcTrace[0].trace; if(subTypes.hasLines(trace) || trace.fill !== 'none') { - var lineCoords = geoJsonUtils.calcTraceToLineCoords(_calcTrace); + var lineCoords = geoJsonUtils.calcTraceToLineCoords(calcTrace); var lineData = (trace.fill !== 'none') ? geoJsonUtils.makePolygon(lineCoords, trace) : @@ -72,15 +66,19 @@ module.exports = function plot(geo, calcData) { } if(subTypes.hasMarkers(trace)) { - s.selectAll('path.point').data(_calcTrace) - .enter().append('path') - .classed('point', true); + s.selectAll('path.point') + .data(Lib.identity) + .enter().append('path') + .classed('point', true) + .each(function(calcPt) { removeBADNUM(calcPt, this); }); } if(subTypes.hasText(trace)) { - s.selectAll('g').data(_calcTrace) + s.selectAll('g') + .data(Lib.identity) .enter().append('g') - .append('text'); + .append('text') + .each(function(calcPt) { removeBADNUM(calcPt, this); }); } }); @@ -88,50 +86,19 @@ module.exports = function plot(geo, calcData) { style(geo); }; -function makeConvertToLonLatFn(trace, topojson) { - if(!Array.isArray(trace.locations)) return Lib.identity; - - var features = getTopojsonFeatures(trace, topojson), - locationmode = trace.locationmode; +function fillLocationLonLat(calcTrace, topojson) { + var trace = calcTrace[0].trace; - return function(calcPt) { - var feature = locationToFeature(locationmode, calcPt.loc, features); + if(!Array.isArray(trace.locations)) return; - if(feature) { - calcPt.lonlat = feature.properties.ct; - return calcPt; - } - else { - // mutate gd.calcdata so that hoverPoints knows to skip this datum - calcPt.lonlat = [null, null]; - return false; - } - }; -} + var features = getTopojsonFeatures(trace, topojson); + var locationmode = trace.locationmode; -function arrayItemToCalcdata(trace, calcItem, i) { - var marker = trace.marker; - - function merge(traceAttr, calcAttr) { - arrayToCalcItem(traceAttr, calcItem, calcAttr, i); - } - - merge(trace.text, 'tx'); - merge(trace.textposition, 'tp'); - if(trace.textfont) { - merge(trace.textfont.size, 'ts'); - merge(trace.textfont.color, 'tc'); - merge(trace.textfont.family, 'tf'); - } + for(var i = 0; i < calcTrace.length; i++) { + var calcPt = calcTrace[i]; + var feature = locationToFeature(locationmode, calcPt.loc, features); - if(marker && marker.line) { - var markerLine = marker.line; - merge(marker.opacity, 'mo'); - merge(marker.symbol, 'mx'); - merge(marker.color, 'mc'); - merge(marker.size, 'ms'); - merge(markerLine.color, 'mlc'); - merge(markerLine.width, 'mlw'); + calcPt.lonlat = feature ? feature.properties.ct : [BADNUM, BADNUM]; } } diff --git a/src/traces/scattermapbox/calc.js b/src/traces/scattermapbox/calc.js deleted file mode 100644 index 6f6230851e8..00000000000 --- a/src/traces/scattermapbox/calc.js +++ /dev/null @@ -1,101 +0,0 @@ -/** -* Copyright 2012-2017, Plotly, Inc. -* All rights reserved. -* -* This source code is licensed under the MIT license found in the -* LICENSE file in the root directory of this source tree. -*/ - - -'use strict'; - -var isNumeric = require('fast-isnumeric'); - -var Lib = require('../../lib'); -var Colorscale = require('../../components/colorscale'); - -var subtypes = require('../scatter/subtypes'); -var calcMarkerColorscale = require('../scatter/colorscale_calc'); -var makeBubbleSizeFn = require('../scatter/make_bubble_size_func'); - - -module.exports = function calc(gd, trace) { - var len = trace.lon.length, - marker = trace.marker; - - var hasMarkers = subtypes.hasMarkers(trace), - hasColorArray = (hasMarkers && Array.isArray(marker.color)), - hasSizeArray = (hasMarkers && Array.isArray(marker.size)), - hasSymbolArray = (hasMarkers && Array.isArray(marker.symbol)), - hasTextArray = Array.isArray(trace.text); - - calcMarkerColorscale(trace); - - var colorFn = Colorscale.hasColorscale(trace, 'marker') ? - Colorscale.makeColorScaleFunc( - Colorscale.extractScale( - marker.colorscale, - marker.cmin, - marker.cmax - ) - ) : - Lib.identity; - - var sizeFn = subtypes.isBubble(trace) ? - makeBubbleSizeFn(trace) : - Lib.identity; - - var calcTrace = [], - cnt = 0; - - // Different than cartesian calc step - // as skip over non-numeric lon, lat pairs. - // This makes the hover and convert calculations simpler. - - for(var i = 0; i < len; i++) { - var lon = trace.lon[i], - lat = trace.lat[i]; - - if(!isNumeric(lon) || !isNumeric(lat)) { - if(cnt > 0) calcTrace[cnt - 1].gapAfter = true; - continue; - } - - var calcPt = {}; - cnt++; - - // coerce numeric strings into numbers - calcPt.lonlat = [+lon, +lat]; - - if(hasMarkers) { - - if(hasColorArray) { - var mc = marker.color[i]; - - calcPt.mc = mc; - calcPt.mcc = colorFn(mc); - } - - if(hasSizeArray) { - var ms = marker.size[i]; - - calcPt.ms = ms; - calcPt.mrc = sizeFn(ms); - } - - if(hasSymbolArray) { - var mx = marker.symbol[i]; - calcPt.mx = (typeof mx === 'string') ? mx : 'circle'; - } - } - - if(hasTextArray) { - var tx = trace.text[i]; - calcPt.tx = (typeof tx === 'string') ? tx : ''; - } - - calcTrace.push(calcPt); - } - - return calcTrace; -}; diff --git a/src/traces/scattermapbox/convert.js b/src/traces/scattermapbox/convert.js index 3e46c8d8950..e4c17f91973 100644 --- a/src/traces/scattermapbox/convert.js +++ b/src/traces/scattermapbox/convert.js @@ -10,8 +10,11 @@ 'use strict'; var Lib = require('../../lib'); +var BADNUM = require('../../constants/numerical').BADNUM; var geoJsonUtils = require('../../lib/geojson_utils'); +var Colorscale = require('../../components/colorscale'); +var makeBubbleSizeFn = require('../scatter/make_bubble_size_func'); var subTypes = require('../scatter/subtypes'); var convertTextOpts = require('../../plots/mapbox/convert_text_opts'); @@ -43,16 +46,16 @@ module.exports = function convert(calcTrace) { }; // early return if not visible or placeholder - if(!isVisible || calcTrace[0].placeholder) return opts; + if(!isVisible) return opts; // fill layer and line layer use the same coords - var coords; + var lineCoords; if(hasFill || hasLines) { - coords = geoJsonUtils.calcTraceToLineCoords(calcTrace); + lineCoords = geoJsonUtils.calcTraceToLineCoords(calcTrace); } if(hasFill) { - fill.geojson = geoJsonUtils.makePolygon(coords); + fill.geojson = geoJsonUtils.makePolygon(lineCoords); fill.layout.visibility = 'visible'; Lib.extendFlat(fill.paint, { @@ -61,7 +64,7 @@ module.exports = function convert(calcTrace) { } if(hasLines) { - line.geojson = geoJsonUtils.makeLine(coords); + line.geojson = geoJsonUtils.makeLine(lineCoords); line.layout.visibility = 'visible'; Lib.extendFlat(line.paint, { @@ -155,12 +158,30 @@ function initContainer() { // // The solution prove to be more robust than trying to generate // `stops` arrays from scale functions. +// +// TODO axe this when we bump mapbox-gl and rewrite this using +// "identity" property functions. +// See https://github.com/plotly/plotly.js/pull/1543 +// function makeCircleGeoJSON(calcTrace, hash) { var trace = calcTrace[0].trace; + var marker = trace.marker; + + var colorFn; + if(Colorscale.hasColorscale(trace, 'marker')) { + colorFn = Colorscale.makeColorScaleFunc( + Colorscale.extractScale(marker.colorscale, marker.cmin, marker.cmax) + ); + } else if(Array.isArray(marker.color)) { + colorFn = Lib.identity; + } - var marker = trace.marker, - hasColorArray = Array.isArray(marker.color), - hasSizeArray = Array.isArray(marker.size); + var sizeFn; + if(subTypes.isBubble(trace)) { + sizeFn = makeBubbleSizeFn(trace); + } else if(Array.isArray(marker.size)) { + sizeFn = Lib.identity; + } // Translate vals in trace arrayOk containers // into a val-to-index hash object @@ -174,16 +195,19 @@ function makeCircleGeoJSON(calcTrace, hash) { for(var i = 0; i < calcTrace.length; i++) { var calcPt = calcTrace[i]; + var lonlat = calcPt.lonlat; + + if(isBADNUM(lonlat)) continue; var props = {}; - if(hasColorArray) translate(props, COLOR_PROP, calcPt.mcc, i); - if(hasSizeArray) translate(props, SIZE_PROP, calcPt.mrc, i); + if(colorFn) translate(props, COLOR_PROP, colorFn(calcPt.mc), i); + if(sizeFn) translate(props, SIZE_PROP, sizeFn(calcPt.ms), i); features.push({ type: 'Feature', geometry: { type: 'Point', - coordinates: calcPt.lonlat + coordinates: lonlat }, properties: props }); @@ -215,6 +239,8 @@ function makeSymbolGeoJSON(calcTrace) { for(var i = 0; i < calcTrace.length; i++) { var calcPt = calcTrace[i]; + if(isBADNUM(calcPt.lonlat)) continue; + features.push({ type: 'Feature', geometry: { @@ -305,3 +331,8 @@ function getFillFunc(attr) { } function blankFillFunc() { return ''; } + +// only need to check lon (OR lat) +function isBADNUM(lonlat) { + return lonlat[0] === BADNUM; +} diff --git a/src/traces/scattermapbox/hover.js b/src/traces/scattermapbox/hover.js index 088d35f6dc5..2bbc77e3ec1 100644 --- a/src/traces/scattermapbox/hover.js +++ b/src/traces/scattermapbox/hover.js @@ -11,7 +11,7 @@ var Fx = require('../../plots/cartesian/graph_interact'); var getTraceColor = require('../scatter/get_trace_color'); - +var BADNUM = require('../../constants/numerical').BADNUM; module.exports = function hoverPoints(pointData, xval, yval) { var cd = pointData.cd, @@ -19,8 +19,6 @@ module.exports = function hoverPoints(pointData, xval, yval) { xa = pointData.xa, ya = pointData.ya; - if(cd[0].placeholder) return; - // compute winding number about [-180, 180] globe var winding = (xval >= 0) ? Math.floor((xval + 180) / 360) : @@ -31,10 +29,13 @@ module.exports = function hoverPoints(pointData, xval, yval) { var xval2 = xval - lonShift; function distFn(d) { - var lonlat = d.lonlat, - dx = Math.abs(xa.c2p(lonlat) - xa.c2p([xval2, lonlat[1]])), - dy = Math.abs(ya.c2p(lonlat) - ya.c2p([lonlat[0], yval])), - rad = Math.max(3, d.mrc || 0); + var lonlat = d.lonlat; + + if(lonlat[0] === BADNUM) return Infinity; + + var dx = Math.abs(xa.c2p(lonlat) - xa.c2p([xval2, lonlat[1]])); + var dy = Math.abs(ya.c2p(lonlat) - ya.c2p([lonlat[0], yval])); + var rad = Math.max(3, d.mrc || 0); return Math.max(Math.sqrt(dx * dx + dy * dy) - rad, 1 - 3 / rad); } diff --git a/src/traces/scattermapbox/index.js b/src/traces/scattermapbox/index.js index fa32f9847a0..6de4241ed82 100644 --- a/src/traces/scattermapbox/index.js +++ b/src/traces/scattermapbox/index.js @@ -14,7 +14,7 @@ var ScatterMapbox = {}; ScatterMapbox.attributes = require('./attributes'); ScatterMapbox.supplyDefaults = require('./defaults'); ScatterMapbox.colorbar = require('../scatter/colorbar'); -ScatterMapbox.calc = require('./calc'); +ScatterMapbox.calc = require('../scattergeo/calc'); ScatterMapbox.hoverPoints = require('./hover'); ScatterMapbox.eventData = require('./event_data'); ScatterMapbox.plot = require('./plot'); diff --git a/test/image/baselines/mapbox_0.png b/test/image/baselines/mapbox_0.png index 80a431490f0..c5378b6cf85 100644 Binary files a/test/image/baselines/mapbox_0.png and b/test/image/baselines/mapbox_0.png differ diff --git a/test/image/baselines/mapbox_angles.png b/test/image/baselines/mapbox_angles.png index 611a6fc132f..54eae19c19f 100644 Binary files a/test/image/baselines/mapbox_angles.png and b/test/image/baselines/mapbox_angles.png differ diff --git a/test/image/baselines/mapbox_bubbles-text.png b/test/image/baselines/mapbox_bubbles-text.png index a0613245a06..01a663867f0 100644 Binary files a/test/image/baselines/mapbox_bubbles-text.png and b/test/image/baselines/mapbox_bubbles-text.png differ diff --git a/test/image/baselines/mapbox_custom-style.png b/test/image/baselines/mapbox_custom-style.png index 7213f229c04..23d2ed05b92 100644 Binary files a/test/image/baselines/mapbox_custom-style.png and b/test/image/baselines/mapbox_custom-style.png differ diff --git a/test/image/baselines/mapbox_fill.png b/test/image/baselines/mapbox_fill.png index ea1a2370a7d..98fc78f86bd 100644 Binary files a/test/image/baselines/mapbox_fill.png and b/test/image/baselines/mapbox_fill.png differ diff --git a/test/image/baselines/mapbox_layers.png b/test/image/baselines/mapbox_layers.png index 4d39e4de195..95da9fd0db4 100644 Binary files a/test/image/baselines/mapbox_layers.png and b/test/image/baselines/mapbox_layers.png differ diff --git a/test/image/baselines/mapbox_symbol-text.png b/test/image/baselines/mapbox_symbol-text.png index 703b49681f2..1ec7cd40459 100644 Binary files a/test/image/baselines/mapbox_symbol-text.png and b/test/image/baselines/mapbox_symbol-text.png differ diff --git a/test/jasmine/tests/scattergeo_test.js b/test/jasmine/tests/scattergeo_test.js index 2391cdfb512..33581c718bd 100644 --- a/test/jasmine/tests/scattergeo_test.js +++ b/test/jasmine/tests/scattergeo_test.js @@ -1,74 +1,223 @@ +var Plots = require('@src/plots/plots'); +var Lib = require('@src/lib'); +var BADNUM = require('@src/constants/numerical').BADNUM; + var ScatterGeo = require('@src/traces/scattergeo'); -describe('Test scattergeo', function() { - 'use strict'; - describe('supplyDefaults', function() { - var traceIn, - traceOut; +describe('Test scattergeo defaults', function() { + var traceIn, + traceOut; - var defaultColor = '#444', - layout = {}; + var defaultColor = '#444', + layout = {}; - beforeEach(function() { - traceOut = {}; - }); + beforeEach(function() { + traceOut = {}; + }); + + it('should slice lat if it it longer than lon', function() { + traceIn = { + lon: [-75], + lat: [45, 45, 45] + }; + + ScatterGeo.supplyDefaults(traceIn, traceOut, defaultColor, layout); + expect(traceOut.lat).toEqual([45]); + expect(traceOut.lon).toEqual([-75]); + }); - it('should slice lat if it it longer than lon', function() { - traceIn = { - lon: [-75], - lat: [45, 45, 45] - }; + it('should slice lon if it it longer than lat', function() { + traceIn = { + lon: [-75, -75, -75], + lat: [45] + }; + + ScatterGeo.supplyDefaults(traceIn, traceOut, defaultColor, layout); + expect(traceOut.lat).toEqual([45]); + expect(traceOut.lon).toEqual([-75]); + }); + + it('should not coerce lat and lon if locations is valid', function() { + traceIn = { + locations: ['CAN', 'USA'], + lon: [20, 40], + lat: [20, 40] + }; + + ScatterGeo.supplyDefaults(traceIn, traceOut, defaultColor, layout); + expect(traceOut.lon).toBeUndefined(); + expect(traceOut.lat).toBeUndefined(); + }); + it('should make trace invisible if lon or lat is omitted and locations not given', function() { + function testOne() { ScatterGeo.supplyDefaults(traceIn, traceOut, defaultColor, layout); - expect(traceOut.lat).toEqual([45]); - expect(traceOut.lon).toEqual([-75]); + expect(traceOut.visible).toBe(false); + } + + traceIn = { + lat: [45, 45, 45] + }; + testOne(); + + traceIn = { + lon: [-75, -75, -75] + }; + traceOut = {}; + testOne(); + + traceIn = {}; + traceOut = {}; + testOne(); + }); +}); + +describe('Test scattergeo calc', function() { + + function _calc(opts) { + var base = { type: 'scattermapbox' }; + var trace = Lib.extendFlat({}, base, opts); + var gd = { data: [trace] }; + + Plots.supplyDefaults(gd); + + var fullTrace = gd._fullData[0]; + return ScatterGeo.calc(gd, fullTrace); + } + + it('should place lon/lat data in lonlat pairs', function() { + var calcTrace = _calc({ + lon: [10, 20, 30], + lat: [20, 30, 10] + }); + + expect(calcTrace).toEqual([ + { lonlat: [10, 20] }, + { lonlat: [20, 30] }, + { lonlat: [30, 10] } + ]); + }); + + it('should coerce numeric strings lon/lat data into numbers', function() { + var calcTrace = _calc({ + lon: [10, 20, '30', '40'], + lat: [20, '30', 10, '50'] }); - it('should slice lon if it it longer than lat', function() { - traceIn = { - lon: [-75, -75, -75], - lat: [45] - }; + expect(calcTrace).toEqual([ + { lonlat: [10, 20] }, + { lonlat: [20, 30] }, + { lonlat: [30, 10] }, + { lonlat: [40, 50] } + ]); + }); - ScatterGeo.supplyDefaults(traceIn, traceOut, defaultColor, layout); - expect(traceOut.lat).toEqual([45]); - expect(traceOut.lon).toEqual([-75]); + it('should set non-numeric values lon/lat pairs to BADNUM', function() { + var calcTrace = _calc({ + lon: [null, 10, null, null, 20, '30', null, '40', null, 10], + lat: [10, 20, '30', null, 10, '50', null, 60, null, null] }); - it('should not coerce lat and lon if locations is valid', function() { - traceIn = { - locations: ['CAN', 'USA'], - lon: [20, 40], - lat: [20, 40] - }; + expect(calcTrace).toEqual([ + { lonlat: [BADNUM, BADNUM] }, + { lonlat: [10, 20] }, + { lonlat: [BADNUM, BADNUM] }, + { lonlat: [BADNUM, BADNUM] }, + { lonlat: [20, 10] }, + { lonlat: [30, 50] }, + { lonlat: [BADNUM, BADNUM] }, + { lonlat: [40, 60] }, + { lonlat: [BADNUM, BADNUM] }, + { lonlat: [BADNUM, BADNUM] } + ]); + }); - ScatterGeo.supplyDefaults(traceIn, traceOut, defaultColor, layout); - expect(traceOut.lon).toBeUndefined(); - expect(traceOut.lat).toBeUndefined(); + it('should fill array text (base case)', function() { + var calcTrace = _calc({ + lon: [10, 20, 30, null, 40], + lat: [20, 30, 10, 'no-good', 50], + text: ['A', 'B', 'C', 'D', 'E'] }); - it('should make trace invisible if lon or lat is omitted and locations not given', function() { - function testOne() { - ScatterGeo.supplyDefaults(traceIn, traceOut, defaultColor, layout); - expect(traceOut.visible).toBe(false); - } + expect(calcTrace).toEqual([ + { lonlat: [10, 20], tx: 'A' }, + { lonlat: [20, 30], tx: 'B' }, + { lonlat: [30, 10], tx: 'C' }, + { lonlat: [BADNUM, BADNUM], tx: 'D' }, + { lonlat: [40, 50], tx: 'E' } + ]); + }); + + it('should fill array text (invalid entry case)', function() { + var calcTrace = _calc({ + lon: [10, 20, 30, null, 40], + lat: [20, 30, 10, 'no-good', 50], + text: ['A', null, 'C', 'D', {}] + }); - traceIn = { - lat: [45, 45, 45] - }; - testOne(); + expect(calcTrace).toEqual([ + { lonlat: [10, 20], tx: 'A' }, + { lonlat: [20, 30], tx: null }, + { lonlat: [30, 10], tx: 'C' }, + { lonlat: [BADNUM, BADNUM], tx: 'D' }, + { lonlat: [40, 50], tx: {} } + ]); + }); + + it('should fill array marker attributes (base case)', function() { + var calcTrace = _calc({ + lon: [10, 20, null, 30], + lat: [20, 30, null, 10], + marker: { + color: ['red', 'blue', 'green', 'yellow'], + size: [10, 20, 8, 10] + } + }); - traceIn = { - lon: [-75, -75, -75] - }; - traceOut = {}; - testOne(); + expect(calcTrace).toEqual([ + { lonlat: [10, 20], mc: 'red', ms: 10 }, + { lonlat: [20, 30], mc: 'blue', ms: 20 }, + { lonlat: [BADNUM, BADNUM], mc: 'green', ms: 8 }, + { lonlat: [30, 10], mc: 'yellow', ms: 10 } + ]); + }); - traceIn = {}; - traceOut = {}; - testOne(); + it('should fill array marker attributes (invalid scale case)', function() { + var calcTrace = _calc({ + lon: [10, 20, null, 30], + lat: [20, 30, null, 10], + marker: { + color: [0, null, 5, 10], + size: [10, NaN, 8, 10], + colorscale: [ + [0, 'blue'], [0.5, 'red'], [1, 'green'] + ] + } }); + + expect(calcTrace).toEqual([ + { lonlat: [10, 20], mc: 0, ms: 10 }, + { lonlat: [20, 30], mc: null, ms: NaN }, + { lonlat: [BADNUM, BADNUM], mc: 5, ms: 8 }, + { lonlat: [30, 10], mc: 10, ms: 10 } + ]); }); + it('should fill marker attributes (symbol case)', function() { + var calcTrace = _calc({ + lon: [10, 20, null, 30], + lat: [20, 30, null, 10], + marker: { + symbol: ['cross', 'square', 'diamond', null] + } + }); + + expect(calcTrace).toEqual([ + { lonlat: [10, 20], mx: 'cross' }, + { lonlat: [20, 30], mx: 'square' }, + { lonlat: [BADNUM, BADNUM], mx: 'diamond' }, + { lonlat: [30, 10], mx: null } + ]); + }); }); diff --git a/test/jasmine/tests/scattermapbox_test.js b/test/jasmine/tests/scattermapbox_test.js index 88aa8adfdb0..ac2c8503648 100644 --- a/test/jasmine/tests/scattermapbox_test.js +++ b/test/jasmine/tests/scattermapbox_test.js @@ -112,143 +112,6 @@ describe('scattermapbox defaults', function() { }); }); -describe('scattermapbox calc', function() { - 'use strict'; - - function _calc(trace) { - var gd = { data: [trace] }; - - Plots.supplyDefaults(gd); - - var fullTrace = gd._fullData[0]; - return ScatterMapbox.calc(gd, fullTrace); - } - - var base = { type: 'scattermapbox' }; - - it('should place lon/lat data in lonlat pairs', function() { - var calcTrace = _calc(Lib.extendFlat({}, base, { - lon: [10, 20, 30], - lat: [20, 30, 10] - })); - - expect(calcTrace).toEqual([ - { lonlat: [10, 20] }, - { lonlat: [20, 30] }, - { lonlat: [30, 10] } - ]); - }); - - it('should coerce numeric strings lon/lat data into numbers', function() { - var calcTrace = _calc(Lib.extendFlat({}, base, { - lon: [10, 20, '30', '40'], - lat: [20, '30', 10, '50'] - })); - - expect(calcTrace).toEqual([ - { lonlat: [10, 20] }, - { lonlat: [20, 30] }, - { lonlat: [30, 10] }, - { lonlat: [40, 50] } - ]); - }); - - it('should keep track of gaps in data', function() { - var calcTrace = _calc(Lib.extendFlat({}, base, { - lon: [null, 10, null, null, 20, '30', null, '40', null, 10], - lat: [10, 20, '30', null, 10, '50', null, 60, null, null] - })); - - expect(calcTrace).toEqual([ - { lonlat: [10, 20], gapAfter: true }, - { lonlat: [20, 10] }, - { lonlat: [30, 50], gapAfter: true }, - { lonlat: [40, 60], gapAfter: true } - ]); - }); - - it('should fill array text (base case)', function() { - var calcTrace = _calc(Lib.extendFlat({}, base, { - lon: [10, 20, 30], - lat: [20, 30, 10], - text: ['A', 'B', 'C'] - })); - - expect(calcTrace).toEqual([ - { lonlat: [10, 20], tx: 'A' }, - { lonlat: [20, 30], tx: 'B' }, - { lonlat: [30, 10], tx: 'C' } - ]); - }); - - it('should fill array text (invalid entry case)', function() { - var calcTrace = _calc(Lib.extendFlat({}, base, { - lon: [10, 20, 30], - lat: [20, 30, 10], - text: ['A', 'B', null] - })); - - expect(calcTrace).toEqual([ - { lonlat: [10, 20], tx: 'A' }, - { lonlat: [20, 30], tx: 'B' }, - { lonlat: [30, 10], tx: '' } - ]); - }); - - it('should fill array marker attributes (base case)', function() { - var calcTrace = _calc(Lib.extendFlat({}, base, { - lon: [10, 20, null, 30], - lat: [20, 30, null, 10], - marker: { - color: ['red', 'blue', 'green', 'yellow'], - size: [10, 20, 8, 10] - } - })); - - expect(calcTrace).toEqual([ - { lonlat: [10, 20], mc: 'red', ms: 10, mcc: 'red', mrc: 5 }, - { lonlat: [20, 30], mc: 'blue', ms: 20, mcc: 'blue', mrc: 10, gapAfter: true }, - { lonlat: [30, 10], mc: 'yellow', ms: 10, mcc: 'yellow', mrc: 5 } - ]); - }); - - it('should fill array marker attributes (invalid scale case)', function() { - var calcTrace = _calc(Lib.extendFlat({}, base, { - lon: [10, 20, null, 30], - lat: [20, 30, null, 10], - marker: { - color: [0, null, 5, 10], - size: [10, NaN, 8, 10], - colorscale: [ - [0, 'blue'], [0.5, 'red'], [1, 'green'] - ] - } - })); - - expect(calcTrace).toEqual([ - { lonlat: [10, 20], mc: 0, ms: 10, mcc: 'rgb(0, 0, 255)', mrc: 5 }, - { lonlat: [20, 30], mc: null, ms: NaN, mcc: '#444', mrc: 0, gapAfter: true }, - { lonlat: [30, 10], mc: 10, ms: 10, mcc: 'rgb(0, 128, 0)', mrc: 5 } - ]); - }); - - it('should fill marker attributes (symbol case)', function() { - var calcTrace = _calc(Lib.extendFlat({}, base, { - lon: [10, 20, null, 30], - lat: [20, 30, null, 10], - marker: { - symbol: ['monument', 'music', 'harbor', null] - } - })); - - expect(calcTrace).toEqual([ - { lonlat: [10, 20], mx: 'monument' }, - { lonlat: [20, 30], mx: 'music', gapAfter: true }, - { lonlat: [30, 10], mx: 'circle' } - ]); - }); -}); - describe('scattermapbox convert', function() { 'use strict'; @@ -376,7 +239,7 @@ describe('scattermapbox convert', function() { return f.properties.text; }); - expect(actualText).toEqual(['A', 'B', 'C', 'F', '']); + expect(actualText).toEqual(['A', 'B', 'C', 'F', undefined]); }); it('should generate correct output for lines traces with trailing gaps', function() { @@ -451,7 +314,9 @@ describe('scattermapbox convert', function() { fill: 'toself' })); - assertVisibility(opts, ['none', 'none', 'none', 'none']); + // not optimal, but doesn't break anything as mapbox-gl accepts empty + // coordinate arrays + assertVisibility(opts, ['visible', 'visible', 'none', 'none']); expect(opts.line.geojson.coordinates).toEqual([], 'line coords'); expect(opts.fill.geojson.coordinates).toEqual([], 'fill coords');