Only works in full screen http://bl.ocks.org/ZJONSSON/raw/5581551/
| body { | |
| background: none repeat scroll 0 0 #DADADA; | |
| color: #333333; | |
| font-family: Helvetica,Arial,sans-serif; | |
| font-size: 14px; | |
| line-height: 1.4; | |
| margin: 0; | |
| padding: 0; | |
| } | |
| .top { | |
| color: #EEEEEE; | |
| background-color: #3F3F3F; | |
| border-bottom: 2px solid #E17600; | |
| padding: 5px; | |
| height: 32px; | |
| font-size: 24px; | |
| text-align: center; | |
| text-shadow: 2px 2px 1px #666666; | |
| } | |
| h2 { | |
| margin-top: 0px; | |
| margin-bottom: 5px; | |
| } | |
| #content { | |
| background-color: #FFFFFF; | |
| border: 1px solid #CCCCCC; | |
| margin-left: auto; | |
| margin-right: auto; | |
| margin-top: 10px; | |
| width: 900px; | |
| padding: 10px; | |
| } | |
| .demo-container { | |
| background: linear-gradient(#F6F6F6 0px, #FFFFFF 50px) repeat scroll 0 0 transparent; | |
| border: 1px solid #DDDDDD; | |
| box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15); | |
| height: 300px; | |
| margin: 10px auto 10px; | |
| padding: 20px 15px 15px; | |
| } | |
| #placeholder { | |
| height: 100%; | |
| width: 100%; | |
| } | |
| .bottom { | |
| color: #888888; | |
| font-size: 12px; | |
| text-align: center; | |
| } | |
| .bottom a { | |
| color: #888888; | |
| } | |
| #threshold { | |
| border: 0; | |
| color: #f6931f; | |
| font-weight: bold; | |
| width: 60px; | |
| } | |
| hr { | |
| margin-top: 15px; | |
| margin-bottom: 5px; | |
| border: 0; | |
| height: 1px; | |
| background: #333; | |
| background-image: -webkit-linear-gradient(left, #ccc, #333, #ccc); | |
| background-image: -moz-linear-gradient(left, #ccc, #333, #ccc); | |
| background-image: -ms-linear-gradient(left, #ccc, #333, #ccc); | |
| background-image: -o-linear-gradient(left, #ccc, #333, #ccc); | |
| } | |
| a { | |
| color: #E17600; | |
| } |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset='utf-8'> | |
| <title>Flot - Downsampling Plugin</title> | |
| <script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script> | |
| <script type="text/javascript" src="http://www.flotcharts.org/flot/jquery.flot.js"></script> | |
| <script type="text/javascript" src="simplify.js"></script> | |
| <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css" /> | |
| <script src="http://code.jquery.com/ui/1.10.2/jquery-ui.js"></script> | |
| <script type="text/javascript" src="https://raw.github.com/sveinn-steinarsson/flot-downsample/master/demo_data.js"></script> | |
| <script type="text/javascript" src="https://raw.github.com/sveinn-steinarsson/flot-downsample/master/example.js"></script> | |
| <link rel="stylesheet" type="text/css" href="example.css" /> | |
| </head> | |
| <body> | |
| <div id="content"> | |
| <h2>Simplification Example</h2> | |
| <div class="demo-container"> | |
| <div id="placeholder"></div> | |
| </div> | |
| <p> | |
| <label for="data">Data:</label> | |
| <select id="data"> | |
| <option value="0">Demo 1</option> | |
| <option value="1">Demo 2</option> | |
| <option value="2">Demo 3</option> | |
| </select> | |
| <label for="threshold">Threshold:</label> | |
| <input type="text" id="threshold" readonly/> (Total count: <span id="length">0</span>) | |
| </p> | |
| <div id="slider-threshold"></div> | |
| <hr /> | |
| <p> | |
| This is a fork of <a href="https://github.com/sveinn-steinarsson/flot-downsample">flot-downsample</a> by <a href="https://github.com/sveinn-steinarsson">Sveinn Steinarsson</a>. I rewrote the simplification procedure to replicate <a href="http://www2.dcs.hull.ac.uk/CISRG/publications/DPs/DP10/DP10.html">Visvalningham's algorithm</a> similar to Mike Bostock's <a href="http://bost.ocks.org/mike/simplify/">simplify</a>. I decided to <a href="http://bl.ocks.org/ZJONSSON/raw/5581551/"></a>write it from scratch</a> to get better understanding of the dynamics. Mike's version uses bianry heap, which could be slightly more efficient than the bisect routine I apply in my example. | |
| </div> | |
| </body> | |
| </html> |
| (function ($) { | |
| "use strict"; | |
| // Calculates the area of a triangle formed by current point, last one and next one | |
| function calcArea(d) { | |
| if (!d.prev || !d.next) return; | |
| var x2 = d[0], y2 = d[1], | |
| x1 = d.prev[0], y1 = d.prev[1], | |
| x3 = d.next[0], y3 = d.next[1]; | |
| return 1/2 * Math.abs(x1*(y2-y3)+x2*(y3-y1)+x3*(y1-y2)); | |
| } | |
| function bisect(a,d,f) { | |
| var lo = 0, hi = a.length; | |
| while (lo < hi) { | |
| var mid = lo + hi >>> 1; | |
| if (f(a[mid]) < f(d)) lo = mid + 1; | |
| else hi = mid; | |
| } | |
| return lo; | |
| } | |
| var area = function(d) { return d.area;}; | |
| function simplify(data) { | |
| // Start by taking a fresh copy just in case | |
| data = data.map(function(d) { return [d[0],d[1]];}); | |
| var triangles = [], | |
| results = [], | |
| first = data[0], | |
| last = data[data.length-1]; | |
| // We want first and last to be always included in filter | |
| first.i = last.i = 0; | |
| data.forEach(function(d,i) { | |
| if ((d.next = data[i+1]) && (d.prev = data[i-1])) | |
| relocate(d); | |
| }); | |
| function relocate(d) { | |
| // If the area already exists, the element is in triangles and must be removed/replaced | |
| if (d.area) triangles.splice(bisect(triangles,d,area),1); | |
| // If we can calculate an area, we need to insert into triangles (sorted by area) | |
| if (d.area = calcArea(d)) triangles.splice(bisect(triangles,d,area),0,d); | |
| return d; | |
| } | |
| while(triangles.length) { | |
| // Remove the top triangle (smallest area) and push to results | |
| var t = triangles.splice(0,1)[0]; | |
| results.push(t); | |
| // Connect the adjecent points and relocate both | |
| t.prev.next = relocate(t.next); | |
| t.next.prev = relocate(t.prev); | |
| } | |
| // Finally we reverse the results so largest triangles are at the top | |
| results = results.reverse() | |
| .map(function(d,i) { d.i = i+2; return d; }) | |
| .sort(function(a,b) { return a[0]-b[0]; }); | |
| return [first].concat(results).concat([last]); | |
| } | |
| // Caches defined | |
| var results = [], | |
| lastData; | |
| function processRawData ( plot, series ) { | |
| // We cache the results, because we can. | |
| if (lastData !== series.data[0]) { | |
| lastData = series.data[0]; | |
| results = simplify(series.data); | |
| } | |
| series.data = results.filter(function(d) { return d.i < series.downsample.threshold;}) | |
| } | |
| var options = { | |
| series: { | |
| downsample: { | |
| threshold: 1000 // 0 disables downsampling for this series. | |
| } | |
| } | |
| }; | |
| function init(plot) { | |
| plot.hooks.processRawData.push(processRawData); | |
| } | |
| $.plot.plugins.push({ | |
| init: init, | |
| options: options, | |
| name: "downsample", | |
| version: "0.1" | |
| }); | |
| })(jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment