source: OpenWorkouts-current/ow/static/js/ow.js @ 5cf5787

currentfeature/docs
Last change on this file since 5cf5787 was 5cf5787, checked in by borja <borja@…>, 5 years ago

Show weekly/monthly versions of the "last 12 months" workout stats chart

in the user profile page.

  • Property mode set to 100644
File size: 13.8 KB
Line 
1
2/*
3
4  OpenWorkouts Javascript code
5
6*/
7
8
9// Namespace
10var owjs = {};
11
12
13owjs.map = function(spec) {
14
15    "use strict";
16
17    // parameters provided when creating an "instance" of a map
18    var map_id = spec.map_id;
19    var latitude = spec.latitude;
20    var longitude = spec.longitude;
21    var zoom = spec.zoom;
22    var gpx_url = spec.gpx_url;
23    var start_icon = spec.start_icon;
24    var end_icon = spec.end_icon;
25    var shadow = spec.shadow;
26    var elevation = spec.elevation;
27    var zoom_control = spec.zoom_control;
28
29    // OpenStreetMap urls and references
30    var openstreetmap_url = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
31    var openstreetmap_attr = 'Map data &copy; <a href="http://www.osm.org">OpenStreetMap</a>';
32
33    // Some vars reused through the code
34    var map;
35    var gpx;
36    var elevation;
37    var ow_charts;
38
39    var create_map = function create_map(latitude, longitude, zoom) {
40        /* Create a Leaflet map, set center point and add tiles */
41        map = L.map(map_id, {zoomControl: zoom_control});
42        map.setView([latitude, longitude], zoom);
43        var tile_layer = L.tileLayer(openstreetmap_url, {
44            attribution: openstreetmap_attr
45        });
46        tile_layer.addTo(map);
47    };
48
49    var add_elevation_chart = function add_elevation_chart() {
50        /*
51           Add the elevation chart support to the map.
52           This has to be called *after* create_map and *before* load_gpx.
53        */
54        elevation = L.control.elevation({
55            position: "bottomright",
56            theme: "steelblue-theme", //default: lime-theme
57            width: 600,
58            height: 125,
59            margins: {
60                top: 10,
61                right: 20,
62                bottom: 30,
63                left: 50
64            },
65            useHeightIndicator: true, //if false a marker is drawn at map position
66            interpolation: "linear", //see https://github.com/mbostock/d3/wiki/SVG-Shapes#wiki-area_interpolate
67            hoverNumber: {
68                decimalsX: 3, //decimals on distance (always in km)
69                decimalsY: 0, //deciamls on height (always in m)
70                formatter: undefined //custom formatter function may be injected
71            },
72            xTicks: undefined, //number of ticks in x axis, calculated by default according to width
73            yTicks: undefined, //number of ticks on y axis, calculated by default according to height
74            collapsed: false    //collapsed mode, show chart on click or mouseover
75        });
76
77        var ele_container = elevation.addTo(map);
78        /* document.getElementById('ow-analysis').appendChild(
79            ele_container._container); */
80    };
81
82    var load_gpx = function load_gpx(gpx_url) {
83        /*
84          Load the gpx from the given url, add it to the map and feed it to the
85          elevation chart
86        */
87        var gpx = new L.GPX(gpx_url, {
88            async: true,
89            marker_options: {
90                startIconUrl: start_icon,
91                endIconUrl: end_icon,
92                shadowUrl: shadow,
93            },
94        });
95
96        gpx.on('loaded', function(e) {
97            map.fitBounds(e.target.getBounds());
98        });
99
100        if (elevation) {
101            gpx.on("addline",function(e){
102                elevation.addData(e.line);
103                // ow_charts.addData(e.line);
104            });
105        };
106
107        gpx.addTo(map);
108    };
109
110    var render = function render() {
111        // create the map, add elevation, load gpx
112        create_map(latitude, longitude, zoom);
113        if (elevation) {
114            add_elevation_chart();
115        }
116        // add_ow_charts();
117        load_gpx(gpx_url);
118    };
119
120    var that = {}
121    that.render = render;
122    return that
123
124};
125
126
127owjs.week_chart = function(spec) {
128
129    "use strict";
130
131    // parameters provided when creating an "instance" of the chart
132    var chart_selector = spec.chart_selector,
133        url = spec.url,
134        current_day_name = spec.current_day_name
135
136    // Helpers
137    function select_x_axis_label(d) {
138        /* Given a value, return the label associated with it */
139        return d3.select('.x-axis')
140            .selectAll('text')
141            .filter(function(x) { return x == d.name; });
142    }
143
144    // Methods
145    var render = function render() {
146        /*
147           Build a d3 bar chart, populated with data from the given url.
148         */
149        var chart = d3.select(chart_selector),
150            margin = {top: 20, right: 20, bottom: 30, left: 50},
151            width = +chart.attr("width") - margin.left - margin.right,
152            height = +chart.attr("height") - margin.top - margin.bottom,
153            g = chart.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"),
154            x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
155            y = d3.scaleLinear().rangeRound([height, 0]);
156
157        d3.json(url).then(function (data) {
158            x.domain(data.map(function (d) {
159                return d.name;
160            }));
161
162            y.domain([0, d3.max(data, function (d) {
163                return Number(d.distance);
164            })]);
165
166            g.append("g")
167                .attr('class', 'x-axis')
168                .attr("transform", "translate(0," + height + ")")
169                .call(d3.axisBottom(x))
170
171            g.selectAll(".bar")
172                .data(data)
173                .enter().append("rect")
174                .attr("class", function(d) {
175                    if (d.name == current_day_name){
176                        select_x_axis_label(d).attr('style', "font-weight: bold;");
177                        return 'bar current'
178                    }
179                    else {
180                        return 'bar'
181                    }
182                })
183                .attr("x", function (d) {
184                    return x(d.name);
185                })
186                .attr("y", function (d) {
187                    return y(Number(d.distance));
188                })
189                .attr("width", x.bandwidth())
190                .attr("height", function (d) {
191                    return height - y(Number(d.distance));
192                })
193                .on('mouseover', function(d) {
194                    if (d.name != current_day_name){
195                        select_x_axis_label(d).attr('style', "font-weight: bold;");
196                    }
197                })
198                .on('mouseout', function(d) {
199                    if (d.name != current_day_name){
200                        select_x_axis_label(d).attr('style', "font-weight: regular;");
201                    }
202                });
203
204            g.selectAll(".text")
205                .data(data)
206                .enter()
207                .append("text")
208                .attr("class","label")
209                .attr("x", function (d) {
210                    return x(d.name) + x.bandwidth()/2;
211                })
212                .attr("y", function (d) {
213                    return y(Number(d.distance) + 5);
214                })
215                .text(function(d) {
216                    if (Number(d.distance) > 0) {
217                        return d.distance;
218                    }
219                });
220
221        });
222    };
223
224    var that = {}
225    that.render = render;
226    return that
227
228};
229
230
231owjs.year_chart = function(spec) {
232
233    "use strict";
234
235    // parameters provided when creating an "instance" of the chart
236    var chart_selector = spec.chart_selector,
237        filters_selector = spec.filters_selector,
238        switcher_selector = spec.switcher_selector,
239        urls = spec.urls,
240        current_month = spec.current_month,
241        current_week = spec.current_week,
242        y_axis_labels = spec.y_axis_labels,
243        filter_by = spec.filter_by,
244        url = spec.url;
245
246    // Helpers
247    function select_x_axis_label(d) {
248        /* Given a value, return the label associated with it */
249        return d3.select('.x-axis-b')
250            .selectAll('text')
251            .filter(function(x) { return x == d.name; });
252    };
253
254    function get_y_value(d, filter_by) {
255        return Number(d[filter_by]);
256    };
257
258    function get_y_axis_label(filter_by) {
259        return y_axis_labels[filter_by];
260    };
261
262    function get_name_for_x(d) {
263        if (d.week == undefined || d.week == 0) {
264            return d.name;
265        }
266        else {
267            return d.id.split('-')[2];
268        }
269    }
270
271    // Methods
272    var filters_setup = function filters_setup() {
273        $(filters_selector).on('click', function(e) {
274            e.preventDefault();
275            filter_by = $(this).attr('class').split('-')[1]
276            var chart = d3.select(chart_selector);
277            chart.selectAll("*").remove();
278            render(filter_by, url);
279        });
280    };
281
282    var switcher_setup = function switcher_setup() {
283        $(switcher_selector).on('click', function(e) {
284            e.preventDefault();
285            url = $(this).attr('class').split('-')[1]
286            var chart = d3.select(chart_selector);
287            chart.selectAll("*").remove();
288            render(filter_by, url);
289        });
290    };
291
292    var render = function render(filter_by, url) {
293        /*
294          Build a d3 bar chart, populated with data from the given url.
295        */
296        var chart = d3.select(chart_selector),
297            margin = {top: 20, right: 20, bottom: 30, left: 50},
298            width = +chart.attr("width") - margin.left - margin.right,
299            height = +chart.attr("height") - margin.top - margin.bottom,
300            g = chart.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"),
301            x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
302            y = d3.scaleLinear().rangeRound([height, 0]);
303
304        d3.json(urls[url]).then(function (data) {
305            x.domain(data.map(function (d) {
306                return get_name_for_x(d);
307                // return d.name;
308            }));
309
310            y.domain([0, d3.max(data, function (d) {
311                return get_y_value(d, filter_by);
312            })]);
313
314            g.append("g")
315                .attr('class', 'x-axis-b')
316                .attr("transform", "translate(0," + height + ")")
317                .call(d3.axisBottom(x))
318
319            g.append("g")
320                .call(d3.axisLeft(y))
321                .append("text")
322                .attr("fill", "#000")
323                .attr("transform", "rotate(-90)")
324                .attr("y", 6)
325                .attr("dy", "0.71em")
326                .attr("text-anchor", "end")
327                .text(get_y_axis_label(filter_by));
328
329            g.selectAll(".bar")
330                .data(data)
331                .enter().append("rect")
332                .attr("class", function(d) {
333                    var sel_week = current_month + '-' + current_week;
334                    if (d.id == current_month || d.id == sel_week){
335                        /* Bar for the currently selected month or week */
336                        select_x_axis_label(d).attr('style', "font-weight: bold;");
337                        return 'bar current';
338                    }
339                    else {
340                        if (!current_week && d.id.indexOf(current_month) >=0 ) {
341                            /*
342                               User selected a month, then switched to weekly
343                               view, we do highlight all the bars for weeks in
344                               that month
345                            */
346                            select_x_axis_label(d).attr('style', "font-weight: bold;");
347                            return 'bar current';
348                        }
349                        else {
350                            /* Non-selected bar */
351                            return 'bar';
352                        }
353
354                    }
355                })
356                .attr("x", function (d) {
357                    return x(get_name_for_x(d));
358                })
359                .attr("y", function (d) {
360                    return y(get_y_value(d, filter_by));
361                })
362                .attr("width", x.bandwidth())
363                .attr("height", function (d) {
364                    return height - y(get_y_value(d, filter_by));
365                })
366                .on('mouseover', function(d) {
367                    if (d.id != current_month){
368                        select_x_axis_label(d).attr('style', "font-weight: bold;");
369                    }
370                })
371                .on('mouseout', function(d) {
372                    if (d.id != current_month){
373                        select_x_axis_label(d).attr('style', "font-weight: regular;");
374                    }
375                })
376                .on('click', function(d) {
377                    window.location.href = d.url;
378                });
379
380            if (url == 'monthly') {
381                g.selectAll(".text")
382                    .data(data)
383                    .enter()
384                    .append("text")
385                    .attr("class","label")
386                    .attr("x", function (d) {
387                        return x(get_name_for_x(d)) + x.bandwidth()/2;
388                    })
389                    .attr("y", function (d) {
390                        /*
391                          Get the value for the current bar, then get the maximum
392                          value to be displayed in the bar, which is used to
393                          calculate the proper position of the label for this bar,
394                          relatively to its height (1% above the bar)
395                        */
396                        var value = get_y_value(d, filter_by);
397                        var max = y.domain()[1];
398                        return y(value + y.domain()[1] * 0.01);
399                    })
400                    .text(function(d) {
401                        var value = get_y_value(d, filter_by)
402                        if ( value > 0) {
403                            return value;
404                        }
405                    });
406            }
407
408            if (url == 'weekly') {
409                g.selectAll(".tick")
410                    .each(function (d, i) {
411                        /*
412                          Remove from the x-axis tickets those without letters
413                          on them (useful for the weekly chart)
414                        */
415                        if (d !== parseInt(d, 10)) {
416                            if(!d.match(/[a-z]/i)) {
417                                this.remove();
418                            }
419                        }
420                    });
421            }
422        });
423    };
424
425    var that = {}
426    that.filters_setup = filters_setup;
427    that.switcher_setup = switcher_setup;
428    that.render = render;
429    return that
430
431};
Note: See TracBrowser for help on using the repository browser.