plotly.js
Version:
The open source javascript graphing library that powers plotly
154 lines (126 loc) • 4.93 kB
JavaScript
;
var d3 = require('@plotly/d3');
var Lib = require('../../lib');
var getTopojsonFeatures = require('../../lib/topojson_utils').getTopojsonFeatures;
var geoJsonUtils = require('../../lib/geojson_utils');
var geoUtils = require('../../lib/geo_location_utils');
var findExtremes = require('../../plots/cartesian/autorange').findExtremes;
var BADNUM = require('../../constants/numerical').BADNUM;
var calcMarkerSize = require('../scatter/calc').calcMarkerSize;
var subTypes = require('../scatter/subtypes');
var style = require('./style');
function plot(gd, geo, calcData) {
var scatterLayer = geo.layers.frontplot.select('.scatterlayer');
var gTraces = Lib.makeTraceGroups(scatterLayer, calcData, 'trace scattergeo');
function removeBADNUM(d, node) {
if (d.lonlat[0] === BADNUM) {
d3.select(node).remove();
}
}
// TODO find a way to order the inner nodes on update
gTraces.selectAll('*').remove();
gTraces.each(function (calcTrace) {
var s = d3.select(this);
var trace = calcTrace[0].trace;
if (subTypes.hasLines(trace) || trace.fill !== 'none') {
var lineCoords = geoJsonUtils.calcTraceToLineCoords(calcTrace);
var lineData =
trace.fill !== 'none' ? geoJsonUtils.makePolygon(lineCoords) : geoJsonUtils.makeLine(lineCoords);
s.selectAll('path.js-line')
.data([{ geojson: lineData, trace: trace }])
.enter()
.append('path')
.classed('js-line', true)
.style('stroke-miterlimit', 2);
}
if (subTypes.hasMarkers(trace)) {
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(Lib.identity)
.enter()
.append('g')
.append('text')
.each(function (calcPt) {
removeBADNUM(calcPt, this);
});
}
// call style here within topojson request callback
style(gd, calcTrace);
});
}
function calcGeoJSON(calcTrace, fullLayout) {
var trace = calcTrace[0].trace;
var geoLayout = fullLayout[trace.geo];
var geo = geoLayout._subplot;
var len = trace._length;
var i, calcPt;
if (Lib.isArrayOrTypedArray(trace.locations)) {
var locationmode = trace.locationmode;
var features =
locationmode === 'geojson-id'
? geoUtils.extractTraceFeature(calcTrace)
: getTopojsonFeatures(trace, geo.topojson);
for (i = 0; i < len; i++) {
calcPt = calcTrace[i];
var feature =
locationmode === 'geojson-id'
? calcPt.fOut
: geoUtils.locationToFeature(locationmode, calcPt.loc, features);
calcPt.lonlat = feature ? feature.properties.ct : [BADNUM, BADNUM];
}
}
var opts = { padded: true };
var lonArray;
var latArray;
// A falsy result (another fitbounds mode, or a Sphere/malformed/empty geojson)
// falls back to the plotted points, similar to `fitbounds === 'locations'`.
const bboxGeojson = geoUtils.fitGeojsonBbox(trace, geoLayout);
if (bboxGeojson) {
const [west, south, east, north] = bboxGeojson;
lonArray = [west, east];
latArray = [south, north];
} else {
lonArray = new Array(len);
latArray = new Array(len);
for (i = 0; i < len; i++) {
calcPt = calcTrace[i];
lonArray[i] = calcPt.lonlat[0];
latArray[i] = calcPt.lonlat[1];
}
opts.ppad = calcMarkerSize(trace, len);
}
trace._extremes.lon = findExtremes(geoLayout.lonaxis._ax, lonArray, opts);
trace._extremes.lat = findExtremes(geoLayout.lataxis._ax, latArray, opts);
}
/**
* Append the coordinates this trace contributes to a subplot-wide `fitbounds`
* bounding box. Keeping it all together allows for proper auto-fitting of
* geometry that crosses the antimeridian.
*
* @param {Array} calcTrace - calcdata for this trace
* @param {object} geoLayout - The subplot's `fullLayout` entry
* @return {Array} `[lon, lat]` pairs
*/
function fitCoords(calcTrace, geoLayout) {
const geojsonCoords = geoUtils.fitGeojsonCoords(calcTrace[0].trace, geoLayout);
if (geojsonCoords.length) return geojsonCoords;
const coords = [];
for (const calcPt of calcTrace) {
if (calcPt.lonlat && isFinite(calcPt.lonlat[0])) coords.push(calcPt.lonlat);
}
return coords;
}
module.exports = {
calcGeoJSON,
fitCoords,
plot
};