source: OpenWorkouts-current/ow/static/js/ow.js @ 688fb7d

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

Ensure the gpx data is loaded into the full page map available under the /map
url for workouts.

Set the proper color for the polyline drawn on the map by leaflet-gpx

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