From f4527c7e79f3d61d91ab00a5321804aa9e1e8ad0 Mon Sep 17 00:00:00 2001 From: Drew Machat Date: Sun, 23 Nov 2014 03:52:23 -0500 Subject: [PATCH 1/8] align directive input with datamaps structure --- .jshintrc | 5 +- dev/index.html | 86 +++++++++----- dist/angular-datamaps.js | 160 +++++++++++---------------- src/directives/datamaps-directive.js | 135 ++++++---------------- 4 files changed, 161 insertions(+), 225 deletions(-) diff --git a/.jshintrc b/.jshintrc index ff3e162..2c7cfd3 100644 --- a/.jshintrc +++ b/.jshintrc @@ -51,6 +51,7 @@ "pause": false, "protractor": false, "moment": false, - "Datamap": false + "Datamap": false, + "d3": false } -} +}, diff --git a/dev/index.html b/dev/index.html index 64a0bfc..e76176b 100644 --- a/dev/index.html +++ b/dev/index.html @@ -29,36 +29,74 @@ // Scope as the parent of the angular-datamap directive app.controller('SimpleCtrl', function($scope) { $scope.map = { - type: 'usa', - data: [{ - values: [ - { "location": "CA", "value": "Y" }, { "location": "OR", "value": "Y" }, { "location": "WA", "value": "N" }, { "location": "NV", "value": "Y" }, { "location": "ID", "value": "Maybe" }, { "location": "UT", "value": "N" }, { "location": "AZ", "value": "Y" }, { "location": "NM", "value": "N" }, { "location": "AK", "value": "Y" }, { "location": "TX", "value": "Y" }, { "location": "CO", "value": "Maybe" }, { "location": "MT", "value": "N" }, { "location": "WY", "value": "N" }, { "location": "ND", "value": "Y" }, { "location": "SD", "value": "N" }, { "location": "NE", "value": "Y" }, { "location": "KS", "value": "Y" }, { "location": "OK", "value": "N" }, { "location": "MN", "value": "Y" }, { "location": "IA", "value": "N" }, { "location": "MO", "value": "Y" }, { "location": "AR", "value": "Y" }, { "location": "LA", "value": "N" }, { "location": "WI", "value": "Y" }, { "location": "MI", "value": "N" }, { "location": "IL", "value": "Maybe" }, { "location": "TN", "value": "Maybe" }, { "location": "KY", "value": "N" }, { "location": "MS", "value": "Y" }, { "location": "AL", "value": "N" }, { "location": "FL", "value": "Maybe" }, { "location": "GA", "value": "N" }, { "location": "SC", "value": "Y" }, { "location": "NC", "value": "Y" }, { "location": "VA", "value": "N" }, { "location": "WV", "value": "Y" }, { "location": "IN", "value": "Y" }, { "location": "OH", "value": "Y" }, { "location": "PA", "value": "Y" }, { "location": "NY", "value": "Y" }, { "location": "VT", "value": "Y" }, { "location": "NH", "value": "Y" }, { "location": "ME", "value": "Y" }, { "location": "MA", "value": "N" }, { "location": "RI", "value": "N" }, { "location": "CT", "value": "Y" }, { "location": "NJ", "value": "Y" }, { "location": "DE", "value": "N" }, { "location": "MD", "value": "N" }, { "location": "DC", "value": "Y" }, { "location": "HI", "value": "N" } - ] - }], - colors: ['#666666', '#b9b9b9', '#fafafa'], + scope: 'usa', + data: { + "CA": "red", + "OR": { + "value": "Y", + "fillKey": "RED" + }, + "WA": { + "value": "N", + "fillKey": "BLUE" + }, + "NV": { + "value": "Y", + "fillKey": "YELLOW" + }, + "ID": { + "value": "Maybe", + "fillKey": "YELLOW" + }, + "UT": { + "value": "N", + "fillKey": "BLUE" + } + }, + fills: { + "BLUE": "blue", + "YELLOW": "yellow", + "RED": "red", + "defaultFill": "#b9b9b9" + }, options: { width: 1110, legend: true, labels: true, labelColor: '#333333', labelSize: 14, + projection: 'mercator' } } $scope.map2 = { - type: 'world', - data: [{ - values: [ - { "location": "USA", "value": 125 }, - { "location": "CAN", "value": 50 }, - { "location": "IRL", "value": 70 }, - { "location": "RUS", "value": 312 } - ] - }], - colors: ['#666666', '#b9b9b9', '#fafafa'], + scope: 'world', + data: { + "USA": { + "value": 125, + "fillKey": "HIGH" + }, + "CAN": { + "value": 50, + "fillKey": "MEDIUM" + }, + "IRL": { + "value": 70, + "fillKey": "LOW" + }, + "RUS": { + "value": 312, + "fillKey": "HIGH" + } + }, + fills: { + "HIGH": '#666666', + "LOW": '#b9b9b9', + "MEDIUM": '#fafafa', + "defaultFill": "orange" + }, options: { - width: 1110, - fillQuartiles: false + width: 1110 } } @@ -91,10 +129,7 @@

Simple Example - USA

@@ -122,10 +157,7 @@

World Map

diff --git a/dist/angular-datamaps.js b/dist/angular-datamaps.js index d529811..a78c3c8 100644 --- a/dist/angular-datamaps.js +++ b/dist/angular-datamaps.js @@ -1,147 +1,119 @@ 'use strict'; + angular.module('datamaps', []); + 'use strict'; -angular.module('datamaps').directive('datamap', [ - '$compile', - function ($compile) { + +angular.module('datamaps') + + .directive('datamap', ['$compile', function($compile) { return { restrict: 'EA', scope: { - data: '=', - options: '=', - colors: '=?', - type: '@?', - onClick: '&?', - fills: '=?' + map: '=', //datamaps objects [required] + onClick: '&?', //geography onClick event [optional] }, - link: function (scope, element, attrs) { + link: function(scope, element, attrs) { + // Generate base map options - function mapOptions() { + function mapOptions(options) { return { element: element[0].children[0], - scope: scope.type === 'usa' || scope.type === 'world' ? scope.type : 'usa', + scope: 'usa', height: scope.height, width: scope.width, - projection: scope.type === 'world' ? 'mercator' : 'equirectangular', - fills: { defaultFill: '#b9b9b9' }, + fills: { + defaultFill: '#b9b9b9' + }, data: {}, - done: function (datamap) { + done: function(datamap) { if (angular.isDefined(attrs.onClick)) { - datamap.svg.selectAll('.datamaps-subunit').on('click', function (geography) { + datamap.svg.selectAll('.datamaps-subunit').on('click', function(geography) { scope.onClick()(geography); }); } } }; } - // Extend the mapOptions object with data and fill values - function mapData(dst, data) { - angular.forEach(data, function (val) { - dst.data[val.location] = { fillKey: val.value }; - }); - if (angular.isDefined(scope.fills)) { - dst.fills = scope.fills; - } else if (!scope.options.fillQuartiles) { - var fillKeys = []; - angular.forEach(data, function (data) { - if (fillKeys.indexOf(data.value) === -1) { - fillKeys.push(data.value); - } - }); - if (angular.isUndefined(scope.colors)) { - scope.colors = defaultColors(); - } - angular.forEach(fillKeys, function (key, idx) { - dst.fills[key] = scope.colors[idx]; - }); - } else { - } - return dst; - } - // Generate default colors - function defaultColors() { - var d3colors = d3.scale.category20(), colors = []; - for (var i = 0; i < 20; i++) { - colors.push(d3colors(i)); - } - return colors; - } - scope.mapOptions = mapOptions(); + scope.api = { - refresh: function () { - scope.api.updateWithOptions(scope.options, scope.data); + + // Fully refresh directive + refresh: function(map) { + scope.api.updateWithOptions(map); }, - updateWithOptions: function (options, data) { + + // Update chart with new options + updateWithOptions: function(map) { + // Clearing scope.api.clearElement(); - // Exit if options or data are not yet bound - if (!angular.isDefined(options) || !scope.data.length) { - return; - } + // Update bounding box - scope.width = options.width || 600; - scope.height = options.height || scope.width * 0.6; - scope.legendHeight = options.legendHeight || 50; - scope.mapOptions = mapOptions(); - // Add data to map redraw - if (data[0].values.length) { - // update the map element - scope.mapOptions = mapData(scope.mapOptions, data[0].values); - } - scope.mapOptions.geographyConfig = angular.extend({}, options.geographyConfig); - scope.map = new Datamap(scope.mapOptions); + scope.width = map.options.width || 600; + scope.height = map.options.height || scope.width * 0.6; + scope.legendHeight = map.options.legendHeight || 50; + + // Set a few defaults for the directive + scope.mapOptions = mapOptions(map.options); + + // Add the good stuff + scope.mapOptions = angular.extend(scope.mapOptions, map); + + scope.datamap = new Datamap(scope.mapOptions); + + // Update options and choropleth + scope.api.refreshOptions(map.options); + scope.api.updateWithData(map.data); + }, + + // Set options on the datamap + refreshOptions: function(options) { if (!options) { return; } + // set labels if (options.labels) { - scope.map.labels({ + scope.datamap.labels({ labelColor: options.labelColor ? options.labelColor : '#333333', fontSize: options.labelSize ? options.labelSize : 12 }); } + // set legend if (options.legend) { - scope.map.legend(); + scope.datamap.legend(); } }, - updateWithData: function (data) { - scope.map.updateChoropleth(data); + + // Update chart with new data + updateWithData: function(data) { + scope.datamap.updateChoropleth(data); }, + + // Fully clear directive element clearElement: function () { - scope.map = null; + scope.datamap = null; element.empty(); var mapContainer = $compile('
')(scope); element.append(mapContainer); } }; - // Watching on options, colors changing - scope.$watch('[options, colors]', function () { - scope.api.refresh(); - }, true); - // Watch data changing. Only refresh if options or data map points have changed - scope.$watch('data', function (data, old) { + + // Watch data changing + scope.$watch('map', function(map, old) { // Return if no data - if (!data.length) { + if (angular.isUndefined(map) || angular.equals({}, map)) { return; } - if (!old.length || data[0].values.length !== old[0].values.length) { - scope.api.refresh(); + // Init the datamap, or update data + if (!scope.datamap || angular.equals(old.data, map.data)) { + scope.api.refresh(map); } else { - var _data = {}; - angular.forEach(data[0].values, function (val) { - _data[val.location] = { fillKey: val.value }; - // Set extra fields in case we want to use them in the popup - angular.forEach(val, function (prop, key) { - if (key !== 'value' && key !== 'location') { - _data[val.location][key] = prop; - } - }); - }); - scope.api.updateWithData(_data); + scope.api.updateWithData(map.data); } }, true); } }; - } -]); \ No newline at end of file + }]); diff --git a/src/directives/datamaps-directive.js b/src/directives/datamaps-directive.js index bb12b49..28677af 100644 --- a/src/directives/datamaps-directive.js +++ b/src/directives/datamaps-directive.js @@ -6,23 +6,18 @@ angular.module('datamaps') return { restrict: 'EA', scope: { - data: '=', //map data, [required] - options: '=', //map options, [required] - colors: '=?', //map colors array, [optional] - type: '@?', //map scope, world or usa, [optional, defaults to usa] - onClick: '&?', //geography onClick event [optional] - fills: '=?' //fills, for preprocessed fills [optional] + map: '=', //datamaps objects [required] + onClick: '&?', //geography onClick event [optional] }, link: function(scope, element, attrs) { // Generate base map options - function mapOptions() { + function mapOptions(options) { return { element: element[0].children[0], - scope: (scope.type === 'usa' || scope.type === 'world') ? scope.type : 'usa', + scope: 'usa', height: scope.height, width: scope.width, - projection: scope.type === 'world' ? 'mercator' : 'equirectangular', fills: { defaultFill: '#b9b9b9' }, @@ -37,91 +32,46 @@ angular.module('datamaps') }; } - // Extend the mapOptions object with data and fill values - function mapData(dst, data) { - angular.forEach(data, function(val) { - dst.data[val.location] = { fillKey: val.value }; - }); - - if (angular.isDefined(scope.fills)) { - dst.fills = scope.fills; - } else if (!scope.options.fillQuartiles) { - var fillKeys = []; - angular.forEach(data, function(data) { - if (fillKeys.indexOf(data.value) === -1) { - fillKeys.push(data.value); - } - }); - - if (angular.isUndefined(scope.colors)) { - scope.colors = defaultColors(); - } - - angular.forEach(fillKeys, function(key, idx) { - dst.fills[key] = scope.colors[idx]; - }); - } - else { - // @TODO Map numeric ranges to quartiles - } - - return dst; - } - - // Generate default colors - function defaultColors() { - var d3colors = d3.scale.category20(), - colors = []; - for (var i = 0; i < 20; i++ ) { - colors.push(d3colors(i)); - } - return colors; - } - - scope.mapOptions = mapOptions(); - scope.api = { // Fully refresh directive - refresh: function() { - scope.api.updateWithOptions(scope.options, scope.data); + refresh: function(map) { + scope.api.updateWithOptions(map); }, // Update chart with new options - updateWithOptions: function(options, data) { + updateWithOptions: function(map) { // Clearing scope.api.clearElement(); - // Exit if options or data are not yet bound - if (!angular.isDefined(options) || !scope.data.length) { - return; - } - // Update bounding box - scope.width = options.width || 600; - scope.height = options.height || scope.width * 0.6; - scope.legendHeight = options.legendHeight || 50; + scope.width = map.options.width || 600; + scope.height = map.options.height || scope.width * 0.6; + scope.legendHeight = map.options.legendHeight || 50; - scope.mapOptions = mapOptions(); + // Set a few defaults for the directive + scope.mapOptions = mapOptions(map.options); - // Add data to map redraw - if (data[0].values.length) { - // update the map element - scope.mapOptions = mapData(scope.mapOptions, data[0].values); - } + // Add the good stuff + scope.mapOptions = angular.extend(scope.mapOptions, map); - scope.mapOptions.geographyConfig = angular.extend({}, options.geographyConfig); + scope.datamap = new Datamap(scope.mapOptions); - scope.map = new Datamap(scope.mapOptions); + // Update options and choropleth + scope.api.refreshOptions(map.options); + scope.api.updateWithData(map.data); + }, + // Set options on the datamap + refreshOptions: function(options) { if (!options) { return; } // set labels if (options.labels) { - scope.map.labels({ + scope.datamap.labels({ labelColor: options.labelColor ? options.labelColor : '#333333', fontSize: options.labelSize ? options.labelSize : 12 }); @@ -129,54 +79,35 @@ angular.module('datamaps') // set legend if (options.legend) { - scope.map.legend(); + scope.datamap.legend(); } - }, // Update chart with new data updateWithData: function(data) { - scope.map.updateChoropleth(data); + scope.datamap.updateChoropleth(data); }, // Fully clear directive element clearElement: function () { - scope.map = null; + scope.datamap = null; element.empty(); var mapContainer = $compile('
')(scope); element.append(mapContainer); } }; - // Watching on options, colors changing - scope.$watch('[options, colors]', function() { - scope.api.refresh(); - }, true); - - // Watch data changing. Only refresh if options or data map points have changed - scope.$watch('data', function(data, old) { + // Watch data changing + scope.$watch('map', function(map, old) { // Return if no data - if (!data.length) { + if (angular.isUndefined(map) || angular.equals({}, map)) { return; } - if (!old.length || data[0].values.length !== old[0].values.length) { - scope.api.refresh(); - } - else { - var _data = {}; - angular.forEach(data[0].values, function(val) { - _data[val.location] = { - fillKey: val.value - }; - - // Set extra fields in case we want to use them in the popup - angular.forEach(val, function(prop, key) { - if (key !== 'value' && key !== 'location') { - _data[val.location][key] = prop; - } - }); - }); - scope.api.updateWithData(_data); + // Init the datamap, or update data + if (!scope.datamap || angular.equals(old.data, map.data)) { + scope.api.refresh(map); + } else { + scope.api.updateWithData(map.data); } }, true); } From f0a6ec83b297a14d7176aa29702d08208ca590e1 Mon Sep 17 00:00:00 2001 From: Drew Machat Date: Tue, 25 Nov 2014 14:49:22 -0500 Subject: [PATCH 2/8] Updating with plugin and zoomable options --- .jshintrc | 2 +- dev/index.html | 309 ++++++++++++++++++++++++--- dist/angular-datamaps.js | 32 ++- src/directives/datamaps-directive.js | 32 ++- 4 files changed, 334 insertions(+), 41 deletions(-) diff --git a/.jshintrc b/.jshintrc index 2c7cfd3..f6b2e14 100644 --- a/.jshintrc +++ b/.jshintrc @@ -54,4 +54,4 @@ "Datamap": false, "d3": false } -}, +} diff --git a/dev/index.html b/dev/index.html index e76176b..b4c7860 100644 --- a/dev/index.html +++ b/dev/index.html @@ -28,49 +28,272 @@ // Scope as the parent of the angular-datamap directive app.controller('SimpleCtrl', function($scope) { + $scope.plugins = { + "customLegend": function(layer, data, options) { + data = data || {}; + if (!this.options.fills) { + return; + } + var html = [''); + d3.select(this.options.element).append('div') + .attr('class', 'datamaps-legend') + .html(html.join('')); + } + }; + $scope.map = { scope: 'usa', - data: { - "CA": "red", - "OR": { - "value": "Y", - "fillKey": "RED" + options: {}, + geographyConfig: { + highlightBorderColor: '#bada55', + popupTemplate: function(geography, data) { + return '
' + + geography.properties.name + + 'Electoral Votes: ' + + (data.electoralVotes || '0') + + '
'; }, - "WA": { - "value": "N", - "fillKey": "BLUE" + highlightBorderWidth: 3 + }, + fills: { + 'Republican': '#CC4731', + 'Democrat': '#306596', + 'Heavy Democrat': '#667FAF', + 'Light Democrat': '#A9C0DE', + 'Heavy Republican': '#CA5E5B', + 'Light Republican': '#EAA9A8', + defaultFill: '#b9b9b9' + }, + data:{ + "AZ": { + "fillKey": "Republican", + "electoralVotes": 5 }, - "NV": { - "value": "Y", - "fillKey": "YELLOW" + "CO": { + "fillKey": "Light Democrat", + "electoralVotes": 5 + }, + "DE": { + "fillKey": "Democrat", + "electoralVotes": 32 + }, + "FL": { + "electoralVotes": 29 + }, + "GA": { + "fillKey": "Republican", + "electoralVotes": 32 + }, + "HI": { + "fillKey": "Democrat", + "electoralVotes": 32 }, "ID": { - "value": "Maybe", - "fillKey": "YELLOW" + "fillKey": "Republican", + "electoralVotes": 32 + }, + "IL": { + "fillKey": "Democrat", + "electoralVotes": 32 + }, + "IN": { + "fillKey": "Republican", + "electoralVotes": 11 + }, + "IA": { + "fillKey": "Light Democrat", + "electoralVotes": 11 + }, + "KS": { + "fillKey": "Republican", + "electoralVotes": 32 + }, + "KY": { + "fillKey": "Republican", + "electoralVotes": 32 + }, + "LA": { + "fillKey": "Republican", + "electoralVotes": 32 + }, + "MD": { + "fillKey": "Democrat", + "electoralVotes": 32 + }, + "ME": { + "fillKey": "Democrat", + "electoralVotes": 32 + }, + "MA": { + "fillKey": "Democrat", + "electoralVotes": 32 + }, + "MN": { + "fillKey": "Democrat", + "electoralVotes": 32 + }, + "MI": { + "fillKey": "Democrat", + "electoralVotes": 32 + }, + "MS": { + "fillKey": "Republican", + "electoralVotes": 32 + }, + "MO": { + "fillKey": "Republican", + "electoralVotes": 13 + }, + "MT": { + "fillKey": "Republican", + "electoralVotes": 32 + }, + "NC": { + "fillKey": "Light Republican", + "electoralVotes": 32 + }, + "NE": { + "fillKey": "Republican", + "electoralVotes": 32 + }, + "NV": { + "fillKey": "Heavy Democrat", + "electoralVotes": 32 + }, + "NH": { + "fillKey": "Light Democrat", + "electoralVotes": 32 + }, + "NJ": { + "fillKey": "Democrat", + "electoralVotes": 32 + }, + "NY": { + "fillKey": "Democrat", + "electoralVotes": 32 + }, + "ND": { + "fillKey": "Republican", + "electoralVotes": 32 + }, + "NM": { + "fillKey": "Democrat", + "electoralVotes": 32 + }, + "OH": { + "electoralVotes": 32 + }, + "OK": { + "fillKey": "Republican", + "electoralVotes": 32 + }, + "OR": { + "fillKey": "Democrat", + "electoralVotes": 32 + }, + "PA": { + "fillKey": "Democrat", + "electoralVotes": 32 + }, + "RI": { + "fillKey": "Democrat", + "electoralVotes": 32 + }, + "SC": { + "fillKey": "Republican", + "electoralVotes": 32 + }, + "SD": { + "fillKey": "Republican", + "electoralVotes": 32 + }, + "TN": { + "fillKey": "Republican", + "electoralVotes": 32 + }, + "TX": { + "fillKey": "Republican", + "electoralVotes": 32 }, "UT": { - "value": "N", - "fillKey": "BLUE" + "fillKey": "Republican", + "electoralVotes": 32 + }, + "WI": { + "fillKey": "Democrat", + "electoralVotes": 32 + }, + "VA": { + "fillKey": "Light Democrat", + "electoralVotes": 32 + }, + "VT": { + "fillKey": "Democrat", + "electoralVotes": 32 + }, + "WA": { + "fillKey": "Democrat", + "electoralVotes": 32 + }, + "WV": { + "fillKey": "Republican", + "electoralVotes": 32 + }, + "WY": { + "fillKey": "Republican", + "electoralVotes": 32 + }, + "CA": { + "fillKey": "Democrat", + "electoralVotes": 32 + }, + "CT": { + "fillKey": "Democrat", + "electoralVotes": 32 + }, + "AK": { + "fillKey": "Republican", + "electoralVotes": 32 + }, + "AR": { + "fillKey": "Republican", + "electoralVotes": 32 + }, + "AL": { + "fillKey": "Republican", + "electoralVotes": 32 } - }, - fills: { - "BLUE": "blue", - "YELLOW": "yellow", - "RED": "red", - "defaultFill": "#b9b9b9" - }, - options: { - width: 1110, - legend: true, - labels: true, - labelColor: '#333333', - labelSize: 14, - projection: 'mercator' } - } + } $scope.map2 = { - scope: 'world', + scope: "world", + projection: "mercator", data: { "USA": { "value": 125, @@ -87,12 +310,32 @@ "RUS": { "value": 312, "fillKey": "HIGH" + }, + "JPN": { + "fillKey": "LOW", + "value": 75 + }, + "ITA": { + "fillKey": "LOW", + "value": 55 + }, + "CRI": { + "fillKey": "MEDIUM", + "value": 130 + }, + "KOR": { + "fillKey": "HIGH", + "value": 230 + }, + "DEU": { + "fillKey": "HIGH", + "value": 300 } }, fills: { "HIGH": '#666666', "LOW": '#b9b9b9', - "MEDIUM": '#fafafa', + "MEDIUM": '#abdda4', "defaultFill": "orange" }, options: { @@ -130,6 +373,8 @@

Simple Example - USA

diff --git a/dist/angular-datamaps.js b/dist/angular-datamaps.js index a78c3c8..0a6f5a6 100644 --- a/dist/angular-datamaps.js +++ b/dist/angular-datamaps.js @@ -11,12 +11,14 @@ angular.module('datamaps') restrict: 'EA', scope: { map: '=', //datamaps objects [required] + plugins: '=?', //datamaps plugins [optional] + zoomable: '@', //zoomable toggle [optional] onClick: '&?', //geography onClick event [optional] }, link: function(scope, element, attrs) { // Generate base map options - function mapOptions(options) { + function mapOptions() { return { element: element[0].children[0], scope: 'usa', @@ -32,6 +34,14 @@ angular.module('datamaps') scope.onClick()(geography); }); } + if (angular.isDefined(attrs.zoomable)) { + datamap.svg.call(d3.behavior.zoom() + .on('zoom', redraw)); + } + function redraw() { + datamap.svg.selectAll('g') + .attr('transform', 'translate(' + d3.event.translate + ')scale(' + d3.event.scale + ')'); + } } }; } @@ -50,9 +60,9 @@ angular.module('datamaps') scope.api.clearElement(); // Update bounding box - scope.width = map.options.width || 600; - scope.height = map.options.height || scope.width * 0.6; - scope.legendHeight = map.options.legendHeight || 50; + scope.width = (map.options || {}).width || null; + scope.height = (map.options || {}).height || (scope.width ? scope.width * 0.6 : null); + scope.legendHeight = (map.options || {}).legendHeight || 50; // Set a few defaults for the directive scope.mapOptions = mapOptions(map.options); @@ -62,11 +72,25 @@ angular.module('datamaps') scope.datamap = new Datamap(scope.mapOptions); + // Update plugins + scope.api.updatePlugins(scope.datamap); + // Update options and choropleth scope.api.refreshOptions(map.options); scope.api.updateWithData(map.data); }, + // Add and initialize optional plugins + updatePlugins: function(datamap) { + if (!scope.plugins) { + return; + } + angular.forEach(scope.plugins, function(plugin, name) { + datamap.addPlugin(name, plugin); + datamap[name](); + }); + }, + // Set options on the datamap refreshOptions: function(options) { if (!options) { diff --git a/src/directives/datamaps-directive.js b/src/directives/datamaps-directive.js index 28677af..7118a9f 100644 --- a/src/directives/datamaps-directive.js +++ b/src/directives/datamaps-directive.js @@ -7,12 +7,14 @@ angular.module('datamaps') restrict: 'EA', scope: { map: '=', //datamaps objects [required] + plugins: '=?', //datamaps plugins [optional] + zoomable: '@', //zoomable toggle [optional] onClick: '&?', //geography onClick event [optional] }, link: function(scope, element, attrs) { // Generate base map options - function mapOptions(options) { + function mapOptions() { return { element: element[0].children[0], scope: 'usa', @@ -28,6 +30,14 @@ angular.module('datamaps') scope.onClick()(geography); }); } + if (angular.isDefined(attrs.zoomable)) { + datamap.svg.call(d3.behavior.zoom() + .on('zoom', redraw)); + } + function redraw() { + datamap.svg.selectAll('g') + .attr('transform', 'translate(' + d3.event.translate + ')scale(' + d3.event.scale + ')'); + } } }; } @@ -46,9 +56,9 @@ angular.module('datamaps') scope.api.clearElement(); // Update bounding box - scope.width = map.options.width || 600; - scope.height = map.options.height || scope.width * 0.6; - scope.legendHeight = map.options.legendHeight || 50; + scope.width = (map.options || {}).width || null; + scope.height = (map.options || {}).height || (scope.width ? scope.width * 0.6 : null); + scope.legendHeight = (map.options || {}).legendHeight || 50; // Set a few defaults for the directive scope.mapOptions = mapOptions(map.options); @@ -58,11 +68,25 @@ angular.module('datamaps') scope.datamap = new Datamap(scope.mapOptions); + // Update plugins + scope.api.updatePlugins(scope.datamap); + // Update options and choropleth scope.api.refreshOptions(map.options); scope.api.updateWithData(map.data); }, + // Add and initialize optional plugins + updatePlugins: function(datamap) { + if (!scope.plugins) { + return; + } + angular.forEach(scope.plugins, function(plugin, name) { + datamap.addPlugin(name, plugin); + datamap[name](); + }); + }, + // Set options on the datamap refreshOptions: function(options) { if (!options) { From 413b5e5f031b01911ae6bdff73003b243a3ee3e0 Mon Sep 17 00:00:00 2001 From: Drew Machat Date: Thu, 4 Dec 2014 00:40:08 -0500 Subject: [PATCH 3/8] Don't hoist zoom redraw --- dist/angular-datamaps.js | 78 +++++++++------------------- dist/angular-datamaps.min.js | 2 +- src/directives/datamaps-directive.js | 8 +-- 3 files changed, 29 insertions(+), 59 deletions(-) diff --git a/dist/angular-datamaps.js b/dist/angular-datamaps.js index 0a6f5a6..78c576c 100644 --- a/dist/angular-datamaps.js +++ b/dist/angular-datamaps.js @@ -1,22 +1,18 @@ 'use strict'; - angular.module('datamaps', []); - 'use strict'; - -angular.module('datamaps') - - .directive('datamap', ['$compile', function($compile) { +angular.module('datamaps').directive('datamap', [ + '$compile', + function ($compile) { return { restrict: 'EA', scope: { - map: '=', //datamaps objects [required] - plugins: '=?', //datamaps plugins [optional] - zoomable: '@', //zoomable toggle [optional] - onClick: '&?', //geography onClick event [optional] + map: '=', + plugins: '=?', + zoomable: '@', + onClick: '&?' }, - link: function(scope, element, attrs) { - + link: function (scope, element, attrs) { // Generate base map options function mapOptions() { return { @@ -24,79 +20,58 @@ angular.module('datamaps') scope: 'usa', height: scope.height, width: scope.width, - fills: { - defaultFill: '#b9b9b9' - }, + fills: { defaultFill: '#b9b9b9' }, data: {}, - done: function(datamap) { + done: function (datamap) { + function redraw() { + datamap.svg.selectAll('g').attr('transform', 'translate(' + d3.event.translate + ')scale(' + d3.event.scale + ')'); + } if (angular.isDefined(attrs.onClick)) { - datamap.svg.selectAll('.datamaps-subunit').on('click', function(geography) { + datamap.svg.selectAll('.datamaps-subunit').on('click', function (geography) { scope.onClick()(geography); }); } if (angular.isDefined(attrs.zoomable)) { - datamap.svg.call(d3.behavior.zoom() - .on('zoom', redraw)); - } - function redraw() { - datamap.svg.selectAll('g') - .attr('transform', 'translate(' + d3.event.translate + ')scale(' + d3.event.scale + ')'); + datamap.svg.call(d3.behavior.zoom().on('zoom', redraw)); } } }; } - scope.api = { - - // Fully refresh directive - refresh: function(map) { + refresh: function (map) { scope.api.updateWithOptions(map); }, - - // Update chart with new options - updateWithOptions: function(map) { - + updateWithOptions: function (map) { // Clearing scope.api.clearElement(); - // Update bounding box scope.width = (map.options || {}).width || null; scope.height = (map.options || {}).height || (scope.width ? scope.width * 0.6 : null); scope.legendHeight = (map.options || {}).legendHeight || 50; - // Set a few defaults for the directive scope.mapOptions = mapOptions(map.options); - // Add the good stuff scope.mapOptions = angular.extend(scope.mapOptions, map); - scope.datamap = new Datamap(scope.mapOptions); - // Update plugins scope.api.updatePlugins(scope.datamap); - // Update options and choropleth scope.api.refreshOptions(map.options); scope.api.updateWithData(map.data); }, - - // Add and initialize optional plugins - updatePlugins: function(datamap) { + updatePlugins: function (datamap) { if (!scope.plugins) { return; } - angular.forEach(scope.plugins, function(plugin, name) { + angular.forEach(scope.plugins, function (plugin, name) { datamap.addPlugin(name, plugin); datamap[name](); }); }, - - // Set options on the datamap - refreshOptions: function(options) { + refreshOptions: function (options) { if (!options) { return; } - // set labels if (options.labels) { scope.datamap.labels({ @@ -104,19 +79,14 @@ angular.module('datamaps') fontSize: options.labelSize ? options.labelSize : 12 }); } - // set legend if (options.legend) { scope.datamap.legend(); } }, - - // Update chart with new data - updateWithData: function(data) { + updateWithData: function (data) { scope.datamap.updateChoropleth(data); }, - - // Fully clear directive element clearElement: function () { scope.datamap = null; element.empty(); @@ -124,9 +94,8 @@ angular.module('datamaps') element.append(mapContainer); } }; - // Watch data changing - scope.$watch('map', function(map, old) { + scope.$watch('map', function (map, old) { // Return if no data if (angular.isUndefined(map) || angular.equals({}, map)) { return; @@ -140,4 +109,5 @@ angular.module('datamaps') }, true); } }; - }]); + } +]); \ No newline at end of file diff --git a/dist/angular-datamaps.min.js b/dist/angular-datamaps.min.js index 2b7b819..18bd2ec 100644 --- a/dist/angular-datamaps.min.js +++ b/dist/angular-datamaps.min.js @@ -1 +1 @@ -"use strict";angular.module("datamaps",[]),angular.module("datamaps").directive("datamap",["$compile",function(a){return{restrict:"EA",scope:{data:"=",options:"=",colors:"=?",type:"@?",onClick:"&?",fills:"=?"},link:function(b,c,d){function e(){return{element:c[0].children[0],scope:"usa"===b.type||"world"===b.type?b.type:"usa",height:b.height,width:b.width,projection:"world"===b.type?"mercator":"equirectangular",fills:{defaultFill:"#b9b9b9"},data:{},done:function(a){angular.isDefined(d.onClick)&&a.svg.selectAll(".datamaps-subunit").on("click",function(a){b.onClick()(a)})}}}function f(a,c){if(angular.forEach(c,function(b){a.data[b.location]={fillKey:b.value}}),angular.isDefined(b.fills))a.fills=b.fills;else if(!b.options.fillQuartiles){var d=[];angular.forEach(c,function(a){-1===d.indexOf(a.value)&&d.push(a.value)}),angular.isUndefined(b.colors)&&(b.colors=g()),angular.forEach(d,function(c,d){a.fills[c]=b.colors[d]})}return a}function g(){for(var a=d3.scale.category20(),b=[],c=0;20>c;c++)b.push(a(c));return b}b.mapOptions=e(),b.api={refresh:function(){b.api.updateWithOptions(b.options,b.data)},updateWithOptions:function(a,c){b.api.clearElement(),angular.isDefined(a)&&b.data.length&&(b.width=a.width||600,b.height=a.height||.6*b.width,b.legendHeight=a.legendHeight||50,b.mapOptions=e(),c[0].values.length&&(b.mapOptions=f(b.mapOptions,c[0].values)),b.mapOptions.geographyConfig=angular.extend({},a.geographyConfig),b.map=new Datamap(b.mapOptions),a&&(a.labels&&b.map.labels({labelColor:a.labelColor?a.labelColor:"#333333",fontSize:a.labelSize?a.labelSize:12}),a.legend&&b.map.legend()))},updateWithData:function(a){b.map.updateChoropleth(a)},clearElement:function(){b.map=null,c.empty();var d=a('
')(b);c.append(d)}},b.$watch("[options, colors]",function(){b.api.refresh()},!0),b.$watch("data",function(a,c){if(a.length)if(c.length&&a[0].values.length===c[0].values.length){var d={};angular.forEach(a[0].values,function(a){d[a.location]={fillKey:a.value},angular.forEach(a,function(b,c){"value"!==c&&"location"!==c&&(d[a.location][c]=b)})}),b.api.updateWithData(d)}else b.api.refresh()},!0)}}}]); \ No newline at end of file +"use strict";angular.module("datamaps",[]),angular.module("datamaps").directive("datamap",["$compile",function(a){return{restrict:"EA",scope:{map:"=",plugins:"=?",zoomable:"@",onClick:"&?"},link:function(b,c,d){function e(){return{element:c[0].children[0],scope:"usa",height:b.height,width:b.width,fills:{defaultFill:"#b9b9b9"},data:{},done:function(a){function c(){a.svg.selectAll("g").attr("transform","translate("+d3.event.translate+")scale("+d3.event.scale+")")}angular.isDefined(d.onClick)&&a.svg.selectAll(".datamaps-subunit").on("click",function(a){b.onClick()(a)}),angular.isDefined(d.zoomable)&&a.svg.call(d3.behavior.zoom().on("zoom",c))}}}b.api={refresh:function(a){b.api.updateWithOptions(a)},updateWithOptions:function(a){b.api.clearElement(),b.width=(a.options||{}).width||null,b.height=(a.options||{}).height||(b.width?.6*b.width:null),b.legendHeight=(a.options||{}).legendHeight||50,b.mapOptions=e(a.options),b.mapOptions=angular.extend(b.mapOptions,a),b.datamap=new Datamap(b.mapOptions),b.api.updatePlugins(b.datamap),b.api.refreshOptions(a.options),b.api.updateWithData(a.data)},updatePlugins:function(a){b.plugins&&angular.forEach(b.plugins,function(b,c){a.addPlugin(c,b),a[c]()})},refreshOptions:function(a){a&&(a.labels&&b.datamap.labels({labelColor:a.labelColor?a.labelColor:"#333333",fontSize:a.labelSize?a.labelSize:12}),a.legend&&b.datamap.legend())},updateWithData:function(a){b.datamap.updateChoropleth(a)},clearElement:function(){b.datamap=null,c.empty();var d=a('
')(b);c.append(d)}},b.$watch("map",function(a,c){angular.isUndefined(a)||angular.equals({},a)||(!b.datamap||angular.equals(c.data,a.data)?b.api.refresh(a):b.api.updateWithData(a.data))},!0)}}}]); \ No newline at end of file diff --git a/src/directives/datamaps-directive.js b/src/directives/datamaps-directive.js index 7118a9f..a6197c0 100644 --- a/src/directives/datamaps-directive.js +++ b/src/directives/datamaps-directive.js @@ -25,6 +25,10 @@ angular.module('datamaps') }, data: {}, done: function(datamap) { + function redraw() { + datamap.svg.selectAll('g') + .attr('transform', 'translate(' + d3.event.translate + ')scale(' + d3.event.scale + ')'); + } if (angular.isDefined(attrs.onClick)) { datamap.svg.selectAll('.datamaps-subunit').on('click', function(geography) { scope.onClick()(geography); @@ -34,10 +38,6 @@ angular.module('datamaps') datamap.svg.call(d3.behavior.zoom() .on('zoom', redraw)); } - function redraw() { - datamap.svg.selectAll('g') - .attr('transform', 'translate(' + d3.event.translate + ')scale(' + d3.event.scale + ')'); - } } }; } From 7ccde924eabde7a65926f57769ffdabb95d50e85 Mon Sep 17 00:00:00 2001 From: Drew Machat Date: Fri, 5 Dec 2014 12:56:33 -0500 Subject: [PATCH 4/8] Update plugins when data changes, bring README and example up to date --- README.md | 100 +++++++++++++++++++-------- dev/index.html | 39 +++-------- dist/angular-datamaps.js | 5 +- dist/angular-datamaps.min.js | 2 +- src/directives/datamaps-directive.js | 5 +- 5 files changed, 85 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index f15ebe1..ea92131 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,18 @@ # Angular Datamaps + +### Note: This directive's scope values have changed as of v0.1.0 to better match the object structure used by DataMaps. + Provides an Angular directive to wrap https://github.com/markmarkoh/datamaps and easily build data maps in your Angular application. - - Lightweight - Automatically updates on changes to bound data and options - - No dependencies beside d3, topojson and datamaps + - onClick events integrate with your parent controllers + - Evaluates plugins passed to the directive + - Easily toggle zoom functionality - Documentation for available options can be found at https://github.com/markmarkoh/datamaps ![Datamap example](/usaMap.png?raw=true "USA Map Example") ## Install -Clone to your vendor directory -```sh -git clone git@github.com:dmachat/angular-datamaps.git -``` - -OR - Install with bower and save to your project's bower.json ```sh bower install angular-datamaps --save @@ -28,7 +25,7 @@ angular.module('app', [ ]); ``` -Load the datamaps and the two libraries datamaps depends on (d3 and topojson). +Load DataMaps and the two libraries DataMaps depends on (d3 and topojson). ```html @@ -36,10 +33,7 @@ Load the datamaps and the two libraries datamaps depends on (d3 and topojson). ``` @@ -47,32 +41,44 @@ Load the datamaps and the two libraries datamaps depends on (d3 and topojson). Add a map configuration object to your scope to bind to the directive ```js $scope.map = { - type: 'usa', - data: [{ - values: [ - { "location": "USA", "value": 125 }, - { "location": "CAN", "value": 50 }, - { "location": "FRA", "value": 70 }, - { "location": "RUS", "value": 312 } - ] - }], - colors: ['#666666', '#b9b9b9', '#fafafa'], + scope: 'usa', options: { width: 1110, legendHeight: 60 // optionally set the padding for the legend - } + }, + geographyConfig: { + highlighBorderColor: '#EAA9A8', + highlighBorderWidth: 2 + }, + fills: { + 'HIGH': '#CC4731', + 'MEDIUM': '#306596', + 'LOW': '#667FAF', + 'defaultFill': '#DDDDDD' + }, + data: { + "AZ": { + "fillkey": "MEDIUM", + }, + "CO": { + "fillkey": "HIGH", + }, + "DE": { + "fillkey": "LOW", + }, + "GA": { + "fillkey": "MEDIUM", + }, + }, } ``` ### Geography click events ### -The datamaps click event can trigger a bound function with the clicked geography object. Just add your custom function to the `on-click` attribute, like this (notice there are no parenthesis): +The DataMaps click event can trigger a bound function with the clicked geography object. just add your custom function to the `on-click` attribute, like this (notice there are no parenthesis): ```html @@ -87,6 +93,40 @@ $scope.updateActiveGeography = function(geography) { } ``` +### Toggle zoom ### +Set the `zoomable` attribute to toggle a simple zoom on the map. + +### Adding plugins ### +You may add plugins that will be evaluated by the DataMaps plugin system in order to extend the labels or legend, for example. Use it by providing an object with plugin functions keyed by name. + +```html + + +``` + +```js +$scope.mapObject = mapObject; +$scope.mapPlugins = { + customLegend: function(layer, data, options) { + var html = ['
    '], + label = ''; + for (var fillKey in this.options.fills) { + html.push('
  • ', + fillKey, + '
  • '); + } + html.push('
'); + d3.select(this.options.element).append('div') + .attr('class', 'datamaps-legend') + .html(html.join('')); + } +}; +``` + ## Build it yourself! angular-datamaps is built with grunt. diff --git a/dev/index.html b/dev/index.html index b4c7860..d78f828 100644 --- a/dev/index.html +++ b/dev/index.html @@ -29,34 +29,13 @@ // Scope as the parent of the angular-datamap directive app.controller('SimpleCtrl', function($scope) { $scope.plugins = { - "customLegend": function(layer, data, options) { - data = data || {}; - if (!this.options.fills) { - return; - } + customLegend: function(layer, data, options) { var html = ['
    '], label = ''; - - if (data.legendTitle) { - html.unshift('

    ' + data.legendTitle + '

    '); - } for (var fillKey in this.options.fills) { - if (fillKey === 'defaultFill') { - if (!data.defaultFillName) { - continue; - } - label = data.defaultFillName; - } else { - if (data.labels && data.labels[fillKey]) { - label = data.labels[fillKey]; - } else { - label = fillKey; - } - } - html.push('
  • ' + - label + + html.push('
  • ', + (fillKey === 'defaultFill' ? 'N/A' : fillKey), '
  • '); } html.push('
'); @@ -103,7 +82,8 @@ "electoralVotes": 32 }, "FL": { - "electoralVotes": 29 + "electoralVotes": 29, + "fillKey": "defaultFill" }, "GA": { "fillKey": "Republican", @@ -206,7 +186,8 @@ "electoralVotes": 32 }, "OH": { - "electoralVotes": 32 + "electoralVotes": 32, + "fillKey": "defaultFill" }, "OK": { "fillKey": "Republican", @@ -337,12 +318,8 @@ "LOW": '#b9b9b9', "MEDIUM": '#abdda4', "defaultFill": "orange" - }, - options: { - width: 1110 } } - }); // Start it up diff --git a/dist/angular-datamaps.js b/dist/angular-datamaps.js index 78c576c..2bb7d31 100644 --- a/dist/angular-datamaps.js +++ b/dist/angular-datamaps.js @@ -9,7 +9,7 @@ angular.module('datamaps').directive('datamap', [ scope: { map: '=', plugins: '=?', - zoomable: '@', + zoomable: '@?', onClick: '&?' }, link: function (scope, element, attrs) { @@ -46,7 +46,7 @@ angular.module('datamaps').directive('datamap', [ scope.api.clearElement(); // Update bounding box scope.width = (map.options || {}).width || null; - scope.height = (map.options || {}).height || (scope.width ? scope.width * 0.6 : null); + scope.height = (map.options || {}).height || (scope.width ? scope.width * 0.5 : null); scope.legendHeight = (map.options || {}).legendHeight || 50; // Set a few defaults for the directive scope.mapOptions = mapOptions(map.options); @@ -86,6 +86,7 @@ angular.module('datamaps').directive('datamap', [ }, updateWithData: function (data) { scope.datamap.updateChoropleth(data); + scope.api.updatePlugins(scope.datamap); }, clearElement: function () { scope.datamap = null; diff --git a/dist/angular-datamaps.min.js b/dist/angular-datamaps.min.js index 18bd2ec..1b01553 100644 --- a/dist/angular-datamaps.min.js +++ b/dist/angular-datamaps.min.js @@ -1 +1 @@ -"use strict";angular.module("datamaps",[]),angular.module("datamaps").directive("datamap",["$compile",function(a){return{restrict:"EA",scope:{map:"=",plugins:"=?",zoomable:"@",onClick:"&?"},link:function(b,c,d){function e(){return{element:c[0].children[0],scope:"usa",height:b.height,width:b.width,fills:{defaultFill:"#b9b9b9"},data:{},done:function(a){function c(){a.svg.selectAll("g").attr("transform","translate("+d3.event.translate+")scale("+d3.event.scale+")")}angular.isDefined(d.onClick)&&a.svg.selectAll(".datamaps-subunit").on("click",function(a){b.onClick()(a)}),angular.isDefined(d.zoomable)&&a.svg.call(d3.behavior.zoom().on("zoom",c))}}}b.api={refresh:function(a){b.api.updateWithOptions(a)},updateWithOptions:function(a){b.api.clearElement(),b.width=(a.options||{}).width||null,b.height=(a.options||{}).height||(b.width?.6*b.width:null),b.legendHeight=(a.options||{}).legendHeight||50,b.mapOptions=e(a.options),b.mapOptions=angular.extend(b.mapOptions,a),b.datamap=new Datamap(b.mapOptions),b.api.updatePlugins(b.datamap),b.api.refreshOptions(a.options),b.api.updateWithData(a.data)},updatePlugins:function(a){b.plugins&&angular.forEach(b.plugins,function(b,c){a.addPlugin(c,b),a[c]()})},refreshOptions:function(a){a&&(a.labels&&b.datamap.labels({labelColor:a.labelColor?a.labelColor:"#333333",fontSize:a.labelSize?a.labelSize:12}),a.legend&&b.datamap.legend())},updateWithData:function(a){b.datamap.updateChoropleth(a)},clearElement:function(){b.datamap=null,c.empty();var d=a('
')(b);c.append(d)}},b.$watch("map",function(a,c){angular.isUndefined(a)||angular.equals({},a)||(!b.datamap||angular.equals(c.data,a.data)?b.api.refresh(a):b.api.updateWithData(a.data))},!0)}}}]); \ No newline at end of file +"use strict";angular.module("datamaps",[]),angular.module("datamaps").directive("datamap",["$compile",function(a){return{restrict:"EA",scope:{map:"=",plugins:"=?",zoomable:"@?",onClick:"&?"},link:function(b,c,d){function e(){return{element:c[0].children[0],scope:"usa",height:b.height,width:b.width,fills:{defaultFill:"#b9b9b9"},data:{},done:function(a){function c(){a.svg.selectAll("g").attr("transform","translate("+d3.event.translate+")scale("+d3.event.scale+")")}angular.isDefined(d.onClick)&&a.svg.selectAll(".datamaps-subunit").on("click",function(a){b.onClick()(a)}),angular.isDefined(d.zoomable)&&a.svg.call(d3.behavior.zoom().on("zoom",c))}}}b.api={refresh:function(a){b.api.updateWithOptions(a)},updateWithOptions:function(a){b.api.clearElement(),b.width=(a.options||{}).width||null,b.height=(a.options||{}).height||(b.width?.5*b.width:null),b.legendHeight=(a.options||{}).legendHeight||50,b.mapOptions=e(a.options),b.mapOptions=angular.extend(b.mapOptions,a),b.datamap=new Datamap(b.mapOptions),b.api.updatePlugins(b.datamap),b.api.refreshOptions(a.options),b.api.updateWithData(a.data)},updatePlugins:function(a){b.plugins&&angular.forEach(b.plugins,function(b,c){a.addPlugin(c,b),a[c]()})},refreshOptions:function(a){a&&(a.labels&&b.datamap.labels({labelColor:a.labelColor?a.labelColor:"#333333",fontSize:a.labelSize?a.labelSize:12}),a.legend&&b.datamap.legend())},updateWithData:function(a){b.datamap.updateChoropleth(a),b.api.updatePlugins(b.datamap)},clearElement:function(){b.datamap=null,c.empty();var d=a('
')(b);c.append(d)}},b.$watch("map",function(a,c){angular.isUndefined(a)||angular.equals({},a)||(!b.datamap||angular.equals(c.data,a.data)?b.api.refresh(a):b.api.updateWithData(a.data))},!0)}}}]); \ No newline at end of file diff --git a/src/directives/datamaps-directive.js b/src/directives/datamaps-directive.js index a6197c0..8888773 100644 --- a/src/directives/datamaps-directive.js +++ b/src/directives/datamaps-directive.js @@ -8,7 +8,7 @@ angular.module('datamaps') scope: { map: '=', //datamaps objects [required] plugins: '=?', //datamaps plugins [optional] - zoomable: '@', //zoomable toggle [optional] + zoomable: '@?', //zoomable toggle [optional] onClick: '&?', //geography onClick event [optional] }, link: function(scope, element, attrs) { @@ -57,7 +57,7 @@ angular.module('datamaps') // Update bounding box scope.width = (map.options || {}).width || null; - scope.height = (map.options || {}).height || (scope.width ? scope.width * 0.6 : null); + scope.height = (map.options || {}).height || (scope.width ? scope.width * 0.5 : null); scope.legendHeight = (map.options || {}).legendHeight || 50; // Set a few defaults for the directive @@ -110,6 +110,7 @@ angular.module('datamaps') // Update chart with new data updateWithData: function(data) { scope.datamap.updateChoropleth(data); + scope.api.updatePlugins(scope.datamap); }, // Fully clear directive element From a75027e3579441fa5afd710c53fa49b59b930789 Mon Sep 17 00:00:00 2001 From: Drew Machat Date: Fri, 5 Dec 2014 17:00:22 -0500 Subject: [PATCH 5/8] Remove unused argument --- dist/angular-datamaps.js | 2 +- dist/angular-datamaps.min.js | 2 +- src/directives/datamaps-directive.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/angular-datamaps.js b/dist/angular-datamaps.js index 2bb7d31..b877f11 100644 --- a/dist/angular-datamaps.js +++ b/dist/angular-datamaps.js @@ -49,7 +49,7 @@ angular.module('datamaps').directive('datamap', [ scope.height = (map.options || {}).height || (scope.width ? scope.width * 0.5 : null); scope.legendHeight = (map.options || {}).legendHeight || 50; // Set a few defaults for the directive - scope.mapOptions = mapOptions(map.options); + scope.mapOptions = mapOptions(); // Add the good stuff scope.mapOptions = angular.extend(scope.mapOptions, map); scope.datamap = new Datamap(scope.mapOptions); diff --git a/dist/angular-datamaps.min.js b/dist/angular-datamaps.min.js index 1b01553..03462f5 100644 --- a/dist/angular-datamaps.min.js +++ b/dist/angular-datamaps.min.js @@ -1 +1 @@ -"use strict";angular.module("datamaps",[]),angular.module("datamaps").directive("datamap",["$compile",function(a){return{restrict:"EA",scope:{map:"=",plugins:"=?",zoomable:"@?",onClick:"&?"},link:function(b,c,d){function e(){return{element:c[0].children[0],scope:"usa",height:b.height,width:b.width,fills:{defaultFill:"#b9b9b9"},data:{},done:function(a){function c(){a.svg.selectAll("g").attr("transform","translate("+d3.event.translate+")scale("+d3.event.scale+")")}angular.isDefined(d.onClick)&&a.svg.selectAll(".datamaps-subunit").on("click",function(a){b.onClick()(a)}),angular.isDefined(d.zoomable)&&a.svg.call(d3.behavior.zoom().on("zoom",c))}}}b.api={refresh:function(a){b.api.updateWithOptions(a)},updateWithOptions:function(a){b.api.clearElement(),b.width=(a.options||{}).width||null,b.height=(a.options||{}).height||(b.width?.5*b.width:null),b.legendHeight=(a.options||{}).legendHeight||50,b.mapOptions=e(a.options),b.mapOptions=angular.extend(b.mapOptions,a),b.datamap=new Datamap(b.mapOptions),b.api.updatePlugins(b.datamap),b.api.refreshOptions(a.options),b.api.updateWithData(a.data)},updatePlugins:function(a){b.plugins&&angular.forEach(b.plugins,function(b,c){a.addPlugin(c,b),a[c]()})},refreshOptions:function(a){a&&(a.labels&&b.datamap.labels({labelColor:a.labelColor?a.labelColor:"#333333",fontSize:a.labelSize?a.labelSize:12}),a.legend&&b.datamap.legend())},updateWithData:function(a){b.datamap.updateChoropleth(a),b.api.updatePlugins(b.datamap)},clearElement:function(){b.datamap=null,c.empty();var d=a('
')(b);c.append(d)}},b.$watch("map",function(a,c){angular.isUndefined(a)||angular.equals({},a)||(!b.datamap||angular.equals(c.data,a.data)?b.api.refresh(a):b.api.updateWithData(a.data))},!0)}}}]); \ No newline at end of file +"use strict";angular.module("datamaps",[]),angular.module("datamaps").directive("datamap",["$compile",function(a){return{restrict:"EA",scope:{map:"=",plugins:"=?",zoomable:"@?",onClick:"&?"},link:function(b,c,d){function e(){return{element:c[0].children[0],scope:"usa",height:b.height,width:b.width,fills:{defaultFill:"#b9b9b9"},data:{},done:function(a){function c(){a.svg.selectAll("g").attr("transform","translate("+d3.event.translate+")scale("+d3.event.scale+")")}angular.isDefined(d.onClick)&&a.svg.selectAll(".datamaps-subunit").on("click",function(a){b.onClick()(a)}),angular.isDefined(d.zoomable)&&a.svg.call(d3.behavior.zoom().on("zoom",c))}}}b.api={refresh:function(a){b.api.updateWithOptions(a)},updateWithOptions:function(a){b.api.clearElement(),b.width=(a.options||{}).width||null,b.height=(a.options||{}).height||(b.width?.5*b.width:null),b.legendHeight=(a.options||{}).legendHeight||50,b.mapOptions=e(),b.mapOptions=angular.extend(b.mapOptions,a),b.datamap=new Datamap(b.mapOptions),b.api.updatePlugins(b.datamap),b.api.refreshOptions(a.options),b.api.updateWithData(a.data)},updatePlugins:function(a){b.plugins&&angular.forEach(b.plugins,function(b,c){a.addPlugin(c,b),a[c]()})},refreshOptions:function(a){a&&(a.labels&&b.datamap.labels({labelColor:a.labelColor?a.labelColor:"#333333",fontSize:a.labelSize?a.labelSize:12}),a.legend&&b.datamap.legend())},updateWithData:function(a){b.datamap.updateChoropleth(a),b.api.updatePlugins(b.datamap)},clearElement:function(){b.datamap=null,c.empty();var d=a('
')(b);c.append(d)}},b.$watch("map",function(a,c){angular.isUndefined(a)||angular.equals({},a)||(!b.datamap||angular.equals(c.data,a.data)?b.api.refresh(a):b.api.updateWithData(a.data))},!0)}}}]); \ No newline at end of file diff --git a/src/directives/datamaps-directive.js b/src/directives/datamaps-directive.js index 8888773..4cc8f57 100644 --- a/src/directives/datamaps-directive.js +++ b/src/directives/datamaps-directive.js @@ -61,7 +61,7 @@ angular.module('datamaps') scope.legendHeight = (map.options || {}).legendHeight || 50; // Set a few defaults for the directive - scope.mapOptions = mapOptions(map.options); + scope.mapOptions = mapOptions(); // Add the good stuff scope.mapOptions = angular.extend(scope.mapOptions, map); From 4e484c81469ff04573ecf85d3c4f6ceefe99690b Mon Sep 17 00:00:00 2001 From: Drew Machat Date: Mon, 25 May 2015 22:15:24 -0400 Subject: [PATCH 6/8] Responsive, choropleth animations, readme and changelog --- CHANGELOG.md | 16 ++++ README.md | 6 ++ bower.json | 4 +- dev/index.html | 9 ++- dist/angular-datamaps.js | 112 ++++++++++++++++++++------- dist/angular-datamaps.min.js | 2 +- src/directives/datamaps-directive.js | 44 ++++++++--- 7 files changed, 147 insertions(+), 46 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b566abd --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,16 @@ +## 0.1.0 +###### _May 25, 2015_ + +##### Breaking Changes +- Refactored scope API variables + - `scope.map` is now the only required input, and maps directly to the object required by Datamaps + +##### General +- Updated Datamaps dependency to v0.4.0 +- Remove unneccessary non-semantic markup hindering Datamap rendering +- API for loading plugins for Datamaps has been added + - Example plugin with a custom legend included in Readme +- Responsive binding has been added +- Zoomable option has been added +- Allow updateChoropleth when geographies don't change +- Slightly cleaner watch diff --git a/README.md b/README.md index ea92131..4d3fb95 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,12 @@ $scope.updateActiveGeography = function(geography) { ### Toggle zoom ### Set the `zoomable` attribute to toggle a simple zoom on the map. +### Responsive ### +Bind the built-in Datamaps responsive methods by setting `$scope.mapObject.responsive = true`. + +### Animated Update Choropleth ### +Set `options.staticGeoData = true` to allow the map to update with only `updateChoropleth`. Update choropleth only works if _updating_ is all we're doing. If geographies are added or removed from data, we have to redraw the map, so use this to explicitly say whether or not the directive can update choropleth mappings only. + ### Adding plugins ### You may add plugins that will be evaluated by the DataMaps plugin system in order to extend the labels or legend, for example. Use it by providing an object with plugin functions keyed by name. diff --git a/bower.json b/bower.json index 4ffec4c..cda744b 100644 --- a/bower.json +++ b/bower.json @@ -1,12 +1,12 @@ { "name": "angular-datamaps", "description": "AngularJS Datamaps -- provides an Angular directive to wrap https://github.com/markmarkoh/datamaps", - "version": "0.0.4", + "version": "0.0.5", "author": "Drew Machat", "main": "dist/angular-datamaps.min.js", "dependencies": { "angular": ">=1.0.8", - "datamaps": "~0.3.2" + "datamaps": "~0.4.0" }, "devDependencies": { "es5-shim": "~2.1.0", diff --git a/dev/index.html b/dev/index.html index d78f828..96d2d25 100644 --- a/dev/index.html +++ b/dev/index.html @@ -41,13 +41,18 @@ html.push(''); d3.select(this.options.element).append('div') .attr('class', 'datamaps-legend') + .style('position', 'absolute') + .style('bottom', 0) .html(html.join('')); } }; $scope.map = { scope: 'usa', - options: {}, + responsive: true, + options: { + staticGeoData: true + }, geographyConfig: { highlightBorderColor: '#bada55', popupTemplate: function(geography, data) { @@ -68,7 +73,7 @@ 'Light Republican': '#EAA9A8', defaultFill: '#b9b9b9' }, - data:{ + data: { "AZ": { "fillKey": "Republican", "electoralVotes": 5 diff --git a/dist/angular-datamaps.js b/dist/angular-datamaps.js index b877f11..a655127 100644 --- a/dist/angular-datamaps.js +++ b/dist/angular-datamaps.js @@ -1,77 +1,111 @@ 'use strict'; + angular.module('datamaps', []); + 'use strict'; -angular.module('datamaps').directive('datamap', [ - '$compile', - function ($compile) { + +angular + + .module('datamaps') + + .directive('datamap', ['$window', function($window) { return { restrict: 'EA', scope: { - map: '=', - plugins: '=?', - zoomable: '@?', - onClick: '&?' + map: '=', //datamaps objects [required] + plugins: '=?', //datamaps plugins [optional] + zoomable: '@?', //zoomable toggle [optional] + onClick: '&?', //geography onClick event [optional] }, - link: function (scope, element, attrs) { + link: function(scope, element, attrs) { + // Generate base map options function mapOptions() { return { - element: element[0].children[0], + element: element[0], scope: 'usa', height: scope.height, width: scope.width, - fills: { defaultFill: '#b9b9b9' }, + fills: { + defaultFill: '#b9b9b9' + }, data: {}, - done: function (datamap) { + done: function(datamap) { function redraw() { - datamap.svg.selectAll('g').attr('transform', 'translate(' + d3.event.translate + ')scale(' + d3.event.scale + ')'); + datamap.svg.selectAll('g') + .attr('transform', 'translate(' + d3.event.translate + ')scale(' + d3.event.scale + ')'); } if (angular.isDefined(attrs.onClick)) { - datamap.svg.selectAll('.datamaps-subunit').on('click', function (geography) { + datamap.svg.selectAll('.datamaps-subunit').on('click', function(geography) { scope.onClick()(geography); }); } if (angular.isDefined(attrs.zoomable)) { - datamap.svg.call(d3.behavior.zoom().on('zoom', redraw)); + datamap.svg.call(d3.behavior.zoom() + .on('zoom', redraw)); } } }; } + scope.api = { - refresh: function (map) { + + // Fully refresh directive + refresh: function(map) { scope.api.updateWithOptions(map); }, - updateWithOptions: function (map) { + + // Update chart with new options + updateWithOptions: function(map) { + // Clearing scope.api.clearElement(); + // Update bounding box scope.width = (map.options || {}).width || null; scope.height = (map.options || {}).height || (scope.width ? scope.width * 0.5 : null); scope.legendHeight = (map.options || {}).legendHeight || 50; + // Set a few defaults for the directive scope.mapOptions = mapOptions(); + // Add the good stuff scope.mapOptions = angular.extend(scope.mapOptions, map); + scope.datamap = new Datamap(scope.mapOptions); + + // Add responsive listeners + if (scope.mapOptions.responsive) { + $window.addEventListener('resize', scope.api.resize); + } else { + $window.removeEventListener('resize', scope.api.resize); + } + // Update plugins scope.api.updatePlugins(scope.datamap); + // Update options and choropleth scope.api.refreshOptions(map.options); scope.api.updateWithData(map.data); }, - updatePlugins: function (datamap) { + + // Add and initialize optional plugins + updatePlugins: function(datamap) { if (!scope.plugins) { return; } - angular.forEach(scope.plugins, function (plugin, name) { + angular.forEach(scope.plugins, function(plugin, name) { datamap.addPlugin(name, plugin); datamap[name](); }); }, - refreshOptions: function (options) { + + // Set options on the datamap + refreshOptions: function(options) { if (!options) { return; } + // set labels if (options.labels) { scope.datamap.labels({ @@ -79,36 +113,54 @@ angular.module('datamaps').directive('datamap', [ fontSize: options.labelSize ? options.labelSize : 12 }); } + // set legend if (options.legend) { scope.datamap.legend(); } }, - updateWithData: function (data) { + + // Trigger datamaps resize method + resize: function() { + console.log('resize attempt'); + scope.datamap.resize(); + }, + + // Update chart with new data + updateWithData: function(data) { scope.datamap.updateChoropleth(data); scope.api.updatePlugins(scope.datamap); }, + + // Fully clear directive element clearElement: function () { scope.datamap = null; - element.empty(); - var mapContainer = $compile('
')(scope); - element.append(mapContainer); + element + .empty() + .css({ + 'position': 'relative', + 'display': 'block', + 'padding-bottom': scope.legendHeight + 'px' + }); } }; + // Watch data changing - scope.$watch('map', function (map, old) { + scope.$watch('map', function(map, old) { // Return if no data - if (angular.isUndefined(map) || angular.equals({}, map)) { + if (!map || angular.equals({}, map)) { return; } - // Init the datamap, or update data - if (!scope.datamap || angular.equals(old.data, map.data)) { + // Allow animated transition when geos don't change + // or fully refresh + if (!scope.datamap || angular.equals(map.data, old.data)) { scope.api.refresh(map); - } else { + } else if ((map.options || {}).staticGeoData) { scope.api.updateWithData(map.data); + } else { + scope.api.refresh(map); } }, true); } }; - } -]); \ No newline at end of file + }]); diff --git a/dist/angular-datamaps.min.js b/dist/angular-datamaps.min.js index 03462f5..43ea69c 100644 --- a/dist/angular-datamaps.min.js +++ b/dist/angular-datamaps.min.js @@ -1 +1 @@ -"use strict";angular.module("datamaps",[]),angular.module("datamaps").directive("datamap",["$compile",function(a){return{restrict:"EA",scope:{map:"=",plugins:"=?",zoomable:"@?",onClick:"&?"},link:function(b,c,d){function e(){return{element:c[0].children[0],scope:"usa",height:b.height,width:b.width,fills:{defaultFill:"#b9b9b9"},data:{},done:function(a){function c(){a.svg.selectAll("g").attr("transform","translate("+d3.event.translate+")scale("+d3.event.scale+")")}angular.isDefined(d.onClick)&&a.svg.selectAll(".datamaps-subunit").on("click",function(a){b.onClick()(a)}),angular.isDefined(d.zoomable)&&a.svg.call(d3.behavior.zoom().on("zoom",c))}}}b.api={refresh:function(a){b.api.updateWithOptions(a)},updateWithOptions:function(a){b.api.clearElement(),b.width=(a.options||{}).width||null,b.height=(a.options||{}).height||(b.width?.5*b.width:null),b.legendHeight=(a.options||{}).legendHeight||50,b.mapOptions=e(),b.mapOptions=angular.extend(b.mapOptions,a),b.datamap=new Datamap(b.mapOptions),b.api.updatePlugins(b.datamap),b.api.refreshOptions(a.options),b.api.updateWithData(a.data)},updatePlugins:function(a){b.plugins&&angular.forEach(b.plugins,function(b,c){a.addPlugin(c,b),a[c]()})},refreshOptions:function(a){a&&(a.labels&&b.datamap.labels({labelColor:a.labelColor?a.labelColor:"#333333",fontSize:a.labelSize?a.labelSize:12}),a.legend&&b.datamap.legend())},updateWithData:function(a){b.datamap.updateChoropleth(a),b.api.updatePlugins(b.datamap)},clearElement:function(){b.datamap=null,c.empty();var d=a('
')(b);c.append(d)}},b.$watch("map",function(a,c){angular.isUndefined(a)||angular.equals({},a)||(!b.datamap||angular.equals(c.data,a.data)?b.api.refresh(a):b.api.updateWithData(a.data))},!0)}}}]); \ No newline at end of file +"use strict";angular.module("datamaps",[]),angular.module("datamaps").directive("datamap",["$compile",function(a){return{restrict:"EA",scope:{map:"=",plugins:"=?",zoomable:"@?",onClick:"&?"},link:function(b,c,d){function e(){return{element:c[0].children[0],scope:"usa",height:b.height,width:b.width,fills:{defaultFill:"#b9b9b9"},data:{},done:function(a){function c(){a.svg.selectAll("g").attr("transform","translate("+d3.event.translate+")scale("+d3.event.scale+")")}angular.isDefined(d.onClick)&&a.svg.selectAll(".datamaps-subunit").on("click",function(a){b.onClick()(a)}),angular.isDefined(d.zoomable)&&a.svg.call(d3.behavior.zoom().on("zoom",c))}}}b.api={refresh:function(a){b.api.updateWithOptions(a)},updateWithOptions:function(a){b.api.clearElement(),b.width=(a.options||{}).width||null,b.height=(a.options||{}).height||(b.width?.5*b.width:null),b.legendHeight=(a.options||{}).legendHeight||50,b.mapOptions=e(),b.mapOptions=angular.extend(b.mapOptions,a),b.datamap=new Datamap(b.mapOptions),b.api.updatePlugins(b.datamap),b.api.refreshOptions(a.options),b.api.updateWithData(a.data)},updatePlugins:function(a){b.plugins&&angular.forEach(b.plugins,function(b,c){a.addPlugin(c,b),a[c]()})},refreshOptions:function(a){a&&(a.labels&&b.datamap.labels({labelColor:a.labelColor?a.labelColor:"#333333",fontSize:a.labelSize?a.labelSize:12}),a.legend&&b.datamap.legend(),a.responsive?window.addEventListener("resize",b.api.resize):window.removeEventListener("resize",b.api.resize))},resize:function(){b.datamap.resize()},updateWithData:function(a){b.datamap.updateChoropleth(a),b.api.updatePlugins(b.datamap)},clearElement:function(){b.datamap=null,c.empty();var d=a('
')(b);c.append(d)}},b.$watch("map",function(a,c){angular.isUndefined(a)||angular.equals({},a)||(b.datamap&&angular.equals(c,a)?b.api.updateWithData(a.data):b.api.refresh(a))},!0)}}}]); \ No newline at end of file diff --git a/src/directives/datamaps-directive.js b/src/directives/datamaps-directive.js index 4cc8f57..f2b6160 100644 --- a/src/directives/datamaps-directive.js +++ b/src/directives/datamaps-directive.js @@ -1,14 +1,16 @@ 'use strict'; -angular.module('datamaps') +angular - .directive('datamap', ['$compile', function($compile) { + .module('datamaps') + + .directive('datamap', ['$window', function($window) { return { restrict: 'EA', scope: { map: '=', //datamaps objects [required] plugins: '=?', //datamaps plugins [optional] - zoomable: '@?', //zoomable toggle [optional] + zoomable: '@?', //zoomable toggle [optional] onClick: '&?', //geography onClick event [optional] }, link: function(scope, element, attrs) { @@ -16,7 +18,7 @@ angular.module('datamaps') // Generate base map options function mapOptions() { return { - element: element[0].children[0], + element: element[0], scope: 'usa', height: scope.height, width: scope.width, @@ -68,6 +70,13 @@ angular.module('datamaps') scope.datamap = new Datamap(scope.mapOptions); + // Add responsive listeners + if (scope.mapOptions.responsive) { + $window.addEventListener('resize', scope.api.resize); + } else { + $window.removeEventListener('resize', scope.api.resize); + } + // Update plugins scope.api.updatePlugins(scope.datamap); @@ -107,6 +116,12 @@ angular.module('datamaps') } }, + // Trigger datamaps resize method + resize: function() { + console.log('resize attempt'); + scope.datamap.resize(); + }, + // Update chart with new data updateWithData: function(data) { scope.datamap.updateChoropleth(data); @@ -116,23 +131,30 @@ angular.module('datamaps') // Fully clear directive element clearElement: function () { scope.datamap = null; - element.empty(); - var mapContainer = $compile('
')(scope); - element.append(mapContainer); + element + .empty() + .css({ + 'position': 'relative', + 'display': 'block', + 'padding-bottom': scope.legendHeight + 'px' + }); } }; // Watch data changing scope.$watch('map', function(map, old) { // Return if no data - if (angular.isUndefined(map) || angular.equals({}, map)) { + if (!map || angular.equals({}, map)) { return; } - // Init the datamap, or update data - if (!scope.datamap || angular.equals(old.data, map.data)) { + // Allow animated transition when geos don't change + // or fully refresh + if (!scope.datamap || angular.equals(map.data, old.data)) { scope.api.refresh(map); - } else { + } else if ((map.options || {}).staticGeoData) { scope.api.updateWithData(map.data); + } else { + scope.api.refresh(map); } }, true); } From 73a62190b835d90ef5ad852f12b0e4434d70db52 Mon Sep 17 00:00:00 2001 From: Drew Machat Date: Mon, 25 May 2015 22:32:42 -0400 Subject: [PATCH 7/8] Minified for release --- Gruntfile.js | 3 +- dist/angular-datamaps.js | 93 +++++++++++------------------------- dist/angular-datamaps.min.js | 2 +- 3 files changed, 31 insertions(+), 67 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index c0fea71..46bf90a 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -126,7 +126,7 @@ module.exports = function (grunt) { '<%= config.dist %>/<%= config.minified %>': '<%= config.dist %>/<%= config.module %>' }, options: { - compress: true + compress: {} } } }, @@ -172,6 +172,7 @@ module.exports = function (grunt) { grunt.registerTask('dev', [ 'clean:dev', 'concat:dev', + 'ngmin:dist', 'jshint', 'connect:livereload', 'watch' diff --git a/dist/angular-datamaps.js b/dist/angular-datamaps.js index a655127..b26a45d 100644 --- a/dist/angular-datamaps.js +++ b/dist/angular-datamaps.js @@ -1,24 +1,18 @@ 'use strict'; - angular.module('datamaps', []); - 'use strict'; - -angular - - .module('datamaps') - - .directive('datamap', ['$window', function($window) { +angular.module('datamaps').directive('datamap', [ + '$window', + function ($window) { return { restrict: 'EA', scope: { - map: '=', //datamaps objects [required] - plugins: '=?', //datamaps plugins [optional] - zoomable: '@?', //zoomable toggle [optional] - onClick: '&?', //geography onClick event [optional] + map: '=', + plugins: '=?', + zoomable: '@?', + onClick: '&?' }, - link: function(scope, element, attrs) { - + link: function (scope, element, attrs) { // Generate base map options function mapOptions() { return { @@ -26,86 +20,64 @@ angular scope: 'usa', height: scope.height, width: scope.width, - fills: { - defaultFill: '#b9b9b9' - }, + fills: { defaultFill: '#b9b9b9' }, data: {}, - done: function(datamap) { + done: function (datamap) { function redraw() { - datamap.svg.selectAll('g') - .attr('transform', 'translate(' + d3.event.translate + ')scale(' + d3.event.scale + ')'); + datamap.svg.selectAll('g').attr('transform', 'translate(' + d3.event.translate + ')scale(' + d3.event.scale + ')'); } if (angular.isDefined(attrs.onClick)) { - datamap.svg.selectAll('.datamaps-subunit').on('click', function(geography) { + datamap.svg.selectAll('.datamaps-subunit').on('click', function (geography) { scope.onClick()(geography); }); } if (angular.isDefined(attrs.zoomable)) { - datamap.svg.call(d3.behavior.zoom() - .on('zoom', redraw)); + datamap.svg.call(d3.behavior.zoom().on('zoom', redraw)); } } }; } - scope.api = { - - // Fully refresh directive - refresh: function(map) { + refresh: function (map) { scope.api.updateWithOptions(map); }, - - // Update chart with new options - updateWithOptions: function(map) { - + updateWithOptions: function (map) { // Clearing scope.api.clearElement(); - // Update bounding box scope.width = (map.options || {}).width || null; scope.height = (map.options || {}).height || (scope.width ? scope.width * 0.5 : null); scope.legendHeight = (map.options || {}).legendHeight || 50; - // Set a few defaults for the directive scope.mapOptions = mapOptions(); - // Add the good stuff scope.mapOptions = angular.extend(scope.mapOptions, map); - scope.datamap = new Datamap(scope.mapOptions); - // Add responsive listeners if (scope.mapOptions.responsive) { $window.addEventListener('resize', scope.api.resize); } else { $window.removeEventListener('resize', scope.api.resize); } - // Update plugins scope.api.updatePlugins(scope.datamap); - // Update options and choropleth scope.api.refreshOptions(map.options); scope.api.updateWithData(map.data); }, - - // Add and initialize optional plugins - updatePlugins: function(datamap) { + updatePlugins: function (datamap) { if (!scope.plugins) { return; } - angular.forEach(scope.plugins, function(plugin, name) { + angular.forEach(scope.plugins, function (plugin, name) { datamap.addPlugin(name, plugin); datamap[name](); }); }, - - // Set options on the datamap - refreshOptions: function(options) { + refreshOptions: function (options) { if (!options) { return; } - // set labels if (options.labels) { scope.datamap.labels({ @@ -113,40 +85,30 @@ angular fontSize: options.labelSize ? options.labelSize : 12 }); } - // set legend if (options.legend) { scope.datamap.legend(); } }, - - // Trigger datamaps resize method - resize: function() { + resize: function () { console.log('resize attempt'); scope.datamap.resize(); }, - - // Update chart with new data - updateWithData: function(data) { + updateWithData: function (data) { scope.datamap.updateChoropleth(data); scope.api.updatePlugins(scope.datamap); }, - - // Fully clear directive element clearElement: function () { scope.datamap = null; - element - .empty() - .css({ - 'position': 'relative', - 'display': 'block', - 'padding-bottom': scope.legendHeight + 'px' - }); + element.empty().css({ + 'position': 'relative', + 'display': 'block', + 'padding-bottom': scope.legendHeight + 'px' + }); } }; - // Watch data changing - scope.$watch('map', function(map, old) { + scope.$watch('map', function (map, old) { // Return if no data if (!map || angular.equals({}, map)) { return; @@ -163,4 +125,5 @@ angular }, true); } }; - }]); + } +]); \ No newline at end of file diff --git a/dist/angular-datamaps.min.js b/dist/angular-datamaps.min.js index 43ea69c..284a2a4 100644 --- a/dist/angular-datamaps.min.js +++ b/dist/angular-datamaps.min.js @@ -1 +1 @@ -"use strict";angular.module("datamaps",[]),angular.module("datamaps").directive("datamap",["$compile",function(a){return{restrict:"EA",scope:{map:"=",plugins:"=?",zoomable:"@?",onClick:"&?"},link:function(b,c,d){function e(){return{element:c[0].children[0],scope:"usa",height:b.height,width:b.width,fills:{defaultFill:"#b9b9b9"},data:{},done:function(a){function c(){a.svg.selectAll("g").attr("transform","translate("+d3.event.translate+")scale("+d3.event.scale+")")}angular.isDefined(d.onClick)&&a.svg.selectAll(".datamaps-subunit").on("click",function(a){b.onClick()(a)}),angular.isDefined(d.zoomable)&&a.svg.call(d3.behavior.zoom().on("zoom",c))}}}b.api={refresh:function(a){b.api.updateWithOptions(a)},updateWithOptions:function(a){b.api.clearElement(),b.width=(a.options||{}).width||null,b.height=(a.options||{}).height||(b.width?.5*b.width:null),b.legendHeight=(a.options||{}).legendHeight||50,b.mapOptions=e(),b.mapOptions=angular.extend(b.mapOptions,a),b.datamap=new Datamap(b.mapOptions),b.api.updatePlugins(b.datamap),b.api.refreshOptions(a.options),b.api.updateWithData(a.data)},updatePlugins:function(a){b.plugins&&angular.forEach(b.plugins,function(b,c){a.addPlugin(c,b),a[c]()})},refreshOptions:function(a){a&&(a.labels&&b.datamap.labels({labelColor:a.labelColor?a.labelColor:"#333333",fontSize:a.labelSize?a.labelSize:12}),a.legend&&b.datamap.legend(),a.responsive?window.addEventListener("resize",b.api.resize):window.removeEventListener("resize",b.api.resize))},resize:function(){b.datamap.resize()},updateWithData:function(a){b.datamap.updateChoropleth(a),b.api.updatePlugins(b.datamap)},clearElement:function(){b.datamap=null,c.empty();var d=a('
')(b);c.append(d)}},b.$watch("map",function(a,c){angular.isUndefined(a)||angular.equals({},a)||(b.datamap&&angular.equals(c,a)?b.api.updateWithData(a.data):b.api.refresh(a))},!0)}}}]); \ No newline at end of file +"use strict";angular.module("datamaps",[]),angular.module("datamaps").directive("datamap",["$window",function(a){return{restrict:"EA",scope:{map:"=",plugins:"=?",zoomable:"@?",onClick:"&?"},link:function(b,c,d){function e(){return{element:c[0],scope:"usa",height:b.height,width:b.width,fills:{defaultFill:"#b9b9b9"},data:{},done:function(a){function c(){a.svg.selectAll("g").attr("transform","translate("+d3.event.translate+")scale("+d3.event.scale+")")}angular.isDefined(d.onClick)&&a.svg.selectAll(".datamaps-subunit").on("click",function(a){b.onClick()(a)}),angular.isDefined(d.zoomable)&&a.svg.call(d3.behavior.zoom().on("zoom",c))}}}b.api={refresh:function(a){b.api.updateWithOptions(a)},updateWithOptions:function(c){b.api.clearElement(),b.width=(c.options||{}).width||null,b.height=(c.options||{}).height||(b.width?.5*b.width:null),b.legendHeight=(c.options||{}).legendHeight||50,b.mapOptions=e(),b.mapOptions=angular.extend(b.mapOptions,c),b.datamap=new Datamap(b.mapOptions),b.mapOptions.responsive?a.addEventListener("resize",b.api.resize):a.removeEventListener("resize",b.api.resize),b.api.updatePlugins(b.datamap),b.api.refreshOptions(c.options),b.api.updateWithData(c.data)},updatePlugins:function(a){b.plugins&&angular.forEach(b.plugins,function(b,c){a.addPlugin(c,b),a[c]()})},refreshOptions:function(a){a&&(a.labels&&b.datamap.labels({labelColor:a.labelColor?a.labelColor:"#333333",fontSize:a.labelSize?a.labelSize:12}),a.legend&&b.datamap.legend())},resize:function(){console.log("resize attempt"),b.datamap.resize()},updateWithData:function(a){b.datamap.updateChoropleth(a),b.api.updatePlugins(b.datamap)},clearElement:function(){b.datamap=null,c.empty().css({position:"relative",display:"block","padding-bottom":b.legendHeight+"px"})}},b.$watch("map",function(a,c){a&&!angular.equals({},a)&&(!b.datamap||angular.equals(a.data,c.data)?b.api.refresh(a):(a.options||{}).staticGeoData?b.api.updateWithData(a.data):b.api.refresh(a))},!0)}}}]); \ No newline at end of file From 72cae5550aae256edfaab2c88d456f09b827fd43 Mon Sep 17 00:00:00 2001 From: Drew Machat Date: Mon, 25 May 2015 22:35:24 -0400 Subject: [PATCH 8/8] match bower version to tag --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index cda744b..a7e6e4a 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "angular-datamaps", "description": "AngularJS Datamaps -- provides an Angular directive to wrap https://github.com/markmarkoh/datamaps", - "version": "0.0.5", + "version": "0.1.0", "author": "Drew Machat", "main": "dist/angular-datamaps.min.js", "dependencies": {