From 88609fbe877eb52488600a4530f7b9059fd1e703 Mon Sep 17 00:00:00 2001 From: Zachary Tong Date: Wed, 10 Apr 2013 15:05:31 -0400 Subject: [PATCH] Cleaning up misconceptions with d3 --- panels/map2/display/binning.js | 66 +++++++++++++--------- panels/map2/display/bullseye.js | 31 +++++------ panels/map2/display/geopoints.js | 43 +++++++++++---- panels/map2/module.js | 94 +++++++++++++++++--------------- 4 files changed, 136 insertions(+), 98 deletions(-) diff --git a/panels/map2/display/binning.js b/panels/map2/display/binning.js index 49dff5e1280..f44258158c2 100644 --- a/panels/map2/display/binning.js +++ b/panels/map2/display/binning.js @@ -1,6 +1,7 @@ function displayBinning(scope, dimensions, projection, path) { + /** * Hexbin-specific setup */ @@ -9,40 +10,51 @@ function displayBinning(scope, dimensions, projection, path) { .radius(scope.panel.display.binning.hexagonSize); - var binPoints = []; + var binPoints = [], + binnedPoints = [], + binRange = 0; - //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; - _.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])); - }) - }); + 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 + if (scope.panel.display.binning.areaEncodingField === 'secondary') { + var max = Math.max.apply(Math, _.map(scope.data, function(k,v){return k;})), + scale = 50/max; + _.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])); + }) + }); + + } else { + + binPoints = scope.projectedPoints; + } + + //bin and sort the points, so we can set the various ranges appropriately + binnedPoints = hexbin(binPoints).sort(function(a, b) { return b.length - a.length; }); + binRange = binnedPoints[0].length; + + //clean up some memory + binPoints = []; } else { - - binPoints = scope.projectedPoints; + binnedPoints = []; + binRange = 0; } - //bin and sort the points, so we can set the various ranges appropriately - var binnedPoints = hexbin(binPoints).sort(function(a, b) { return b.length - a.length; });; - //clean up some memory - binPoints = []; var radius = d3.scale.sqrt() - .domain([0, binnedPoints[0].length]) + .domain([0, binRange]) .range([0, scope.panel.display.binning.hexagonSize]); var color = d3.scale.linear() - .domain([0,binnedPoints[0].length]) + .domain([0,binRange]) .range(["white", "steelblue"]) .interpolate(d3.interpolateLab); @@ -51,10 +63,10 @@ function displayBinning(scope, dimensions, projection, path) { * D3 Drawing */ + var hex = scope.g.selectAll(".hexagon") + .data(binnedPoints); - scope.g.selectAll("hexagon") - .data(binnedPoints) - .enter().append("path") + hex.enter().append("path") .attr("d", function (d) { if (scope.panel.display.binning.areaEncoding === false) { return hexbin.hexagon(); @@ -74,4 +86,6 @@ function displayBinning(scope, dimensions, projection, path) { } }) .attr("opacity", scope.panel.display.binning.hexagonAlpha); -} \ No newline at end of file + + hex.exit().remove(); +} diff --git a/panels/map2/display/bullseye.js b/panels/map2/display/bullseye.js index b4c5b30081e..539bdbac50d 100644 --- a/panels/map2/display/bullseye.js +++ b/panels/map2/display/bullseye.js @@ -5,30 +5,27 @@ function displayBullseye(scope, projection, path) { var circle = d3.geo.circle(); - var data = [ - {lat: parseFloat(scope.panel.display.bullseye.coord.lat), lon: parseFloat(scope.panel.display.bullseye.coord.lon)} - ]; + var data = []; - scope.g.selectAll("arc") - .data(data) - .enter().append("path") + if (scope.panel.display.bullseye.enabled) { + data = [ + {lat: parseFloat(scope.panel.display.bullseye.coord.lat), lon: parseFloat(scope.panel.display.bullseye.coord.lon)} + ]; + } + + + 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)(); }) .attr("d", path) .attr("class", "arc"); + arcs.exit().remove(); - /* - scope.g.append("path") - .attr("d", arc) - .attr("transform", "translate(" + coords[0] + "," + coords[1] + ")"); - - scope.g - .append("circle") - .attr("r", 1) - .attr("opacity", 1) - .attr("transform", "translate(" + coords[0] + "," + coords[1] + ")"); -*/ } \ No newline at end of file diff --git a/panels/map2/display/geopoints.js b/panels/map2/display/geopoints.js index 06956a82dd5..d181a2e0cd2 100644 --- a/panels/map2/display/geopoints.js +++ b/panels/map2/display/geopoints.js @@ -1,29 +1,48 @@ function displayGeopoints(scope, path) { /* - scope.g.selectAll("circles.points") - .data(points) - .enter() - .append("circle") - .attr("r", scope.panel.display.geopoints.pointSize) - .attr("opacity", scope.panel.display.geopoints.pointAlpha) - .attr("transform", function (d) { - return "translate(" + d[0] + "," + d[1] + ")"; - }); + 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.bullseye.enabled) { + if (scope.panel.display.geopoints.enabled) { points = scope.points; } var circle = d3.geo.circle(); var degrees = 180 / Math.PI - var geopoints = scope.g.selectAll("geopoints") + var geopoints = scope.g.selectAll(".geopoint") .data(points); geopoints.enter().append("path") diff --git a/panels/map2/module.js b/panels/map2/module.js index cbe61eea6ed..c5d31b3a67b 100644 --- a/panels/map2/module.js +++ b/panels/map2/module.js @@ -122,6 +122,7 @@ angular.module('kibana.map2', []) _.each(results.facets.map.terms, function (v) { + var metric = 'count'; //If it is a Term facet, use count, otherwise use Total @@ -190,7 +191,7 @@ angular.module('kibana.map2', []) //elem.html('') - + scope.initializing = false; scope.worldData = null; scope.worldNames = null; @@ -228,39 +229,38 @@ angular.module('kibana.map2', []) + scope.init_or_render = function() { + if (typeof scope.svg === 'undefined') { + console.log("init"); + + //prevent duplicate initialization steps, if render is called again + //before the svg is setup + if (!scope.initializing) { + init_panel(); + } + } else { + console.log("render"); + render_panel(); + } + }; // Receive render events scope.$on('render', function () { console.log("$on render"); - - if (typeof scope.svg === 'undefined') { - console.log("init"); - init_panel(); - } else { - console.log("render"); - render_panel(); - } + scope.init_or_render(); }); // Or if the window is resized angular.element(window).bind('resize', function () { console.log("resize render"); - - if (typeof scope.svg === 'undefined') { - console.log("init"); - init_panel(); - } else { - console.log("render"); - render_panel(); - } - - + scope.init_or_render(); }); function init_panel() { + scope.initializing = true; // Using LABjs, wait until all scripts are loaded before rendering panel var scripts = $LAB.script("panels/map2/lib/d3.v3.min.js?rand="+Math.floor(Math.random()*10000)) .script("panels/map2/lib/topojson.v1.min.js?rand="+Math.floor(Math.random()*10000)) @@ -334,16 +334,11 @@ angular.module('kibana.map2', []) scope.g = scope.svg.append("g"); - //Overlay is used so that the entire map is draggable, not just the locations - //where countries are - scope.svg.append("rect") - .attr("class", "overlay") - .attr("width", width) - .attr("height", height) - .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); + console.log("finished initing"); + scope.initializing = false; render_panel(); }); @@ -399,8 +394,8 @@ angular.module('kibana.map2', []) var decoded = geohash.decode(v); return [decoded.longitude, decoded.latitude]; }); - scope.projectedPoints = _.map(scope.points, function (k, v) { - return scope.projection(v); + scope.projectedPoints = _.map(scope.points, function (coords) { + return scope.projection(coords); }); @@ -410,14 +405,29 @@ angular.module('kibana.map2', []) + + //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") + .attr("class", "overlay") + .attr("width", width) + .attr("height", height) + .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); //Draw the countries, if this is a choropleth, draw with fancy colors - scope.g.selectAll("countries") - .data(scope.countries) - .enter().append("path") + var countryPath = scope.g.selectAll(".land") + .data(scope.countries); + + + + countryPath.enter().append("path") .attr("class", function(d) { if (scope.panel.display.choropleth.enabled) { return 'land ' + scope.quantize(scope.data[d.short]); @@ -427,11 +437,16 @@ angular.module('kibana.map2', []) }) .attr("d", path); + countryPath.exit().remove(); + + + /* //draw boundaries scope.g.selectAll("land").append("path") .datum(topojson.mesh(scope.worldData, scope.worldData.objects.land, function(a, b) { return a !== b; })) .attr("class", "land boundary") .attr("d", path); + */ @@ -465,20 +480,13 @@ angular.module('kibana.map2', []) */ //Hexagonal Binning - if (scope.panel.display.binning.enabled) { - //@todo fix this - var dimensions = [width, height]; - displayBinning(scope, dimensions, scope.projection, path); - } - //Raw geopoints - //if (scope.panel.display.geopoints.enabled) { - displayGeopoints(scope, path); - //} + //@todo fix this + var dimensions = [width, height]; + displayBinning(scope, dimensions, scope.projection, path); + displayGeopoints(scope, path); + displayBullseye(scope, scope.projection, path); - //if (scope.panel.display.bullseye.enabled) { - displayBullseye(scope, scope.projection, path); - //} //d3.select(elem[0]).select(".loading").remove();