From a1fea1e6feec23937faee30850a4f745cedfd5cc Mon Sep 17 00:00:00 2001 From: Zachary Tong Date: Fri, 5 Apr 2013 09:40:48 -0400 Subject: [PATCH] Start of map settings, tab control --- panels/map2/editor.html | 44 +++- panels/map2/module.js | 525 ++++++++++++++++++++-------------------- 2 files changed, 300 insertions(+), 269 deletions(-) diff --git a/panels/map2/editor.html b/panels/map2/editor.html index a755e4fed9b..b5e9d3afc36 100644 --- a/panels/map2/editor.html +++ b/panels/map2/editor.html @@ -1,4 +1,4 @@ -
+
The map panel uses 2 letter country or US state codes to plot concentrations on a map. Darker terroritories mean more records matched that area. If multiple queries are sent from a single panel the first query will be displayed
@@ -21,15 +21,47 @@
Map
+
-
-
-

Static tab content A

-

Static tab content B

-
+
+
+

Display Options

+ +
+ + +
+ + + + + + + + +
+ + + + + +
+
+
+
Panel Spy
diff --git a/panels/map2/module.js b/panels/map2/module.js index 7ab19ee3eab..c55fef9ebeb 100644 --- a/panels/map2/module.js +++ b/panels/map2/module.js @@ -1,287 +1,286 @@ angular.module('kibana.map2', []) -.controller('map2', function($scope, eventBus) { - - // Set and populate defaults - var _d = { - query : "*", - map : "world", - colors : ['#C8EEFF', '#0071A4'], - size : 1000, - exclude : [], - spyable : true, - group : "default", - index_limit : 0 - } - - _.defaults($scope.panel,_d) - - console.log("$scope.panel", $scope.panel); - console.log("_d", _d); - $scope.init = function() { - console.log("init"); - eventBus.register($scope,'time', function(event,time){set_time(time)}); - eventBus.register($scope,'query', function(event, query) { - $scope.panel.query = _.isArray(query) ? query[0] : query; - $scope.get_data(); - }); - // Now that we're all setup, request the time from our group - eventBus.broadcast($scope.$id,$scope.panel.group,'get_time') - } - - $scope.isNumber = function(n) { - return !isNaN(parseFloat(n)) && isFinite(n); - } - - $scope.get_data = function() { - - console.log("get_data"); - // Make sure we have everything for the request to complete - if(_.isUndefined($scope.panel.index) || _.isUndefined($scope.time)) - return - - - $scope.panel.loading = true; - var request = $scope.ejs.Request().indices($scope.panel.index); - - - var facet = $scope.ejs.TermsFacet('map') - .field($scope.panel.field) - .size(1000) - .exclude($scope.panel.exclude) - .facetFilter(ejs.QueryFilter( - ejs.FilteredQuery( - ejs.QueryStringQuery($scope.panel.query || '*'), - ejs.RangeFilter($scope.time.field) - .from($scope.time.from) - .to($scope.time.to) - ))); - - - - - - - // Then the insert into facet and make the request - var request = request.facet(facet).size(0); - - $scope.populate_modal(request); - - var results = request.doSearch(); - - // Populate scope when we have results - results.then(function(results) { - $scope.panel.loading = false; - $scope.hits = results.hits.total; - $scope.data = {}; - console.log("results",results); - _.each(results.facets.map.terms, function(v) { - - //FIX THIS - if (!$scope.isNumber(v.term)) { - $scope.data[v.term.toUpperCase()] = v.count; - } else { - $scope.data[v.term] = v.count; - } - - }); - - console.log("emit render"); - $scope.$emit('render') - }); - - } - - // I really don't like this function, too much dom manip. Break out into directive? - $scope.populate_modal = function(request) { - $scope.modal = { - title: "Inspector", - body : "
Last Elasticsearch Query
"+
-          'curl -XGET '+config.elasticsearch+'/'+$scope.panel.index+"/_search?pretty -d'\n"+
-          angular.toJson(JSON.parse(request.toString()),true)+
-        "'
", - } - } - - function set_time(time) { - $scope.time = time; - $scope.panel.index = _.isUndefined(time.index) ? $scope.panel.index : time.index - $scope.get_data(); - } - - $scope.build_search = function(field,value) { - $scope.panel.query = add_to_query($scope.panel.query,field,value,false) - $scope.get_data(); - eventBus.broadcast($scope.$id,$scope.panel.group,'query',$scope.panel.query); - } - -}) -.directive('map2', function() { - return { - restrict: 'A', - link: function(scope, elem, attrs) { - - elem.html('
') - - // Receive render events - scope.$on('render',function(){ - console.log("render"); - render_panel(); - }); - - // Or if the window is resized - angular.element(window).bind('resize', function(){ - console.log("resize"); - render_panel(); - }); - - function render_panel() { - - console.log("render_panel"); - console.log(scope.panel); - console.log(elem); - - - // Using LABjs, wait until all scripts are loaded before rendering panel - var scripts = $LAB.script("panels/map2/lib/d3.v3.min.js") - .script("panels/map2/lib/topojson.v0.min.js") - .script("panels/map2/lib/node-geohash.js") - .script("panels/map2/lib/d3.hexbin.v0.min.js"); - - // Populate element. Note that jvectormap appends, does not replace. - scripts.wait(function(){ - elem.text(''); - - - - //Better way to get these values? Seems kludgy to use jQuery on the div... - var width = $(elem[0]).width(), - height = $(elem[0]).height(); - - console.log("draw map", width, height); - - //Scale the map by whichever dimension is the smallest, helps to make sure the whole map is shown - var scale = (width > height) ? (height / 2 / Math.PI) : (width / 2 / Math.PI); - - - var projection = d3.geo.mercator() - .translate([0, 0]) - .scale(scale); - - var zoom = d3.behavior.zoom() - .scaleExtent([1, 8]) - .on("zoom", move); - - var path = d3.geo.path() - .projection(projection); - - - var svg = d3.select(elem[0]).append("svg") - .attr("width", width) - .attr("height", height) - .append("g") - .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")") - .call(zoom); - - var g = svg.append("g"); - - svg.append("rect") - .attr("class", "overlay") - .attr("x", -width / 2) - .attr("y", -height / 2) - .attr("width", width) - .attr("height", height); - - d3.json("panels/map2/lib/world-50m.json", function(error, world) { - g.append("path") - .datum(topojson.object(world, world.objects.countries)) - .attr("class", "land") - .attr("d", path); - - g.append("path") - .datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; })) - .attr("class", "boundary") - .attr("d", path); - - - - - - - - - var points = _.map(scope.data, function (k,v) { - var decoded = geohash.decode(v); - return projection([decoded.longitude, decoded.latitude]); - }) - - var color = d3.scale.linear() - .domain([0, 20]) - .range(["white", "steelblue"]) - .interpolate(d3.interpolateLab); - - var hexbin = d3.hexbin() - .size([width, height]) - .radius(10); - - - + .controller('map2', function ($scope, eventBus) { + + // Set and populate defaults + var _d = { + query: "*", + map: "world", + colors: ['#C8EEFF', '#0071A4'], + size: 1000, + exclude: [], + spyable: true, + group: "default", + index_limit: 0, + display: { + geopoints: { + enabled: true, + enabledText: "Enabled" + }, + binning: { + + } + }, + displayTabs: ["Geopoints", "Binning"], + activeDisplayTab:"Geopoints" + }; + + _.defaults($scope.panel, _d) + + $scope.init = function () { + console.log("init"); + eventBus.register($scope, 'time', function (event, time) { + set_time(time) + }); + eventBus.register($scope, 'query', function (event, query) { + $scope.panel.query = _.isArray(query) ? query[0] : query; + $scope.get_data(); + }); + // Now that we're all setup, request the time from our group + eventBus.broadcast($scope.$id, $scope.panel.group, 'get_time') + }; - g.selectAll(".hexagon") - .data(hexbin(points)) - .enter().append("path") - .attr("d", function(d) { return hexbin.hexagon(); }) - .attr("class", "hexagon") - .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) - .style("fill", function(d) { console.log(d); return color(d.length); }) - .attr("opacity", 1); + $scope.isNumber = function (n) { + return !isNaN(parseFloat(n)) && isFinite(n); + }; + $scope.get_data = function () { + console.log("get_data"); + // Make sure we have everything for the request to complete + if (_.isUndefined($scope.panel.index) || _.isUndefined($scope.time)) + return - /* + $scope.panel.loading = true; + var request = $scope.ejs.Request().indices($scope.panel.index); - raw, ugly points - */ - var points = _.map(scope.data, function (k,v) { - var decoded = geohash.decode(v); - return {lat: decoded.latitude, lon: decoded.longitude}; - }) + var facet = $scope.ejs.TermsFacet('map') + .field($scope.panel.field) + .size(1000) + .exclude($scope.panel.exclude) + .facetFilter(ejs.QueryFilter( + ejs.FilteredQuery( + ejs.QueryStringQuery($scope.panel.query || '*'), + ejs.RangeFilter($scope.time.field) + .from($scope.time.from) + .to($scope.time.to)))); - g.selectAll("circles.points") - .data(points) - .enter() - .append("circle") - .attr("r",1) - .attr("transform", function(d) {return "translate(" + projection([d.lon,d.lat]) + ")";}); + // Then the insert into facet and make the request + var request = request.facet(facet).size(0); + $scope.populate_modal(request); + var results = request.doSearch(); + // Populate scope when we have results + results.then(function (results) { + $scope.panel.loading = false; + $scope.hits = results.hits.total; + $scope.data = {}; + _.each(results.facets.map.terms, function (v) { + //FIX THIS + if (!$scope.isNumber(v.term)) { + $scope.data[v.term.toUpperCase()] = v.count; + } else { + $scope.data[v.term] = v.count; + } + }); + console.log("emit render"); + $scope.$emit('render') }); + }; - - - - - function move() { - var t = d3.event.translate, - s = d3.event.scale; - t[0] = Math.min(width / 2 * (s - 1), Math.max(width / 2 * (1 - s), t[0])); - t[1] = Math.min(height / 2 * (s - 1) + 230 * s, Math.max(height / 2 * (1 - s) - 230 * s, t[1])); - zoom.translate(t); - g.style("stroke-width", 1 / s).attr("transform", "translate(" + t + ")scale(" + s + ")"); + // I really don't like this function, too much dom manip. Break out into directive? + $scope.populate_modal = function (request) { + $scope.modal = { + title: "Inspector", + body: "
Last Elasticsearch Query
" + 'curl -XGET ' + config.elasticsearch + '/' + $scope.panel.index + "/_search?pretty -d'\n" + angular.toJson(JSON.parse(request.toString()), true) + "'
", } + }; + function set_time(time) { + $scope.time = time; + $scope.panel.index = _.isUndefined(time.index) ? $scope.panel.index : time.index + $scope.get_data(); + } + $scope.build_search = function (field, value) { + $scope.panel.query = add_to_query($scope.panel.query, field, value, false) + $scope.get_data(); + eventBus.broadcast($scope.$id, $scope.panel.group, 'query', $scope.panel.query); + }; + $scope.isActive = function(tab) { + return (tab.toLowerCase() === $scope.panel.activeDisplayTab.toLowerCase()); + } + $scope.tabClick = function(tab) { + $scope.panel.activeDisplayTab = tab; + } + }) + .filter('enabledText', function() { + return function (value) { + console.log(value); + if (value === true) { + return "Enabled"; + } else { + return "Disabled"; + } + } + }) + .directive('map2', function () { + return { + restrict: 'A', + link: function (scope, elem, attrs) { + + elem.html('
') + + // Receive render events + scope.$on('render', function () { + console.log("render"); + render_panel(); + }); + + // Or if the window is resized + angular.element(window).bind('resize', function () { + console.log("resize"); + render_panel(); + }); + + function render_panel() { + console.log("render_panel"); + console.log(scope.panel); + console.log(elem); + + // Using LABjs, wait until all scripts are loaded before rendering panel + var scripts = $LAB.script("panels/map2/lib/d3.v3.min.js") + .script("panels/map2/lib/topojson.v0.min.js") + .script("panels/map2/lib/node-geohash.js") + .script("panels/map2/lib/d3.hexbin.v0.min.js"); + + // Populate element. Note that jvectormap appends, does not replace. + scripts.wait(function () { + elem.text(''); + + //Better way to get these values? Seems kludgy to use jQuery on the div... + var width = $(elem[0]).width(), + height = $(elem[0]).height(); + + console.log("draw map", width, height); + + //Scale the map by whichever dimension is the smallest, helps to make sure the whole map is shown + var scale = (width > height) ? (height / 2 / Math.PI) : (width / 2 / Math.PI); + + var projection = d3.geo.mercator() + .translate([0, 0]) + .scale(scale); + + var zoom = d3.behavior.zoom() + .scaleExtent([1, 8]) + .on("zoom", move); + + var path = d3.geo.path() + .projection(projection); + + var svg = d3.select(elem[0]).append("svg") + .attr("width", width) + .attr("height", height) + .append("g") + .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")") + .call(zoom); + + var g = svg.append("g"); + + svg.append("rect") + .attr("class", "overlay") + .attr("x", -width / 2) + .attr("y", -height / 2) + .attr("width", width) + .attr("height", height); + + d3.json("panels/map2/lib/world-50m.json", function (error, world) { + g.append("path") + .datum(topojson.object(world, world.objects.countries)) + .attr("class", "land") + .attr("d", path); + + g.append("path") + .datum(topojson.mesh(world, world.objects.countries, function (a, b) { + return a !== b; + })) + .attr("class", "boundary") + .attr("d", path); + + var points = _.map(scope.data, function (k, v) { + var decoded = geohash.decode(v); + return projection([decoded.longitude, decoded.latitude]); + }); + + var color = d3.scale.linear() + .domain([0, 20]) + .range(["white", "steelblue"]) + .interpolate(d3.interpolateLab); + + var hexbin = d3.hexbin() + .size([width, height]) + .radius(10); + + g.selectAll(".hexagon") + .data(hexbin(points)) + .enter().append("path") + .attr("d", function (d) { + return hexbin.hexagon(); + }) + .attr("class", "hexagon") + .attr("transform", function (d) { + return "translate(" + d.x + "," + d.y + ")"; + }) + .style("fill", function (d) { + return color(d.length); + }) + .attr("opacity", 1); + + /* + + raw, ugly points + */ + + var points = _.map(scope.data, function (k, v) { + var decoded = geohash.decode(v); + return { + lat: decoded.latitude, + lon: decoded.longitude + }; + }); + + g.selectAll("circles.points") + .data(points) + .enter() + .append("circle") + .attr("r", 1) + .attr("transform", function (d) { + return "translate(" + projection([d.lon, d.lat]) + ")"; + }); + + }); + + function move() { + var t = d3.event.translate, + s = d3.event.scale; + t[0] = Math.min(width / 2 * (s - 1), Math.max(width / 2 * (1 - s), t[0])); + t[1] = Math.min(height / 2 * (s - 1) + 230 * s, Math.max(height / 2 * (1 - s) - 230 * s, t[1])); + zoom.translate(t); + g.style("stroke-width", 1 / s).attr("transform", "translate(" + t + ")scale(" + s + ")"); + } + + }) + } - }) - } - } - }; -}); \ No newline at end of file + } + }; + }); \ No newline at end of file