Zachary Tong 12 лет назад
Родитель
Сommit
1dcf9d6564

+ 17 - 16
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);
 

+ 5 - 6
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)();

+ 8 - 36
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;
-
-     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 = [];
+    var circle = d3.geo.circle();
+    var degrees = 180 / Math.PI
 
-    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);

+ 44 - 81
panels/map2/module.js

@@ -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
+                     * Display option rendering
+                     * Order is important to render order here!
                      */
 
-                    //Hexagonal Binning
-
                     //@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(),