Commit 1dbedff4 by Rashid Khan

Merge pull request #167 from rashidkpc/master

Added bettermap panel and plugin status messages
parents 0803ed1b 35de5de3
...@@ -22,6 +22,6 @@ var config = new Settings( ...@@ -22,6 +22,6 @@ var config = new Settings(
kibana_index: "kibana-int", kibana_index: "kibana-int",
modules: ['histogram','map','pie','table','stringquery','sort', modules: ['histogram','map','pie','table','stringquery','sort',
'timepicker','text','fields','hits','dashcontrol', 'timepicker','text','fields','hits','dashcontrol',
'column','derivequeries','trends'], 'column','derivequeries','trends','bettermap'],
} }
); );
<div class="row-fluid">
<div class="span11">
This panel uses geoJSON points in a field to place markers on a map.
Coordinates <strong>must be stored as an array in Elasticsearch</strong>.
Also note that geoJSON is <strong>long,lat NOT lat,long</strong>.
</div>
</div>
<div class="row-fluid">
<div class="span11">
<h6>Query</h6>
<input type="text" style="width:100%" ng-model="panel.query">
</div>
</div>
<div class="row-fluid">
<div class="span4">
<form>
<h6>Coordinate Field</h6>
<input bs-typeahead="fields.list" type="text" class="input-small" ng-model="panel.field">
</form>
</div>
<div class="span4">
<form>
<h6>Tooltip Field</h6>
<input bs-typeahead="fields.list" type="text" class="input-small" ng-model="panel.tooltip">
</form>
</div>
<div class="span2"><h6>Max Points</h6>
<input type="number" class="input-small" ng-model="panel.size">
</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 <i class='icon-eye-open'></i> in the top right
of the panel.
</div>
</div>
This source diff could not be displayed because it is too large. You can view the blob instead.
.leaflet-vml-shape {
width: 1px;
height: 1px;
}
.lvml {
behavior: url(#default#VML);
display: inline-block;
position: absolute;
}
.leaflet-control {
display: inline;
}
.leaflet-popup-tip {
width: 21px;
_width: 27px;
margin: 0 auto;
_margin-top: -3px;
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
}
.leaflet-popup-tip-container {
margin-top: -1px;
}
.leaflet-popup-content-wrapper, .leaflet-popup-tip {
border: 1px solid #999;
}
.leaflet-popup-content-wrapper {
zoom: 1;
}
.leaflet-control-zoom,
.leaflet-control-layers {
border: 3px solid #999;
}
.leaflet-control-layers-toggle {
}
.leaflet-control-attribution,
.leaflet-control-layers,
.leaflet-control-scale-line {
background: white;
}
.leaflet-zoom-box {
filter: alpha(opacity=50);
}
.leaflet-control-attribution {
border-top: 1px solid #bbb;
border-left: 1px solid #bbb;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
.leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker-shadow {
-webkit-transition: -webkit-transform 0.2s ease-out, opacity 0.2s ease-in;
-moz-transition: -moz-transform 0.2s ease-out, opacity 0.2s ease-in;
-o-transition: -o-transform 0.2s ease-out, opacity 0.2s ease-in;
transition: transform 0.2s ease-out, opacity 0.2s ease-in;
}
.marker-cluster-small {
background-color: rgba(181, 226, 140, 0.6);
}
.marker-cluster-small div {
background-color: rgba(110, 204, 57, 0.6);
}
.marker-cluster-medium {
background-color: rgba(241, 211, 87, 0.6);
}
.marker-cluster-medium div {
background-color: rgba(240, 194, 12, 0.6);
}
.marker-cluster-large {
background-color: rgba(253, 156, 115, 0.6);
}
.marker-cluster-large div {
background-color: rgba(241, 128, 23, 0.6);
}
.marker-cluster {
background-clip: padding-box;
border-radius: 20px;
}
.marker-cluster div {
width: 30px;
height: 30px;
margin-left: 5px;
margin-top: 5px;
text-align: center;
border-radius: 15px;
font: 12px "Helvetica Neue", Arial, Helvetica, sans-serif;
}
.marker-cluster span {
line-height: 30px;
}
.leaflet-label {
background: #1f1f1f;
background-clip: padding-box;
border-radius: 4px;
border-style: solid;
border-width: 0px;
display: block;
font-weight: 200;
font-size: 11pt;
padding: 5px;
position: absolute;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
white-space: nowrap;
z-index: 99999 !important;
}
.leaflet-label:before {
border-right: 6px solid black;
border-right-color: inherit;
border-top: 6px solid transparent;
border-bottom: 6px solid transparent;
content: "";
position: absolute;
top: 5px;
left: -10px;
display: none;
}
\ No newline at end of file
<kibana-panel ng-controller='bettermap' ng-init="init()">
<!-- questionable if this is allowed in HTML5 -->
<link rel="stylesheet" href="panels/bettermap/lib/leaflet.css" />
<link rel="stylesheet" href="panels/bettermap/lib/plugins.css" />
<span ng-show="panel.spyable" style="position:absolute;right:0px;top:0px" class='panelextra pointer'>
<i bs-modal="'partials/modal.html'" class="icon-eye-open"></i>
</span>
<!-- This solution might work well for other panels that have trouble with heights -->
<div style="padding-right:10px;padding-top:10px;height:{{panel.height|| row.height}};overflow:hidden">
<div bettermap id='bettermap' params="{{panel}}" style="height:100%"></div>
</div>
</kibana-panel>
\ No newline at end of file
/*
## Better maps
So the cavaet for this panel is that, for better or worse, it does NOT use the terms facet and it
DOES query sequentially. This however means that
### Parameters
* query :: A single query string, not and array. This panel can only handle one
query at a time.
* size :: How many results to show, more results = slower
* field :: field containing a 2 element array in the format [lon,lat]
* tooltip :: field to extract the tool tip value from
* spyable :: Show the 'eye' icon that reveals the last ES query
### Group Events
#### Sends
* get_time :: On panel initialization get time range to query
#### Receives
* time :: An object containing the time range to use and the index(es) to query
* query :: An Array of queries, this panel uses only the first one
*/
angular.module('kibana.bettermap', [])
.controller('bettermap', function($scope, eventBus) {
// Set and populate defaults
var _d = {
status : "Experimental",
query : "*",
size : 1000,
spyable : true,
tooltip : "_id",
field : null,
group : "default"
}
_.defaults($scope.panel,_d)
$scope.init = function() {
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.get_data = function(segment,query_id) {
$scope.panel.error = false;
// Make sure we have everything for the request to complete
if(_.isUndefined($scope.index) || _.isUndefined($scope.time))
return
if(_.isUndefined($scope.panel.field)) {
$scope.panel.error = "Please select a field that contains geo point in [lon,lat] format"
return
}
//$scope.panel.loading = true;
var _segment = _.isUndefined(segment) ? 0 : segment
$scope.segment = _segment;
var request = $scope.ejs.Request().indices($scope.index[_segment])
.query(ejs.FilteredQuery(
ejs.QueryStringQuery(($scope.panel.query || '*') + " AND _exists_:"+$scope.panel.field),
ejs.RangeFilter($scope.time.field)
.from($scope.time.from)
.to($scope.time.to)
)
)
.fields([$scope.panel.field,$scope.panel.tooltip])
.size($scope.panel.size)
.sort($scope.time.field,'desc');
$scope.populate_modal(request)
var results = request.doSearch()
// Populate scope when we have results
results.then(function(results) {
$scope.panel.loading = false;
if(_segment === 0) {
$scope.hits = 0;
$scope.data = [];
query_id = $scope.query_id = new Date().getTime()
}
// Check for error and abort if found
if(!(_.isUndefined(results.error))) {
$scope.panel.error = $scope.parse_error(results.error);
return;
}
// Check that we're still on the same query, if not stop
if($scope.query_id === query_id) {
var scripts = $LAB.script("panels/bettermap/lib/leaflet.js").wait()
scripts.wait(function(){
$scope.data = $scope.data.concat(_.map(results.hits.hits, function(hit) {
return {
coordinates : new L.LatLng(hit.fields[$scope.panel.field][1],hit.fields[$scope.panel.field][0]),
tooltip : hit.fields[$scope.panel.tooltip]
}
}));
});
// Keep only what we need for the set
$scope.data = $scope.data.slice(0,$scope.panel.size)
} else {
return;
}
$scope.$emit('draw')
// Get $size results then stop querying
if($scope.data.length < $scope.panel.size && _segment+1 < $scope.index.length)
$scope.get_data(_segment+1,$scope.query_id)
});
}
// 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 : "<h5>Last Elasticsearch Query</h5><pre>"+
'curl -XGET '+config.elasticsearch+'/'+$scope.index+"/_search?pretty -d'\n"+
angular.toJson(JSON.parse(request.toString()),true)+
"'</pre>",
}
}
function set_time(time) {
$scope.time = time;
$scope.index = _.isUndefined(time.index) ? $scope.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('bettermap', function() {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
elem.html('<center><img src="common/img/load_big.gif"></center>')
// Receive render events
scope.$on('draw',function(){
render_panel();
});
scope.$on('render', function(){
if(!_.isUndefined(map)) {
map.invalidateSize();
var panes = map.getPanes()
}
})
var map, markers, layerGroup, mcg;
function render_panel() {
scope.panel.loading = false;
var scripts = $LAB.script("panels/bettermap/lib/leaflet.js").wait()
.script("panels/bettermap/lib/plugins.js")
//add markers dynamically
scripts.wait(function(){
if(_.isUndefined(map)) {
map = L.map(attrs.id, {
scrollWheelZoom: false,
center: [40, -86],
zoom: 10
});
L.tileLayer('http://{s}.tile.cloudmade.com/57cbb6ca8cac418dbb1a402586df4528/22677/256/{z}/{x}/{y}.png', {
maxZoom: 18,
minZoom: 2
}).addTo(map);
layerGroup = new L.MarkerClusterGroup({maxClusterRadius:30});
} else {
layerGroup.clearLayers();
}
_.each(scope.data, function(p) {
if(!_.isUndefined(p.tooltip) && p.tooltip !== '')
layerGroup.addLayer(L.marker(p.coordinates).bindLabel(p.tooltip))
else
layerGroup.addLayer(L.marker(p.coordinates))
})
layerGroup.addTo(map)
map.fitBounds(_.pluck(scope.data,'coordinates'));
})
}
}
};
});
\ No newline at end of file
...@@ -19,6 +19,7 @@ angular.module('kibana.column', []) ...@@ -19,6 +19,7 @@ angular.module('kibana.column', [])
.controller('column', function($scope, $rootScope) { .controller('column', function($scope, $rootScope) {
// Set and populate defaults // Set and populate defaults
var _d = { var _d = {
status: "Stable",
panels : [ panels : [
] ]
} }
......
...@@ -32,6 +32,7 @@ angular.module('kibana.dashcontrol', []) ...@@ -32,6 +32,7 @@ angular.module('kibana.dashcontrol', [])
$scope.panel = $scope.panel || {}; $scope.panel = $scope.panel || {};
// Set and populate defaults // Set and populate defaults
var _d = { var _d = {
status : "Stable",
group : "default", group : "default",
save : { save : {
gist: false, gist: false,
......
...@@ -21,6 +21,7 @@ angular.module('kibana.debug', []) ...@@ -21,6 +21,7 @@ angular.module('kibana.debug', [])
// Set and populate defaults // Set and populate defaults
var _d = { var _d = {
status : "Experimental",
group : "ALL", group : "ALL",
style : {'font-size':'9pt'}, style : {'font-size':'9pt'},
size : 20 size : 20
......
...@@ -26,6 +26,7 @@ angular.module('kibana.derivequeries', []) ...@@ -26,6 +26,7 @@ angular.module('kibana.derivequeries', [])
// Set and populate defaults // Set and populate defaults
var _d = { var _d = {
status : "Beta",
label : "Search", label : "Search",
query : "*", query : "*",
group : "default", group : "default",
......
...@@ -22,6 +22,7 @@ angular.module('kibana.fields', []) ...@@ -22,6 +22,7 @@ angular.module('kibana.fields', [])
// Set and populate defaults // Set and populate defaults
var _d = { var _d = {
status : "Beta",
group : "default", group : "default",
style : {}, style : {},
arrange : 'vertical', arrange : 'vertical',
......
...@@ -46,6 +46,7 @@ angular.module('kibana.histogram', []) ...@@ -46,6 +46,7 @@ angular.module('kibana.histogram', [])
// Set and populate defaults // Set and populate defaults
var _d = { var _d = {
status : "Stable",
group : "default", group : "default",
query : [ {query: "*", label:"Query"} ], query : [ {query: "*", label:"Query"} ],
mode : 'count', mode : 'count',
......
...@@ -26,6 +26,7 @@ angular.module('kibana.hits', []) ...@@ -26,6 +26,7 @@ angular.module('kibana.hits', [])
// Set and populate defaults // Set and populate defaults
var _d = { var _d = {
status : "Beta",
query : ["*"], query : ["*"],
group : "default", group : "default",
style : { "font-size": '10pt'}, style : { "font-size": '10pt'},
......
...@@ -32,6 +32,7 @@ angular.module('kibana.map', []) ...@@ -32,6 +32,7 @@ angular.module('kibana.map', [])
// Set and populate defaults // Set and populate defaults
var _d = { var _d = {
status : "Beta",
query : "*", query : "*",
map : "world", map : "world",
colors : ['#A0E2E2', '#265656'], colors : ['#A0E2E2', '#265656'],
......
...@@ -3,6 +3,7 @@ angular.module('kibana.map2', []) ...@@ -3,6 +3,7 @@ angular.module('kibana.map2', [])
// Set and populate defaults // Set and populate defaults
var _d = { var _d = {
status : "Broken",
query: "*", query: "*",
map: "world", map: "world",
colors: ['#C8EEFF', '#0071A4'], colors: ['#C8EEFF', '#0071A4'],
......
...@@ -6,6 +6,7 @@ angular.module('kibana.parallelcoordinates', []) ...@@ -6,6 +6,7 @@ angular.module('kibana.parallelcoordinates', [])
// Set and populate defaults // Set and populate defaults
var _d = { var _d = {
status : "Broken",
query : "*", query : "*",
size : 100, // Per page size : 100, // Per page
pages : 5, // Pages available pages : 5, // Pages available
......
...@@ -37,6 +37,7 @@ angular.module('kibana.pie', []) ...@@ -37,6 +37,7 @@ angular.module('kibana.pie', [])
// Set and populate defaults // Set and populate defaults
var _d = { var _d = {
status : "Deprecating Soon",
query : { field:"_all", query:"*", goal: 1}, query : { field:"_all", query:"*", goal: 1},
size : 10, size : 10,
exclude : [], exclude : [],
......
...@@ -25,6 +25,7 @@ angular.module('kibana.sort', []) ...@@ -25,6 +25,7 @@ angular.module('kibana.sort', [])
// Set and populate defaults // Set and populate defaults
var _d = { var _d = {
status : "Stable",
label : "Sort", label : "Sort",
sort : ['_score','desc'], sort : ['_score','desc'],
group : "default" group : "default"
......
...@@ -24,6 +24,7 @@ angular.module('kibana.stringquery', []) ...@@ -24,6 +24,7 @@ angular.module('kibana.stringquery', [])
// Set and populate defaults // Set and populate defaults
var _d = { var _d = {
status : "Stable",
label : "Search", label : "Search",
query : "*", query : "*",
group : "default", group : "default",
......
...@@ -33,6 +33,7 @@ angular.module('kibana.table', []) ...@@ -33,6 +33,7 @@ angular.module('kibana.table', [])
// Set and populate defaults // Set and populate defaults
var _d = { var _d = {
status : "Stable",
query : "*", query : "*",
size : 100, // Per page size : 100, // Per page
pages : 5, // Pages available pages : 5, // Pages available
......
...@@ -16,6 +16,7 @@ angular.module('kibana.text', []) ...@@ -16,6 +16,7 @@ angular.module('kibana.text', [])
// Set and populate defaults // Set and populate defaults
var _d = { var _d = {
status : "Stable",
group : "default", group : "default",
mode : "markdown", mode : "markdown",
content : "", content : "",
......
...@@ -32,6 +32,7 @@ angular.module('kibana.timepicker', []) ...@@ -32,6 +32,7 @@ angular.module('kibana.timepicker', [])
// Set and populate defaults // Set and populate defaults
var _d = { var _d = {
status : "Stable",
mode : "relative", mode : "relative",
time_options : ['5m','15m','1h','6h','12h','24h','2d','7d','30d'], time_options : ['5m','15m','1h','6h','12h','24h','2d','7d','30d'],
timespan : '15m', timespan : '15m',
......
...@@ -23,6 +23,7 @@ angular.module('kibana.trends', []) ...@@ -23,6 +23,7 @@ angular.module('kibana.trends', [])
// Set and populate defaults // Set and populate defaults
var _d = { var _d = {
status : "Beta",
query : ["*"], query : ["*"],
group : "default", group : "default",
style : { "font-size": '14pt'}, style : { "font-size": '14pt'},
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<div ng-include src="'partials/panelgeneral.html'"></div> <div ng-include src="'partials/panelgeneral.html'"></div>
<h4 style="text-transform: capitalize;">{{panel.type}} <small> panel settings</small></h4> <h4 style="text-transform: capitalize;">{{panel.type}} <small> panel settings. <strong ng-show="!_.isUndefined(panel.status)">({{panel.status}})</strong></small></h4>
<div ng-include src="edit_path(panel.type)">No additional settings are available for this type of panel.</div> <div ng-include src="edit_path(panel.type)">No additional settings are available for this type of panel.</div>
</div> </div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment