Cleaning up, commenting
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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)();
|
||||
|
||||
@@ -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);
|
||||
|
||||
+45
-82
@@ -186,7 +186,6 @@ angular.module('kibana.map2', [])
|
||||
.directive('map2', function () {
|
||||
return {
|
||||
restrict: 'A',
|
||||
template: '<div class="loading"><center><img src="common/img/load_big.gif" style="display:none"></center></div><div id="{{uuid}}"></div>',
|
||||
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(),
|
||||
|
||||
Reference in New Issue
Block a user