Difference between revisions of "Widget:WFSPCompare"

From Open Energy Information

m (Text replacement - "002F6C" to "001980")
(upgrades to viz)
Line 29: Line 29:
  
 
.area_context {
 
.area_context {
 +
  fill: none;
 +
  clip-path: url(#context_data_clip);
 +
}
 +
 +
.area_context_unselected {
 
   fill: none;
 
   fill: none;
 
}
 
}
  
 
.brush {
 
.brush {
 +
  clip-path: url(#brush_clip);
 +
}
 +
 +
.leftHandle {
 +
  clip-path: url(#brush_clip);
 +
}
 +
.rightHandle {
 
   clip-path: url(#brush_clip);
 
   clip-path: url(#brush_clip);
 
}
 
}
  
 
.zoom {
 
.zoom {
   cursor: move;
+
   cursor: ew-resize;
 
   fill: none;
 
   fill: none;
   pointer-events: all;
+
   pointer-events: auto;
 
}
 
}
  
Line 104: Line 116:
 
</style>
 
</style>
  
 +
 +
<!-- <link rel="stylesheet" type="text/css" href="/w/skins/OpenEI/resources/air-datepicker/css/datepicker.min.css">
 +
<script type="text/javascript" src="/w/skins/OpenEI/resources/air-datepicker/js/datepicker.min.js"></script>
 +
<script type="text/javascript" src="/w/skins/OpenEI/resources/air-datepicker/js/i18n/datepicker.en.js"></script>
 +
 +
<div>
 +
  <input id="MaxDatepicker" type="text"
 +
  multiple-dates-separator: " - ">
 +
</div> -->
 +
 +
<!-- <input id="MaxDatepicker" type="text"> -->
 +
<script>
 +
  // $('#MaxDatepicker').datepicker({
 +
  //    range: "true"
 +
  //    language: "en"
 +
  //    class: "datepicker-here"/>
 +
  //    language: 'en',
 +
  //    maxDate: new Date() // Now can select only dates up until today
 +
  // });
 +
</script>
  
  
Line 124: Line 156:
 
             <label for="customEndDate">End Date</label>
 
             <label for="customEndDate">End Date</label>
 
             <input type="date" class="form-control" id="customEndDate" name="end-date">
 
             <input type="date" class="form-control" id="customEndDate" name="end-date">
          </div>
 
          <div class="form-button">
 
            <input type="button" class="btn btn-primary" id="view_custom_range" value="Submit" data-dismiss="modal">
 
 
           </div>
 
           </div>
 
         </form>
 
         </form>
 
       </div>
 
       </div>
       <div class="modal-footer"><div type="button" class="btn btn-default" data-dismiss="modal">Close</div></div>
+
       <div class="modal-footer">
 +
        <div class="form-button">
 +
          <input type="button" class="btn btn-primary" id="view_custom_range" value="Submit" data-dismiss="modal">
 +
          <div type="button" class="btn btn-default" data-dismiss="modal">Close</div>
 +
        </div>
 +
      </div>
 
     </div>
 
     </div>
 
   </div>
 
   </div>
Line 152: Line 186:
 
<!-- Preset Buttons -->
 
<!-- Preset Buttons -->
 
<div id="div_preset_btns" style="overflow: scroll; display: none">
 
<div id="div_preset_btns" style="overflow: scroll; display: none">
   <svg id="svg_preset_btns" width="585" height="50"></svg>
+
   <svg id="svg_preset_btns" width="700" height="50"></svg>
 
</div>
 
</div>
  
Line 497: Line 531:
 
           context.selectAll("path").remove();
 
           context.selectAll("path").remove();
 
           context.selectAll("g").remove();
 
           context.selectAll("g").remove();
 +
          context.selectAll("rect").remove();
 
           context.selectAll("text").remove();
 
           context.selectAll("text").remove();
 
           svg.selectAll(".zoom").remove();
 
           svg.selectAll(".zoom").remove();
 
           svg2.selectAll("g").remove();
 
           svg2.selectAll("g").remove();
 +
          var x = document.getElementById('div_graphs');
 +
          x.style.display = 'none';
 +
          var x = document.getElementById('div_preset_btns');
 +
          x.style.display = 'none';
 
         }
 
         }
 
       }
 
       }
Line 599: Line 638:
 
     context.selectAll("path").remove();
 
     context.selectAll("path").remove();
 
     context.selectAll("g").remove();
 
     context.selectAll("g").remove();
 +
    context.selectAll("rect").remove();
 
     context.selectAll("text").remove();
 
     context.selectAll("text").remove();
 
     svg.selectAll(".zoom").remove();
 
     svg.selectAll(".zoom").remove();
Line 623: Line 663:
 
       var num_turbineIDs = turbineIDs_selected.length;
 
       var num_turbineIDs = turbineIDs_selected.length;
 
       //var data_light = d3.shuffle(data.filter((element, index) => { return index % num_turbineIDs === 0; }));
 
       //var data_light = d3.shuffle(data.filter((element, index) => { return index % num_turbineIDs === 0; }));
       let data_light = d3.shuffle(data.filter(function(element, index) { return index % num_turbineIDs === 0; }));
+
       let data_light = (data.filter(function(element, index) { return index % num_turbineIDs === 0; })); // d3.shuffle()
 
     // });
 
     // });
  
Line 630: Line 670:
  
 
       var current_date = new Date();
 
       var current_date = new Date();
       var data_start = d3.min(data, function(d) { return d.dateTime; })
+
       var data_start = d3.min(data, function(d) { return d.dateTime; });
 +
      var timeslider_start = data_start;
 
       // xScale_focus1.domain(d3.extent(data, function(d) { return d.dateTime; }));
 
       // xScale_focus1.domain(d3.extent(data, function(d) { return d.dateTime; }));
 
       xScale_focus1.domain( [ data_start, current_date ] );
 
       xScale_focus1.domain( [ data_start, current_date ] );
Line 637: Line 678:
 
       xScale_context.domain(xScale_focus1.domain());
 
       xScale_context.domain(xScale_focus1.domain());
 
       yScale_context.domain(yScale_focus.domain());
 
       yScale_context.domain(yScale_focus.domain());
 
  
 
       // Draw time series
 
       // Draw time series
Line 658: Line 698:
 
         .attr('height', height_context);
 
         .attr('height', height_context);
  
 +
      svg
 +
        .append('clipPath')
 +
        .attr('id', 'context_data_clip')
 +
        .attr('class', 'context_data_clip')
 +
        .append('rect')
 +
        .attr('x', 0)
 +
        .attr('y', 0)
 +
        .attr('width', width_context)
 +
        .attr('height', height_context);
  
 
        
 
        
Line 711: Line 760:
 
           .attr("height", height_focus);
 
           .attr("height", height_focus);
  
      var queue = 0;
 
  
 
       brush.on("brush end", function(d) {
 
       brush.on("brush end", function(d) {
 
         brushed(d);
 
         brushed(d);
        // queue += 1;
 
        // console.log(queue);
 
 
         update_scatter(d);
 
         update_scatter(d);
        // setTimeout( function(){ update_scatter(d) }, 100 * queue );
 
 
       });
 
       });
  
Line 731: Line 776:
 
           .attr("cy", function(d) { return yScale_focus(d.powerOutput); })
 
           .attr("cy", function(d) { return yScale_focus(d.powerOutput); })
 
           .attr("r", 4);
 
           .attr("r", 4);
 +
 +
      var dots = focus2.selectAll(".dot_selected").data(data_light);
 +
      dots.enter().append("circle")
 +
          .attr("class", "dot_selected")
 +
          .attr("cx", function(d) { return xScale_focus2(d.windSpeed);  })
 +
          .attr("cy", function(d) { return yScale_focus(d.powerOutput); })
 +
          .attr("r", 4)
 +
          .style("opacity", "0")
 +
          .style("fill", function(d) { return chart_colors[turbineIDs_selected.indexOf(String(d.turbineID))]});
 +
 +
      var last_start = xScale_focus1.domain()[0]
 +
      var last_end = xScale_focus1.domain()[1]
 +
      var current_start = xScale_focus1.domain()[0]
 +
      var current_end = xScale_focus1.domain()[1]
  
 
       function update_scatter(d) {
 
       function update_scatter(d) {
        // console.log("Scatter");
 
        updating_scatter = "True";
 
 
         // draw scatter
 
         // draw scatter
         var dots = focus2.selectAll(".dot_selected").data(data_light.filter(function(d){ return ( ( d.dateTime > xScale_focus1.domain()[0] ) & ( d.dateTime < xScale_focus1.domain()[1] ) ); }));
+
         current_start = xScale_focus1.domain()[0]
         dots.enter().append("circle")
+
        current_end = xScale_focus1.domain()[1]
            .attr("class", "dot_selected")
+
        var s1 = 0, s2 = 0, e1 = 0, e2 = 0;
            .attr("cx", function(d) { return xScale_focus2(d.windSpeed)})
+
 
            .attr("cy", function(d) { return yScale_focus(d.powerOutput); })
+
        if (current_start > last_start) { s1 = last_start; s2 = current_start; start_mod = "remove";
            .attr("r", 4);
+
        } else { s2 = last_start; s1 = current_start; start_mod = "add"; };
        dots
+
         if (current_end > last_end) { e1 = last_end; e2 = current_end; end_mod = "add";
            .attr("cx", function(d) { return xScale_focus2(d.windSpeed)})
+
        } else { e2 = last_end; e1 = current_end; end_mod = "remove"; };
            .attr("cy", function(d) { return yScale_focus(d.powerOutput); })
+
 
            .style("fill", function(d) {return chart_colors[turbineIDs_selected.indexOf(String(d.turbineID))]
+
        focus2.selectAll(".dot_selected")
            });
+
          .filter(function(d){ return (( d.dateTime > s1 ) & ( d.dateTime < s2 )) })
         dots.exit().remove();
+
          .style("opacity", function(){ if ( start_mod == "add" ) { return "0.5" } else { return "0" } });
         // queue -= 1;
+
        focus2.selectAll(".dot_selected")
 +
          .filter(function(d){ return (( d.dateTime > e1 ) & ( d.dateTime < e2 )) })
 +
          .style("opacity", function(){ if ( end_mod == "add" ) { return "0.5" } else { return "0" } });
 +
         last_start = current_start;
 +
         last_end = current_end;
 
       }
 
       }
  
  
 +
      context.append("rect")
 +
          .style("fill", "steelblue")
 +
          .style("stroke", "#000")
 +
          .style("stroke-width", "2")
 +
          .style("opacity", "0.2")
 +
          .attr("width", width_context)
 +
          .attr("height", height_context);
  
 +
      context.append("path")  // append unselected context data as white lines
 +
          .datum(data)
 +
          .attr("class", "area_context_unselected")
 +
          .attr("d", area_context)
 +
          .style("stroke", "#ffffff");
  
 
+
       for (var i = 0; i < num_turbineIDs; i++) { // append selected context data as colored lines
       for (var i = 0; i < num_turbineIDs; i++) {
 
 
         Turbine_ID = turbineIDs_selected[i];
 
         Turbine_ID = turbineIDs_selected[i];
 
         series_color = chart_colors[i];
 
         series_color = chart_colors[i];
 
 
         context.append("path")
 
         context.append("path")
 
             .datum(data.filter(function(d){ return ( d.turbineID == Turbine_ID );} ))  
 
             .datum(data.filter(function(d){ return ( d.turbineID == Turbine_ID );} ))  
Line 764: Line 835:
 
             .attr("d", area_context)
 
             .attr("d", area_context)
 
             .style("stroke", series_color);
 
             .style("stroke", series_color);
 
 
       }
 
       }
  
 +
      var leftHandle = context.append("rect")
 +
          .attr("class", "leftHandle")
 +
          .style("fill", "grey")
 +
          .style("stroke", "#000")
 +
          .style("stroke-width", "1")
 +
          .attr("x", "0")
 +
          .attr("y", "0")
 +
          .attr("width", "8")
 +
          .attr("height", height_context);
  
 +
      var rightHandle = context.append("rect")
 +
          .attr("class", "rightHandle")
 +
          .style("fill", "grey")
 +
          .style("stroke", "#000")
 +
          .style("stroke-width", "1")
 +
          .attr("x", "0")
 +
          .attr("y", "0")
 +
          .attr("width", "8")
 +
          .attr("height", height_context);
  
 
       context.append("g")
 
       context.append("g")
Line 793: Line 881:
 
                           .attr("id","allButtons");
 
                           .attr("id","allButtons");
  
       var labels= ['Today','Last 7 Days','Last 30 Days','Custom Range','Reset'];
+
       var labels= ['Today','Last 7 Days','Last 30 Days','Last Year','Custom Range','Reset'];
  
 
       var defaultColor= "#303f9f";
 
       var defaultColor= "#303f9f";
Line 834: Line 922:
 
                                     .translate(-s[0], 0));
 
                                     .translate(-s[0], 0));
 
                                 } else if (i == 3) {
 
                                 } else if (i == 3) {
 +
                                    var t_svg = svg.transition()
 +
                                        .duration(1000);
 +
                                    var one_Year_Ago = d3.timeYear.offset(current_date, -1);
 +
                                    var s = [xScale_context(one_Year_Ago), xScale_context(current_date)];
 +
                                    t_svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
 +
                                    .scale(width_context / (s[1] - s[0]))
 +
                                    .translate(-s[0], 0));
 +
                                } else if (i == 4) {
 
                                       // var t_svg = svg.transition()
 
                                       // var t_svg = svg.transition()
 
                                       //    .duration(1000);
 
                                       //    .duration(1000);
Line 841: Line 937:
 
                                       // .scale(width_context / (s[1] - s[0]))
 
                                       // .scale(width_context / (s[1] - s[0]))
 
                                       // .translate(-s[0], 0));
 
                                       // .translate(-s[0], 0));
                                 } else if (i == 4) {
+
                                 } else if (i == 5) {
 +
                                    xScale_context.domain( [ data_start, current_date ] );
 +
                                    timeslider_start = data_start
 +
 
 +
                                    // REDRAW CONTEXT
 +
                                    context.selectAll("path").remove();
 +
                                    area_context.x(function(d) { return xScale_context(d.dateTime); });
 +
                                    context.append("path")  // append unselected context data as white lines
 +
                                        .datum(data)
 +
                                        .attr("class", "area_context_unselected")
 +
                                        .attr("d", area_context)
 +
                                        .style("stroke", "#ffffff");
 +
                                    for (var i = 0; i < num_turbineIDs; i++) {  // append selected context data as colored lines
 +
                                      Turbine_ID = turbineIDs_selected[i];
 +
                                      series_color = chart_colors[i];
 +
                                      context.append("path")
 +
                                          .datum(data.filter(function(d){ return ( d.turbineID == Turbine_ID );} ))
 +
                                          .attr("class", "area_context area"+(i+1))
 +
                                          .attr("d", area_context)
 +
                                          .style("stroke", series_color);
 +
                                    }
 +
                                    // Move Slider
 
                                     var t_svg = svg.transition()
 
                                     var t_svg = svg.transition()
 
                                         .duration(2000);
 
                                         .duration(2000);
Line 918: Line 1,035:
 
         .translate(-s[0], 0));
 
         .translate(-s[0], 0));
  
       }
+
       };
  
 
       var custom_range_btn = d3.select("#view_custom_range");
 
       var custom_range_btn = d3.select("#view_custom_range");
Line 926: Line 1,043:
 
         var customEndDate = parseCustomDate( String(document.getElementById("customEndDate").value) );
 
         var customEndDate = parseCustomDate( String(document.getElementById("customEndDate").value) );
 
          
 
          
         if (customBeginDate == null) { var customBegin = data_start } else { var customBegin = customBeginDate }
+
         if (customBeginDate == null) { var customBegin = data_start } else { var customBegin = customBeginDate } // use data start as default
         if (customEndDate == null) { var customEnd = current_date } else { var customEnd = customEndDate }
+
         if (customEndDate == null) { var customEnd = current_date } else { var customEnd = customEndDate } // use current date as default
  
         if (customBegin > customEnd ) { customBegin = [customEnd, customEnd = customBegin][0] }
+
         if (customBegin > customEnd ) { customBegin = [customEnd, customEnd = customBegin][0] } // Switch fields if entered backwards
         if (customBegin-customEnd == 0) { customEnd = d3.timeDay.offset(customBegin, 1) }
+
         if (customBegin-customEnd == 0) { customEnd = d3.timeDay.offset(customBegin, 1) } // Add 1 day interval if fields are the same
 +
       
 +
        if (customBegin < timeslider_start) {    // Rescale the timeslider to accomodate custom range
 +
          xScale_context.domain( [ customBegin, current_date ] );
 +
          timeslider_start = customBegin
  
 +
          // REDRAW CONTEXT
 +
          context.selectAll("path").remove();
 +
          area_context.x(function(d) { return xScale_context(d.dateTime); });
 +
          context.append("path")  // append unselected context data as white lines
 +
              .datum(data)
 +
              .attr("class", "area_context_unselected")
 +
              .attr("d", area_context)
 +
              .style("stroke", "#ffffff");
 +
          for (var i = 0; i < num_turbineIDs; i++) {  // append selected context data as colored lines
 +
            Turbine_ID = turbineIDs_selected[i];
 +
            series_color = chart_colors[i];
 +
            context.append("path")
 +
                .datum(data.filter(function(d){ return ( d.turbineID == Turbine_ID );} ))
 +
                .attr("class", "area_context area"+(i+1))
 +
                .attr("d", area_context)
 +
                .style("stroke", series_color);
 +
          }
 +
        }
 
         var t_svg = svg.transition()
 
         var t_svg = svg.transition()
 
             .duration(1000);
 
             .duration(1000);
Line 950: Line 1,089:
 
           .style("text-anchor", "front")
 
           .style("text-anchor", "front")
 
           .style("font-size", function(){return (String( $(window).width() / 900 )+"em") })
 
           .style("font-size", function(){return (String( $(window).width() / 900 )+"em") })
           .text("Select a Date Range:");
+
           .text("Selected Date Range:");
 +
 
 +
      context.append("text")
 +
          .attr("class", "label_date")
 +
          .attr("x", width_context)
 +
          .attr("y", 0)
 +
          .attr("dy", "-0.5em")
 +
          .style("text-anchor", "end")
 +
          .style("font-size", function(){return (String( $(window).width() / 900 )+"em") })
 +
          .text("  --  ");
  
 
       focus1.append("text")
 
       focus1.append("text")
Line 1,001: Line 1,149:
 
           .text("Power (kW)");
 
           .text("Power (kW)");
  
 +
      focus2.append("text")
 +
          .attr("class", "label")
 +
          .attr("x", 40).attr("y", 20).style("text-anchor", "start")
 +
          .style("font-size", function(){return (String( $(window).width() / 1000 )+"em") })
 +
          .text("Data Out of Date Range");
  
 +
      focus2.append("rect")
 +
          .attr("x", 20).attr("y", 8)
 +
          .style("fill", "white")
 +
          .style("stroke", "#000")
 +
          .style("stroke-width", "0.5")
 +
          .attr("width", "12")
 +
          .attr("height", "12");
  
 
       // Grid
 
       // Grid
Line 1,029: Line 1,189:
 
         .scale(width_context / (s[1] - s[0]))
 
         .scale(width_context / (s[1] - s[0]))
 
         .translate(-s[0], 0));
 
         .translate(-s[0], 0));
 +
    svg.select("#context_data_clip").select("rect")  // Modify clipping path for selected data in context time slider
 +
      .attr("x", s[0])
 +
      .attr("width", s[1] - s[0]);
 +
    svg.select(".label_date").text(String(xScale_context.invert(s[0])).slice(0,21) + "  --  " + String(xScale_context.invert(s[1])).slice(0,21));    // Show the exact timestamps being focused on
 +
    svg.select(".leftHandle").attr("x", s[0]); svg.select(".rightHandle").attr("x", s[1]-8); // Move handles
 
   }
 
   }
  
Line 1,038: Line 1,203:
 
     focus1.select(".axis--x").call(xAxis_focus1);    // Updates focus based on all activity
 
     focus1.select(".axis--x").call(xAxis_focus1);    // Updates focus based on all activity
 
     context.select(".brush").call(brush.move, xScale_context.range().map(t.invertX, t));    // Updates the Selector based on Focus motion
 
     context.select(".brush").call(brush.move, xScale_context.range().map(t.invertX, t));    // Updates the Selector based on Focus motion
 +
    svg.select("#context_data_clip").select("rect")
 +
      .attr("x", xScale_context.range().map(t.invertX, t)[0])
 +
      .attr("width", xScale_context.range().map(t.invertX, t)[1] - xScale_context.range().map(t.invertX, t)[0]);
 +
    svg.select(".label_date").text(String(xScale_context.invert(xScale_context.range().map(t.invertX, t)[0])).slice(0,21) + "  --  " + String(xScale_context.invert(xScale_context.range().map(t.invertX, t)[1])).slice(0,21));    // Show the exact timestamps being focused on
 +
    svg.select(".leftHandle").attr("x", xScale_context.range().map(t.invertX, t)[0]); svg.select(".rightHandle").attr("x", xScale_context.range().map(t.invertX, t)[1]-8); // Move handles
 
   }
 
   }
  

Revision as of 08:15, 8 September 2017

This widget provides a comparison tool for the Wind for Schools Portal content. It generates a table, visualizations, and interactive interface for exploring/comparing wind turbine data between participating schools.

For example:

{{#Widget:WFSPCompare}}