diff --git a/panels/map2/display/binning.js b/panels/map2/display/binning.js
index f44258158c2..fa376295beb 100644
--- a/panels/map2/display/binning.js
+++ b/panels/map2/display/binning.js
@@ -1,10 +1,11 @@
+/**
+ * Hexagonal binning
+ * Rendered as normally projected svg paths, which mean they *do not*
+ * clip on spheres appropriately. To fix this, we would need to translate
+ * the svg path into a geo-path
+ */
+function displayBinning(scope, dimensions) {
-function displayBinning(scope, dimensions, projection, path) {
-
-
- /**
- * Hexbin-specific setup
- */
var hexbin = d3.hexbin()
.size(dimensions)
.radius(scope.panel.display.binning.hexagonSize);
@@ -16,10 +17,13 @@ function displayBinning(scope, dimensions, projection, path) {
if (scope.panel.display.binning.enabled) {
- //primary field is just binning raw counts
- //secondary field is binning some metric like mean/median/total. Hexbins doesn't support that,
- //so we cheat a little and just add more points to compensate.
- //However, we don't want to add a million points, so normalize against the largest value
+ /**
+ * primary field is just binning raw counts
+ *
+ * Secondary field is binning some metric like mean/median/total. Hexbins doesn't support that,
+ * so we cheat a little and just add more points to compensate.
+ * However, we don't want to add a million points, so normalize against the largest value
+ */
if (scope.panel.display.binning.areaEncodingField === 'secondary') {
var max = Math.max.apply(Math, _.map(scope.data, function(k,v){return k;})),
scale = 50/max;
@@ -27,12 +31,11 @@ function displayBinning(scope, dimensions, projection, path) {
_.map(scope.data, function (k, v) {
var decoded = geohash.decode(v);
return _.map(_.range(0, k*scale), function(a,b) {
- binPoints.push(projection([decoded.longitude, decoded.latitude]));
+ binPoints.push(scope.projection([decoded.longitude, decoded.latitude]));
})
});
} else {
-
binPoints = scope.projectedPoints;
}
@@ -43,6 +46,8 @@ function displayBinning(scope, dimensions, projection, path) {
//clean up some memory
binPoints = [];
} else {
+
+ //not enabled, so just set an empty array. D3.exit will take care of the rest
binnedPoints = [];
binRange = 0;
}
@@ -59,10 +64,6 @@ function displayBinning(scope, dimensions, projection, path) {
.interpolate(d3.interpolateLab);
- /**
- * D3 Drawing
- */
-
var hex = scope.g.selectAll(".hexagon")
.data(binnedPoints);
diff --git a/panels/map2/display/bullseye.js b/panels/map2/display/bullseye.js
index 539bdbac50d..baad275b5dd 100644
--- a/panels/map2/display/bullseye.js
+++ b/panels/map2/display/bullseye.js
@@ -1,10 +1,11 @@
-function displayBullseye(scope, projection, path) {
+/**
+ * Renders bullseyes as geo-json poly gon entities
+ * Allows for them to clip on spheres correctly
+ */
+function displayBullseye(scope, path) {
var degrees = 180 / Math.PI
-
-
var circle = d3.geo.circle();
-
var data = [];
if (scope.panel.display.bullseye.enabled) {
@@ -13,11 +14,9 @@ function displayBullseye(scope, projection, path) {
];
}
-
var arcs = scope.g.selectAll(".arc")
.data(data);
-
arcs.enter().append("path")
.datum(function(d) {
return circle.origin([d.lon, d.lat]).angle(1000 / 6371 * degrees)();
diff --git a/panels/map2/display/geopoints.js b/panels/map2/display/geopoints.js
index d181a2e0cd2..9d7eb5b8175 100644
--- a/panels/map2/display/geopoints.js
+++ b/panels/map2/display/geopoints.js
@@ -1,46 +1,18 @@
+/**
+ * Renders geopoints as geo-json poly gon entities
+ * Allows for them to clip on spheres correctly
+ */
function displayGeopoints(scope, path) {
- /*
- var points = {};
- var circle = d3.geo.circle();
- var degrees = 180 / Math.PI;
+ var points = [];
+ var circle = d3.geo.circle();
+ var degrees = 180 / Math.PI
- if (scope.panel.display.geopoints.enabled) {
- //points = scope.points;
-
- var features = _.map(scope.points, function(coords) {
- return {
- feature: circle.origin(scope.projection([coords[0], coords[1]]))
- .angle(scope.panel.display.geopoints.pointSize / 6371 * degrees)()
- };
- });
-
- console.log("features", features);
- points = {
- type: "FeatureCollection",
- features: features
- };
- console.log("points", points);
-
- }
-
- console.log("points2", points);
- scope.svg.append("path")
- .datum(points)
- .attr("d", d3.geo.path());
-
-
- */
-
-
-
- var points = []
if (scope.panel.display.geopoints.enabled) {
points = scope.points;
}
- var circle = d3.geo.circle();
- var degrees = 180 / Math.PI
+
var geopoints = scope.g.selectAll(".geopoint")
.data(points);
diff --git a/panels/map2/module.js b/panels/map2/module.js
index c5d31b3a67b..39f16214e32 100644
--- a/panels/map2/module.js
+++ b/panels/map2/module.js
@@ -186,7 +186,6 @@ angular.module('kibana.map2', [])
.directive('map2', function () {
return {
restrict: 'A',
- template: '

',
link: function (scope, elem, attrs) {
@@ -215,20 +214,9 @@ angular.module('kibana.map2', [])
};
- //These should be moved to utility classes
- var s4 = function() {
- return Math.floor((1 + Math.random()) * 0x10000)
- .toString(16)
- .substring(1);
- };
-
-
- scope.uuid = s4() + s4() + '-' + s4() + '-' + s4() + '-' +
- s4() + '-' + s4() + s4() + s4();
-
-
-
-
+ /**
+ * Initialize the panels if new, or render existing panels
+ */
scope.init_or_render = function() {
if (typeof scope.svg === 'undefined') {
console.log("init");
@@ -245,19 +233,25 @@ angular.module('kibana.map2', [])
};
- // Receive render events
+ /**
+ * Receive render events
+ */
scope.$on('render', function () {
- console.log("$on render");
scope.init_or_render();
});
- // Or if the window is resized
+ /**
+ * On window resize, re-render the panel
+ */
angular.element(window).bind('resize', function () {
- console.log("resize render");
scope.init_or_render();
});
+ /**
+ * Load the various panel-specific scripts, map data, then initialize
+ * the svg and set appropriate D3 settings
+ */
function init_panel() {
scope.initializing = true;
@@ -281,9 +275,6 @@ angular.module('kibana.map2', [])
scope.worldData = world;
scope.worldNames = names;
- console.log('initializing svg');
-
-
//Better way to get these values? Seems kludgy to use jQuery on the div...
var width = $(elem[0]).width(),
@@ -299,6 +290,7 @@ angular.module('kibana.map2', [])
.on("zoom", translate_map);
//used by choropleth
+ //@todo change domain so that it reflects the domain of the data
scope.quantize = d3.scale.quantize()
.domain([0, 1000])
.range(d3.range(9).map(function(i) { return "q" + (i+1); }));
@@ -319,100 +311,68 @@ angular.module('kibana.map2', [])
});
-
-
- //remove our old svg...is there a better way to update than remove/append?
- //d3.select("#" + scope.uuid).select("svg").remove();
-
//create the new svg
scope.svg = d3.select(elem[0]).append("svg")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.call(scope.zoom);
-
-
scope.g = scope.svg.append("g");
-
-
- console.log("finished initing");
scope.initializing = false;
render_panel();
});
-
});
}
-
+ /**
+ * Render updates to the SVG. Typically happens when the data changes (time, query)
+ * or when new options are selected
+ */
function render_panel() {
-
-
- console.log("render_panel scope.svg", scope.svg);
-
-
var width = $(elem[0]).width(),
height = $(elem[0]).height();
- /**
- * D3 and general config section
- */
-
-
- scope.projection;
-
+ //Projection is dependant on the map-type
if (scope.panel.display.data.type === 'mercator') {
scope.projection = d3.geo.mercator()
.translate([width/2, height/2])
.scale(scope.scale);
+
} else if (scope.panel.display.data.type === 'orthographic') {
scope.projection = d3.geo.orthographic()
.translate([width/2, height/2])
.scale(100)
.clipAngle(90);
- var λ = d3.scale.linear()
- .domain([0, width])
- .range([-180, 180]);
+ //recenters the sphere more towards the US...not really necessary
+ scope.projection.rotate([100 / 2, 20 / 2, scope.projection.rotate()[2]]);
- var φ = d3.scale.linear()
- .domain([0, height])
- .range([90, -90]);
}
var path = d3.geo.path()
.projection(scope.projection);
- //Geocoded points are decoded into lat/lon, then projected onto x/y
+ //Geocoded points are decoded into lonlat
scope.points = _.map(scope.data, function (k, v) {
var decoded = geohash.decode(v);
return [decoded.longitude, decoded.latitude];
});
+
+ //And also projected projected to x/y. Both sets of points are used
+ //by different functions
scope.projectedPoints = _.map(scope.points, function (coords) {
return scope.projection(coords);
});
-
-
-
-
-
-
-
- //set up listener for ctrl key
- //scope.svg
-
- //Overlay is used so that the entire map is draggable, not just the locations
- //where countries are
-
scope.svg.select(".overlay").remove();
scope.svg.append("rect")
@@ -421,12 +381,11 @@ angular.module('kibana.map2', [])
.attr("height", height)
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
+
//Draw the countries, if this is a choropleth, draw with fancy colors
var countryPath = scope.g.selectAll(".land")
.data(scope.countries);
-
-
countryPath.enter().append("path")
.attr("class", function(d) {
if (scope.panel.display.choropleth.enabled) {
@@ -450,9 +409,10 @@ angular.module('kibana.map2', [])
+ //If this is a sphere, set up drag and keypress listeners
+ //@todo implement a global "keypress service", since this fails if there are >1 spheres
if (scope.panel.display.data.type === 'orthographic') {
- //set up some key listeners for our sphere dragging
window.focus();
d3.select(window)
.on("keydown", function() {
@@ -465,35 +425,32 @@ angular.module('kibana.map2', [])
scope.svg.style("cursor", "move")
.call(d3.behavior.drag()
- .origin(function() { var rotate = projection.rotate(); return {x: 2 * rotate[0], y: -2 * rotate[1]}; })
+ .origin(function() { var rotate = scope.projection.rotate(); return {x: 2 * rotate[0], y: -2 * rotate[1]}; })
.on("drag", function() {
if ( scope.ctrlKey) {
- projection.rotate([d3.event.x / 2, -d3.event.y / 2, projection.rotate()[2]]);
+ scope.projection.rotate([d3.event.x / 2, -d3.event.y / 2, scope.projection.rotate()[2]]);
scope.svg.selectAll("path").attr("d", path);
- //displayBinning(scope, dimensions, projection, path);
}
}));
}
- /**
- * Display Options
- */
- //Hexagonal Binning
+ /**
+ * Display option rendering
+ * Order is important to render order here!
+ */
//@todo fix this
var dimensions = [width, height];
- displayBinning(scope, dimensions, scope.projection, path);
+ displayBinning(scope, dimensions);
displayGeopoints(scope, path);
- displayBullseye(scope, scope.projection, path);
+ displayBullseye(scope, path);
- //d3.select(elem[0]).select(".loading").remove();
- /**
- * Zoom Functionality
- */
+ //If the panel scale is not default (e.g. the user has moved the maps around)
+ //set the scale and position to the last saved config
if (scope.panel.display.scale != -1) {
scope.zoom.scale(scope.panel.display.scale).translate(scope.panel.display.translate);
scope.g.style("stroke-width", 1 / scope.panel.display.scale).attr("transform", "translate(" + scope.panel.display.translate + ") scale(" + scope.panel.display.scale + ")");
@@ -502,6 +459,12 @@ angular.module('kibana.map2', [])
}
+
+ /**
+ * On D3 zoom events, pan/zoom the map
+ * Only applies if the ctrl-key is not pressed, so it doesn't clobber
+ * sphere dragging
+ */
function translate_map() {
var width = $(elem[0]).width(),