From 262e08947b9b0ee6f9ebff69fe066e4adfe35f52 Mon Sep 17 00:00:00 2001 From: Aashish Chaudhary Date: Wed, 13 Apr 2016 12:52:25 -0400 Subject: [PATCH] Updated to new build and test infrastructure --- src/canvas/index.js | 1 + src/heatmapFeature.js | 165 +++++++++++++++++++++++++++++++++++++++++ src/index.js | 1 + tests/cases/heatmap.js | 94 +++++++++++++++++++++++ 4 files changed, 261 insertions(+) create mode 100644 src/heatmapFeature.js create mode 100644 tests/cases/heatmap.js diff --git a/src/canvas/index.js b/src/canvas/index.js index 349a21f152..ad55e55bb2 100644 --- a/src/canvas/index.js +++ b/src/canvas/index.js @@ -4,5 +4,6 @@ module.exports = { canvasRenderer: require('./canvasRenderer'), quadFeature: require('./quadFeature'), + heatmapFeature: require('./heatmapFeature'), tileLayer: require('./tileLayer') }; diff --git a/src/heatmapFeature.js b/src/heatmapFeature.js new file mode 100644 index 0000000000..a93f1be10b --- /dev/null +++ b/src/heatmapFeature.js @@ -0,0 +1,165 @@ +var $ = require('jquery'); +var inherit = require('./inherit'); +var feature = require('./feature'); + +////////////////////////////////////////////////////////////////////////////// +/** + * Create a new instance of class heatmapFeature + * + * @class + * @param {Object} arg Options object + * @extends geo.feature + * @param {Object|string|Function} [color] Color for quads without images. + * Default is white ({r: 1, g: 1, b: 1}). + * @param {number|Function} [opacity=1] Opacity for quad + * @param {Object|Function} [radius=10] Radius of a point in terms of number +* of pixels. + * @param {Object|Function} [blurRadius=10] Gaussian blur radius. + * @param {Object|Function} [position] Position of the data. Default is + * (data). The position is an Object which specifies the location of the + * data in geo-spatial context. + * @param {boolean} [intensity] Scalar value that of each data point. Scalar + * value will be used to compute the weight for each data point for the final + * computation of its opacity. + * @param {boolean} [maxIntensity=1] Maximum intensity of the data. Maximum + * intensity will be used to normalize all intensities with a dataset. + * @returns {geo.heatmapFetures} + */ +////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////// +var heatmapFeature = function (arg) { + 'use strict'; + if (!(this instanceof heatmapFeature)){ + return new heatmapFeature(arg); + } + arg = arg || {}; + feature.call(this, arg); + + //////////////////////////////////////////////////////////////////////////// + /** + * @private + */ + //////////////////////////////////////////////////////////////////////////// + var m_this = this, + m_position, + m_intensity, + m_maxIntensity, + m_minIntensity, + s_init = this._init, + s_data = this.data; + + m_position = arg.position || function (d) { return d; }; + m_intensity = arg.intensity || function (d) { return 1; }; + m_maxIntensity = arg.maxIntensity || 1; + m_minIntensity = arg.minIntensity ? arg.minIntensity : 0; + + //////////////////////////////////////////////////////////////////////////// + /** + * Get/Set maxIntensity + * + * @returns {geo.heatmap} + */ + //////////////////////////////////////////////////////////////////////////// + this.maxIntensity = function (val) { + if (val === undefined) { + return m_maxIntensity; + } else { + m_maxIntensity = val; + m_this.dataTime().modified(); + m_this.modified(); + } + return m_this; + }; + + //////////////////////////////////////////////////////////////////////////// + /** + * Get/Set maxIntensity + * + * @returns {geo.heatmap} + */ + //////////////////////////////////////////////////////////////////////////// + this.minIntensity = function (val) { + if (val === undefined) { + return m_minIntensity; + } else { + m_minIntensity = val; + m_this.dataTime().modified(); + m_this.modified(); + } + return m_this; + }; + + //////////////////////////////////////////////////////////////////////////// + /** + * Get/Set position accessor + * + * @returns {geo.heatmap} + */ + //////////////////////////////////////////////////////////////////////////// + this.position = function (val) { + if (val === undefined) { + return m_position; + } else { + m_position = val; + m_this.dataTime().modified(); + m_this.modified(); + } + return m_this; + }; + + //////////////////////////////////////////////////////////////////////////// + /** + * Get/Set intensity + * + * @returns {geo.heatmap} + */ + //////////////////////////////////////////////////////////////////////////// + this.intensity = function (val) { + if (val === undefined) { + return m_intensity; + } else { + m_intensity = val; + m_this.dataTime().modified(); + m_this.modified(); + } + return m_this; + }; + + //////////////////////////////////////////////////////////////////////////// + /** + * Initialize + */ + //////////////////////////////////////////////////////////////////////////// + this._init = function (arg) { + s_init.call(m_this, arg); + + var defaultStyle = $.extend( + {}, + { + opacity: 0.1, + radius: 10, + blurRadius: 10, + color: {0: {r: 0, g: 0, b: 0.0, a: 0.0}, + .25: {r: 0, g: 0, b: 1, a: 0.5}, + .5: {r: 0, g: 1, b: 1, a: 0.6}, + .75: {r: 1, g: 1, b: 0, a: 0.7}, + 1: {r: 1, g: 0, b: 0, a: 0.8}} + }, + arg.style === undefined ? {} : arg.style + ); + + m_this.style(defaultStyle); + + if (m_position) { + m_this.dataTime().modified(); + } + }; + + this._init(arg); + return this; + +}; + +inherit(heatmapFeature, feature) +module.exports = heatmapFeature; diff --git a/src/index.js b/src/index.js index 2ffb0e1773..9a5e2282a6 100644 --- a/src/index.js +++ b/src/index.js @@ -52,6 +52,7 @@ module.exports = $.extend({ pointFeature: require('./pointFeature'), polygonFeature: require('./polygonFeature'), quadFeature: require('./quadFeature'), + heatmapFeature: require('./heatmapFeature'), renderer: require('./renderer'), sceneObject: require('./sceneObject'), tile: require('./tile'), diff --git a/tests/cases/heatmap.js b/tests/cases/heatmap.js new file mode 100644 index 0000000000..458fd3beaf --- /dev/null +++ b/tests/cases/heatmap.js @@ -0,0 +1,94 @@ +// Test geo.core.osmLayer +var geo = require('../test-utils').geo; +var $ = require('jquery'); + +beforeEach(function () { + $('
').appendTo('body') + .css({width: '500px', height: '400px'}); +}); + +afterEach(function () { + $('#map-canvas-heatmap-feature').remove(); +}); + +describe('canvas heatmap feature', function () { + 'use strict'; + + var mockAnimationFrame = require('../test-utils').mockAnimationFrame; + var stepAnimationFrame = require('../test-utils').stepAnimationFrame; + var unmockAnimationFrame = require('../test-utils').unmockAnimationFrame; + + var map, width = 800, height = 600, layer, feature1, feature2, + testData = "Strength, Lat,Lon\ + 0.6,42.8584,-70.9301\ + 0.233,42.2776,-83.7409\ + 0.2,42.2776,-83.7409,"; + testData = testData.split(/\r\n|\n|\r/); + testData = testData.map( function (r) { + var fields = r.split(','); + return [fields[12], fields[24], fields[25]].map(parseFloat); + }); + testData.splice(0, 1); + + it('Setup map', function () { + map = geo.map({node: '#map-canvas-heatmap-feature', center: [0, 0], zoom: 3}); + layer = map.createLayer('feature', {'renderer': 'canvas'}); + map.resize(0, 0, width, height); + }); + + it('Add features to a layer', function () { + feature1 = layer.createFeature('heatmap') + .data(testData) + .intensity(function (d) { + return d[0]; + }) + .position(function (d) { + return { + x: d[2], + y: d[1] + }; + }) + .style('radius', 5) + .style('blurRadius', 15) + .style('opacity', 1.0); + + mockAnimationFrame(); + map.draw(); + stepAnimationFrame(new Date().getTime()); + expect(layer.children().length).toBe(1) + unmockAnimationFrame(); + }); + + it('Validate selection API option', function () { + expect(feature1.selectionAPI()).toBe(false); + }); + + it('Validate position', function () { + expect(feature1.position()([0.6, 42.8584, -70.9301])) + .toEqual({x:-70.9301, y:42.8584}); + }); + + it('Validate maximum intensity', function () { + expect(feature1.maxIntensity()).toBe(1); + }); + + it('Validate minimum intensity', function () { + expect(feature1.minIntensity()).toBe(0); + }); + + it('Remove a feature from a layer', function () { + layer.deleteFeature(feature1).draw(); + expect(layer.children().length).toBe(0); + }); + + it('Compute gradient', function () { + feature1.style("color", {0: {r: 0, g: 0, b: 0.0, a: 0.0}, + 0.25: {r: 0, g: 0, b: 1, a: 0.5}, + 0.5: {r: 0, g: 1, b: 1, a: 0.6}, + 0.75: {r: 1, g: 1, b: 0, a: 0.7}, + 1: {r: 1, g: 0, b: 0, a: 0.1}}); + feature1._computeGradient(); + expect(layer.node()[0].children[0].getContext('2d'). + getImageData(1, 0, 1, 1).data.length).toBe(4) + }); +});