refactor
diff --git a/asterix-examples/src/main/resources/black-cherry/static/js/cherry.js b/asterix-examples/src/main/resources/black-cherry/static/js/cherry.js
index 2d42251..84fc8c9 100755
--- a/asterix-examples/src/main/resources/black-cherry/static/js/cherry.js
+++ b/asterix-examples/src/main/resources/black-cherry/static/js/cherry.js
@@ -52,6 +52,13 @@
map_tweet_markers = [];
map_info_windows = {};
+ // Legend Container
+ // Create a rainbow from a pretty color scheme.
+ // http://www.colourlovers.com/palette/292482/Terra
+ rainbow = new Rainbow();
+ rainbow.setSpectrum("#E8DDCB", "#CDB380", "#036564", "#033649", "#031634");
+ buildLegend();
+
// UI Elements - Modals & perspective tabs
$('#drilldown_modal').modal('hide');
$('#explore-mode').click( onLaunchExploreMode );
@@ -523,7 +530,7 @@
// TODO these are all included in coordinates already...
var coordinates = [];
var weights = [];
- var al = 1;
+ var maxWeight = 0;
// Parse resulting JSON objects. Here is an example record:
// { "cell": { rectangle: [{ point: [22.5, 64.5]}, { point: [24.5, 66.5]}]}, "count": { int64: 5 }}
@@ -544,11 +551,11 @@
"weight" : record.count.int64
}
- weights.push(coordinate["weight"]);
+ maxWeight = Math.max(coordinate["weight"], maxWeight);
coordinates.push(coordinate);
});
- triggerUIUpdate(coordinates, weights);
+ triggerUIUpdate(coordinates, maxWeight);
}
/**
@@ -556,66 +563,54 @@
* @param [Array] mapPlotData, an array of coordinate and weight objects
* @param [Array] plotWeights, a list of weights of the spatial cells - e.g., number of tweets
*/
-function triggerUIUpdate(mapPlotData, plotWeights) {
+function triggerUIUpdate(mapPlotData, maxWeight) {
/** Clear anything currently on the map **/
mapWidgetClearMap();
- // Compute data point spread
- var dataBreakpoints = mapWidgetLegendComputeNaturalBreaks(plotWeights);
-
+ // Initialize info windows.
map_info_windows = {};
- $.each(mapPlotData, function (m, val) {
-
- // Only map points in data range of top 4 natural breaks
- if (mapPlotData[m].weight > dataBreakpoints[0]) {
-
- // Get color value of legend
- var mapColor = mapWidgetLegendGetHeatValue(mapPlotData[m].weight, dataBreakpoints);
- var markerRadius = mapWidgetComputeCircleRadius(mapPlotData[m], dataBreakpoints);
- var point_opacity = 1.0;
-
- var point_center = new google.maps.LatLng(
- (mapPlotData[m].latSW + mapPlotData[m].latNE)/2.0,
- (mapPlotData[m].lngSW + mapPlotData[m].lngNE)/2.0);
-
- // Create and plot marker
- var map_circle_options = {
- center: point_center,
- anchorPoint: point_center,
- radius: markerRadius,
- map: map,
- fillOpacity: point_opacity,
- fillColor: mapColor,
- clickable: true
- };
- var map_circle = new google.maps.Circle(map_circle_options);
- map_circle.val = mapPlotData[m];
-
- map_info_windows[m] = new google.maps.InfoWindow({
- content: mapPlotData[m].weight + " tweets",
- position: point_center
- });
+ $.each(mapPlotData, function (m) {
+
+ var point_center = new google.maps.LatLng(
+ (mapPlotData[m].latSW + mapPlotData[m].latNE)/2.0,
+ (mapPlotData[m].lngSW + mapPlotData[m].lngNE)/2.0);
- // Clicking on a circle drills down map to that value, hovering over it displays a count
- // of tweets at that location.
- google.maps.event.addListener(map_circle, 'click', function (event) {
- $.each(map_info_windows, function(i) {
- map_info_windows[i].close();
- });
- onMapPointDrillDown(map_circle.val);
- });
+ var map_circle_options = {
+ center: point_center,
+ anchorPoint: point_center,
+ radius: mapWidgetComputeCircleRadius(mapPlotData[m], maxWeight),
+ map: map,
+ fillOpacity: 0.85,
+ fillColor: rainbow.colourAt(Math.ceil(100 * (mapPlotData[m].weight / maxWeight))),
+ clickable: true
+ };
+ var map_circle = new google.maps.Circle(map_circle_options);
+ map_circle.val = mapPlotData[m];
- google.maps.event.addListener(map_circle, 'mouseover', function(event) {
- if (!map_info_windows[m].getMap()) {
- map_info_windows[m].setPosition(map_circle.center);
- map_info_windows[m].open(map);
- }
+ map_info_windows[m] = new google.maps.InfoWindow({
+ content: mapPlotData[m].weight + " tweets",
+ position: point_center
+ });
+
+ // Clicking on a circle drills down map to that value, hovering over it displays a count
+ // of tweets at that location.
+ google.maps.event.addListener(map_circle, 'click', function (event) {
+ $.each(map_info_windows, function(i) {
+ map_info_windows[i].close();
});
+ onMapPointDrillDown(map_circle.val);
+ });
- // Add this marker to global marker cells
- map_cells.push(map_circle);
- }
+ google.maps.event.addListener(map_circle, 'mouseover', function(event) {
+ if (!map_info_windows[m].getMap()) {
+ map_info_windows[m].setPosition(map_circle.center);
+ map_info_windows[m].open(map);
+ }
+ });
+
+ // Add this marker to global marker cells
+ map_cells.push(map_circle);
});
}
@@ -1137,6 +1132,11 @@
}
map_cells = [];
+ $.each(map_info_windows, function(i) {
+ map_info_windows[i].close();
+ });
+ map_info_windows = {};
+
for (m in map_tweet_markers) {
map_tweet_markers[m].setMap(null);
}
@@ -1146,93 +1146,46 @@
}
/**
-* Uses jenks algorithm in geostats library to find natural breaks in numeric data
-* @param {number Array} weights of points to plot
-* @returns {number Array} array of natural breakpoints, of which the top 4 subsets will be plotted
-*/
-function mapWidgetLegendComputeNaturalBreaks(weights) {
-
- if (weights.length < 10) {
- return [0];
- }
-
- var plotDataWeights = new geostats(weights.sort());
- return plotDataWeights.getJenks(6).slice(2,7);
-}
-
-/**
-* Computes values for map legend given a value and an array of jenks breakpoints
-* @param {number} weight of point to plot on map
-* @param {number Array} breakpoints, an array of 5 points corresponding to bounds of 4 natural ranges
-* @returns {String} an RGB value corresponding to a subset of data
+* buildLegend
+*
+* no params
+*
+* Generates gradient, button action for legend bar
*/
-function mapWidgetLegendGetHeatValue(weight, breakpoints) {
-
- // Determine into which range the weight falls
- var weightColor = 0;
+function buildLegend() {
- if (breakpoints.length == 1) {
- weightColor = 2;
- } else {
- if (weight >= breakpoints[3]) {
- weightColor = 3;
- } else if (weight >= breakpoints[2]) {
- weightColor = 2;
- } else if (weight >= breakpoints[1]) {
- weightColor = 1;
- }
+ // Fill in legend area with colors
+ var gradientColor;
+
+ for (i = 0; i=100; i++) {
+ $("#rainbow-legend-container").append("" + rainbow.colourAt(i));
}
-
- // Get default map color palette
- var colorValues = mapWidgetGetColorPalette();
- return colorValues[weightColor];
-}
-
-/**
-* Returns an array containing a 4-color palette, lightest to darkest
-* External palette source: http://www.colourlovers.com/palette/2763366/s_i_l_e_n_c_e_r
-* @returns {Array} [colors]
-*/
-function mapWidgetGetColorPalette() {
- return [
- "rgb(115,189,158)",
- "rgb(74,142,145)",
- "rgb(19,93,96)",
- "rgb(7,51,46)"
- ];
-}
+
+ // Window clear button closes all info count windows
+ $("#windows-off-btn").on("click", function(e) {
+ $.each(map_info_windows, function(i) {
+ map_info_windows[i].close();
+ });
+ });
+}
/**
* Computes radius for a given data point from a spatial cell
* @param {Object} keys => ["latSW" "lngSW" "latNE" "lngNE" "weight"]
* @returns {number} radius between 2 points in metres
*/
-function mapWidgetComputeCircleRadius(spatialCell, breakpoints) {
-
- var weight = spatialCell.weight;
-
- if (breakpoints.length == 1) {
- var weightColor = 0.25;
- } else {
- // Compute weight color
- var weightColor = 0.25;
- if (weight >= breakpoints[3]) {
- weightColor = 1.0;
- } else if (weight >= breakpoints[2]) {
- weightColor = 0.75;
- } else if (weight >= breakpoints[1]) {
- weightColor = 0.5;
- }
- }
+function mapWidgetComputeCircleRadius(spatialCell, wLimit) {
// Define Boundary Points
var point_center = new google.maps.LatLng((spatialCell.latSW + spatialCell.latNE)/2.0, (spatialCell.lngSW + spatialCell.lngNE)/2.0);
var point_left = new google.maps.LatLng((spatialCell.latSW + spatialCell.latNE)/2.0, spatialCell.lngSW);
var point_top = new google.maps.LatLng(spatialCell.latNE, (spatialCell.lngSW + spatialCell.lngNE)/2.0);
- // TODO not actually a weight color :)
- //return weightColor * 1000 * Math.min(distanceBetweenPoints_(point_center, point_left), distanceBetweenPoints_(point_center, point_top));
- return 1000 * Math.min(distanceBetweenPoints_(point_center, point_left), distanceBetweenPoints_(point_center, point_top));
+ // Circle scale modifier =
+ var scale = 500 + 500*(spatialCell.weight / wLimit);
+
+ // Return proportionate value so that circles mostly line up.
+ return scale * Math.min(distanceBetweenPoints_(point_center, point_left), distanceBetweenPoints_(point_center, point_top));
}
/** External Utility Methods **/
diff --git a/asterix-examples/src/main/resources/black-cherry/static/js/rainbowvis.js b/asterix-examples/src/main/resources/black-cherry/static/js/rainbowvis.js
new file mode 100644
index 0000000..1f444dc
--- /dev/null
+++ b/asterix-examples/src/main/resources/black-cherry/static/js/rainbowvis.js
@@ -0,0 +1,178 @@
+/*
+RainbowVis-JS
+Released under MIT License
+
+Source: https://github.com/anomal/RainbowVis-JS
+*/
+
+function Rainbow()
+{
+ var gradients = null;
+ var minNum = 0;
+ var maxNum = 100;
+ var colours = ['ff0000', 'ffff00', '00ff00', '0000ff'];
+ setColours(colours);
+
+ function setColours (spectrum)
+ {
+ if (spectrum.length < 2) {
+ throw new Error('Rainbow must have two or more colours.');
+ } else {
+ var increment = (maxNum - minNum)/(spectrum.length - 1);
+ var firstGradient = new ColourGradient();
+ firstGradient.setGradient(spectrum[0], spectrum[1]);
+ firstGradient.setNumberRange(minNum, minNum + increment);
+ gradients = [ firstGradient ];
+
+ for (var i = 1; i < spectrum.length - 1; i++) {
+ var colourGradient = new ColourGradient();
+ colourGradient.setGradient(spectrum[i], spectrum[i + 1]);
+ colourGradient.setNumberRange(minNum + increment * i, minNum + increment * (i + 1));
+ gradients[i] = colourGradient;
+ }
+
+ colours = spectrum;
+ return this;
+ }
+ }
+
+ this.setColors = this.setColours;
+
+ this.setSpectrum = function ()
+ {
+ setColours(arguments);
+ return this;
+ }
+
+ this.setSpectrumByArray = function (array)
+ {
+ setColours(array);
+ return this;
+ }
+
+ this.colourAt = function (number)
+ {
+ if (isNaN(number)) {
+ throw new TypeError(number + ' is not a number');
+ } else if (gradients.length === 1) {
+ return gradients[0].colourAt(number);
+ } else {
+ var segment = (maxNum - minNum)/(gradients.length);
+ var index = Math.min(Math.floor((Math.max(number, minNum) - minNum)/segment), gradients.length - 1);
+ return gradients[index].colourAt(number);
+ }
+ }
+
+ this.colorAt = this.colourAt;
+
+ this.setNumberRange = function (minNumber, maxNumber)
+ {
+ if (maxNumber > minNumber) {
+ minNum = minNumber;
+ maxNum = maxNumber;
+ setColours(colours);
+ } else {
+ throw new RangeError('maxNumber (' + maxNumber + ') is not greater than minNumber (' + minNumber + ')');
+ }
+ return this;
+ }
+}
+
+function ColourGradient()
+{
+ var startColour = 'ff0000';
+ var endColour = '0000ff';
+ var minNum = 0;
+ var maxNum = 100;
+
+ this.setGradient = function (colourStart, colourEnd)
+ {
+ startColour = getHexColour(colourStart);
+ endColour = getHexColour(colourEnd);
+ }
+
+ this.setNumberRange = function (minNumber, maxNumber)
+ {
+ if (maxNumber > minNumber) {
+ minNum = minNumber;
+ maxNum = maxNumber;
+ } else {
+ throw new RangeError('maxNumber (' + maxNumber + ') is not greater than minNumber (' + minNumber + ')');
+ }
+ }
+
+ this.colourAt = function (number)
+ {
+ return calcHex(number, startColour.substring(0,2), endColour.substring(0,2))
+ + calcHex(number, startColour.substring(2,4), endColour.substring(2,4))
+ + calcHex(number, startColour.substring(4,6), endColour.substring(4,6));
+ }
+
+ function calcHex(number, channelStart_Base16, channelEnd_Base16)
+ {
+ var num = number;
+ if (num < minNum) {
+ num = minNum;
+ }
+ if (num > maxNum) {
+ num = maxNum;
+ }
+ var numRange = maxNum - minNum;
+ var cStart_Base10 = parseInt(channelStart_Base16, 16);
+ var cEnd_Base10 = parseInt(channelEnd_Base16, 16);
+ var cPerUnit = (cEnd_Base10 - cStart_Base10)/numRange;
+ var c_Base10 = Math.round(cPerUnit * (num - minNum) + cStart_Base10);
+ return formatHex(c_Base10.toString(16));
+ }
+
+ formatHex = function (hex)
+ {
+ if (hex.length === 1) {
+ return '0' + hex;
+ } else {
+ return hex;
+ }
+ }
+
+ function isHexColour(string)
+ {
+ var regex = /^#?[0-9a-fA-F]{6}$/i;
+ return regex.test(string);
+ }
+
+ function getHexColour(string)
+ {
+ if (isHexColour(string)) {
+ return string.substring(string.length - 6, string.length);
+ } else {
+ var colourNames =
+ [
+ ['red', 'ff0000'],
+ ['lime', '00ff00'],
+ ['blue', '0000ff'],
+ ['yellow', 'ffff00'],
+ ['orange', 'ff8000'],
+ ['aqua', '00ffff'],
+ ['fuchsia', 'ff00ff'],
+ ['white', 'ffffff'],
+ ['black', '000000'],
+ ['gray', '808080'],
+ ['grey', '808080'],
+ ['silver', 'c0c0c0'],
+ ['maroon', '800000'],
+ ['olive', '808000'],
+ ['green', '008000'],
+ ['teal', '008080'],
+ ['navy', '000080'],
+ ['purple', '800080']
+ ];
+ for (var i = 0; i < colourNames.length; i++) {
+ if (string.toLowerCase() === colourNames[i][0]) {
+ return colourNames[i][1];
+ }
+ }
+ throw new Error(string + ' is not a valid colour.');
+ }
+ }
+}
+