Fix merge conflicts

This commit is contained in:
Rashid Khan 2015-05-13 14:41:38 -07:00
commit ec74972e0d
11 changed files with 646 additions and 56 deletions

View file

@ -39,6 +39,7 @@
"inflection": "~1.3.5",
"jquery": "~2.1.0",
"leaflet": "0.7.3",
"Leaflet.heat": "Leaflet/Leaflet.heat#627ede7c11bbe43",
"lesshat": "~3.0.2",
"lodash": "~2.4.1",
"moment": "~2.9.0",

View file

@ -0,0 +1,8 @@
<table>
<tbody>
<tr ng-repeat="detail in details" >
<td><b>{{detail.label}}</b></td>
<td>{{detail.value}}</td>
</tr>
</tbody>
</table>

View file

@ -0,0 +1,32 @@
define(function (require) {
return function TileMapTooltipFormatter($compile, $rootScope) {
var $ = require('jquery');
var $tooltipScope = $rootScope.$new();
var $tooltip = $(require('text!components/agg_response/geo_json/_tooltip.html'));
$compile($tooltip)($tooltipScope);
return function tooltipFormatter(feature) {
if (!feature) return '';
var details = $tooltipScope.details = [];
var lat = feature.geometry.coordinates[1];
var lng = feature.geometry.coordinates[0];
var metric = {
label: feature.properties.valueLabel,
value: feature.properties.count
};
var location = {
label: 'Center',
value: lat.toFixed(4) + ', ' + lng.toFixed(4)
};
details.push(metric, location);
$tooltipScope.$apply();
return $tooltip[0].outerHTML;
};
};
});

View file

@ -3,6 +3,8 @@ define(function (require) {
var _ = require('lodash');
var readRows = require('components/agg_response/geo_json/_read_rows');
var tooltipFormatter = Private(require('components/agg_response/geo_json/_tooltip_formatter'));
function findCol(table, name) {
return _.findIndex(table.columns, function (col) {
return col.aggConfig.schema.name === name;
@ -25,6 +27,7 @@ define(function (require) {
});
var chart = {};
chart.tooltipFormatter = tooltipFormatter;
var geoJson = chart.geoJson = {
type: 'FeatureCollection',
features: []
@ -37,6 +40,10 @@ define(function (require) {
max: 0
};
if (agg.metric._opts.params && agg.metric._opts.params.field) {
props.metricField = agg.metric._opts.params.field;
}
// set precision from the bucketting column, if we have one
if (agg.geo) {
props.precision = _.parseInt(agg.geo.params.precision);

View file

@ -76,19 +76,56 @@
.leaflet-popup {
margin-bottom: 16px !important;
pointer-events: none;
}
.leaflet-popup-content-wrapper {
background: rgba(70, 82, 93, 0.95) !important;
color: @gray-lighter !important;
background: @tooltip-bg !important;
color: @tooltip-color !important;
border-radius: 4px !important;
padding: 0 !important;
}
.leaflet-popup-content {
padding: 8px !important;
margin: 0 !important;
line-height: 14px !important;
line-height: 1.1 !important;
font-size: 12px;
font-weight: normal;
word-wrap: break-word;
overflow: hidden;
pointer-events: none;
> :last-child {
margin-bottom: @tooltip-space;
}
> * {
margin: @tooltip-space @tooltip-space 0;
}
table {
td,th {
padding: @tooltip-space-tight;
&.row-bucket {
word-break: break-all;
}
}
// if there is a header, give it a border that matches
// those in the body
thead tr {
border-bottom: 1px solid @gray;
}
// only apply to tr in the body, not the header
tbody tr {
border-top: 1px solid @gray;
&:first-child {
border-top: none;
}
}
}
}
.leaflet-popup-tip-container, .leaflet-popup-close-button {
@ -109,7 +146,7 @@
}
.leaflet-draw-tooltip {
display: none;
display: none;
}
/* filter to desaturate mapquest tiles */

View file

@ -3,11 +3,11 @@ define(function (require) {
var _ = require('lodash');
var $ = require('jquery');
var L = require('leaflet');
require('leaflet-heat');
require('leaflet-draw');
var Dispatch = Private(require('components/vislib/lib/dispatch'));
var Chart = Private(require('components/vislib/visualizations/_chart'));
var errors = require('errors');
require('css!components/vislib/styles/main');
@ -29,11 +29,14 @@ define(function (require) {
if (!(this instanceof TileMap)) {
return new TileMap(handler, chartEl, chartData);
}
TileMap.Super.apply(this, arguments);
// track the map objects
this.maps = [];
this.tooltipFormatter = chartData.tooltipFormatter;
this.events = new Dispatch(handler);
// add allmin and allmax to geoJson
@ -55,7 +58,7 @@ define(function (require) {
// clean up old maps
self.destroy();
// create a new maps array
// clear maps array
self.maps = [];
self.popups = [];
@ -71,10 +74,10 @@ define(function (require) {
mapCenter = self._attr.mapCenter;
}
var mapData = data.geoJson;
var div = $(this).addClass('tilemap');
// add leaflet latLngs to properties for tooltip
var mapData = self.addLatLng(data.geoJson);
var featureLayer;
var div = $(this).addClass('tilemap');
var tileLayer = L.tileLayer('https://otile{s}-s.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.jpeg', {
attribution: 'Tiles by <a href="http://www.mapquest.com/">MapQuest</a> &mdash; ' +
'Map data &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, ' +
@ -82,7 +85,6 @@ define(function (require) {
subdomains: '1234'
});
var drawOptions = {draw: {}};
_.each(['polyline', 'polygon', 'circle', 'marker', 'rectangle'], function (drawShape) {
if (!self.events.listenerCount(drawShape)) {
@ -111,24 +113,27 @@ define(function (require) {
var map = L.map(div[0], mapOptions);
var featureLayer = self.markerType(map, mapData).addTo(map);
if (data.geoJson.features.length) {
map.addControl(new L.Control.Draw(drawOptions));
}
tileLayer.on('tileload', function () {
function saturateTiles() {
self.saturateTiles();
});
}
featureLayer = self.markerType(map, mapData).addTo(map);
tileLayer.on('tileload', saturateTiles);
map.on('unload', function () {
tileLayer.off('tileload', self.saturateTiles);
tileLayer.off('tileload', saturateTiles);
});
map.on('moveend', function setZoomCenter() {
mapZoom = self._attr.mapZoom = map.getZoom();
mapCenter = self._attr.mapCenter = map.getCenter();
featureLayer.clearLayers();
map.removeLayer(featureLayer);
featureLayer = self.markerType(map, mapData).addTo(map);
});
@ -191,7 +196,11 @@ define(function (require) {
};
/**
* Return features within the map bounds
* return whether feature is within map bounds
*
* @method _filterToMapBounds
* @param map {Leaflet Object}
* @return {boolean}
*/
TileMap.prototype._filterToMapBounds = function (map) {
return function (feature) {
@ -237,20 +246,36 @@ define(function (require) {
};
/**
* zoom map to fit all features in featureLayer
* add Leaflet latLng to mapData properties
*
* @method fitBounds
* @param map {Object}
* @param featureLayer {Leaflet object}
* @return {Leaflet object} featureLayer
* @method addLatLng
* @param mapData {geoJson Object}
* @return mapData {geoJson Object}
*/
TileMap.prototype.fitBounds = function (map, featureLayer) {
TileMap.prototype.addLatLng = function (mapData) {
for (var i = 0; i < mapData.features.length; i++) {
var latLng = L.latLng(mapData.features[i].geometry.coordinates[1], mapData.features[i].geometry.coordinates[0]);
mapData.features[i].properties.latLng = latLng;
}
map.fitBounds(featureLayer.getBounds());
return mapData;
};
/**
* remove css class on map tiles
* zoom map to fit all features in featureLayer
*
* @method fitBounds
* @param map {Leaflet Object}
* @param mapData {geoJson Object}
* @return {undefined}
*/
TileMap.prototype.fitBounds = function (map, mapData) {
map.fitBounds(this.getBounds(mapData));
};
/**
* remove css class for desat filters on map tiles
*
* @method saturateTiles
* @return {Leaflet object} featureLayer
@ -261,17 +286,119 @@ define(function (require) {
}
};
/**
* Finds nearest feature in mapData to event latlng
*
* @method nearestFeature
* @param point {Leaflet Object}
* @param mapData {geoJson Object}
* @return nearestPoint {Leaflet Object}
*/
TileMap.prototype.nearestFeature = function (point, mapData) {
var self = this;
var distance = Infinity;
var nearest;
if (point.lng < -180 || point.lng > 180) {
return;
}
for (var i = 0; i < mapData.features.length; i++) {
var dist = point.distanceTo(mapData.features[i].properties.latLng);
if (dist < distance) {
distance = dist;
nearest = mapData.features[i];
}
}
nearest.properties.eventDistance = distance;
return nearest;
};
/**
* display tooltip if feature is close enough to event latlng
*
* @method tooltipProximity
* @param latlng {Leaflet Object}
* @param zoom {Number}
* @param feature {geoJson Object}
* @param map {Leaflet Object}
* @return boolean
*/
TileMap.prototype.tooltipProximity = function (latlng, zoom, feature, map) {
if (!feature) {
return;
}
var showTip = false;
// zoomScale takes map zoom and returns proximity value for tooltip display
// domain (input values) is map zoom (min 1 and max 18)
// range (output values) is distance in meters
// used to compare proximity of event latlng to feature latlng
var zoomScale = d3.scale.linear()
.domain([1, 4, 7, 10, 13, 16, 18])
.range([1000000, 300000, 100000, 15000, 2000, 150, 50]);
var proximity = zoomScale(zoom);
var distance = latlng.distanceTo(feature.properties.latLng);
// maxLngDif is max difference in longitudes
// to prevent feature tooltip from appearing 360°
// away from event latlng
var maxLngDif = 40;
var lngDif = Math.abs(latlng.lng - feature.properties.latLng.lng);
if (distance < proximity && lngDif < maxLngDif) {
showTip = true;
}
delete feature.properties.eventDistance;
var testScale = d3.scale.pow().exponent(0.2)
.domain([1, 18])
.range([1500000, 50]);
return showTip;
};
/**
* Checks if event latlng is within bounds of mapData
* features and shows tooltip for that feature
*
* @method showTooltip
* @param e {Event}
* @param map {Leaflet Object}
* @param mapData {geoJson Object}
* @return {undefined}
*/
TileMap.prototype.showTooltip = function (map, feature) {
var content = this.tooltipFormatter(feature);
if (!content) {
return;
}
var lat = feature.geometry.coordinates[1];
var lng = feature.geometry.coordinates[0];
var latLng = L.latLng(lat, lng);
L.popup({autoPan: false})
.setLatLng(latLng)
.setContent(content)
.openOn(map);
};
/**
* Switch type of data overlay for map:
* creates featurelayer from mapData (geoJson)
*
* @method markerType
* @param map {Object}
* @param mapData {Object}
* @param map {Leaflet Object}
* @param mapData {geoJson Object}
* @return {Leaflet object} featureLayer
*/
TileMap.prototype.markerType = function (map, mapData) {
var featureLayer;
if (mapData) {
if (this._attr.mapType === 'Scaled Circle Markers') {
featureLayer = this.scaledCircleMarkers(map, mapData);
@ -279,6 +406,8 @@ define(function (require) {
featureLayer = this.shadedCircleMarkers(map, mapData);
} else if (this._attr.mapType === 'Shaded Geohash Grid') {
featureLayer = this.shadedGeohashGrid(map, mapData);
} else if (this._attr.mapType === 'Heatmap') {
featureLayer = this.heatMap(map, mapData);
} else {
featureLayer = this.scaledCircleMarkers(map, mapData);
}
@ -293,8 +422,8 @@ define(function (require) {
* with circle markers that are scaled to illustrate values
*
* @method scaledCircleMarkers
* @param map {Object}
* @param mapData {Object}
* @param map {Leaflet Object}
* @param mapData {geoJson Object}
* @return {Leaflet object} featureLayer
*/
TileMap.prototype.scaledCircleMarkers = function (map, mapData) {
@ -335,8 +464,8 @@ define(function (require) {
* with circle markers that are shaded to illustrate values
*
* @method shadedCircleMarkers
* @param map {Object}
* @param mapData {Object}
* @param map {Leaflet Object}
* @param mapData {geoJson Object}
* @return {Leaflet object} featureLayer
*/
TileMap.prototype.shadedCircleMarkers = function (map, mapData) {
@ -375,8 +504,8 @@ define(function (require) {
* with rectangles that show the geohash grid bounds
*
* @method geohashGrid
* @param map {Object}
* @param mapData {Object}
* @param map {Leaflet Object}
* @param mapData {geoJson Object}
* @return {undefined}
*/
TileMap.prototype.shadedGeohashGrid = function (map, mapData) {
@ -392,12 +521,12 @@ define(function (require) {
pointToLayer: function (feature, latlng) {
var geohashRect = feature.properties.rectangle;
// get bounds from northEast[3] and southWest[1]
// points in geohash rectangle
var bounds = [
// corners in geohash rectangle
var corners = [
[geohashRect[3][1], geohashRect[3][0]],
[geohashRect[1][1], geohashRect[1][0]]
];
return L.rectangle(bounds);
return L.rectangle(corners);
},
onEachFeature: function (feature, layer) {
self.bindPopup(feature, layer);
@ -425,12 +554,79 @@ define(function (require) {
return featureLayer;
};
/**
* Type of data overlay for map:
* creates canvas layer from mapData (geoJson)
* with leaflet.heat plugin
*
* @method heatMap
* @param map {Leaflet Object}
* @param mapData {geoJson Object}
* @return featureLayer {Leaflet object}
*/
TileMap.prototype.heatMap = function (map, mapData) {
var self = this;
var max = mapData.properties.allmax;
var points = this.dataToHeatArray(mapData, max);
var options = {
radius: +this._attr.heatRadius,
blur: +this._attr.heatBlur,
maxZoom: +this._attr.heatMaxZoom,
minOpacity: +this._attr.heatMinOpacity
};
var featureLayer = L.heatLayer(points, options);
if (self._attr.addTooltip && self.tooltipFormatter && !self._attr.disableTooltips) {
map.on('mousemove', _.debounce(mouseMoveLocation, 15, {
'leading': true,
'trailing': false
}));
map.on('mouseout', function (e) {
map.closePopup();
});
map.on('mousedown', function () {
self._attr.disableTooltips = true;
map.closePopup();
});
map.on('mouseup', function () {
self._attr.disableTooltips = false;
});
}
function mouseMoveLocation(e) {
map.closePopup();
// unhighlight all svgs
d3.selectAll('path.geohash', this.chartEl).classed('geohash-hover', false);
if (!mapData.features.length || self._attr.disableTooltips) {
return;
}
var latlng = e.latlng;
// find nearest feature to event latlng
var feature = self.nearestFeature(latlng, mapData);
var zoom = map.getZoom();
// show tooltip if close enough to event latlng
if (self.tooltipProximity(latlng, zoom, feature, map)) {
self.showTooltip(map, feature, latlng);
}
}
return featureLayer;
};
/**
* Adds label div to each map when data is split
*
* @method addLabel
* @param mapLabel {String}
* @param map {Object}
* @param map {Leaflet Object}
* @return {undefined}
*/
TileMap.prototype.addLabel = function (mapLabel, map) {
@ -452,7 +648,7 @@ define(function (require) {
*
* @method addLegend
* @param data {Object}
* @param map {Object}
* @param map {Leaflet Object}
* @return {undefined}
*/
TileMap.prototype.addLegend = function (data, map) {
@ -522,6 +718,7 @@ define(function (require) {
* Invalidate the size of the map, so that leaflet will resize to fit.
* then moves to center
*
* @method resizeArea
* @return {undefined}
*/
TileMap.prototype.resizeArea = function () {
@ -562,6 +759,48 @@ define(function (require) {
this.popups.push({elem: popup, layer: layer});
};
/**
* get bounds of features from geoJson
*
* @method getBounds
* @param mapData {geoJson Object}
* @return bounds {Leaflet Object}
*/
TileMap.prototype.getBounds = function (mapData) {
var bounds = L.geoJson(mapData).getBounds();
return bounds;
};
/**
* retuns data for data for heat map intensity
* if heatNormalizeData attribute is checked/true
normalizes data for heat map intensity
*
* @param mapData {geoJson Object}
* @param nax {Number}
* @method dataToHeatArray
* @return {Array}
*/
TileMap.prototype.dataToHeatArray = function (mapData, max) {
var self = this;
return mapData.features.map(function (feature) {
var lat = feature.geometry.coordinates[1];
var lng = feature.geometry.coordinates[0];
var heatIntensity;
if (!self._attr.heatNormalizeData) {
// show bucket count on heatmap
heatIntensity = feature.properties.count;
} else {
// show bucket count normalized to max value
heatIntensity = parseInt(feature.properties.count / max * 100);
}
return [lat, lng, heatIntensity];
});
};
/**
* geohashMinDistance returns a min distance in meters for sizing
* circle markers to fit within geohash grid rectangle
@ -596,15 +835,16 @@ define(function (require) {
* @method radiusScale
* @param count {Number}
* @param max {Number}
* @param precision {Number}
* @param feature {Object}
* @return {Number}
*/
TileMap.prototype.radiusScale = function (count, max, feature) {
// exp = 0.5 for square root ratio
// exp = 1 for linear ratio
var exp = 0.6;
var maxr = this.geohashMinDistance(feature);
return Math.pow(count, exp) / Math.pow(max, exp) * maxr;
var maxRadius = this.geohashMinDistance(feature);
var pct = count / max;
return Math.pow(pct, exp) * maxRadius;
};
/**

View file

@ -1,19 +1,122 @@
<!-- vis type specific options -->
<div class="form-group">
<label>Map type</label>
<select
name="agg"
<select name="agg"
class="form-control"
ng-model="vis.params.mapType"
ng-init="vis.params.mapType || vis.type.params.mapTypes[0]"
ng-options="mapType as mapType for mapType in vis.type.params.mapTypes">
ng-options="mapType as mapType for mapType in vis.type.params.mapTypes"
>
</select>
</div>
<div class="vis-option-item">
</br>
<div ng-if="vis.params.mapType === 'Heatmap'" class="form-group">
<div>
<label>
Radius
&nbsp;<kbn-info placement="right" info="Size of heatmap dots. Default: 25"></kbn-info>
</label>
<div class="vis-editor-agg-form-row">
<input
name="heatRadius"
ng-model="vis.params.heatRadius"
required
class="form-control"
type="range"
min="5"
max="50"
step="1"
>
<div class="form-group vis-editor-agg-form-value">
{{vis.params.heatRadius}}
</div>
</div>
</div>
<div>
<label>
Blur
&nbsp;<kbn-info placement="right" info="Amount of blur applied to dots. Default: 15"></kbn-info>
</label>
<div class="vis-editor-agg-form-row">
<input
name="heatBlur"
ng-model="vis.params.heatBlur"
required
class="form-control"
type="range"
min="1"
max="25"
step="1"
>
<div class="form-group vis-editor-agg-form-value">
{{vis.params.heatBlur}}
</div>
</div>
</div>
<div>
<label>
Maximum zoom
&nbsp;<kbn-info placement="right" info="Map zoom at which all dots are display at full intensity. Default: 16"></kbn-info>
</label>
<div class="vis-editor-agg-form-row">
<input
name="heatMaxZoom"
ng-model="vis.params.heatMaxZoom"
required
class="form-control"
type="range"
min="1"
max="18"
step="1"
>
<div class="vis-editor-agg-form-value">
{{vis.params.heatMaxZoom}}
</div>
</div>
</div>
<div>
<label>
Minimum opacity
&nbsp;<kbn-info placement="right" info="Minimum opacity of dots. Default: 0.1"></kbn-info>
</label>
<div class="vis-editor-agg-form-row">
<input
name="heatMinOpacity"
ng-model="vis.params.heatMinOpacity"
required
class="form-control"
type="range"
min="0"
max="1.0"
step="0.01"
>
<div class="vis-editor-agg-form-value">
{{vis.params.heatMinOpacity}}
</div>
</div>
</div>
<div class="form-group">
<span class="hintbox-label" ng-click="heatNormalizeData = !heatNormalizeData">
<label>
<input type="checkbox" value="{{heatNormalizeData}}" ng-model="vis.params.heatNormalizeData" name="heatNormalizeData" ng-checked="vis.params.heatNormalizeData">
Normalize data for heatmap intesity
</label>
<i class="fa fa-info-circle"></i>
</span>
<div class="hintbox" ng-show="heatNormalizeData">Default is checked to show heatmap intensity of bucket count as a percent of max count. Uncheck to show heatmap intensity simply as bucket count.</div>
</div>
<div class="form-group">
<label>
<input type="checkbox" ng-model="vis.params.addTooltip">
Show Tooltip
</label>
</div>
</div>
<div class="vis-option-item form-group">
<label>
<input type="checkbox" value="{{isDesaturated}}" ng-model="vis.params.isDesaturated" name="isDesaturated" ng-checked="vis.params.isDesaturated">
Desaturate map tiles
</label>
</div>
</div>

View file

@ -14,9 +14,15 @@ define(function (require) {
params: {
defaults: {
mapType: 'Scaled Circle Markers',
isDesaturated: true
isDesaturated: true,
heatMaxZoom: 16,
heatMinOpacity: 0.1,
heatRadius: 25,
heatBlur: 15,
heatNormalizeData: true,
addTooltip: true
},
mapTypes: ['Scaled Circle Markers', 'Shaded Circle Markers', 'Shaded Geohash Grid'],
mapTypes: ['Scaled Circle Markers', 'Shaded Circle Markers', 'Shaded Geohash Grid', 'Heatmap'],
editor: require('text!plugins/vis_types/vislib/editors/tile_map.html')
},
listeners: {

View file

@ -3,4 +3,4 @@
view options
</div>
<div class="visualization-options"></div>
</li>
</li>

View file

@ -25,6 +25,7 @@ require.config({
faker: 'bower_components/Faker/faker',
file_saver: 'bower_components/FileSaver/FileSaver',
gridster: 'bower_components/gridster/dist/jquery.gridster',
'leaflet-heat': 'bower_components/Leaflet.heat/dist/leaflet-heat',
inflection: 'bower_components/inflection/lib/inflection',
jquery: 'bower_components/jquery/dist/jquery',
leaflet: 'bower_components/leaflet/dist/leaflet',
@ -51,6 +52,9 @@ require.config({
'ace-json': ['ace'],
'angular-ui-ace': ['angular', 'ace', 'ace-json'],
'ng-clip': ['angular', 'zeroclipboard'],
'leaflet-heat': {
deps: ['leaflet']
},
inflection: {
exports: 'inflection'
},

View file

@ -12,7 +12,7 @@ define(function (require) {
];
var names = ['geojson', 'columns', 'rows'];
// TODO: Test the specific behavior of each these
var mapTypes = ['Scaled Circle Markers', 'Shaded Circle Markers', 'Shaded Geohash Grid'];
var mapTypes = ['Scaled Circle Markers', 'Shaded Circle Markers', 'Shaded Geohash Grid', 'Heatmap'];
angular.module('TileMapFactory', ['kibana']);
@ -25,6 +25,7 @@ define(function (require) {
mapType: type
};
module('TileMapFactory');
inject(function (Private) {
vis = Private(require('vislib_fixtures/_vis_fixture'))(visLibParams);
@ -130,10 +131,26 @@ define(function (require) {
describe('Methods', function () {
var vis;
var leafletContainer;
var map;
var mapData;
var i;
var feature;
var point;
var min;
var max;
var zoom;
beforeEach(function () {
vis = bootstrapAndRender(dataArray[0], 'Scaled Circle Markers');
leafletContainer = $(vis.el).find('.leaflet-container');
map = vis.handler.charts[0].maps[0];
mapData = vis.data.geoJson;
i = _.random(0, mapData.features.length - 1);
feature = mapData.features[i];
point = feature.properties.latLng;
min = mapData.properties.allmin;
max = mapData.properties.allmax;
zoom = _.random(1, 12);
});
afterEach(function () {
@ -167,8 +184,7 @@ define(function (require) {
describe('geohashMinDistance method', function () {
it('should return a number', function () {
vis.handler.charts.forEach(function (chart) {
var feature = chart.chartData.geoJson.features[0];
expect(_.isNumber(chart.geohashMinDistance(feature))).to.be(true);
expect(_.isFinite(chart.geohashMinDistance(feature))).to.be(true);
});
});
});
@ -178,9 +194,10 @@ define(function (require) {
vis.handler.charts.forEach(function (chart) {
var count = Math.random() * 50;
var max = 50;
var precision = 1;
var feature = chart.chartData.geoJson.features[0];
expect(_.isNumber(chart.radiusScale(count, max, feature))).to.be(true);
var pct = count / max;
var maxRadius = chart.geohashMinDistance(feature);
var scaledRadius = Math.pow(pct, 0.6) * maxRadius;
expect(chart.radiusScale(count, max, feature)).to.be(scaledRadius);
});
});
});
@ -227,6 +244,141 @@ define(function (require) {
});
});
});
describe('dataToHeatArray method', function () {
it('should return an array', function () {
vis.handler.charts.forEach(function (chart) {
expect(chart.dataToHeatArray(mapData, max)).to.be.an(Array);
});
});
it('should return an array item for each feature', function () {
vis.handler.charts.forEach(function (chart) {
expect(chart.dataToHeatArray(mapData, max).length).to.be(mapData.features.length);
});
});
it('should return an array item with lat, lng, metric for each feature', function () {
vis.handler.charts.forEach(function (chart) {
var lat = feature.geometry.coordinates[1];
var lng = feature.geometry.coordinates[0];
var intensity = feature.properties.count;
var array = chart.dataToHeatArray(mapData, max);
expect(array[i][0]).to.be(lat);
expect(array[i][1]).to.be(lng);
expect(array[i][2]).to.be(intensity);
});
});
it('should return an array item with lat, lng, normalized metric for each feature', function () {
vis.handler.charts.forEach(function (chart) {
chart._attr.heatNormalizeData = true;
var lat = feature.geometry.coordinates[1];
var lng = feature.geometry.coordinates[0];
var intensity = parseInt(feature.properties.count / max * 100);
var array = chart.dataToHeatArray(mapData, max);
expect(array[i][0]).to.be(lat);
expect(array[i][1]).to.be(lng);
expect(array[i][2]).to.be(intensity);
});
});
});
describe('applyShadingStyle method', function () {
it('should return an object', function () {
vis.handler.charts.forEach(function (chart) {
expect(chart.applyShadingStyle(feature, min, max)).to.be.an(Object);
});
});
});
describe('getBounds method', function () {
it('should return bounds object that contains every point in mapData', function () {
vis.handler.charts.forEach(function (chart) {
var containsPoint = [];
var bounds = chart.getBounds(mapData);
for (var i = 0; i < mapData.features.length; i++) {
var pt = [mapData.features[i].geometry.coordinates[1], mapData.features[i].geometry.coordinates[0]];
containsPoint.push(bounds.contains(pt));
}
containsPoint = _.compact(containsPoint);
expect(containsPoint.length).to.be(mapData.features.length);
});
});
});
describe('showTooltip method', function () {
it('should create a .leaflet-popup-kibana div for the tooltip', function () {
vis.handler.charts.forEach(function (chart) {
chart.tooltipFormatter = function (str) {
return str;
};
var layerIds = _.keys(map._layers);
var id = layerIds[_.random(1, layerIds.length - 1)]; // layer 0 is tileLayer
map._layers[id].fire('mouseover');
expect($(vis.el).find('.leaflet-popup-kibana').length).to.be(1);
});
});
});
describe('tooltipProximity method', function () {
it('should return true if feature is close enough to event latlng to display tooltip', function () {
vis.handler.charts.forEach(function (chart) {
expect(chart.tooltipProximity(point, zoom, feature, map)).to.be(true);
});
});
it('should return false if feature is not close enough to event latlng to display tooltip', function () {
vis.handler.charts.forEach(function (chart) {
var point = L.latLng(90, -180);
expect(chart.tooltipProximity(point, zoom, feature, map)).to.be(false);
});
});
});
describe('nearestFeature method', function () {
it('should return an object', function () {
vis.handler.charts.forEach(function (chart) {
expect(chart.nearestFeature(point, mapData)).to.be.an(Object);
});
});
it('should return a geoJson feature', function () {
vis.handler.charts.forEach(function (chart) {
expect(chart.nearestFeature(point, mapData).type).to.be('Feature');
});
});
it('should return the geoJson feature with same latlng as point', function () {
vis.handler.charts.forEach(function (chart) {
expect(chart.nearestFeature(point, mapData)).to.be(feature);
});
});
});
describe('addLatLng method', function () {
it('should add object to properties of each feature', function () {
vis.handler.charts.forEach(function (chart) {
expect(feature.properties.latLng).to.be.an(Object);
});
});
it('should add latLng with lat to properties of each feature', function () {
vis.handler.charts.forEach(function (chart) {
var lat = feature.geometry.coordinates[1];
expect(feature.properties.latLng.lat).to.be(lat);
});
});
it('should add latLng with lng to properties of each feature', function () {
vis.handler.charts.forEach(function (chart) {
var lng = feature.geometry.coordinates[0];
expect(feature.properties.latLng.lng).to.be(lng);
});
});
});
});
});