Skip to content

Commit

Permalink
Responsive, choropleth animations, readme and changelog
Browse files Browse the repository at this point in the history
  • Loading branch information
Drew Machat committed May 26, 2015
1 parent a75027e commit 4e484c8
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 46 deletions.
16 changes: 16 additions & 0 deletions 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
6 changes: 6 additions & 0 deletions README.md
Expand Up @@ -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.

Expand Down
4 changes: 2 additions & 2 deletions 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<drew@alleyinteractive.com>",
"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",
Expand Down
9 changes: 7 additions & 2 deletions dev/index.html
Expand Up @@ -41,13 +41,18 @@
html.push('</ul>');
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) {
Expand All @@ -68,7 +73,7 @@
'Light Republican': '#EAA9A8',
defaultFill: '#b9b9b9'
},
data:{
data: {
"AZ": {
"fillKey": "Republican",
"electoralVotes": 5
Expand Down
112 changes: 82 additions & 30 deletions dist/angular-datamaps.js
@@ -1,114 +1,166 @@
'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({
labelColor: options.labelColor ? options.labelColor : '#333333',
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('<div style="position: relative; display: block; padding-bottom: {{ legendHeight }}px;"></div>')(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);
}
};
}
]);
}]);
2 changes: 1 addition & 1 deletion dist/angular-datamaps.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 33 additions & 11 deletions src/directives/datamaps-directive.js
@@ -1,22 +1,24 @@
'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) {

// Generate base map options
function mapOptions() {
return {
element: element[0].children[0],
element: element[0],
scope: 'usa',
height: scope.height,
width: scope.width,
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand All @@ -116,23 +131,30 @@ angular.module('datamaps')
// Fully clear directive element
clearElement: function () {
scope.datamap = null;
element.empty();
var mapContainer = $compile('<div style="position: relative; display: block; padding-bottom: {{ legendHeight }}px;"></div>')(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);
}
Expand Down

0 comments on commit 4e484c8

Please sign in to comment.