Skip to content

Commit 2115e36

Browse files
committed
Merge branch 'heatmap-contour-refactor' into carpet-plots
2 parents 1e53d57 + af9c983 commit 2115e36

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+5200
-1065
lines changed

circle.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ general:
66
machine:
77
node:
88
version: 6.1.0
9+
timezone:
10+
America/Anchorage
911
services:
1012
- docker
1113

lib/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Plotly.register([
3131

3232
require('./scattergl'),
3333
require('./pointcloud'),
34+
require('./heatmapgl'),
3435

3536
require('./scattermapbox'),
3637

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
"gl-contour2d": "^1.1.2",
6262
"gl-error2d": "^1.2.1",
6363
"gl-error3d": "^1.0.0",
64-
"gl-heatmap2d": "^1.0.2",
64+
"gl-heatmap2d": "^1.0.3",
6565
"gl-line2d": "^1.4.1",
6666
"gl-line3d": "^1.1.0",
6767
"gl-mat4": "^1.1.2",

src/components/drawing/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,10 @@ drawing.setClipUrl = function(s, localId) {
568568
var url = '#' + localId,
569569
base = d3.select('base');
570570

571-
if(base.size() && base.attr('href')) url = window.location.href + url;
571+
// add id to location href w/o hashes if any)
572+
if(base.size() && base.attr('href')) {
573+
url = window.location.href.split('#')[0] + url;
574+
}
575+
572576
s.attr('clip-path', 'url(' + url + ')');
573577
};

src/components/rangeselector/get_update_object.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ function getXRange(axisLayout, buttonLayout) {
4242

4343
switch(buttonLayout.stepmode) {
4444
case 'backward':
45-
range0 = Lib.ms2DateTime(+d3.time[step].offset(base, -count));
45+
range0 = Lib.ms2DateTime(+d3.time[step].utc.offset(base, -count));
4646
break;
4747

4848
case 'todate':
49-
var base2 = d3.time[step].offset(base, -count);
49+
var base2 = d3.time[step].utc.offset(base, -count);
5050

51-
range0 = Lib.ms2DateTime(+d3.time[step].ceil(base2));
51+
range0 = Lib.ms2DateTime(+d3.time[step].utc.ceil(base2));
5252
break;
5353
}
5454

src/lib/dates.js

Lines changed: 76 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
'use strict';
1111

1212
var d3 = require('d3');
13-
var isNumeric = require('fast-isnumeric');
1413

1514
var logError = require('./loggers').error;
1615

@@ -21,6 +20,11 @@ var ONEHOUR = constants.ONEHOUR;
2120
var ONEMIN = constants.ONEMIN;
2221
var ONESEC = constants.ONESEC;
2322

23+
var DATETIME_REGEXP = /^\s*(-?\d\d\d\d|\d\d)(-(0?[1-9]|1[012])(-([0-3]?\d)([ Tt]([01]?\d|2[0-3])(:([0-5]\d)(:([0-5]\d(\.\d+)?))?(Z|z|[+\-]\d\d:?\d\d)?)?)?)?)?\s*$/m;
24+
25+
// for 2-digit years, the first year we map them onto
26+
var YFIRST = new Date().getFullYear() - 70;
27+
2428
// is an object a javascript date?
2529
exports.isJSDate = function(v) {
2630
return typeof v === 'object' && v !== null && typeof v.getTime === 'function';
@@ -32,20 +36,33 @@ exports.isJSDate = function(v) {
3236
var MIN_MS, MAX_MS;
3337

3438
/**
35-
* dateTime2ms - turn a date object or string s of the form
36-
* YYYY-mm-dd HH:MM:SS.sss into milliseconds (relative to 1970-01-01,
37-
* per javascript standard)
38-
* may truncate after any full field, and sss can be any length
39-
* even >3 digits, though javascript dates truncate to milliseconds
40-
* returns BADNUM if it doesn't find a date
39+
* dateTime2ms - turn a date object or string s into milliseconds
40+
* (relative to 1970-01-01, per javascript standard)
41+
*
42+
* Returns BADNUM if it doesn't find a date
43+
*
44+
* strings should have the form:
45+
*
46+
* -?YYYY-mm-dd<sep>HH:MM:SS.sss<tzInfo>?
47+
*
48+
* <sep>: space (our normal standard) or T or t (ISO-8601)
49+
* <tzInfo>: Z, z, or [+\-]HH:?MM and we THROW IT AWAY
50+
* this format comes from https://tools.ietf.org/html/rfc3339#section-5.6
51+
* but we allow it even with a space as the separator
52+
*
53+
* May truncate after any full field, and sss can be any length
54+
* even >3 digits, though javascript dates truncate to milliseconds,
55+
* we keep as much as javascript numeric precision can hold, but we only
56+
* report back up to 100 microsecond precision, because most dates support
57+
* this precision (close to 1970 support more, very far away support less)
4158
*
4259
* Expanded to support negative years to -9999 but you must always
4360
* give 4 digits, except for 2-digit positive years which we assume are
4461
* near the present time.
4562
* Note that we follow ISO 8601:2004: there *is* a year 0, which
4663
* is 1BC/BCE, and -1===2BC etc.
4764
*
48-
* 2-digit to 4-digit year conversion, where to cut off?
65+
* Where to cut off 2-digit years between 1900s and 2000s?
4966
* from http://support.microsoft.com/kb/244664:
5067
* 1930-2029 (the most retro of all...)
5168
* but in my mac chrome from eg. d=new Date(Date.parse('8/19/50')):
@@ -70,96 +87,36 @@ var MIN_MS, MAX_MS;
7087
exports.dateTime2ms = function(s) {
7188
// first check if s is a date object
7289
if(exports.isJSDate(s)) {
73-
s = Number(s);
90+
// Convert to the UTC milliseconds that give the same
91+
// hours as this date has in the local timezone
92+
s = Number(s) - s.getTimezoneOffset() * ONEMIN;
7493
if(s >= MIN_MS && s <= MAX_MS) return s;
7594
return BADNUM;
7695
}
7796
// otherwise only accept strings and numbers
7897
if(typeof s !== 'string' && typeof s !== 'number') return BADNUM;
7998

80-
var y, m, d, h;
81-
// split date and time parts
82-
// TODO: we strip leading/trailing whitespace but not other
83-
// characters like we do for numbers - do we want to?
84-
var datetime = String(s).trim().split(' ');
85-
if(datetime.length > 2) return BADNUM;
86-
87-
var p = datetime[0].split('-'); // date part
88-
89-
var CE = true; // common era, ie positive year
90-
if(p[0] === '') {
91-
// first part is blank: year starts with a minus sign
92-
CE = false;
93-
p.splice(0, 1);
99+
var match = String(s).match(DATETIME_REGEXP);
100+
if(!match) return BADNUM;
101+
var y = match[1],
102+
m = Number(match[3] || 1),
103+
d = Number(match[5] || 1),
104+
H = Number(match[7] || 0),
105+
M = Number(match[9] || 0),
106+
S = Number(match[11] || 0);
107+
if(y.length === 2) {
108+
y = (Number(y) + 2000 - YFIRST) % 100 + YFIRST;
94109
}
95-
96-
var plen = p.length;
97-
if(plen > 3 || (plen !== 3 && datetime[1]) || !plen) return BADNUM;
98-
99-
// year
100-
if(p[0].length === 4) y = Number(p[0]);
101-
else if(p[0].length === 2) {
102-
if(!CE) return BADNUM;
103-
var yNow = new Date().getFullYear();
104-
y = ((Number(p[0]) - yNow + 70) % 100 + 200) % 100 + yNow - 70;
105-
}
106-
else return BADNUM;
107-
if(!isNumeric(y)) return BADNUM;
110+
else y = Number(y);
108111

109112
// javascript takes new Date(0..99,m,d) to mean 1900-1999, so
110113
// to support years 0-99 we need to use setFullYear explicitly
111-
var baseDate = new Date(0, 0, 1);
112-
baseDate.setFullYear(CE ? y : -y);
113-
if(p.length > 1) {
114-
115-
// month - may be 1 or 2 digits
116-
m = Number(p[1]) - 1; // new Date() uses zero-based months
117-
if(p[1].length > 2 || !(m >= 0 && m <= 11)) return BADNUM;
118-
baseDate.setMonth(m);
119-
120-
if(p.length > 2) {
121-
122-
// day - may be 1 or 2 digits
123-
d = Number(p[2]);
124-
if(p[2].length > 2 || !(d >= 1 && d <= 31)) return BADNUM;
125-
baseDate.setDate(d);
126-
127-
// does that date exist in this month?
128-
if(baseDate.getDate() !== d) return BADNUM;
129-
130-
if(datetime[1]) {
114+
var date = new Date(Date.UTC(2000, m - 1, d, H, M));
115+
date.setUTCFullYear(y);
131116

132-
p = datetime[1].split(':');
133-
if(p.length > 3) return BADNUM;
117+
if(date.getUTCDate() !== d) return BADNUM;
134118

135-
// hour - may be 1 or 2 digits
136-
h = Number(p[0]);
137-
if(p[0].length > 2 || !p[0].length || !(h >= 0 && h <= 23)) return BADNUM;
138-
baseDate.setHours(h);
139-
140-
// does that hour exist in this day? (Daylight time!)
141-
// (TODO: remove this check when we move to UTC)
142-
if(baseDate.getHours() !== h) return BADNUM;
143-
144-
if(p.length > 1) {
145-
d = baseDate.getTime();
146-
147-
// minute - must be 2 digits
148-
m = Number(p[1]);
149-
if(p[1].length !== 2 || !(m >= 0 && m <= 59)) return BADNUM;
150-
d += ONEMIN * m;
151-
if(p.length === 2) return d;
152-
153-
// second (and milliseconds) - must have 2-digit seconds
154-
if(p[2].split('.')[0].length !== 2) return BADNUM;
155-
s = Number(p[2]);
156-
if(!(s >= 0 && s < 60)) return BADNUM;
157-
return d + s * ONESEC;
158-
}
159-
}
160-
}
161-
}
162-
return baseDate.getTime();
119+
return date.getTime() + S * ONESEC;
163120
};
164121

165122
MIN_MS = exports.MIN_MS = exports.dateTime2ms('-9999');
@@ -191,16 +148,41 @@ exports.ms2DateTime = function(ms, r) {
191148

192149
if(!r) r = 0;
193150

194-
var d = new Date(Math.floor(ms)),
195-
dateStr = d3.time.format('%Y-%m-%d')(d),
151+
var msecTenths = Math.round(((ms % 1) + 1) * 10) % 10,
152+
d = new Date(Math.round(ms - msecTenths / 10)),
153+
dateStr = d3.time.format.utc('%Y-%m-%d')(d),
196154
// <90 days: add hours and minutes - never *only* add hours
197-
h = (r < NINETYDAYS) ? d.getHours() : 0,
198-
m = (r < NINETYDAYS) ? d.getMinutes() : 0,
155+
h = (r < NINETYDAYS) ? d.getUTCHours() : 0,
156+
m = (r < NINETYDAYS) ? d.getUTCMinutes() : 0,
199157
// <3 hours: add seconds
200-
s = (r < THREEHOURS) ? d.getSeconds() : 0,
158+
s = (r < THREEHOURS) ? d.getUTCSeconds() : 0,
201159
// <5 minutes: add ms (plus one extra digit, this is msec*10)
202-
msec10 = (r < FIVEMIN) ? Math.round((d.getMilliseconds() + (((ms % 1) + 1) % 1)) * 10) : 0;
160+
msec10 = (r < FIVEMIN) ? d.getUTCMilliseconds() * 10 + msecTenths : 0;
203161

162+
return includeTime(dateStr, h, m, s, msec10);
163+
};
164+
165+
// For converting old-style milliseconds to date strings,
166+
// we use the local timezone rather than UTC like we use
167+
// everywhere else, both for backward compatibility and
168+
// because that's how people mostly use javasript date objects.
169+
// Clip one extra day off our date range though so we can't get
170+
// thrown beyond the range by the timezone shift.
171+
exports.ms2DateTimeLocal = function(ms) {
172+
if(!(ms >= MIN_MS + ONEDAY && ms <= MAX_MS - ONEDAY)) return BADNUM;
173+
174+
var msecTenths = Math.round(((ms % 1) + 1) * 10) % 10,
175+
d = new Date(Math.round(ms - msecTenths / 10)),
176+
dateStr = d3.time.format('%Y-%m-%d')(d),
177+
h = d.getHours(),
178+
m = d.getMinutes(),
179+
s = d.getSeconds(),
180+
msec10 = d.getUTCMilliseconds() * 10 + msecTenths;
181+
182+
return includeTime(dateStr, h, m, s, msec10);
183+
};
184+
185+
function includeTime(dateStr, h, m, s, msec10) {
204186
// include each part that has nonzero data in or after it
205187
if(h || m || s || msec10) {
206188
dateStr += ' ' + lpad(h, 2) + ':' + lpad(m, 2);
@@ -217,7 +199,7 @@ exports.ms2DateTime = function(ms, r) {
217199
}
218200
}
219201
return dateStr;
220-
};
202+
}
221203

222204
// normalize date format to date string, in case it starts as
223205
// a Date object or milliseconds
@@ -227,7 +209,7 @@ exports.cleanDate = function(v, dflt) {
227209
// NOTE: if someone puts in a year as a number rather than a string,
228210
// this will mistakenly convert it thinking it's milliseconds from 1970
229211
// that is: '2012' -> Jan. 1, 2012, but 2012 -> 2012 epoch milliseconds
230-
v = exports.ms2DateTime(+v);
212+
v = exports.ms2DateTimeLocal(+v);
231213
if(!v && dflt !== undefined) return dflt;
232214
}
233215
else if(!exports.isDateTime(v)) {

src/lib/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ var datesModule = require('./dates');
2828
lib.dateTime2ms = datesModule.dateTime2ms;
2929
lib.isDateTime = datesModule.isDateTime;
3030
lib.ms2DateTime = datesModule.ms2DateTime;
31+
lib.ms2DateTimeLocal = datesModule.ms2DateTimeLocal;
3132
lib.cleanDate = datesModule.cleanDate;
3233
lib.isJSDate = datesModule.isJSDate;
3334
lib.MIN_MS = datesModule.MIN_MS;

0 commit comments

Comments
 (0)