-
Notifications
You must be signed in to change notification settings - Fork 3.5k
/
TimeInterval.js
411 lines (373 loc) · 17.4 KB
/
TimeInterval.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
define([
'./Check',
'./defaultValue',
'./defined',
'./defineProperties',
'./DeveloperError',
'./freezeObject',
'./JulianDate'
], function(
Check,
defaultValue,
defined,
defineProperties,
DeveloperError,
freezeObject,
JulianDate) {
'use strict';
/**
* An interval defined by a start and a stop time; optionally including those times as part of the interval.
* Arbitrary data can optionally be associated with each instance for used with {@link TimeIntervalCollection}.
*
* @alias TimeInterval
* @constructor
*
* @param {Object} [options] Object with the following properties:
* @param {JulianDate} [options.start=new JulianDate()] The start time of the interval.
* @param {JulianDate} [options.stop=new JulianDate()] The stop time of the interval.
* @param {Boolean} [options.isStartIncluded=true] <code>true</code> if <code>options.start</code> is included in the interval, <code>false</code> otherwise.
* @param {Boolean} [options.isStopIncluded=true] <code>true</code> if <code>options.stop</code> is included in the interval, <code>false</code> otherwise.
* @param {Object} [options.data] Arbitrary data associated with this interval.
*
* @example
* // Create an instance that spans August 1st, 1980 and is associated
* // with a Cartesian position.
* var timeInterval = new Cesium.TimeInterval({
* start : Cesium.JulianDate.fromIso8601('1980-08-01T00:00:00Z'),
* stop : Cesium.JulianDate.fromIso8601('1980-08-02T00:00:00Z'),
* isStartIncluded : true,
* isStopIncluded : false,
* data : Cesium.Cartesian3.fromDegrees(39.921037, -75.170082)
* });
*
* @example
* // Create two instances from ISO 8601 intervals with associated numeric data
* // then compute their intersection, summing the data they contain.
* var left = Cesium.TimeInterval.fromIso8601({
* iso8601 : '2000/2010',
* data : 2
* });
*
* var right = Cesium.TimeInterval.fromIso8601({
* iso8601 : '1995/2005',
* data : 3
* });
*
* //The result of the below intersection will be an interval equivalent to
* //var intersection = Cesium.TimeInterval.fromIso8601({
* // iso8601 : '2000/2005',
* // data : 5
* //});
* var intersection = new Cesium.TimeInterval();
* Cesium.TimeInterval.intersect(left, right, intersection, function(leftData, rightData) {
* return leftData + rightData;
* });
*
* @example
* // Check if an interval contains a specific time.
* var dateToCheck = Cesium.JulianDate.fromIso8601('1982-09-08T11:30:00Z');
* var containsDate = Cesium.TimeInterval.contains(timeInterval, dateToCheck);
*/
function TimeInterval(options) {
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
/**
* Gets or sets the start time of this interval.
* @type {JulianDate}
*/
this.start = defined(options.start) ? JulianDate.clone(options.start) : new JulianDate();
/**
* Gets or sets the stop time of this interval.
* @type {JulianDate}
*/
this.stop = defined(options.stop) ? JulianDate.clone(options.stop) : new JulianDate();
/**
* Gets or sets the data associated with this interval.
* @type {*}
*/
this.data = options.data;
/**
* Gets or sets whether or not the start time is included in this interval.
* @type {Boolean}
* @default true
*/
this.isStartIncluded = defaultValue(options.isStartIncluded, true);
/**
* Gets or sets whether or not the stop time is included in this interval.
* @type {Boolean}
* @default true
*/
this.isStopIncluded = defaultValue(options.isStopIncluded, true);
}
defineProperties(TimeInterval.prototype, {
/**
* Gets whether or not this interval is empty.
* @memberof TimeInterval.prototype
* @type {Boolean}
* @readonly
*/
isEmpty : {
get : function() {
var stopComparedToStart = JulianDate.compare(this.stop, this.start);
return stopComparedToStart < 0 || (stopComparedToStart === 0 && (!this.isStartIncluded || !this.isStopIncluded));
}
}
});
var scratchInterval = {
start : undefined,
stop : undefined,
isStartIncluded : undefined,
isStopIncluded : undefined,
data : undefined
};
/**
* Creates a new instance from a {@link http://en.wikipedia.org/wiki/ISO_8601|ISO 8601} interval.
*
* @throws DeveloperError if options.iso8601 does not match proper formatting.
*
* @param {Object} options Object with the following properties:
* @param {String} options.iso8601 An ISO 8601 interval.
* @param {Boolean} [options.isStartIncluded=true] <code>true</code> if <code>options.start</code> is included in the interval, <code>false</code> otherwise.
* @param {Boolean} [options.isStopIncluded=true] <code>true</code> if <code>options.stop</code> is included in the interval, <code>false</code> otherwise.
* @param {Object} [options.data] Arbitrary data associated with this interval.
* @param {TimeInterval} [result] An existing instance to use for the result.
* @returns {TimeInterval} The modified result parameter or a new instance if none was provided.
*/
TimeInterval.fromIso8601 = function(options, result) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object('options', options);
Check.typeOf.string('options.iso8601', options.iso8601);
//>>includeEnd('debug');
var dates = options.iso8601.split('/');
if (dates.length !== 2) {
throw new DeveloperError('options.iso8601 is an invalid ISO 8601 interval.');
}
var start = JulianDate.fromIso8601(dates[0]);
var stop = JulianDate.fromIso8601(dates[1]);
var isStartIncluded = defaultValue(options.isStartIncluded, true);
var isStopIncluded = defaultValue(options.isStopIncluded, true);
var data = options.data;
if (!defined(result)) {
scratchInterval.start = start;
scratchInterval.stop = stop;
scratchInterval.isStartIncluded = isStartIncluded;
scratchInterval.isStopIncluded = isStopIncluded;
scratchInterval.data = data;
return new TimeInterval(scratchInterval);
}
result.start = start;
result.stop = stop;
result.isStartIncluded = isStartIncluded;
result.isStopIncluded = isStopIncluded;
result.data = data;
return result;
};
/**
* Creates an ISO8601 representation of the provided interval.
*
* @param {TimeInterval} timeInterval The interval to be converted.
* @param {Number} [precision] The number of fractional digits used to represent the seconds component. By default, the most precise representation is used.
* @returns {String} The ISO8601 representation of the provided interval.
*/
TimeInterval.toIso8601 = function(timeInterval, precision) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object('timeInterval', timeInterval);
//>>includeEnd('debug');
return JulianDate.toIso8601(timeInterval.start, precision) + '/' + JulianDate.toIso8601(timeInterval.stop, precision);
};
/**
* Duplicates the provided instance.
*
* @param {TimeInterval} [timeInterval] The instance to clone.
* @param {TimeInterval} [result] An existing instance to use for the result.
* @returns {TimeInterval} The modified result parameter or a new instance if none was provided.
*/
TimeInterval.clone = function(timeInterval, result) {
if (!defined(timeInterval)) {
return undefined;
}
if (!defined(result)) {
return new TimeInterval(timeInterval);
}
result.start = timeInterval.start;
result.stop = timeInterval.stop;
result.isStartIncluded = timeInterval.isStartIncluded;
result.isStopIncluded = timeInterval.isStopIncluded;
result.data = timeInterval.data;
return result;
};
/**
* Compares two instances and returns <code>true</code> if they are equal, <code>false</code> otherwise.
*
* @param {TimeInterval} [left] The first instance.
* @param {TimeInterval} [right] The second instance.
* @param {TimeInterval~DataComparer} [dataComparer] A function which compares the data of the two intervals. If omitted, reference equality is used.
* @returns {Boolean} <code>true</code> if the dates are equal; otherwise, <code>false</code>.
*/
TimeInterval.equals = function(left, right, dataComparer) {
return left === right ||
defined(left) && defined(right) &&
(left.isEmpty && right.isEmpty ||
left.isStartIncluded === right.isStartIncluded &&
left.isStopIncluded === right.isStopIncluded &&
JulianDate.equals(left.start, right.start) &&
JulianDate.equals(left.stop, right.stop) &&
(left.data === right.data || (defined(dataComparer) && dataComparer(left.data, right.data))));
};
/**
* Compares two instances and returns <code>true</code> if they are within <code>epsilon</code> seconds of
* each other. That is, in order for the dates to be considered equal (and for
* this function to return <code>true</code>), the absolute value of the difference between them, in
* seconds, must be less than <code>epsilon</code>.
*
* @param {TimeInterval} [left] The first instance.
* @param {TimeInterval} [right] The second instance.
* @param {Number} epsilon The maximum number of seconds that should separate the two instances.
* @param {TimeInterval~DataComparer} [dataComparer] A function which compares the data of the two intervals. If omitted, reference equality is used.
* @returns {Boolean} <code>true</code> if the two dates are within <code>epsilon</code> seconds of each other; otherwise <code>false</code>.
*/
TimeInterval.equalsEpsilon = function(left, right, epsilon, dataComparer) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.number('epsilon', epsilon);
//>>includeEnd('debug');
return left === right ||
defined(left) && defined(right) &&
(left.isEmpty && right.isEmpty ||
left.isStartIncluded === right.isStartIncluded &&
left.isStopIncluded === right.isStopIncluded &&
JulianDate.equalsEpsilon(left.start, right.start, epsilon) &&
JulianDate.equalsEpsilon(left.stop, right.stop, epsilon) &&
(left.data === right.data || (defined(dataComparer) && dataComparer(left.data, right.data))));
};
/**
* Computes the intersection of two intervals, optionally merging their data.
*
* @param {TimeInterval} left The first interval.
* @param {TimeInterval} [right] The second interval.
* @param {TimeInterval} result An existing instance to use for the result.
* @param {TimeInterval~MergeCallback} [mergeCallback] A function which merges the data of the two intervals. If omitted, the data from the left interval will be used.
* @returns {TimeInterval} The modified result parameter.
*/
TimeInterval.intersect = function(left, right, result, mergeCallback) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object('left', left);
Check.typeOf.object('result', result);
//>>includeEnd('debug');
if (!defined(right)) {
return TimeInterval.clone(TimeInterval.EMPTY, result);
}
var leftStart = left.start;
var leftStop = left.stop;
var rightStart = right.start;
var rightStop = right.stop;
var intersectsStartRight = JulianDate.greaterThanOrEquals(rightStart, leftStart) && JulianDate.greaterThanOrEquals(leftStop, rightStart);
var intersectsStartLeft = !intersectsStartRight && JulianDate.lessThanOrEquals(rightStart, leftStart) && JulianDate.lessThanOrEquals(leftStart, rightStop);
if (!intersectsStartRight && !intersectsStartLeft) {
return TimeInterval.clone(TimeInterval.EMPTY, result);
}
var leftIsStartIncluded = left.isStartIncluded;
var leftIsStopIncluded = left.isStopIncluded;
var rightIsStartIncluded = right.isStartIncluded;
var rightIsStopIncluded = right.isStopIncluded;
var leftLessThanRight = JulianDate.lessThan(leftStop, rightStop);
result.start = intersectsStartRight ? rightStart : leftStart;
result.isStartIncluded = (leftIsStartIncluded && rightIsStartIncluded) || (!JulianDate.equals(rightStart, leftStart) && ((intersectsStartRight && rightIsStartIncluded) || (intersectsStartLeft && leftIsStartIncluded)));
result.stop = leftLessThanRight ? leftStop : rightStop;
result.isStopIncluded = leftLessThanRight ? leftIsStopIncluded : (leftIsStopIncluded && rightIsStopIncluded) || (!JulianDate.equals(rightStop, leftStop) && rightIsStopIncluded);
result.data = defined(mergeCallback) ? mergeCallback(left.data, right.data) : left.data;
return result;
};
/**
* Checks if the specified date is inside the provided interval.
*
* @param {TimeInterval} timeInterval The interval.
* @param {JulianDate} julianDate The date to check.
* @returns {Boolean} <code>true</code> if the interval contains the specified date, <code>false</code> otherwise.
*/
TimeInterval.contains = function(timeInterval, julianDate) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.object('timeInterval', timeInterval);
Check.typeOf.object('julianDate', julianDate);
//>>includeEnd('debug');
if (timeInterval.isEmpty) {
return false;
}
var startComparedToDate = JulianDate.compare(timeInterval.start, julianDate);
if (startComparedToDate === 0) {
return timeInterval.isStartIncluded;
}
var dateComparedToStop = JulianDate.compare(julianDate, timeInterval.stop);
if (dateComparedToStop === 0) {
return timeInterval.isStopIncluded;
}
return startComparedToDate < 0 && dateComparedToStop < 0;
};
/**
* Duplicates this instance.
*
* @param {TimeInterval} [result] An existing instance to use for the result.
* @returns {TimeInterval} The modified result parameter or a new instance if none was provided.
*/
TimeInterval.prototype.clone = function(result) {
return TimeInterval.clone(this, result);
};
/**
* Compares this instance against the provided instance componentwise and returns
* <code>true</code> if they are equal, <code>false</code> otherwise.
*
* @param {TimeInterval} [right] The right hand side interval.
* @param {TimeInterval~DataComparer} [dataComparer] A function which compares the data of the two intervals. If omitted, reference equality is used.
* @returns {Boolean} <code>true</code> if they are equal, <code>false</code> otherwise.
*/
TimeInterval.prototype.equals = function(right, dataComparer) {
return TimeInterval.equals(this, right, dataComparer);
};
/**
* Compares this instance against the provided instance componentwise and returns
* <code>true</code> if they are within the provided epsilon,
* <code>false</code> otherwise.
*
* @param {TimeInterval} [right] The right hand side interval.
* @param {Number} epsilon The epsilon to use for equality testing.
* @param {TimeInterval~DataComparer} [dataComparer] A function which compares the data of the two intervals. If omitted, reference equality is used.
* @returns {Boolean} <code>true</code> if they are within the provided epsilon, <code>false</code> otherwise.
*/
TimeInterval.prototype.equalsEpsilon = function(right, epsilon, dataComparer) {
return TimeInterval.equalsEpsilon(this, right, epsilon, dataComparer);
};
/**
* Creates a string representing this TimeInterval in ISO8601 format.
*
* @returns {String} A string representing this TimeInterval in ISO8601 format.
*/
TimeInterval.prototype.toString = function() {
return TimeInterval.toIso8601(this);
};
/**
* An immutable empty interval.
*
* @type {TimeInterval}
* @constant
*/
TimeInterval.EMPTY = freezeObject(new TimeInterval({
start : new JulianDate(),
stop : new JulianDate(),
isStartIncluded : false,
isStopIncluded : false
}));
/**
* Function interface for merging interval data.
* @callback TimeInterval~MergeCallback
*
* @param {*} leftData The first data instance.
* @param {*} rightData The second data instance.
* @returns {*} The result of merging the two data instances.
*/
/**
* Function interface for comparing interval data.
* @callback TimeInterval~DataComparer
* @param {*} leftData The first data instance.
* @param {*} rightData The second data instance.
* @returns {Boolean} <code>true</code> if the provided instances are equal, <code>false</code> otherwise.
*/
return TimeInterval;
});