openstudio-workflow 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ # Packaged Measure Folder
2
+
3
+ This folder is used to ship packaged measures with the AMIs.
4
+
5
+ Current the only packaged measure is the StandardReports because it is run every simulation. This folder will be copied into each data_point folder for now, but will eventually be part of the standard upload of the project and live in the scripts
@@ -0,0 +1,212 @@
1
+ require 'erb'
2
+
3
+ #start the measure
4
+ class StandardReports < OpenStudio::Ruleset::ReportingUserScript
5
+
6
+ #define the name that a user will see, this method may be deprecated as
7
+ #the display name in PAT comes from the name field in measure.xml
8
+ def name
9
+ return "Standard Reports"
10
+ end
11
+
12
+ #define the arguments that the user will input
13
+ def arguments()
14
+ args = OpenStudio::Ruleset::OSArgumentVector.new
15
+
16
+ return args
17
+ end #end the arguments method
18
+
19
+ #sql_query method
20
+ def sql_query(sql, report_name, query)
21
+ val = 10e9
22
+ result = sql.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='#{report_name}' AND #{query}")
23
+ if not result.empty?
24
+ val = result.get
25
+ end
26
+ return val
27
+ end
28
+
29
+ #define what happens when the measure is run
30
+ def run(runner, user_arguments)
31
+ super(runner, user_arguments)
32
+
33
+ #use the built-in error checking
34
+ if not runner.validateUserArguments(arguments(), user_arguments)
35
+ return false
36
+ end
37
+
38
+ os_version = OpenStudio::VersionString.new(OpenStudio::openStudioVersion())
39
+ min_version_feature1 = OpenStudio::VersionString.new("1.2.3")
40
+
41
+ # get the last model and sql file
42
+
43
+ model = runner.lastOpenStudioModel
44
+ if model.empty?
45
+ runner.registerError("Cannot find last model.")
46
+ return false
47
+ end
48
+ model = model.get
49
+
50
+ sqlFile = runner.lastEnergyPlusSqlFile
51
+ if sqlFile.empty?
52
+ runner.registerError("Cannot find last sql file.")
53
+ return false
54
+ end
55
+ sqlFile = sqlFile.get
56
+ model.setSqlFile(sqlFile)
57
+
58
+ # put data into variables, these are available in the local scope binding
59
+ #building_name = model.getBuilding.name.get
60
+
61
+ web_asset_path = OpenStudio::getSharedResourcesPath() / OpenStudio::Path.new("web_assets")
62
+
63
+ energy = "var consumption = {\n"
64
+ fuel_type = ""
65
+ units = ""
66
+
67
+ site_energy_use = 0.0
68
+ OpenStudio::EndUseFuelType::getValues.each do |fuel_type|
69
+ energy << "\t\""
70
+ fuel_type = OpenStudio::EndUseFuelType.new(fuel_type).valueDescription
71
+ energy << OpenStudio::EndUseFuelType.new(fuel_type).valueDescription # append this to remove whitespace between words ".delete(' ')"
72
+ energy << " Consumption\":{\n\t\t\"units\":"
73
+ if fuel_type == "Electricity"
74
+ units = "\"kWh\""
75
+ unit_str = "kWh"
76
+ else
77
+ units = "\"Million Btu\""
78
+ unit_str = "MBtu"
79
+ end
80
+ fuel_type_aggregation = 0.0
81
+ energy << units
82
+ energy << ",\n\t\t\"data\":{\n\t\t\t\""
83
+ OpenStudio::EndUseCategoryType::getValues.each do |category_type|
84
+ fuel_and_category_aggregation = 0.0
85
+ category_str = OpenStudio::EndUseCategoryType.new(category_type).valueDescription
86
+ energy << category_str # append this to remove whitespace between words ".delete(' ')"
87
+ energy << "\":["
88
+ OpenStudio::MonthOfYear::getValues.each do |month|
89
+ if month >= 1 and month <= 12
90
+ if not sqlFile.energyConsumptionByMonth(OpenStudio::EndUseFuelType.new(fuel_type),
91
+ OpenStudio::EndUseCategoryType.new(category_type),
92
+ OpenStudio::MonthOfYear.new(month)).empty?
93
+ valInJ = sqlFile.energyConsumptionByMonth(OpenStudio::EndUseFuelType.new(fuel_type),
94
+ OpenStudio::EndUseCategoryType.new(category_type),
95
+ OpenStudio::MonthOfYear.new(month)).get
96
+ fuel_and_category_aggregation += valInJ
97
+ valInUnits = OpenStudio::convert(valInJ,"J",unit_str).get()
98
+ temp = sprintf "%.3f", valInUnits
99
+ energy << temp.to_s
100
+ energy << ","
101
+ if os_version >= min_version_feature1
102
+ month_str = OpenStudio::MonthOfYear.new(month).valueDescription
103
+ prefix_str = OpenStudio::toUnderscoreCase("#{fuel_type}_#{category_str}_#{month_str}")
104
+ runner.registerValue("#{prefix_str}_si",valInJ,"J")
105
+ runner.registerValue("#{prefix_str}_ip",valInUnits,unit_str)
106
+ end
107
+ else
108
+ energy << "0,"
109
+ end
110
+ end
111
+ end
112
+ energy = energy[0..-2]
113
+ energy << "],\n\t\t\t\""
114
+ if (os_version >= min_version_feature1)
115
+ prefix_str = OpenStudio::toUnderscoreCase("#{fuel_type}_#{category_str}")
116
+ runner.registerValue("#{prefix_str}_si",fuel_and_category_aggregation,"J")
117
+ runner.registerValue("#{prefix_str}_ip",OpenStudio::convert(fuel_and_category_aggregation,"J",unit_str).get,unit_str)
118
+ end
119
+ fuel_type_aggregation += fuel_and_category_aggregation
120
+ end
121
+ energy = energy[0..-7]
122
+ energy << "\n\t\t}\n\t},\n"
123
+ if (os_version >= min_version_feature1)
124
+ runner.registerValue(OpenStudio::toUnderscoreCase("#{fuel_type}_si"),fuel_type_aggregation,"J")
125
+ runner.registerValue(OpenStudio::toUnderscoreCase("#{fuel_type}_ip"),
126
+ OpenStudio::convert(fuel_type_aggregation,"J",unit_str).get,
127
+ unit_str)
128
+ end
129
+ site_energy_use += fuel_type_aggregation
130
+ end
131
+ energy = energy[0..-3]
132
+ energy << "\n};\n"
133
+ if (os_version >= min_version_feature1)
134
+ runner.registerValue("site_energy_use_si",OpenStudio::convert(site_energy_use,"J","GJ").get,"GJ")
135
+ runner.registerValue("site_energy_use_ip",OpenStudio::convert(site_energy_use,"J","MBtu").get,"MBtu")
136
+
137
+ # queries that don't have API methods yet
138
+ total_building_area = sql_query(sqlFile, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Building Area' AND RowName='Total Building Area' AND ColumnName='Area'")
139
+ runner.registerValue("total_building_area",total_building_area,"m2")
140
+
141
+ net_conditioned_building_area = sql_query(sqlFile, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Building Area' AND RowName='Net Conditioned Building Area' AND ColumnName='Area'")
142
+ runner.registerValue("net_conditioned_building_area",net_conditioned_building_area,"m2")
143
+
144
+ unconditioned_building_area = sql_query(sqlFile, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Building Area' AND RowName='Unconditioned Building Area' AND ColumnName='Area'")
145
+ runner.registerValue("unconditioned_building_area",unconditioned_building_area,"m2")
146
+
147
+ total_site_energy_eui = sql_query(sqlFile, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Total Site Energy' AND ColumnName='Energy Per Conditioned Building Area'")
148
+ runner.registerValue("total_site_energy_eui",total_site_energy_eui,"MJ/m2")
149
+
150
+ total_source_energy_eui = sql_query(sqlFile, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Total Source Energy' AND ColumnName='Energy Per Conditioned Building Area'")
151
+ runner.registerValue("total_source_energy_eui",total_source_energy_eui,"MJ/m2")
152
+
153
+ time_setpoint_not_met_during_occupied_heating = sql_query(sqlFile, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Comfort and Setpoint Not Met Summary' AND RowName='Time Setpoint Not Met During Occupied Heating' AND ColumnName='Facility'")
154
+ runner.registerValue("time_setpoint_not_met_during_occupied_heating",time_setpoint_not_met_during_occupied_heating,"hr")
155
+
156
+ time_setpoint_not_met_during_occupied_cooling = sql_query(sqlFile, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Comfort and Setpoint Not Met Summary' AND RowName='Time Setpoint Not Met During Occupied Cooling' AND ColumnName='Facility'")
157
+ runner.registerValue("time_setpoint_not_met_during_occupied_cooling",time_setpoint_not_met_during_occupied_cooling,"hr")
158
+
159
+ time_setpoint_not_met_during_occupied_hours = time_setpoint_not_met_during_occupied_heating + time_setpoint_not_met_during_occupied_cooling
160
+ runner.registerValue("time_setpoint_not_met_during_occupied_hours",time_setpoint_not_met_during_occupied_hours,"hr")
161
+
162
+ total_life_cycle_cost = sql_query(sqlFile, 'Life-Cycle Cost Report', "TableName='Present Value by Category' AND RowName='Grand Total' AND ColumnName='Present Value'")
163
+ runner.registerValue("total_life_cycle_cost",total_life_cycle_cost,"$")
164
+
165
+ end
166
+
167
+ # echo out our values
168
+ #runner.registerInfo("This building is named #{building_name}.")
169
+
170
+ # read in template
171
+ html_in_path = "#{File.dirname(__FILE__)}/resources/report.html.in"
172
+ if File.exist?(html_in_path)
173
+ html_in_path = html_in_path
174
+ else
175
+ html_in_path = "#{File.dirname(__FILE__)}/report.html.in"
176
+ end
177
+ html_in = ""
178
+ File.open(html_in_path, 'r') do |file|
179
+ html_in = file.read
180
+ end
181
+
182
+ # configure template with variable values
183
+ renderer = ERB.new(html_in)
184
+ html_out = renderer.result(binding)
185
+
186
+ # write html file
187
+ html_out_path = 'report.html'
188
+ File.open(html_out_path, 'w') do |file|
189
+ file << html_out
190
+
191
+ # make sure data is written to the disk one way or the other
192
+ begin
193
+ file.fsync
194
+ rescue
195
+ file.flush
196
+ end
197
+ end
198
+
199
+ #closing the sql file
200
+ sqlFile.close
201
+
202
+ #reporting final condition
203
+ runner.registerFinalCondition("Standard Report generated successfully.")
204
+
205
+ return true
206
+
207
+ end #end the run method
208
+
209
+ end #end the measure
210
+
211
+ #this allows the measure to be use by the application
212
+ StandardReports.new.registerWithApplication
@@ -0,0 +1,53 @@
1
+ <measure>
2
+ <name>Standard Reports</name>
3
+ <uid>fc337100-8634-404e-8966-01243d292a79</uid>
4
+ <version_id>4f1230e3-0954-43ab-a772-c345030ab14b</version_id>
5
+ <description>Change me</description>
6
+ <modeler_description>Change me</modeler_description>
7
+ <provenances/>
8
+ <tags>
9
+ <tag>Reporting.QAQC</tag>
10
+ </tags>
11
+ <attributes>
12
+ <attribute>
13
+ <name>Measure Type</name>
14
+ <value>ReportingMeasure</value>
15
+ <datatype>string</datatype>
16
+ </attribute>
17
+ <attribute>
18
+ <name>Uses SketchUp API</name>
19
+ <value>false</value>
20
+ <datatype>boolean</datatype>
21
+ </attribute>
22
+ </attributes>
23
+ <files>
24
+ <file>
25
+ <filename>ExampleModel.osm</filename>
26
+ <filetype>osm</filetype>
27
+ <usage_type>test</usage_type>
28
+ <checksum>0D4162D2</checksum>
29
+ </file>
30
+ <file>
31
+ <filename>report.html.in</filename>
32
+ <filetype>in</filetype>
33
+ <usage_type>resource</usage_type>
34
+ <checksum>093FE760</checksum>
35
+ </file>
36
+ <file>
37
+ <filename>StandardReports_Test.rb</filename>
38
+ <filetype>rb</filetype>
39
+ <usage_type>test</usage_type>
40
+ <checksum>6790E2F5</checksum>
41
+ </file>
42
+ <file>
43
+ <version>
44
+ <software_program>OpenStudio</software_program>
45
+ <identifier>1.1.2</identifier>
46
+ </version>
47
+ <filename>measure.rb</filename>
48
+ <filetype>rb</filetype>
49
+ <usage_type>script</usage_type>
50
+ <checksum>4263B498</checksum>
51
+ </file>
52
+ </files>
53
+ </measure>
@@ -0,0 +1,298 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Results | OpenStudio</title>
6
+ <link href="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet">
7
+ <link href="file:///<%= web_asset_path %>/bootstrap.min.css" rel="stylesheet">
8
+ <style type="text/css">
9
+ body {
10
+ font: 10px sans-serif;
11
+ min-width: 1280px;
12
+ }
13
+ table {
14
+ max-width:800px;
15
+ }
16
+ .axis path, .axis line {
17
+ fill: none;
18
+ stroke: #000;
19
+ shape-rendering: crispEdges;
20
+ }
21
+ .bar {
22
+ fill: steelblue;
23
+ }
24
+ .x.axis path {
25
+ display: none;
26
+ }
27
+ .d3-tip {
28
+ line-height: 1;
29
+ font-weight: bold;
30
+ padding: 12px;
31
+ background: rgba(0, 0, 0, 0.8);
32
+ color: #fff;
33
+ border-radius: 2px;
34
+ }
35
+ /* Creates a small triangle extender for the tooltip */
36
+ .d3-tip:after {
37
+ box-sizing: border-box;
38
+ display: inline;
39
+ font-size: 10px;
40
+ width: 100%;
41
+ line-height: 1;
42
+ color: rgba(0, 0, 0, 0.8);
43
+ content:"\25BC";
44
+ position: absolute;
45
+ text-align: center;
46
+ }
47
+ /* Style northward tooltips differently */
48
+ .d3-tip.n:after {
49
+ margin: -1px 0 0 0;
50
+ top: 100%;
51
+ left: 0;
52
+ }
53
+ </style>
54
+ <script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
55
+ <script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.3.9/d3.min.js"></script>
56
+ <script type="text/javascript" src="file:///<%= web_asset_path %>/jquery.min.js"></script>
57
+ <script type="text/javascript" src="file:///<%= web_asset_path %>/d3.min.js"></script>
58
+ <script type="text/javascript">
59
+ // http://labratrevenge.com/d3-tip/javascripts/d3.tip.min.js
60
+ d3.tip=function(){function t(t){v=m(t),w=v.createSVGPoint(),document.body.appendChild(x)}function e(){return"n"}function n(){return[0,0]}function r(){return" "}function o(){var t=p();return{top:t.n.y-x.offsetHeight,left:t.n.x-x.offsetWidth/2}}function s(){var t=p();return{top:t.s.y,left:t.s.x-x.offsetWidth/2}}function l(){var t=p();return{top:t.e.y-x.offsetHeight/2,left:t.e.x}}function u(){var t=p();return{top:t.w.y-x.offsetHeight/2,left:t.w.x-x.offsetWidth}}function f(){var t=p();return{top:t.nw.y-x.offsetHeight,left:t.nw.x-x.offsetWidth}}function i(){var t=p();return{top:t.ne.y-x.offsetHeight,left:t.ne.x}}function a(){var t=p();return{top:t.sw.y,left:t.sw.x-x.offsetWidth}}function c(){var t=p();return{top:t.se.y,left:t.e.x}}function d(){var t=d3.select(document.createElement("div"));return t.style({position:"absolute",opacity:0,pointerEvents:"none",boxSizing:"border-box"}),t.node()}function m(t){return t=t.node(),"svg"==t.tagName.toLowerCase()?t:t.ownerSVGElement}function p(){var t=d3.event.target,e={},n=t.getScreenCTM(),r=t.getBBox(),o=r.width,s=r.height,l=r.x,u=r.y,f=document.body.scrollTop,i=document.body.scrollLeft;return document.documentElement&&document.documentElement.scrollTop&&(f=document.documentElement.scrollTop,i=document.documentElement.scrollLeft),w.x=l+i,w.y=u+f,e.nw=w.matrixTransform(n),w.x+=o,e.ne=w.matrixTransform(n),w.y+=s,e.se=w.matrixTransform(n),w.x-=o,e.sw=w.matrixTransform(n),w.y-=s/2,e.w=w.matrixTransform(n),w.x+=o,e.e=w.matrixTransform(n),w.x-=o/2,w.y-=s/2,e.n=w.matrixTransform(n),w.y+=s,e.s=w.matrixTransform(n),e}var y=e,g=n,h=r,x=d(),v=null,w=null;t.show=function(){var e,n=h.apply(this,arguments),r=g.apply(this,arguments),o=y.apply(this,arguments),s=d3.select(x),l=0;for(s.html(n).style({opacity:1,pointerEvents:"all"});l--;)s.classed(E[l],!1);return e=T.get(o).apply(this),s.classed(o,!0).style({top:e.top+r[0]+"px",left:e.left+r[1]+"px"}),t},t.hide=function(){return nodel=d3.select(x),nodel.style({opacity:0,pointerEvents:"none"}),t},t.attr=function(e){if(2>arguments.length&&"string"==typeof e)return d3.select(x).attr(e);var n=Array.prototype.slice.call(arguments);return d3.selection.prototype.attr.apply(d3.select(x),n),t},t.style=function(e){if(2>arguments.length&&"string"==typeof e)return d3.select(x).style(e);var n=Array.prototype.slice.call(arguments);return d3.selection.prototype.style.apply(d3.select(x),n),t},t.direction=function(e){return arguments.length?(y=null==e?e:d3.functor(e),t):y},t.offset=function(e){return arguments.length?(g=null==e?e:d3.functor(e),t):g},t.html=function(e){return arguments.length?(h=null==e?e:d3.functor(e),t):h};var T=d3.map({n:o,s:s,e:l,w:u,nw:f,ne:i,sw:a,se:c}),E=T.keys();return t};
61
+ </script>
62
+ </head>
63
+ <body>
64
+ <div class="row-fluid">
65
+ <div class="span5">
66
+ <h4 id="title-0"></h4>
67
+ <div id="chart-0"></div>
68
+ </div>
69
+ <div class="span7">
70
+ <h4 id="title-1"></h4>
71
+ <div id="chart-1"></div>
72
+ </div>
73
+ </div>
74
+ <script type="text/javascript">
75
+ function numFormat(n) {
76
+ var parts = n.toString().split(".");
77
+ return parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",") + (parts[1] ? "." + parts[1] : "");
78
+ }
79
+
80
+ var months = [
81
+ "Jan",
82
+ "Feb",
83
+ "Mar",
84
+ "Apr",
85
+ "May",
86
+ "Jun",
87
+ "Jul",
88
+ "Aug",
89
+ "Sep",
90
+ "Oct",
91
+ "Nov",
92
+ "Dec"];
93
+
94
+ <%= energy %>
95
+
96
+ function outputTable(name, units, obj) {
97
+ if (name == "Electricity Consumption" | name == "Natural Gas Consumption" | name == "District Cooling Consumption" | name == "District Heating Consumption") {
98
+ $('<h4>').text(name + " (" + units + ")").appendTo('body');
99
+ var $table = $('<table id="' + name.toLowerCase().replace(/ /g, '-') + '">').addClass("table table-striped table-bordered table-condensed");
100
+ $table.append('<thead>').children('thead').append('<tr />').children('tr').append('<th>&nbsp;</th><th>Jan</th><th>Feb</th><th>Mar</th><th>Apr</th><th>May</th><th>Jun</th><th>Jul</th><th>Aug</th><th>Sep</th><th>Oct</th><th>Nov</th><th>Dec</th><th>Total</th>');
101
+ var $tbody = $table.append('<tbody />').children('tbody');
102
+ var key;
103
+ var totalTotal = 0;
104
+ var columnTotal = [];
105
+ for (key in obj) {
106
+ if (obj.hasOwnProperty(key)) {
107
+ var $row = $tbody.append('<tr />').children('tr:last');
108
+ $row.append("<td>" + key + "</td>");
109
+ var total = 0;
110
+ for (i = 0; i < obj[key].length; i++) {
111
+ if (columnTotal[i] === undefined) columnTotal[i] = 0;
112
+ $row.append("<td>" + (obj[key][i] === 0 ? "&mdash;" : numFormat(obj[key][i])) + "</td>");
113
+ total += obj[key][i];
114
+ columnTotal[i] += obj[key][i];
115
+ }
116
+ totalTotal += total;
117
+ $row.append("<td>" + (total === 0 ? "&mdash;" : numFormat(total.toFixed(3))) + "</td>");
118
+ }
119
+ }
120
+ // Append column totals as new, last row of table
121
+ if (columnTotal[columnTotal.length] === undefined) columnTotal[columnTotal.length] = 0;
122
+ columnTotal[columnTotal.length - 1] = totalTotal;
123
+ var $row = $tbody.append('<tr />').children('tr:last');
124
+ $row.append("<td>Total</td>");
125
+ for (i = 0; i < columnTotal.length; i++) {
126
+ $row.append("<td>" + (columnTotal[i] === 0 ? "&mdash;" : numFormat(columnTotal[i].toFixed(3))) + "</td>");
127
+ }
128
+ $table.appendTo('body');
129
+ }
130
+ }
131
+
132
+ $.each(consumption, function (key, obj) {
133
+ outputTable(key, obj.units, obj.data);
134
+ });
135
+
136
+ var margin = {
137
+ top: 20.5,
138
+ right: 20,
139
+ bottom: 30,
140
+ left: 40.5
141
+ },
142
+ width = 560 - margin.left - margin.right,
143
+ height = 300 - margin.top - margin.bottom;
144
+
145
+ var x = d3.scale.ordinal()
146
+ .rangeRoundBands([0, width], 0.1);
147
+
148
+ var y = d3.scale.linear()
149
+ .rangeRound([height, 0]);
150
+
151
+ var color = d3.scale.ordinal()
152
+ .range(["#EF1C21", "#0071BD", "#F7DF10", "#DEC310", "#4A4D4A", "#B5B2B5", "#FF79AD", "#632C94", "#F75921", "#293094", "#CE5921", "#FFB239", "#29AAE7", "#8CC739"]);
153
+
154
+ var xAxis = d3.svg.axis()
155
+ .scale(x)
156
+ .orient("bottom");
157
+
158
+ var yAxis = d3.svg.axis()
159
+ .scale(y)
160
+ .orient("left")
161
+ .tickFormat(d3.format(".2s"));
162
+
163
+ var tip = d3.tip()
164
+ .attr('class', 'd3-tip')
165
+ .offset([-10, 0])
166
+ .html(function (d) {
167
+ return "<strong>" + d.name + ":</strong> <span style='color:red'>" + (d.y1 - d.y0).toFixed(2) + "</span>";
168
+ });
169
+
170
+ var charts = ["Electricity Consumption", "Natural Gas Consumption"];
171
+
172
+ for (var c in charts) {
173
+
174
+ if (c == charts.length - 1) {
175
+ margin.right = 125;
176
+ }
177
+
178
+ $('#title-' + c).text(charts[c]);
179
+
180
+ var svg = d3.select("#chart-" + c).append("svg")
181
+ .attr("width", width + margin.left + margin.right)
182
+ .attr("height", height + margin.top + margin.bottom)
183
+ .append("g")
184
+ .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
185
+ svg.call(tip);
186
+
187
+ data = [];
188
+ for (var enduse in consumption[charts[c]].data) {
189
+ var enduse_data = consumption[charts[c]].data[enduse];
190
+ for (var month = 0; month < months.length; month++) {
191
+ if (data.length < months.length) {
192
+ var temp = {
193
+ "Month": months[month]
194
+ };
195
+ temp[enduse] = enduse_data[month];
196
+ data.push(temp);
197
+ } else {
198
+ data[month][enduse] = enduse_data[month];
199
+ }
200
+ }
201
+ }
202
+ color.domain(d3.keys(data[0]).filter(function (key) {
203
+ return key !== "Month";
204
+ }));
205
+
206
+ data.forEach(function (d) {
207
+ var y0 = 0;
208
+ d.consumption = color.domain().map(function (name) {
209
+ return {
210
+ name: name,
211
+ y0: y0,
212
+ y1: y0 += +d[name]
213
+ };
214
+ });
215
+ d.total = d.consumption[d.consumption.length - 1].y1;
216
+ });
217
+
218
+ //data.sort(function(a, b) { return b.total - a.total; });
219
+
220
+ x.domain(data.map(function (d) {
221
+ return d.Month;
222
+ }));
223
+ y.domain([0, d3.max(data, function (d) {
224
+ return d.total;
225
+ })]);
226
+
227
+ svg.append("g")
228
+ .attr("class", "x axis")
229
+ .attr("transform", "translate(0," + height + ")")
230
+ .call(xAxis);
231
+
232
+ svg.append("g")
233
+ .attr("class", "y axis")
234
+ .call(yAxis)
235
+ .append("text")
236
+ .attr("transform", "rotate(-90)")
237
+ .attr("y", -36)
238
+ .attr("dy", ".71em")
239
+ .style("text-anchor", "end")
240
+ .text(consumption[charts[c]].units);
241
+
242
+ var month = svg.selectAll(".month")
243
+ .data(data)
244
+ .enter().append("g")
245
+ .attr("class", "g")
246
+ .attr("transform", function (d) {
247
+ return "translate(" + x(d.Month) + ",0)";
248
+ });
249
+
250
+ month.selectAll("rect")
251
+ .data(function (d) {
252
+ return d.consumption;
253
+ })
254
+ .enter().append("rect")
255
+ .attr("width", x.rangeBand())
256
+ .attr("y", function (d) {
257
+ return y(d.y1);
258
+ })
259
+ .attr("height", function (d) {
260
+ return y(d.y0) - y(d.y1);
261
+ })
262
+ .style("fill", function (d) {
263
+ return color(d.name);
264
+ })
265
+ .on('mouseover', tip.show)
266
+ .on('mouseout', tip.hide);
267
+
268
+ if (c == charts.length - 1) {
269
+ var legend = svg.selectAll(".legend")
270
+ .data(color.domain().slice())
271
+ .enter().append("g")
272
+ .attr("class", "legend")
273
+ .attr("transform", function (d, i) {
274
+ return "translate(0," + i * 20 + ")";
275
+ });
276
+
277
+ legend.append("rect")
278
+ .attr("x", width + 105)
279
+ .attr("width", 18)
280
+ .attr("height", 18)
281
+ .style("fill", color);
282
+
283
+ legend.append("text")
284
+ .attr("x", width + 105 - 3)
285
+ .attr("y", 9)
286
+ .attr("dy", ".35em")
287
+ .style("text-anchor", "end")
288
+ .text(function (d) {
289
+ return d;
290
+ });
291
+ }
292
+
293
+ svg.selectAll(".g").attr("stroke", "black")
294
+ .attr("stroke-width", "1");
295
+ }
296
+ </script>
297
+ </body>
298
+ </html>