Kaynağa Gözat

Cleaning up misconceptions with d3

Zachary Tong 12 yıl önce
ebeveyn
işleme
88609fbe87

+ 40 - 26
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;
 
-    } else {
+            _.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;
+        }
 
-        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 {
+        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);
-}
+
+    hex.exit().remove();
+}

+ 14 - 17
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] + ")");
-*/
 
 }

+ 31 - 12
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")

+ 50 - 42
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', [])
 
 
 
-
-
-                // Receive render events
-                scope.$on('render', function () {
-                    console.log("$on render");
-
+                scope.init_or_render = function() {
                     if (typeof scope.svg === 'undefined') {
                         console.log("init");
-                        init_panel();
+
+                        //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");
+                    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();