-
Notifications
You must be signed in to change notification settings - Fork 2
/
bike.js
568 lines (435 loc) · 16.2 KB
/
bike.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
/*******************************
* Development Options
*/
var write_to_carto = true;
var write_local_db = false;
var dropadd_local_db = false; // clear local DB
var reset_rideNum = false; // clear localStorage
var reset_distLifetime = false; // clear localStorage
var use_dummy_data = false; // true for off-phone browser dev
/*******************************
* Setup Variables & Local DB
*/
// setup CartoDB
var urlBase = "https://ideapublic.cartodb.com/api/v1/sql?api_key=";
var cartoKey = "d1003f790f91855f9a72363ac887e14010974332";
// setup ride vars
var gpsInterval = 5000; // milliseconds
var startLat, startLong,prevLat, prevLong; // used for rough distance
var distance, distLifetime, distRide = 0;
var userID = 17;
var username;
var rideID; // localStorage.rideNum
var counter=0;
var startTime, endTime;
var timer;
var timer_is_on=0;
function initBike() {
// check for username
if ( !localStorage.getItem('username') ) {
//window.location.href = "settings.html"
document.getElementById('settings').style.display = "block";
document.getElementById('stats').style.display = "none";
}
else {
username = localStorage.getItem('username');
console.log("username: " + username);
}
lifetimeDistance();
}
// set up local storage
if (reset_rideNum) {
console.log("clear ride number");
localStorage.removeItem('rideNum');
}
if ( !localStorage.getItem('rideNum') ) {
console.log("init ride number");
localStorage.setItem('rideNum',0);
}
localStorage.setItem('userID',userID);
rideID = Number(localStorage.rideNum); // convert, stored as string.
// set up local db
var db = openDatabase('bikedb', '1.0', 'bikedb', 2 * 1024);
if (dropadd_local_db) { dbDrop(); }
else { init_db(); }
/*******************************
* User Actions
*/
// start ride: triggered by user
function iotbike() {
if (timer_is_on == 0) {
timer_is_on=1;
toggleUI();
rideID = rideID + 1;
document.getElementById('distance').innerHTML = "0.00 km";
startTime = new Date();
startTimer();
console.log("rideID " + rideID +" started at " + startTime.toLocaleTimeString() );
localStorage.setItem('rideNum',rideID);
feedback(); // could be run on CartoDB xmlHttp.responseText
if (!use_dummy_data) { bikeLocation(); }
else { fakeLocation(); }
//*** js in main.js
// toggleAccel();
// toggleCompass();
// check_net_connection();
vibrate();
}
}
// stop ride: triggered by user
function iotOff() {
timer_is_on=0;
toggleUI();
endTime = new Date();
var elapsed = Math.round( (endTime - startTime)/1000 );
var startMinutes = startTime.getMinutes();
var endMinutes = endTime.getMinutes();
distLifetime = distLifetime + distRide;
localStorage.setItem('distLifetime',distLifetime);
lifetimeDistance();
console.log("rideID " + rideID +" complete: " + distRide +" meters & " + counter +" points in " + elapsed + " seconds.");
console.log("lifetime Distance " + distLifetime );
cartodbLine(rideID);
// could clear local DB if cartodbLine returns ok
alert("Ride #" + rideID + " is complete with " + distRide + " meters.");
counter = 0;
vibrate();
//rideCheck();
}
var vibrate = function() {
// navigator.notification.vibrate(1);
};
/*******************************
* Interface
*/
function toggleUI() {
var startbutton = document.getElementById('start');
var stopbutton = document.getElementById('stop');
var data = document.getElementById('ridedata');
var hardware = document.getElementById('hardware');
var clock = document.getElementById('time');
var maplink = document.getElementById('maplink');
if ( startbutton.style.display == "none" ) {
startbutton.style.display = "block";
stopbutton.style.display = "none";
maplink.style.display = "block";
clock.style.color = "#aaa";
}
else {
startbutton.style.display = "none";
stopbutton.style.display = "block";
maplink.style.display = "none";
clock.style.color = "#fff";
}
}
// data to UI
// could be run on CartoDB xmlHttp.responseText
function feedback() {
document.getElementById('ride-number').innerHTML = "Ride #" + rideID;
document.getElementById('opendata').innerHTML = "Data transmission log. User: " + userID + ". Ride: " + rideID + ". ";
}
function initmap() {
username = localStorage.getItem('username');
var mapurl = "https://ideapublic.cartodb.com/tables/rides/embed_map?sql=SELECT%20*%20FROM%20rides%20where%20username%3D'"+username +"'";
document.getElementById('mapframe').src = mapurl;
}
/*******************************
* User & Ride Data
*/
// smartly random for fakeLocation()
var randX = Math.round( Math.random() * 10 ) / 10 ; // start location
var randY = Math.round( Math.random() * 10 ) / 10 ;
var randA = Math.random()/5000; // speed
var randB = Math.random()/5000;
function fakeLocation() {
if (timer_is_on==1) {
var lati = 40.3 + randX + (randA * counter) - (Math.random()/90000); // random variation
var longi = -74.5 + randY + (randB * counter) - (Math.random()/90000);
// -74.0, 40.7 NYC
// grab location to calc distance
if ( counter == 0 ) {
startLat = lati;
startLong = longi;
distance = 0;
distRide = 0;
// console.log(startLat,startLong);
}
else {
rideDistance(prevLat,prevLong,lati,longi);
}
// console.log("point #" + counter + " lati:" + lati + " longi:" + longi );
dbWrite(rideID,counter,lati,longi,distance,distRide);
cartodbTrace(rideID,counter,lati,longi);
openthedata(counter,lati,longi,distance);
counter=counter+1;
prevLat = lati;
prevLong = longi;
timer=setTimeout("fakeLocation()",gpsInterval);
}
else { ; }
}
// location by GPS
function bikeLocation() {
var getBikeLocation = function() {
var geoSuccess = function(p) {
var lati = p.coords.latitude;
var longi = p.coords.longitude;
// set initial dist to 0
if ( counter == 0 ) {
startLat = lati;
startLong = longi;
distance = 0;
distRide = 0;
// console.log(startLat,startLong);
}
else { // calculate distance
rideDistance(prevLat,prevLong,lati,longi);
}
dbWrite(rideID,counter,lati,longi,distance,distRide);
cartodbTrace(rideID,counter,lati,longi);
openthedata(counter,lati,longi,distance);
// could be used save data locally and then send when online:
// https://github.com/alexgibson/OfflineForm/blob/master/offlineData.js
counter=counter+1; // increment only on success?
prevLat = lati; // set location for next distance measurement
prevLong = longi;
}; // end if success
var geoFail = function() {
// write failure to cartoDB ??
};
navigator.geolocation.getCurrentPosition(geoSuccess, geoFail);
timer=setTimeout("bikeLocation()",gpsInterval);
};
if (timer_is_on==1) {
getBikeLocation();
}
}
// reveal data to the user
function openthedata(counter,lati,longi) {
document.getElementById('opendata').innerHTML += "Point: " + counter + ". ";
document.getElementById('opendata').innerHTML += "Lat: " + lati + ". ";
document.getElementById('opendata').innerHTML += "Long: " + longi + ". ";
}
// mobile connect status
function check_net_connection() {
var networkState = navigator.network.connection.type;
var states = {};
states[Connection.UNKNOWN] = 'Unknown';
states[Connection.ETHERNET] = 'Ethernet';
states[Connection.WIFI] = 'WiFi';
states[Connection.CELL_2G] = '2G';
states[Connection.CELL_3G] = '3G';
states[Connection.CELL_4G] = '4G';
states[Connection.NONE] = 'No connection';
var net_connect = states[networkState];
document.getElementById('connection').innerHTML = net_connect;
}
/*******************************
* CartoDB
*/
// add point to CartoDB
function cartodbTrace(rideID,count,lati,longi) {
//INSERT A GPS TRACE
if (write_to_carto) { // if write_to_carto AND timer_is_on ??
var gpsTimestamp ="now()";
var sqlInsert ="&q=INSERT INTO gps_traces(gps_timestamp,ride_id,trace_id,username,the_geom) VALUES("+ gpsTimestamp +","+ rideID +","+ count +",'"+ username +"',ST_SetSrid(st_makepoint("+ longi +","+ lati +"),4326))";
var theUrl = urlBase + cartoKey + sqlInsert;
var xmlHttp = null;
xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", theUrl, false );
xmlHttp.send( null );
if (xmlHttp.responseText) {
// console.log("rideID:" + rideID + ", trace:" + counter);
}
else { console.log("Trace to Carto failed: " + counter); }
// console.log("cartoDB response: " + xmlHttp.responseText);
// return xmlHttp.responseText;
}
}
// ride complete. make line in CartoDB from points.
function cartodbLine(rideID) {
//CREATE THE RIDE LINE (WHEN DONE)
if (write_to_carto) {
var sqlInsert = "&q=INSERT INTO rides(the_geom,username,ride_id) SELECT ST_Multi(ST_MakeLine(traces.the_geom)) as the_geom,'"+ username +"' as username,"+ rideID +" as ride_id FROM (SELECT the_geom, username FROM gps_traces WHERE username='"+ username +"' AND ride_id="+ rideID +") as traces";
var theUrl = urlBase + cartoKey + sqlInsert;
var xmlHttp = null;
xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", theUrl, false );
xmlHttp.send( null );
if (xmlHttp.responseText) {
console.log("Line written to Carto for RideID: " + rideID );
// dbDrop(); // clear localDB
}
else { console.log("Line in Carto failed for rideID: " + rideID ); }
// console.log(theUrl);
// console.log("cartoDB line response: " + xmlHttp.responseText);
// return xmlHttp.responseText;
}
}
/*******************************
* Local DB
*/
function dbStatus() {
db.transaction(function (tx) {
tx.executeSql('SELECT * FROM bikedb', [], function (tx, results) {
var dbtotal = results.rows.length;
}, function (tx, err) {
console.log("Error: "+ err.message);
});
});
}
function dbDrop() {
db.transaction(function (tx) {
tx.executeSql('DROP TABLE bikedb');
console.log("db dropped");
init_db();
}, function (err) {
console.log( "Drop error: " + err.message);
init_db();
});
}
function init_db() {
db.transaction(function (tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS bikedb (dbkey INTEGER PRIMARY KEY, userid INTEGER, rideid INTEGER, count INTEGER, lati INTEGER, longi INTEGER, distance INTEGER, distRide INTEGER)');
console.log("db init");
});
}
function dbWrite(rideid,thecount,lati,longi,distance) {
if (write_local_db) {
//console.log("write to localDB");
var userid = userID;
db.transaction(function (tx) {
//tx.executeSql('INSERT INTO bikedb (count, lati, longi) VALUES ("'+ counter + '", "'+ lati +'", "'+ longi +'")' );
tx.executeSql('INSERT INTO bikedb (userid, rideid, count, lati, longi, distance, distRide) VALUES (?,?,?,?,?,?,?);',[userid,rideid,thecount,lati,longi,distance,distRide] );
});
dbStatus();
}
}
// check ride data
// not currently in use
// could be used to confirm localStorage.rideNum
// could be used to estimate elapsed time & ride distance
function rideCheck() {
db.transaction(function (tx) {
tx.executeSql('SELECT * FROM bikedb ORDER BY rideid DESC', [], function (tx, results) {
var themax = results.rows.item(0).rideid;
console.log("last ride: ",themax);
}, function (tx, err) {
console.log( "rideCheck Error: " + err.message );
});
});
}
/*******************************
* Distance
* calculate each interval & aggregate the ride
*/
// clear lifetime distance
if (reset_distLifetime) {
console.log("clear lifetime distance");
localStorage.removeItem('distLifetime');
}
// check total distance
function lifetimeDistance() {
if ( !localStorage.getItem('distLifetime') ) {
console.log("init lifetime distance");
localStorage.setItem('distLifetime',0);
}
distLifetime = Number(localStorage.distLifetime); // convert, stored as string.
document.getElementById('dist-Lifetime').innerHTML = displayDistance(distLifetime);
}
function rideDistance(lat1,lon1,lat2,lon2) {
unit = "K";
var radlat1 = Math.PI * lat1/180
var radlat2 = Math.PI * lat2/180
var radlon1 = Math.PI * lon1/180
var radlon2 = Math.PI * lon2/180
var theta = lon1-lon2
var radtheta = Math.PI * theta/180
var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
dist = Math.acos(dist)
dist = dist * 180/Math.PI
dist = dist * 60 * 1.1515
if (unit=="K") { dist = dist * 1.609344 }
if (unit=="N") { dist = dist * 0.8684 }
alert(dist+"km");
distance = Math.round(dist * 1000); // km to meters
distRide += distance;
document.getElementById('distance').innerHTML = displayDistance(distRide);
}
// display meters as km
function displayDistance(distRide){
var distString = distRide.toString();
var distKm;
var distM;
// console.log(distRide,distString,distString.length);
// extract km
if ( distString.length > 4 ) { // if 10000+ meters
distKm = distString.substr(-5,2); // get km
}
else if ( distString.length > 3 ) { // if 1000+ meters
distKm = distString.substr(-4,1); // get km
}
else { distKm = 0;}
// extract m
if ( distString.length > 2 ) { // if 100+ meters
distM = distString.substr(-3,2); // get km decimal
}
else if ( distString.length > 1 ) { // if 10+ meters
distM = "0" + distString.substr(-2,1); // get km decimal
}
else { distM = "00";}
// console.log(distKm,distM);
var output = distKm + "." + distM + " km";
return output;
}
/*******************************
* Elapsed Timer
*/
function startTimer() {
var today=new Date();
var elapsed = (today - startTime)/1000;
var days = 0;
var hours = Math.floor((elapsed - (days * 86400 ))/3600);
var minutes = Math.floor((elapsed - (days * 86400 ) - (hours *3600 ))/60);
var secs = Math.floor((elapsed - (days * 86400 ) - (hours *3600 ) - (minutes*60)));
// add a zero in front of numbers<10
minutes=checkTimer(minutes);
secs=checkTimer(secs);
if (timer_is_on==1) {
document.getElementById('time').innerHTML=hours+":"+minutes+":"+secs;
t=setTimeout('startTimer()',500);
}
}
function checkTimer(i) {
if (i<10) {
i="0" + i;
}
return i;
}
/*******************************
* User Settings
*/
function saveSettings() {
localStorage.username = document.getElementById('username').value;
console.log("saved username: " + localStorage.username);
//localStorage.email = document.getElementById('email').value;
//console.log("saved email: " + localStorage.email);
window.location.href = "index.html"
return false;
}
function loadSettings() {
if (!localStorage.username) {
localStorage.username = "";
}
document.getElementById('username').value = localStorage.username;
document.getElementById('settings').style.display = "block";
}
function deleteSettings() {
var del=confirm("Delete Username?\nYou will lose access to your past rides.");
if (del==true) {
localStorage.username = "";
console.log("username removed");
loadSettings();
}
return false;
}