Ver código fonte

Fix bug with duplicate svgs being appended

Zachary Tong 12 anos atrás
pai
commit
37161ba37a

+ 4 - 4
panels/map2/display/binning.js

@@ -1,10 +1,10 @@
 
-function displayBinning() {
+function displayBinning(scope, dimensions, projection) {
     /**
      * Hexbin-specific setup
      */
     var hexbin = d3.hexbin()
-        .size([width, height])
+        .size(dimensions)
         .radius(scope.panel.display.binning.hexagonSize);
 
 
@@ -16,7 +16,7 @@ function displayBinning() {
     //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 = 10/max;
+            scale = 50/max;
 
         _.map(scope.data, function (k, v) {
             var decoded = geohash.decode(v);
@@ -50,7 +50,7 @@ function displayBinning() {
      */
 
 
-    g.selectAll(".hexagon")
+    scope.g.selectAll(".hexagon")
         .data(binnedPoints)
         .enter().append("path")
         .attr("d", function (d) {

+ 2 - 2
panels/map2/display/geopoints.js

@@ -1,5 +1,5 @@
-function displayGeopoints() {
-    g.selectAll("circles.points")
+function displayGeopoints(scope) {
+    scope.g.selectAll("circles.points")
         .data(points)
         .enter()
         .append("circle")

+ 247 - 204
panels/map2/editor.html

@@ -8,230 +8,273 @@
         padding-bottom:10px;
     }
 </style>
-<div class="row-fluid" ng-controller="map2">
-    <div class="span11">
-        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 <strong>first query will be
-        displayed</strong>
+<div ng-controller="map2">
+    <div class="row-fluid" >
+        <div class="span11">
+            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 <strong>first query will be
+            displayed</strong>
+        </div>
     </div>
-</div>
 
 
-<div class="row-fluid">
-    <div class="span10">
-        <form class="form-horizontal">
-            <div class="control-group">
-                <label class="control-label" for="panelfield">Primary Field</label>
-                <div class="controls">
-                    <input type="text" id="panelfield" class="input"
-                           ng-model="panel.field"
-                           ng-change="get_data()" />
+    <div class="row-fluid">
+        <div class="span10">
+            <form class="form-horizontal">
+                <div class="control-group">
+                    <label class="control-label" for="panelfield">Primary Field</label>
+                    <div class="controls">
+                        <input type="text" id="panelfield" class="input"
+                               ng-model="panel.field"
+                               ng-change="get_data()" />
+                    </div>
                 </div>
-            </div>
-            <div class="control-group">
-                <label class="control-label" for="panelsecondaryfield">Secondary Field</label>
-                <div class="controls">
-                    <input type="text" id="panelsecondaryfield" class="input"
-                           ng-model="panel.secondaryfield"
-                           ng-change="get_data()"
-                           data-placement="right"
-                           placeholder="Optional"
-                           bs-tooltip="'Allows aggregating on Primary field, while counting stats on a secondary (e.g. Group By user_id, Sum(purchase_price)).'" />
+                <div class="control-group">
+                    <label class="control-label" for="panelsecondaryfield">Secondary Field</label>
+                    <div class="controls">
+                        <input type="text" id="panelsecondaryfield" class="input"
+                               ng-model="panel.secondaryfield"
+                               ng-change="get_data()"
+                               data-placement="right"
+                               placeholder="Optional"
+                               bs-tooltip="'Allows aggregating on Primary field, while counting stats on a secondary (e.g. Group By user_id, Sum(purchase_price)).'" />
+                    </div>
                 </div>
-            </div>
-            <div class="control-group">
-                <label class="control-label" for="panelquery">Query</label>
-                <div class="controls">
-                    <input type="text" id="panelquery" class="input" ng-model="panel.query">
+                <div class="control-group">
+                    <label class="control-label" for="panelquery">Query</label>
+                    <div class="controls">
+                        <input type="text" id="panelquery" class="input" ng-model="panel.query">
+                    </div>
                 </div>
-            </div>
-        </form>
-    </div>
-</div>
-<div class="row-fluid">
-    <div class="span11">
-        <h4>Display Options</h4>
+            </form>
+        </div>
     </div>
-    <!--
-        Rolling our own tab control here because the Angular-Strap Tab directive doesn't allow
-        updates to components inside, which is quite bizarre.  Or I just can't figure it out...
-    -->
+    <div class="row-fluid">
+        <div class="span11">
+            <h4>Display Options</h4>
+        </div>
+        <!--
+            Rolling our own tab control here because the Angular-Strap Tab directive doesn't allow
+            updates to components inside, which is quite bizarre.  Or I just can't figure it out...
+        -->
 
-    <div class="span11">
-        <ul class="nav nav-tabs" ng-cloak="">
-            <li ng-repeat="tab in panel.displayTabs" ng-class="{active:isActive(tab)}">
-                <a ng-click="tabClick(tab)">{{tab}}</a>
-            </li>
-        </ul>
+        <div class="span11">
+            <ul class="nav nav-tabs" ng-cloak="">
+                <li ng-repeat="tab in panel.displayTabs" ng-class="{active:isActive(tab)}">
+                    <a ng-click="tabClick(tab)">{{tab}}</a>
+                </li>
+            </ul>
+        </div>
     </div>
-</div>
 
-<div class="row-fluid tabDetails" ng-show="isActive('geopoints')">
-    <div class="span8 offset1">
-        <table>
-            <tbody  >
-            <tr>
-                <td>Geopoints</td>
-                <td>
-                    <button type="button" class="btn" bs-button
-                        ng-change="$emit('render')"
-                        ng-class="{'btn-success': panel.display.geopoints.enabled}"
-                        ng-model="panel.display.geopoints.enabled">{{panel.display.geopoints.enabled|enabledText}}</button>
-                </td>
-            </tr>
-            <tr>
-                <td>Point size</td>
-                <td>
-                    <input type="text" style="width:100px"
-                        ng-change="$emit('render')"
-                        data-placement="right"
-                        bs-tooltip="'Controls the size of the geopoints on the map'"
-                        ng-model="panel.display.geopoints.pointSize"
-                        value="{{panel.display.geopoints.pointSize}}" />
-                </td>
-            </tr>
-            <tr>
-                <td>Point Transparency</td>
-                <td>
-                    <input type="text" style="width:100px"
-                           ng-change="$emit('render')"
-                           data-placement="right"
-                           bs-tooltip="'Controls the transparency of geopoints. Valid numbers are between 0.0 and 1.0'"
-                           ng-model="panel.display.geopoints.pointAlpha"
-                           value="{{panel.display.geopoints.pointAlpha}}" />
-                </td>
-            </tr>
-            <tr>
-                <td>Autosizing</td>
-                <td>
-                    <input type="checkbox"
-                           ng-change="$emit('render')"
-                           data-placement="right"
-                           bs-tooltip="'Allows point sizes to scale as you zoom in and out of the map.'" />
-                </td>
-            </tr>
-            </tbody>
-        </table>
-    </div>
-</div>
-
-<div class="row-fluid tabDetails" ng-show="isActive('binning')">
-    <div class="span8 offset1">
-        <table>
-            <tbody  >
-            <tr>
-                <td>Binning</td>
-                <td>
-                    <button type="button" class="btn" bs-button
-                            ng-change="$emit('render')"
-                            ng-class="{'btn-success': panel.display.binning.enabled}"
-                            ng-model="panel.display.binning.enabled">{{panel.display.binning.enabled|enabledText}}</button>
-                </td>
-            </tr>
-            <tr>
-                <td>Hexagon size</td>
-                <td>
-                    <input type="text" style="width:100px"
-                           ng-change="$emit('render')"
-                           data-placement="right"
-                           bs-tooltip="'Controls the size of the hexagonal binning'"
-                           ng-model="panel.display.binning.hexagonSize"
-                           value="{{panel.display.binning.hexagonSize}}" />
-                </td>
-            </tr>
-            <tr>
-                <td>Hexagon Transparency</td>
-                <td>
-                    <input type="text" style="width:100px"
-                           ng-change="$emit('render')"
-                           data-placement="right"
-                           bs-tooltip="'Controls the transparency of hexagonal bins. Valid numbers are between 0.0 and 1.0'"
-                           ng-model="panel.display.binning.hexagonAlpha"
-                           value="{{panel.display.binning.hexagonAlpha}}" />
-                </td>
-            </tr>
-            <tr>
-                <td>
-                    <button type="button" class="btn" bs-button
+    <div class="row-fluid tabDetails" ng-show="isActive('geopoints')">
+        <div class="span8 offset1">
+            <table>
+                <tbody  >
+                <tr>
+                    <td>Geopoints</td>
+                    <td>
+                        <button type="button" class="btn" bs-button
                             ng-change="$emit('render')"
-                            ng-class="{'btn-success': panel.display.binning.areaEncoding}"
-                            ng-model="panel.display.binning.areaEncoding">Area</button>
-                </td>
-                <td>
-                    <div class="btn-group" ng-model="panel.display.binning.areaEncodingField" bs-buttons-radio ng-change="$emit('render')">
-                        <button type="button" class="btn" value="primary">Primary Field</button>
-                        <button type="button" class="btn" value="secondary">Secondary Field</button>
-                    </div>
-                </td>
-            </tr>
-            <tr>
-                <td>
-                    <button type="button" class="btn" bs-button
+                            ng-class="{'btn-success': panel.display.geopoints.enabled}"
+                            ng-model="panel.display.geopoints.enabled">{{panel.display.geopoints.enabled|enabledText}}</button>
+                    </td>
+                </tr>
+                <tr>
+                    <td>Point size</td>
+                    <td>
+                        <input type="text" style="width:100px"
                             ng-change="$emit('render')"
-                            ng-class="{'btn-success': panel.display.binning.colorEncoding}"
-                            ng-model="panel.display.binning.colorEncoding">Color</button>
-                </td>
-                <td>
-                    <div class="btn-group" ng-model="panel.display.binning.colorEncodingField" bs-buttons-radio ng-change="$emit('render')">
-                        <button type="button" class="btn" value="primary">Primary Field</button>
-                        <button type="button" class="btn" value="secondary">Secondary Field</button>
-                    </div>
-                </td>
-            </tr>
-            </tbody>
-        </table>
+                            data-placement="right"
+                            bs-tooltip="'Controls the size of the geopoints on the map'"
+                            ng-model="panel.display.geopoints.pointSize"
+                            value="{{panel.display.geopoints.pointSize}}" />
+                    </td>
+                </tr>
+                <tr>
+                    <td>Point Transparency</td>
+                    <td>
+                        <input type="text" style="width:100px"
+                               ng-change="$emit('render')"
+                               data-placement="right"
+                               bs-tooltip="'Controls the transparency of geopoints. Valid numbers are between 0.0 and 1.0'"
+                               ng-model="panel.display.geopoints.pointAlpha"
+                               value="{{panel.display.geopoints.pointAlpha}}" />
+                    </td>
+                </tr>
+                <tr>
+                    <td>Autosizing</td>
+                    <td>
+                        <input type="checkbox"
+                               ng-change="$emit('render')"
+                               data-placement="right"
+                               ng-model="panel.display.geopoints.autosize"
+                               bs-tooltip="'Allows point sizes to scale as you zoom in and out of the map.'" />
+                    </td>
+                </tr>
+                </tbody>
+            </table>
+        </div>
     </div>
-</div>
 
-<div class="row-fluid tabDetails" ng-show="isActive('choropleth')">
-    <div class="span8 offset1">
-        <table>
-            <tbody  >
-            <tr>
-                <td>Choropleth</td>
-                <td>
-                    <button type="button" class="btn" bs-button
-                            ng-change="$emit('render')"
-                            ng-class="{'btn-success': panel.display.choropleth.enabled}"
-                            ng-model="panel.display.choropleth.enabled">{{panel.display.choropleth.enabled|enabledText}}</button>
-                </td>
-            </tr>
-            </tbody>
-        </table>
+    <div class="row-fluid tabDetails" ng-show="isActive('binning')">
+        <div class="span8 offset1">
+            <table>
+                <tbody  >
+                <tr>
+                    <td>Binning</td>
+                    <td>
+                        <button type="button" class="btn" bs-button
+                                ng-change="$emit('render')"
+                                ng-class="{'btn-success': panel.display.binning.enabled}"
+                                ng-model="panel.display.binning.enabled">{{panel.display.binning.enabled|enabledText}}</button>
+                    </td>
+                </tr>
+                <tr>
+                    <td>Hexagon size</td>
+                    <td>
+                        <input type="text" style="width:100px"
+                               ng-change="$emit('render')"
+                               data-placement="right"
+                               bs-tooltip="'Controls the size of the hexagonal binning'"
+                               ng-model="panel.display.binning.hexagonSize"
+                               value="{{panel.display.binning.hexagonSize}}" />
+                    </td>
+                </tr>
+                <tr>
+                    <td>Hexagon Transparency</td>
+                    <td>
+                        <input type="text" style="width:100px"
+                               ng-change="$emit('render')"
+                               data-placement="right"
+                               bs-tooltip="'Controls the transparency of hexagonal bins. Valid numbers are between 0.0 and 1.0'"
+                               ng-model="panel.display.binning.hexagonAlpha"
+                               value="{{panel.display.binning.hexagonAlpha}}" />
+                    </td>
+                </tr>
+                <tr>
+                    <td>
+                        <button type="button" class="btn" bs-button
+                                ng-change="$emit('render')"
+                                ng-class="{'btn-success': panel.display.binning.areaEncoding}"
+                                ng-model="panel.display.binning.areaEncoding">Area</button>
+                    </td>
+                    <td>
+                        <div class="btn-group" ng-model="panel.display.binning.areaEncodingField" bs-buttons-radio ng-change="$emit('render')">
+                            <button type="button" class="btn" value="primary">Primary Field</button>
+                            <button type="button" class="btn" value="secondary">Secondary Field</button>
+                        </div>
+                    </td>
+                </tr>
+                <tr>
+                    <td>
+                        <button type="button" class="btn" bs-button
+                                ng-change="$emit('render')"
+                                ng-class="{'btn-success': panel.display.binning.colorEncoding}"
+                                ng-model="panel.display.binning.colorEncoding">Color</button>
+                    </td>
+                    <td>
+                        <div class="btn-group" ng-model="panel.display.binning.colorEncodingField" bs-buttons-radio ng-change="$emit('render')">
+                            <button type="button" class="btn" value="primary">Primary Field</button>
+                            <button type="button" class="btn" value="secondary">Secondary Field</button>
+                        </div>
+                    </td>
+                </tr>
+                </tbody>
+            </table>
+        </div>
     </div>
-</div>
 
-<div class="row-fluid tabDetails" ng-show="isActive('data')">
-    <div class="span8 offset1">
-        <table>
-            <tbody  >
-            <tr>
-                <td>Data Points</td>
-                <td>
-                    <input type="text" style="width:100px"
-                           ng-change="get_data()"
-                           data-placement="right"
-                           bs-tooltip="'Controls the number of samples used in the map. Be careful with this value!'"
-                           ng-model="panel.display.data.samples"
-                           value="{{panel.display.data.samples}}" />
-                </td>
-            </tr>
-            </tbody>
-        </table>
+    <div class="row-fluid tabDetails" ng-show="isActive('choropleth')">
+        <div class="span8 offset1">
+            <table>
+                <tbody  >
+                <tr>
+                    <td>Choropleth</td>
+                    <td>
+                        <button type="button" class="btn" bs-button
+                                data-placement="right"
+                                bs-tooltip="'Choropleths color country regions according to your selected field. Only compatible with Country-Coded fields'"
+                                ng-change="$emit('render')"
+                                ng-class="{'btn-success': panel.display.choropleth.enabled}"
+                                ng-model="panel.display.choropleth.enabled">{{panel.display.choropleth.enabled|enabledText}}</button>
+                    </td>
+                </tr>
+                </tbody>
+            </table>
+        </div>
     </div>
-</div>
 
-<h5>Panel Spy</h5>
+    <div class="row-fluid tabDetails" ng-show="isActive('bullseye')">
+        <div class="span8 offset1">
+            <table>
+                <tbody  >
+                <tr>
+                    <td>Bullseye</td>
+                    <td>
+                        <button type="button" class="btn" bs-button
+                                ng-change="$emit('render')"
+                                ng-class="{'btn-success': panel.display.bullseye.enabled}"
+                                ng-model="panel.display.bullseye.enabled">{{panel.display.choropleth.enabled|enabledText}}</button>
+                    </td>
+                </tr>
+                <tr>
+                    <td>Bullseye Coordinates</td>
+                    <td>
+                        <input type="text" style="width:100px"
+                               ng-change="$emit('render')"
+                               placeholder="Latitude"
+                               data-placement="right"
+                               bs-tooltip="'Latitude of Bullseye'"
+                               ng-model="panel.display.bullseye.coord.lat"
+                               value="{{panel.display.bullseye.coord.lat}}" />
 
-<div class="row-fluid">
-    <div class="span2">
-        <label class="small">Spyable</label> <input type="checkbox" ng-model=
-            "panel.spyable" ng-checked="panel.spyable">
+                        <input type="text" style="width:100px"
+                               placeholder="Longitude"
+                               ng-change="$emit('render')"
+                               data-placement="right"
+                               bs-tooltip="'Longitude of Bullseye'"
+                               ng-model="panel.display.bullseye.coord.lon"
+                               value="{{panel.display.bullseye.coord.lon}}" />
+                    </td>
+                </tr>
+                </tbody>
+            </table>
+        </div>
     </div>
 
-    <div class="span9 small">
-        The panel spy shows 'behind the scenes' information about a panel. It can be
-        accessed by clicking the in the top right of the panel.
+    <div class="row-fluid tabDetails" ng-show="isActive('data')">
+        <div class="span8 offset1">
+            <table>
+                <tbody  >
+                <tr>
+                    <td>Data Points</td>
+                    <td>
+                        <input type="text" style="width:100px"
+                               ng-change="get_data()"
+                               data-placement="right"
+                               bs-tooltip="'Controls the number of samples used in the map. Be careful with this value!'"
+                               ng-model="panel.display.data.samples"
+                               value="{{panel.display.data.samples}}" />
+                    </td>
+                </tr>
+                </tbody>
+            </table>
+        </div>
+    </div>
+
+    <h5>Panel Spy</h5>
+
+    <div class="row-fluid">
+        <div class="span2">
+            <label class="small">Spyable</label> <input type="checkbox" ng-model=
+                "panel.spyable" ng-checked="panel.spyable">
+        </div>
+
+        <div class="span9 small">
+            The panel spy shows 'behind the scenes' information about a panel. It can be
+            accessed by clicking the in the top right of the panel.
+        </div>
     </div>
 </div>

+ 30 - 19
panels/map2/module.js

@@ -34,9 +34,12 @@ angular.module('kibana.map2', [])
                 },
                 choropleth: {
                     enabled: false
+                },
+                bullseye: {
+                    enabled: false
                 }
             },
-            displayTabs: ["Geopoints", "Binning", "Choropleth", "Data"],
+            displayTabs: ["Geopoints", "Binning", "Choropleth", "Bullseye", "Data"],
             activeDisplayTab:"Geopoints"
         };
 
@@ -138,7 +141,7 @@ angular.module('kibana.map2', [])
         $scope.populate_modal = function (request) {
             $scope.modal = {
                 title: "Inspector",
-                body: "<h5>Last Elasticsearch Query</h5><pre>" + 'curl -XGET ' + config.elasticsearch + '/' + $scope.panel.index + "/_search?pretty -d'\n" + angular.toJson(JSON.parse(request.toString()), true) + "'</pre>",
+                body: "<h5>Last Elasticsearch Query</h5><pre>" + 'curl -XGET ' + config.elasticsearch + '/' + $scope.panel.index + "/_search?pretty -d'\n" + angular.toJson(JSON.parse(request.toString()), true) + "'</pre>"
             }
         };
 
@@ -179,8 +182,10 @@ angular.module('kibana.map2', [])
 
                 elem.html('<center><img src="common/img/load_big.gif"></center>')
 
-                var worldData = null,
-                    worldNames = null;
+                scope.worldData = null;
+                scope.worldNames = null;
+                scope.svg = null;
+                scope.g = null;
 
                 // Receive render events
                 scope.$on('render', function () {
@@ -209,13 +214,13 @@ angular.module('kibana.map2', [])
 
                         //these files can take a bit of time to process, so save them in a variable
                         //and use those on redraw
-                        if (worldData === null || worldNames === null) {
+                        if (scope.worldData === null || scope.worldNames === null) {
                             queue()
                                 .defer(d3.json, "panels/map2/lib/world-110m.json")
                                 .defer(d3.tsv, "panels/map2/lib/world-country-names.tsv")
                                 .await(function(error, world, names) {
-                                    worldData = world;
-                                    worldNames = names;
+                                    scope.worldData = world;
+                                    scope.worldNames = names;
                                     ready();
                                 });
                         } else {
@@ -229,8 +234,10 @@ angular.module('kibana.map2', [])
                  */
                 function ready() {
 
-                    var world = worldData,
-                        names = worldNames;
+
+
+                    var world = scope.worldData,
+                        names = scope.worldNames;
 
                     //Better way to get these values?  Seems kludgy to use jQuery on the div...
                     var width = $(elem[0]).width(),
@@ -287,18 +294,21 @@ angular.module('kibana.map2', [])
                      * D3 SVG Setup
                      */
 
-                    var svg = d3.select(elem[0]).append("svg")
+                    //remove our old svg...is there a better way to update than remove/append?
+                    d3.select(elem[0]).select("svg").remove();
+
+                    //create the new svg
+                    scope.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");
+                    scope.g = scope.svg.append("g");
 
                     //Overlay is used so that the entire map is draggable, not just the locations
                     //where countries are
-                    svg.append("rect")
+                    scope.svg.append("rect")
                         .attr("class", "overlay")
                         .attr("x", -width / 2)
                         .attr("y", -height / 2)
@@ -306,7 +316,7 @@ angular.module('kibana.map2', [])
                         .attr("height", height);
 
                     //Draw the countries, if this is a choropleth, draw with fancy colors
-                    g.selectAll("path")
+                    scope.g.selectAll("path")
                         .data(countries)
                         .enter().append("path")
                         .attr("class", function(d) {
@@ -319,7 +329,7 @@ angular.module('kibana.map2', [])
                         .attr("d", path);
 
                     //draw boundaries
-                    g.append("path")
+                    scope.g.selectAll("land").append("path")
                         .datum(topojson.mesh(world, world.objects.land, function(a, b) { return a !== b; }))
                         .attr("class", "land boundary")
                         .attr("d", path);
@@ -332,12 +342,13 @@ angular.module('kibana.map2', [])
 
                     //Hexagonal Binning
                     if (scope.panel.display.binning.enabled) {
-                        displayBinning();
+                        var dimensions = [width, height];
+                        displayBinning(scope, dimensions, projection);
                     }
 
                     //Raw geopoints
                     if (scope.panel.display.geopoints.enabled) {
-                        displayGeopoints();
+                        displayGeopoints(scope);
                     }
 
 
@@ -346,7 +357,7 @@ angular.module('kibana.map2', [])
                      */
                     if (scope.panel.display.scale != -1) {
                         zoom.scale(scope.panel.display.scale).translate(scope.panel.display.translate);
-                        g.style("stroke-width", 1 / scope.panel.display.scale).attr("transform", "translate(" + scope.panel.display.translate + ") scale(" + scope.panel.display.scale + ")");
+                        scope.g.style("stroke-width", 1 / scope.panel.display.scale).attr("transform", "translate(" + scope.panel.display.translate + ") scale(" + scope.panel.display.scale + ")");
 
                     }
 
@@ -359,7 +370,7 @@ angular.module('kibana.map2', [])
 
                         scope.panel.display.translate = t;
                         scope.panel.display.scale = s;
-                        g.style("stroke-width", 1 / s).attr("transform", "translate(" + t + ")scale(" + s + ")");
+                        scope.g.style("stroke-width", 1 / s).attr("transform", "translate(" + t + ")scale(" + s + ")");
                     }
 
                 }