blacklight_heatmaps 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  /**
2
- * geostats() is a tiny and standalone javascript library for classification
2
+ * geostats() is a tiny and standalone javascript library for classification
3
3
  * Project page - https://github.com/simogeo/geostats
4
- * Copyright (c) 2011 Simon Georget, http://www.empreinte-urbaine.eu
4
+ * Copyright (c) 2011 Simon Georget, http://www.intermezzo-coop.eu
5
5
  * Licensed under the MIT license
6
6
  */
7
7
 
@@ -85,13 +85,13 @@ var geostats = function(a) {
85
85
  this.is_uniqueValues = false;
86
86
  this.debug = false;
87
87
  this.silent = false;
88
-
88
+
89
89
  this.bounds = Array();
90
90
  this.ranges = Array();
91
91
  this.inner_ranges = null;
92
92
  this.colors = Array();
93
93
  this.counter = Array();
94
-
94
+
95
95
  // statistics information
96
96
  this.stat_sorted = null;
97
97
  this.stat_mean = null;
@@ -104,62 +104,62 @@ var geostats = function(a) {
104
104
  this.stat_stddev = null;
105
105
  this.stat_cov = null;
106
106
 
107
-
107
+
108
108
  /**
109
109
  * logging method
110
110
  */
111
111
  this.log = function(msg, force) {
112
-
112
+
113
113
  if(this.debug == true || force != null)
114
114
  console.log(this.objectID + "(object id) :: " + msg);
115
-
115
+
116
116
  };
117
-
117
+
118
118
  /**
119
119
  * Set bounds
120
120
  */
121
121
  this.setBounds = function(a) {
122
-
122
+
123
123
  this.log('Setting bounds (' + a.length + ') : ' + a.join());
124
-
124
+
125
125
  this.bounds = Array() // init empty array to prevent bug when calling classification after another with less items (sample getQuantile(6) and getQuantile(4))
126
-
126
+
127
127
  this.bounds = a;
128
128
  //this.bounds = this.decimalFormat(a);
129
-
129
+
130
130
  };
131
-
131
+
132
132
  /**
133
133
  * Set a new serie
134
134
  */
135
135
  this.setSerie = function(a) {
136
-
136
+
137
137
  this.log('Setting serie (' + a.length + ') : ' + a.join());
138
-
138
+
139
139
  this.serie = Array() // init empty array to prevent bug when calling classification after another with less items (sample getQuantile(6) and getQuantile(4))
140
140
  this.serie = a;
141
-
141
+
142
142
  //reset statistics after changing serie
143
143
  this.resetStatistics();
144
-
144
+
145
145
  this.setPrecision();
146
-
146
+
147
147
  };
148
-
148
+
149
149
  /**
150
150
  * Set colors
151
151
  */
152
152
  this.setColors = function(colors) {
153
-
153
+
154
154
  this.log('Setting color ramp (' + colors.length + ') : ' + colors.join());
155
-
155
+
156
156
  this.colors = colors;
157
-
157
+
158
158
  };
159
-
159
+
160
160
  /**
161
161
  * Get feature count
162
- * With bounds array(0, 0.75, 1.5, 2.25, 3);
162
+ * With bounds array(0, 0.75, 1.5, 2.25, 3);
163
163
  * should populate this.counter with 5 keys
164
164
  * and increment counters for each key
165
165
  */
@@ -167,19 +167,25 @@ var geostats = function(a) {
167
167
 
168
168
  if (this._nodata())
169
169
  return;
170
-
170
+
171
171
 
172
172
  var tmp = this.sorted();
173
-
173
+
174
174
  this.counter = new Array();
175
-
175
+
176
176
  // we init counter with 0 value
177
- for(i = 0; i < this.bounds.length -1; i++) {
178
- this.counter[i]= 0;
177
+ if(this.is_uniqueValues == true) {
178
+ for (var i = 0; i < this.bounds.length; i++) {
179
+ this.counter[i]= 0;
180
+ }
181
+ } else {
182
+ for (var i = 0; i < this.bounds.length -1; i++) {
183
+ this.counter[i]= 0;
184
+ }
179
185
  }
180
-
181
- for(j=0; j < tmp.length; j++) {
182
-
186
+
187
+ for (var j=0; j < tmp.length; j++) {
188
+
183
189
  // get current class for value to increment the counter
184
190
  var cclass = this.getClass(tmp[j]);
185
191
  this.counter[cclass]++;
@@ -187,239 +193,262 @@ var geostats = function(a) {
187
193
  }
188
194
 
189
195
  };
190
-
196
+
191
197
  /**
192
198
  * Set decimal precision according to user input
193
199
  * or automatcally determined according
194
200
  * to the given serie.
195
201
  */
196
202
  this.setPrecision = function(decimals) {
197
-
203
+
198
204
  // only when called from user
199
205
  if(typeof decimals !== "undefined") {
200
206
  this.precisionflag = 'manual';
201
207
  this.precision = decimals;
202
208
  }
203
-
209
+
204
210
  // we calculate the maximal decimal length on given serie
205
211
  if(this.precisionflag == 'auto') {
206
-
212
+
207
213
  for (var i = 0; i < this.serie.length; i++) {
208
-
214
+
209
215
  // check if the given value is a number and a float
210
216
  if (!isNaN((this.serie[i]+"")) && (this.serie[i]+"").toString().indexOf('.') != -1) {
211
217
  var precision = (this.serie[i] + "").split(".")[1].length;
212
218
  } else {
213
219
  var precision = 0;
214
220
  }
215
-
221
+
216
222
  if(precision > this.precision) {
217
223
  this.precision = precision;
218
224
  }
219
-
225
+
220
226
  }
221
-
227
+
228
+ }
229
+ if(this.precision > 20) {
230
+ // prevent "Uncaught RangeError: toFixed() digits argument must be between 0 and 20" bug. See https://github.com/simogeo/geostats/issues/34
231
+ this.log('this.precision value (' + this.precision + ') is greater than max value. Automatic set-up to 20 to prevent "Uncaught RangeError: toFixed()" when calling decimalFormat() method.');
232
+ this.precision = 20;
222
233
  }
223
234
 
224
235
  this.log('Calling setPrecision(). Mode : ' + this.precisionflag + ' - Decimals : '+ this.precision);
225
-
236
+
226
237
  this.serie = this.decimalFormat(this.serie);
227
-
238
+
228
239
  };
229
-
240
+
230
241
  /**
231
242
  * Format array numbers regarding to precision
232
243
  */
233
244
  this.decimalFormat = function(a) {
234
-
245
+
235
246
  var b = new Array();
236
-
247
+
237
248
  for (var i = 0; i < a.length; i++) {
238
249
  // check if the given value is a number
239
250
  if (isNumber(a[i])) {
240
- b[i] = parseFloat(a[i].toFixed(this.precision));
251
+ b[i] = parseFloat(parseFloat(a[i]).toFixed(this.precision));
241
252
  } else {
242
253
  b[i] = a[i];
243
254
  }
244
255
  }
245
-
256
+
246
257
  return b;
247
258
  }
248
-
259
+
249
260
  /**
250
261
  * Transform a bounds array to a range array the following array : array(0,
251
262
  * 0.75, 1.5, 2.25, 3); becomes : array('0-0.75', '0.75-1.5', '1.5-2.25',
252
263
  * '2.25-3');
253
264
  */
254
265
  this.setRanges = function() {
255
-
266
+
256
267
  this.ranges = Array(); // init empty array to prevent bug when calling classification after another with less items (sample getQuantile(6) and getQuantile(4))
257
-
258
- for (i = 0; i < (this.bounds.length - 1); i++) {
268
+
269
+ for (var i = 0; i < (this.bounds.length - 1); i++) {
259
270
  this.ranges[i] = this.bounds[i] + this.separator + this.bounds[i + 1];
260
271
  }
261
272
  };
262
273
 
263
274
  /** return min value */
264
- this.min = function() {
265
-
275
+ this.min = function(exclude = []) {
276
+
266
277
  if (this._nodata())
267
278
  return;
268
-
269
- this.stat_min = Math.min.apply(null, this.serie);
270
-
279
+
280
+ if(!exclude.includes(this.serie[0])) this.stat_min = this.serie[0];
281
+ else this.stat_min = 999999999999;
282
+
283
+
284
+ for (var i = 0; i < this.pop(); i++) {
285
+ if (this.serie[i] < this.stat_min && !exclude.includes(this.serie[i])) {
286
+ this.stat_min = this.serie[i];
287
+ }
288
+ }
289
+
271
290
  return this.stat_min;
272
291
  };
273
292
 
274
293
  /** return max value */
275
- this.max = function() {
276
-
277
- this.stat_max = Math.max.apply(null, this.serie);
278
-
294
+ this.max = function(exclude = []) {
295
+
296
+ if (this._nodata())
297
+ return;
298
+
299
+ if(!exclude.includes(this.serie[0])) this.stat_max = this.serie[0];
300
+ else this.stat_max = -999999999999;
301
+
302
+ for (var i = 0; i < this.pop(); i++) {
303
+ if (this.serie[i] > this.stat_max && !exclude.includes(this.serie[i])) {
304
+ this.stat_max = this.serie[i];
305
+ }
306
+ }
307
+
279
308
  return this.stat_max;
280
309
  };
281
310
 
282
311
  /** return sum value */
283
312
  this.sum = function() {
284
-
313
+
285
314
  if (this._nodata())
286
315
  return;
287
-
316
+
288
317
  if (this.stat_sum == null) {
289
-
318
+
290
319
  this.stat_sum = 0;
291
- for (i = 0; i < this.pop(); i++) {
320
+ for (var i = 0; i < this.pop(); i++) {
292
321
  this.stat_sum += parseFloat(this.serie[i]);
293
322
  }
294
-
323
+
295
324
  }
296
-
325
+
297
326
  return this.stat_sum;
298
327
  };
299
328
 
300
329
  /** return population number */
301
330
  this.pop = function() {
302
-
331
+
303
332
  if (this._nodata())
304
333
  return;
305
-
334
+
306
335
  if (this.stat_pop == null) {
307
-
336
+
308
337
  this.stat_pop = this.serie.length;
309
-
338
+
310
339
  }
311
-
340
+
312
341
  return this.stat_pop;
313
342
  };
314
343
 
315
344
  /** return mean value */
316
345
  this.mean = function() {
317
-
346
+
318
347
  if (this._nodata())
319
348
  return;
320
349
 
321
350
  if (this.stat_mean == null) {
322
-
351
+
323
352
  this.stat_mean = parseFloat(this.sum() / this.pop());
324
-
353
+
325
354
  }
326
-
355
+
327
356
  return this.stat_mean;
328
357
  };
329
358
 
330
359
  /** return median value */
331
360
  this.median = function() {
332
-
361
+
333
362
  if (this._nodata())
334
363
  return;
335
-
364
+
336
365
  if (this.stat_median == null) {
337
-
366
+
338
367
  this.stat_median = 0;
339
368
  var tmp = this.sorted();
340
-
369
+
341
370
  // serie pop is odd
342
371
  if (tmp.length % 2) {
343
372
  this.stat_median = parseFloat(tmp[(Math.ceil(tmp.length / 2) - 1)]);
344
-
373
+
345
374
  // serie pop is even
346
375
  } else {
347
376
  this.stat_median = ( parseFloat(tmp[((tmp.length / 2) - 1)]) + parseFloat(tmp[(tmp.length / 2)]) ) / 2;
348
377
  }
349
-
378
+
350
379
  }
351
-
380
+
352
381
  return this.stat_median;
353
382
  };
354
383
 
355
384
  /** return variance value */
356
385
  this.variance = function() {
357
-
358
- round = (typeof round === "undefined") ? true : false;
359
-
386
+
387
+ var round = (typeof round === "undefined") ? true : false;
388
+
360
389
  if (this._nodata())
361
390
  return;
362
-
391
+
363
392
  if (this.stat_variance == null) {
364
393
 
365
- var tmp = 0;
394
+ var tmp = 0, serie_mean = this.mean();
366
395
  for (var i = 0; i < this.pop(); i++) {
367
- tmp += Math.pow( (this.serie[i] - this.mean()), 2 );
396
+ tmp += Math.pow( (this.serie[i] - serie_mean), 2 );
368
397
  }
369
398
 
370
- this.stat_variance = tmp / this.pop();
371
-
399
+ this.stat_variance = tmp / this.pop();
400
+
372
401
  if(round == true) {
373
402
  this.stat_variance = Math.round(this.stat_variance * Math.pow(10,this.roundlength) )/ Math.pow(10,this.roundlength);
374
403
  }
375
-
404
+
376
405
  }
377
-
406
+
378
407
  return this.stat_variance;
379
408
  };
380
-
409
+
381
410
  /** return standard deviation value */
382
411
  this.stddev = function(round) {
383
-
384
- round = (typeof round === "undefined") ? true : false;
385
-
412
+
413
+ var round = (typeof round === "undefined") ? true : false;
414
+
386
415
  if (this._nodata())
387
416
  return;
388
-
417
+
389
418
  if (this.stat_stddev == null) {
390
-
419
+
391
420
  this.stat_stddev = Math.sqrt(this.variance());
392
-
421
+
393
422
  if(round == true) {
394
423
  this.stat_stddev = Math.round(this.stat_stddev * Math.pow(10,this.roundlength) )/ Math.pow(10,this.roundlength);
395
424
  }
396
-
425
+
397
426
  }
398
-
427
+
399
428
  return this.stat_stddev;
400
429
  };
401
-
430
+
402
431
  /** coefficient of variation - measure of dispersion */
403
432
  this.cov = function(round) {
404
-
405
- round = (typeof round === "undefined") ? true : false;
406
-
433
+
434
+ var round = (typeof round === "undefined") ? true : false;
435
+
407
436
  if (this._nodata())
408
437
  return;
409
-
438
+
410
439
  if (this.stat_cov == null) {
411
-
440
+
412
441
  this.stat_cov = this.stddev() / this.mean();
413
-
442
+
414
443
  if(round == true) {
415
444
  this.stat_cov = Math.round(this.stat_cov * Math.pow(10,this.roundlength) )/ Math.pow(10,this.roundlength);
416
445
  }
417
-
446
+
418
447
  }
419
-
448
+
420
449
  return this.stat_cov;
421
450
  };
422
-
451
+
423
452
  /** reset all attributes after setting a new serie */
424
453
  this.resetStatistics = function() {
425
454
  this.stat_sorted = null;
@@ -433,34 +462,63 @@ var geostats = function(a) {
433
462
  this.stat_stddev = null;
434
463
  this.stat_cov = null;
435
464
  }
436
-
465
+
437
466
  /** data test */
438
467
  this._nodata = function() {
439
468
  if (this.serie.length == 0) {
440
-
469
+
441
470
  if(this.silent) this.log("[silent mode] Error. You should first enter a serie!", true);
442
- else alert("Error. You should first enter a serie!");
471
+ else throw new TypeError("Error. You should first enter a serie!");
443
472
  return 1;
444
473
  } else
445
474
  return 0;
446
-
475
+
476
+ };
477
+
478
+ /** data test */
479
+ this._classificationCheck = function(nbClass) {
480
+
481
+ if(nbClass >= this.pop()) {
482
+ var errnum ='Error. ' + nbClass + ' classes are defined for only ' + this.pop() + ' values in serie ! For the current serie, no more than ' + (this.pop() - 1 ) + ' classes can be defined.';
483
+
484
+ if(this.silent) this.log(errnum, true);
485
+ else {
486
+ alert(errnum);
487
+ throw new TypeError(errnum);
488
+ }
489
+
490
+ }
491
+
447
492
  };
448
-
493
+
494
+ /** ensure nbClass is an integer */
495
+ this._nbClassInt = function(nbClass) {
496
+
497
+ var nbclassTmp = parseInt(nbClass, 10);
498
+ if (isNaN(nbclassTmp)) {
499
+ if(this.silent) this.log("[silent mode] '" + nbclassTmp + "' is not a valid integer. Enable to set class number.", true);
500
+ else throw new TypeError("'" + nbclassTmp + "' is not a valid integer. Enable to set class number.");
501
+ } else {
502
+ return nbclassTmp;
503
+ }
504
+
505
+ };
506
+
449
507
  /** check if the serie contains negative value */
450
508
  this._hasNegativeValue = function() {
451
-
452
- for (i = 0; i < this.serie.length; i++) {
509
+
510
+ for (var i = 0; i < this.serie.length; i++) {
453
511
  if(this.serie[i] < 0)
454
512
  return true;
455
513
  }
456
514
 
457
515
  return false;
458
516
  };
459
-
517
+
460
518
  /** check if the serie contains zero value */
461
519
  this._hasZeroValue = function() {
462
-
463
- for (i = 0; i < this.serie.length; i++) {
520
+
521
+ for (var i = 0; i < this.serie.length; i++) {
464
522
  if(parseFloat(this.serie[i]) === 0)
465
523
  return true;
466
524
  }
@@ -470,9 +528,9 @@ var geostats = function(a) {
470
528
 
471
529
  /** return sorted values (as array) */
472
530
  this.sorted = function() {
473
-
531
+
474
532
  if (this.stat_sorted == null) {
475
-
533
+
476
534
  if(this.is_uniqueValues == false) {
477
535
  this.stat_sorted = this.serie.sort(function(a, b) {
478
536
  return a - b;
@@ -486,33 +544,33 @@ var geostats = function(a) {
486
544
  })
487
545
  }
488
546
  }
489
-
547
+
490
548
  return this.stat_sorted;
491
-
549
+
492
550
  };
493
551
 
494
552
  /** return all info */
495
553
  this.info = function() {
496
-
554
+
497
555
  if (this._nodata())
498
556
  return;
499
-
557
+
500
558
  var content = '';
501
559
  content += _t('Population') + ' : ' + this.pop() + ' - [' + _t('Min')
502
560
  + ' : ' + this.min() + ' | ' + _t('Max') + ' : ' + this.max()
503
561
  + ']' + "\n";
504
562
  content += _t('Mean') + ' : ' + this.mean() + ' - ' + _t('Median') + ' : ' + this.median() + "\n";
505
- content += _t('Variance') + ' : ' + this.variance() + ' - ' + _t('Standard deviation') + ' : ' + this.stddev()
563
+ content += _t('Variance') + ' : ' + this.variance() + ' - ' + _t('Standard deviation') + ' : ' + this.stddev()
506
564
  + ' - ' + _t('Coefficient of variation') + ' : ' + this.cov() + "\n";
507
565
 
508
566
  return content;
509
567
  };
510
-
568
+
511
569
  /**
512
570
  * Set Manual classification Return an array with bounds : ie array(0,
513
571
  * 0.75, 1.5, 2.25, 3);
514
572
  * Set ranges and prepare data for displaying legend
515
- *
573
+ *
516
574
  */
517
575
  this.setClassManually = function(array) {
518
576
 
@@ -521,13 +579,13 @@ var geostats = function(a) {
521
579
 
522
580
  if(array[0] !== this.min() || array[array.length-1] !== this.max()) {
523
581
  if(this.silent) this.log("[silent mode] " + t('Given bounds may not be correct! please check your input.\nMin value : ' + this.min() + ' / Max value : ' + this.max()), true);
524
- else alert(_t('Given bounds may not be correct! please check your input.\nMin value : ' + this.min() + ' / Max value : ' + this.max()));
525
- return;
582
+ else throw new TypeError(_t('Given bounds may not be correct! please check your input.\nMin value : ' + this.min() + ' / Max value : ' + this.max()));
583
+ return;
526
584
  }
527
585
 
528
586
  this.setBounds(array);
529
587
  this.setRanges();
530
-
588
+
531
589
  // we specify the classification method
532
590
  this.method = _t('manual classification') + ' (' + (array.length -1) + ' ' + _t('classes') + ')';
533
591
 
@@ -540,17 +598,19 @@ var geostats = function(a) {
540
598
  */
541
599
  this.getClassEqInterval = function(nbClass, forceMin, forceMax) {
542
600
 
601
+ var nbClass = this._nbClassInt(nbClass); // ensure nbClass is an integer
602
+
543
603
  if (this._nodata())
544
604
  return;
545
605
 
546
606
  var tmpMin = (typeof forceMin === "undefined") ? this.min() : forceMin;
547
607
  var tmpMax = (typeof forceMax === "undefined") ? this.max() : forceMax;
548
-
608
+
549
609
  var a = Array();
550
610
  var val = tmpMin;
551
611
  var interval = (tmpMax - tmpMin) / nbClass;
552
612
 
553
- for (i = 0; i <= nbClass; i++) {
613
+ for (var i = 0; i <= nbClass; i++) {
554
614
  a[i] = val;
555
615
  val += interval;
556
616
  }
@@ -560,15 +620,18 @@ var geostats = function(a) {
560
620
 
561
621
  this.setBounds(a);
562
622
  this.setRanges();
563
-
623
+
564
624
  // we specify the classification method
565
625
  this.method = _t('eq. intervals') + ' (' + nbClass + ' ' + _t('classes') + ')';
566
626
 
567
627
  return this.bounds;
568
628
  };
569
-
629
+
570
630
 
571
631
  this.getQuantiles = function(nbClass) {
632
+
633
+ var nbClass = this._nbClassInt(nbClass); // ensure nbClass is an integer
634
+
572
635
  var tmp = this.sorted();
573
636
  var quantiles = [];
574
637
 
@@ -587,9 +650,13 @@ var geostats = function(a) {
587
650
  */
588
651
  this.getClassQuantile = function(nbClass) {
589
652
 
653
+ var nbClass = this._nbClassInt(nbClass); // ensure nbClass is an integer
654
+
590
655
  if (this._nodata())
591
656
  return;
592
657
 
658
+ this._classificationCheck(nbClass); // be sure number of classes is valid
659
+
593
660
  var tmp = this.sorted();
594
661
  var bounds = this.getQuantiles(nbClass);
595
662
  bounds.unshift(tmp[0]);
@@ -606,7 +673,7 @@ var geostats = function(a) {
606
673
  return this.bounds;
607
674
 
608
675
  };
609
-
676
+
610
677
  /**
611
678
  * Standard Deviation classification
612
679
  * Return an array with bounds : ie array(0,
@@ -614,180 +681,194 @@ var geostats = function(a) {
614
681
  */
615
682
  this.getClassStdDeviation = function(nbClass, matchBounds) {
616
683
 
684
+ var nbClass = this._nbClassInt(nbClass); // ensure nbClass is an integer
685
+
617
686
  if (this._nodata())
618
687
  return;
619
688
 
620
689
  var tmpMax = this.max();
621
- var tmpMin = this.min();
622
-
690
+ var tmpMin = this.min();
691
+ var tmpStdDev = this.stddev();
692
+ var tmpMean = this.mean();
693
+
623
694
  var a = Array();
624
-
695
+
625
696
  // number of classes is odd
626
697
  if(nbClass % 2 == 1) {
627
698
 
628
699
  // Euclidean division to get the inferior bound
629
700
  var infBound = Math.floor(nbClass / 2);
630
-
701
+
631
702
  var supBound = infBound + 1;
632
-
703
+
633
704
  // we set the central bounds
634
- a[infBound] = this.mean() - ( this.stddev() / 2);
635
- a[supBound] = this.mean() + ( this.stddev() / 2);
636
-
705
+ a[infBound] = tmpMean - (tmpStdDev / 2);
706
+ a[supBound] = tmpMean + (tmpStdDev / 2);
707
+
637
708
  // Values < to infBound, except first one
638
- for (i = infBound - 1; i > 0; i--) {
639
- var val = a[i+1] - this.stddev();
709
+ for (var i = infBound - 1; i > 0; i--) {
710
+ var val = a[i+1] - tmpStdDev;
640
711
  a[i] = val;
641
712
  }
642
-
713
+
643
714
  // Values > to supBound, except last one
644
- for (i = supBound + 1; i < nbClass; i++) {
645
- var val = a[i-1] + this.stddev();
715
+ for (var i = supBound + 1; i < nbClass; i++) {
716
+ var val = a[i-1] + tmpStdDev;
646
717
  a[i] = val;
647
718
  }
648
-
719
+
649
720
  // number of classes is even
650
721
  } else {
651
-
722
+
652
723
  var meanBound = nbClass / 2;
653
-
724
+
654
725
  // we get the mean value
655
- a[meanBound] = this.mean();
656
-
726
+ a[meanBound] = tmpMean;
727
+
657
728
  // Values < to the mean, except first one
658
- for (i = meanBound - 1; i > 0; i--) {
659
- var val = a[i+1] - this.stddev();
729
+ for (var i = meanBound - 1; i > 0; i--) {
730
+ var val = a[i+1] - tmpStdDev;
660
731
  a[i] = val;
661
732
  }
662
-
733
+
663
734
  // Values > to the mean, except last one
664
- for (i = meanBound + 1; i < nbClass; i++) {
665
- var val = a[i-1] + this.stddev();
735
+ for (var i = meanBound + 1; i < nbClass; i++) {
736
+ var val = a[i-1] + tmpStdDev;
666
737
  a[i] = val;
667
738
  }
668
739
  }
669
-
670
-
740
+
741
+
671
742
  // we finally set the first value
672
- // do we excatly match min value or not ?
673
- a[0] = (typeof matchBounds === "undefined") ? a[1]-this.stddev() : this.min();
674
-
743
+ // do we excatly match min value or not ?
744
+ a[0] = (typeof matchBounds === "undefined") ? a[1]- tmpStdDev : tmpMin;
745
+
675
746
  // we finally set the last value
676
- // do we excatly match max value or not ?
677
- a[nbClass] = (typeof matchBounds === "undefined") ? a[nbClass-1]+this.stddev() : this.max();
747
+ // do we excatly match max value or not ?
748
+ a[nbClass] = (typeof matchBounds === "undefined") ? a[nbClass-1] + tmpStdDev : tmpMax;
678
749
 
679
750
  this.setBounds(a);
680
751
  this.setRanges();
681
-
752
+
682
753
  // we specify the classification method
683
- this.method = _t('std deviation') + ' (' + nbClass + ' ' + _t('classes')+ ')';
684
-
754
+ this.method = _t('std deviation') + ' (' + nbClass + ' ' + _t('classes')+ ')';
755
+
685
756
  return this.bounds;
686
757
  };
687
-
688
-
758
+
759
+
689
760
  /**
690
- * Geometric Progression classification
761
+ * Geometric Progression classification
691
762
  * http://en.wikipedia.org/wiki/Geometric_progression
692
763
  * Return an array with bounds : ie array(0,
693
764
  * 0.75, 1.5, 2.25, 3);
694
765
  */
695
766
  this.getClassGeometricProgression = function(nbClass) {
696
767
 
768
+ var nbClass = this._nbClassInt(nbClass); // ensure nbClass is an integer
769
+
697
770
  if (this._nodata())
698
771
  return;
699
772
 
700
773
  if(this._hasNegativeValue() || this._hasZeroValue()) {
701
774
  if(this.silent) this.log("[silent mode] " + _t('geometric progression can\'t be applied with a serie containing negative or zero values.'), true);
702
- else alert(_t('geometric progression can\'t be applied with a serie containing negative or zero values.'));
775
+ else throw new TypeError(_t('geometric progression can\'t be applied with a serie containing negative or zero values.'));
703
776
  return;
704
777
  }
705
-
778
+
706
779
  var a = Array();
707
780
  var tmpMin = this.min();
708
781
  var tmpMax = this.max();
709
-
782
+
710
783
  var logMax = Math.log(tmpMax) / Math.LN10; // max decimal logarithm (or base 10)
711
784
  var logMin = Math.log(tmpMin) / Math.LN10;; // min decimal logarithm (or base 10)
712
-
785
+
713
786
  var interval = (logMax - logMin) / nbClass;
714
-
787
+
715
788
  // we compute log bounds
716
- for (i = 0; i < nbClass; i++) {
789
+ for (var i = 0; i < nbClass; i++) {
717
790
  if(i == 0) {
718
791
  a[i] = logMin;
719
792
  } else {
720
793
  a[i] = a[i-1] + interval;
721
794
  }
722
795
  }
723
-
796
+
724
797
  // we compute antilog
725
798
  a = a.map(function(x) { return Math.pow(10, x); });
726
-
799
+
727
800
  // and we finally add max value
728
801
  a.push(this.max());
729
-
802
+
730
803
  this.setBounds(a);
731
804
  this.setRanges();
732
-
805
+
733
806
  // we specify the classification method
734
807
  this.method = _t('geometric progression') + ' (' + nbClass + ' ' + _t('classes') + ')';
735
808
 
736
809
  return this.bounds;
737
810
  };
738
-
811
+
739
812
  /**
740
- * Arithmetic Progression classification
813
+ * Arithmetic Progression classification
741
814
  * http://en.wikipedia.org/wiki/Arithmetic_progression
742
815
  * Return an array with bounds : ie array(0,
743
816
  * 0.75, 1.5, 2.25, 3);
744
817
  */
745
818
  this.getClassArithmeticProgression = function(nbClass) {
746
819
 
820
+ var nbClass = this._nbClassInt(nbClass); // ensure nbClass is an integer
821
+
747
822
  if (this._nodata())
748
823
  return;
749
-
824
+
750
825
  var denominator = 0;
751
-
826
+
752
827
  // we compute the (french) "Raison"
753
- for (i = 1; i <= nbClass; i++) {
828
+ for (var i = 1; i <= nbClass; i++) {
754
829
  denominator += i;
755
830
  }
756
831
 
757
832
  var a = Array();
758
833
  var tmpMin = this.min();
759
834
  var tmpMax = this.max();
760
-
835
+
761
836
  var interval = (tmpMax - tmpMin) / denominator;
762
837
 
763
- for (i = 0; i <= nbClass; i++) {
838
+ for (var i = 0; i <= nbClass; i++) {
764
839
  if(i == 0) {
765
840
  a[i] = tmpMin;
766
- } else {
841
+ } else if(i == nbClass) {
842
+ a[i] = tmpMax;
843
+ } else {
767
844
  a[i] = a[i-1] + (i * interval);
768
845
  }
769
846
  }
770
847
 
771
848
  this.setBounds(a);
772
849
  this.setRanges();
773
-
850
+
774
851
  // we specify the classification method
775
852
  this.method = _t('arithmetic progression') + ' (' + nbClass + ' ' + _t('classes') + ')';
776
853
 
777
854
  return this.bounds;
778
855
  };
779
-
856
+
780
857
  /**
781
858
  * Credits : Doug Curl (javascript) and Daniel J Lewis (python implementation)
782
859
  * http://www.arcgis.com/home/item.html?id=0b633ff2f40d412995b8be377211c47b
783
860
  * http://danieljlewis.org/2010/06/07/jenks-natural-breaks-algorithm-in-python/
784
861
  */
785
- this.getClassJenks = function(nbClass) {
786
-
862
+ this.getClassJenks2 = function(nbClass) {
863
+
864
+ var nbClass = this._nbClassInt(nbClass); // ensure nbClass is an integer
865
+
787
866
  if (this._nodata())
788
867
  return;
789
-
790
- dataList = this.sorted();
868
+
869
+ this._classificationCheck(nbClass); // be sure number of classes is valid
870
+
871
+ var dataList = this.sorted();
791
872
 
792
873
  // now iterate through the datalist:
793
874
  // determine mat1 and mat2
@@ -859,7 +940,7 @@ var geostats = function(a) {
859
940
  var kclass = []
860
941
 
861
942
  // fill the kclass (classification) array with zeros:
862
- for (i = 0; i <= nbClass; i++) {
943
+ for (var i = 0; i <= nbClass; i++) {
863
944
  kclass.push(0);
864
945
  }
865
946
 
@@ -888,43 +969,186 @@ var geostats = function(a) {
888
969
  this.setBounds(kclass);
889
970
  this.setRanges();
890
971
 
891
-
972
+
973
+ this.method = _t('Jenks2') + ' (' + nbClass + ' ' + _t('classes') + ')';
974
+
975
+ return this.bounds; //array of breaks
976
+ }
977
+
978
+ /**
979
+ * Credits from simple-statistics library
980
+ * https://github.com/simple-statistics/simple-statistics
981
+ * https://gist.githubusercontent.com/tmcw/4969184/raw/cfd9572d00db6bcdc34f07b088738fc3a47846b4/simple_statistics.js
982
+ */
983
+ this.getClassJenks = function(nbClass) {
984
+
985
+ var nbClass = this._nbClassInt(nbClass); // ensure nbClass is an integer
986
+
987
+ if (this._nodata())
988
+ return;
989
+
990
+ this._classificationCheck(nbClass); // be sure number of classes is valid
991
+
992
+ var dataList = this.sorted();
993
+
994
+ // Compute the matrices required for Jenks breaks. These matrices
995
+ // can be used for any classing of data with `classes <= n_classes`
996
+ var jenksMatrices = function(data, n_classes) {
997
+
998
+ // in the original implementation, these matrices are referred to
999
+ // as `LC` and `OP`
1000
+ //
1001
+ // * lower_class_limits (LC): optimal lower class limits
1002
+ // * variance_combinations (OP): optimal variance combinations for all classes
1003
+ var lower_class_limits = [],
1004
+ variance_combinations = [],
1005
+ // loop counters
1006
+ i, j,
1007
+ // the variance, as computed at each step in the calculation
1008
+ variance = 0;
1009
+
1010
+ // Initialize and fill each matrix with zeroes
1011
+ for (var i = 0; i < data.length + 1; i++) {
1012
+ var tmp1 = [], tmp2 = [];
1013
+ for (var j = 0; j < n_classes + 1; j++) {
1014
+ tmp1.push(0);
1015
+ tmp2.push(0);
1016
+ }
1017
+ lower_class_limits.push(tmp1);
1018
+ variance_combinations.push(tmp2);
1019
+ }
1020
+
1021
+ for (var i = 1; i < n_classes + 1; i++) {
1022
+ lower_class_limits[1][i] = 1;
1023
+ variance_combinations[1][i] = 0;
1024
+ // in the original implementation, 9999999 is used but
1025
+ // since Javascript has `Infinity`, we use that.
1026
+ for (var j = 2; j < data.length + 1; j++) {
1027
+ variance_combinations[j][i] = Infinity;
1028
+ }
1029
+ }
1030
+
1031
+ for (var l = 2; l < data.length + 1; l++) {
1032
+
1033
+ // `SZ` originally. this is the sum of the values seen thus
1034
+ // far when calculating variance.
1035
+ var sum = 0,
1036
+ // `ZSQ` originally. the sum of squares of values seen
1037
+ // thus far
1038
+ sum_squares = 0,
1039
+ // `WT` originally. This is the number of
1040
+ w = 0,
1041
+ // `IV` originally
1042
+ i4 = 0;
1043
+
1044
+ // in several instances, you could say `Math.pow(x, 2)`
1045
+ // instead of `x * x`, but this is slower in some browsers
1046
+ // introduces an unnecessary concept.
1047
+ for (var m = 1; m < l + 1; m++) {
1048
+
1049
+ // `III` originally
1050
+ var lower_class_limit = l - m + 1,
1051
+ val = data[lower_class_limit - 1];
1052
+
1053
+ // here we're estimating variance for each potential classing
1054
+ // of the data, for each potential number of classes. `w`
1055
+ // is the number of data points considered so far.
1056
+ w++;
1057
+
1058
+ // increase the current sum and sum-of-squares
1059
+ sum += val;
1060
+ sum_squares += val * val;
1061
+
1062
+ // the variance at this point in the sequence is the difference
1063
+ // between the sum of squares and the total x 2, over the number
1064
+ // of samples.
1065
+ variance = sum_squares - (sum * sum) / w;
1066
+
1067
+ i4 = lower_class_limit - 1;
1068
+
1069
+ if (i4 !== 0) {
1070
+ for (var j = 2; j < n_classes + 1; j++) {
1071
+ if (variance_combinations[l][j] >=
1072
+ (variance + variance_combinations[i4][j - 1])) {
1073
+ lower_class_limits[l][j] = lower_class_limit;
1074
+ variance_combinations[l][j] = variance +
1075
+ variance_combinations[i4][j - 1];
1076
+ }
1077
+ }
1078
+ }
1079
+ }
1080
+
1081
+ lower_class_limits[l][1] = 1;
1082
+ variance_combinations[l][1] = variance;
1083
+ }
1084
+
1085
+ return {
1086
+ lower_class_limits: lower_class_limits,
1087
+ variance_combinations: variance_combinations
1088
+ };
1089
+ };
1090
+
1091
+ // get our basic matrices
1092
+ var matrices = jenksMatrices(dataList, nbClass),
1093
+ // we only need lower class limits here
1094
+ lower_class_limits = matrices.lower_class_limits,
1095
+ k = dataList.length - 1,
1096
+ kclass = [],
1097
+ countNum = nbClass;
1098
+
1099
+ // the calculation of classes will never include the upper and
1100
+ // lower bounds, so we need to explicitly set them
1101
+ kclass[nbClass] = dataList[dataList.length - 1];
1102
+ kclass[0] = dataList[0];
1103
+
1104
+ // the lower_class_limits matrix is used as indexes into itself
1105
+ // here: the `k` variable is reused in each iteration.
1106
+ while (countNum > 1) {
1107
+ kclass[countNum - 1] = dataList[lower_class_limits[k][countNum] - 2];
1108
+ k = lower_class_limits[k][countNum] - 1;
1109
+ countNum--;
1110
+ }
1111
+
1112
+ this.setBounds(kclass);
1113
+ this.setRanges();
1114
+
1115
+
892
1116
  this.method = _t('Jenks') + ' (' + nbClass + ' ' + _t('classes') + ')';
893
-
1117
+
894
1118
  return this.bounds; //array of breaks
895
1119
  }
896
-
897
-
1120
+
1121
+
898
1122
  /**
899
- * Quantile classification Return an array with bounds : ie array(0, 0.75,
1123
+ * Unique classification Return as many entries as unique values : ie array('blue', 'red', yellow')
900
1124
  * 1.5, 2.25, 3);
901
1125
  */
902
1126
  this.getClassUniqueValues = function() {
903
1127
 
904
1128
  if (this._nodata())
905
1129
  return;
906
-
1130
+
907
1131
  this.is_uniqueValues = true;
908
-
1132
+
909
1133
  var tmp = this.sorted(); // display in alphabetical order
910
1134
 
911
1135
  var a = Array();
912
1136
 
913
- for (i = 0; i < this.pop(); i++) {
1137
+ for (var i = 0; i < this.pop(); i++) {
914
1138
  if(a.indexOf(tmp[i]) === -1)
915
1139
  a.push(tmp[i]);
916
1140
  }
917
-
1141
+
918
1142
  this.bounds = a;
919
-
1143
+
920
1144
  // we specify the classification method
921
1145
  this.method = _t('unique values');
922
-
1146
+
923
1147
  return a;
924
1148
 
925
1149
  };
926
-
927
-
1150
+
1151
+
928
1152
  /**
929
1153
  * Return the class of a given value.
930
1154
  * For example value : 6
@@ -933,12 +1157,14 @@ var geostats = function(a) {
933
1157
  */
934
1158
  this.getClass = function(value) {
935
1159
 
936
- for(i = 0; i < this.bounds.length; i++) {
937
-
938
-
1160
+ for (var i = 0; i < this.bounds.length; i++) {
1161
+
939
1162
  if(this.is_uniqueValues == true) {
940
- if(value == this.bounds[i])
1163
+
1164
+ if(value == this.bounds[i]) {
1165
+ // console.log(value + ' - ' + this.bounds[i] + ' returned value : ' + i);
941
1166
  return i;
1167
+ }
942
1168
  } else {
943
1169
  // parseFloat() is necessary
944
1170
  if(parseFloat(value) <= this.bounds[i + 1]) {
@@ -946,9 +1172,9 @@ var geostats = function(a) {
946
1172
  }
947
1173
  }
948
1174
  }
949
-
1175
+
950
1176
  return _t("Unable to get value's class.");
951
-
1177
+
952
1178
  };
953
1179
 
954
1180
  /**
@@ -956,120 +1182,120 @@ var geostats = function(a) {
956
1182
  * '2.25-3');
957
1183
  */
958
1184
  this.getRanges = function() {
959
-
1185
+
960
1186
  return this.ranges;
961
-
1187
+
962
1188
  };
963
1189
 
964
1190
  /**
965
1191
  * Returns the number/index of this.ranges that value falls into
966
1192
  */
967
1193
  this.getRangeNum = function(value) {
968
-
1194
+
969
1195
  var bounds, i;
970
1196
 
971
- for (i = 0; i < this.ranges.length; i++) {
1197
+ for (var i = 0; i < this.ranges.length; i++) {
972
1198
  bounds = this.ranges[i].split(/ - /);
973
1199
  if (value <= parseFloat(bounds[1])) {
974
1200
  return i;
975
1201
  }
976
1202
  }
977
1203
  }
978
-
1204
+
979
1205
  /*
980
- * Compute inner ranges based on serie.
981
- * Produce discontinous ranges used for legend - return an array similar to :
1206
+ * Compute inner ranges based on serie.
1207
+ * Produce discontinous ranges used for legend - return an array similar to :
982
1208
  * array('0.00-0.74', '0.98-1.52', '1.78-2.25', '2.99-3.14');
983
1209
  * If inner ranges already computed, return array values.
984
1210
  */
985
1211
  this.getInnerRanges = function() {
986
-
1212
+
987
1213
  // if already computed, we return the result
988
1214
  if(this.inner_ranges != null)
989
1215
  return this.inner_ranges;
990
1216
 
991
-
1217
+
992
1218
  var a = new Array();
993
1219
  var tmp = this.sorted();
994
-
1220
+
995
1221
  var cnt = 1; // bounds array counter
996
-
997
- for (i = 0; i < tmp.length; i++) {
998
-
1222
+
1223
+ for (var i = 0; i < tmp.length; i++) {
1224
+
999
1225
  if(i == 0) var range_firstvalue = tmp[i]; // we init first range value
1000
-
1226
+
1001
1227
  if(parseFloat(tmp[i]) > parseFloat(this.bounds[cnt])) {
1002
-
1228
+
1003
1229
  a[cnt - 1] = '' + range_firstvalue + this.separator + tmp[i-1];
1004
-
1230
+
1005
1231
  var range_firstvalue = tmp[i];
1006
-
1232
+
1007
1233
  cnt++;
1008
1234
 
1009
1235
  }
1010
-
1236
+
1011
1237
  // we reach the last range, we finally complete manually
1012
1238
  // and return the array
1013
1239
  if(cnt == (this.bounds.length - 1)) {
1014
1240
  // we set the last value
1015
1241
  a[cnt - 1] = '' + range_firstvalue + this.separator + tmp[tmp.length-1];
1016
-
1242
+
1017
1243
  this.inner_ranges = a;
1018
1244
  return this.inner_ranges;
1019
1245
  }
1020
-
1246
+
1021
1247
 
1022
1248
  }
1023
-
1249
+
1024
1250
  };
1025
-
1251
+
1026
1252
  this.getSortedlist = function() {
1027
-
1253
+
1028
1254
  return this.sorted().join(', ');
1029
-
1255
+
1030
1256
  };
1031
-
1257
+
1032
1258
  /**
1033
1259
  * Return an html legend
1034
1260
  * colors : specify an array of color (hexadecimal values)
1035
1261
  * legend : specify a text input for the legend. By default, just displays 'legend'
1036
1262
  * counter : if not null, display counter value
1037
1263
  * callback : if not null, callback function applied on legend boundaries
1038
- * mode : null, 'default', 'distinct', 'discontinuous' :
1264
+ * mode : null, 'default', 'distinct', 'discontinuous' :
1039
1265
  * - if mode is null, will display legend as 'default mode'
1040
1266
  * - 'default' : displays ranges like in ranges array (continuous values), sample : 29.26 - 378.80 / 378.80 - 2762.25 / 2762.25 - 6884.84
1041
- * - 'distinct' : Add + 1 according to decimal precision to distinguish classes (discrete values), sample : 29.26 - 378.80 / 378.81 - 2762.25 / 2762.26 - 6884.84
1267
+ * - 'distinct' : Add + 1 according to decimal precision to distinguish classes (discrete values), sample : 29.26 - 378.80 / 378.81 - 2762.25 / 2762.26 - 6884.84
1042
1268
  * - 'discontinuous' : indicates the range of data actually falling in each class , sample : 29.26 - 225.43 / 852.12 - 2762.20 / 3001.25 - 6884.84 / not implemented yet
1043
1269
  * order : null, 'ASC', 'DESC'
1044
1270
  */
1045
1271
  this.getHtmlLegend = function(colors, legend, counter, callback, mode, order) {
1046
-
1272
+
1047
1273
  var cnt= '';
1048
1274
  var elements = new Array();
1049
-
1275
+
1050
1276
  this.doCount(); // we do count, even if not displayed
1051
-
1277
+
1052
1278
  if(colors != null) {
1053
1279
  ccolors = colors;
1054
1280
  }
1055
1281
  else {
1056
1282
  ccolors = this.colors;
1057
1283
  }
1058
-
1284
+
1059
1285
  if(legend != null) {
1060
1286
  lg = legend;
1061
1287
  }
1062
1288
  else {
1063
1289
  lg = 'Legend';
1064
1290
  }
1065
-
1291
+
1066
1292
  if(counter != null) {
1067
1293
  getcounter = true;
1068
1294
  }
1069
1295
  else {
1070
1296
  getcounter = false;
1071
1297
  }
1072
-
1298
+
1073
1299
  if(callback != null) {
1074
1300
  fn = callback;
1075
1301
  }
@@ -1084,34 +1310,34 @@ var geostats = function(a) {
1084
1310
  // check if some classes are not populated / equivalent of in_array function
1085
1311
  if(this.counter.indexOf(0) !== -1) {
1086
1312
  if(this.silent) this.log("[silent mode] " + _t("Geostats cannot apply 'discontinuous' mode to the getHtmlLegend() method because some classes are not populated.\nPlease switch to 'default' or 'distinct' modes. Exit!"), true);
1087
- else alert(_t("Geostats cannot apply 'discontinuous' mode to the getHtmlLegend() method because some classes are not populated.\nPlease switch to 'default' or 'distinct' modes. Exit!"));
1313
+ else throw new TypeError(_t("Geostats cannot apply 'discontinuous' mode to the getHtmlLegend() method because some classes are not populated.\nPlease switch to 'default' or 'distinct' modes. Exit!"));
1088
1314
  return;
1089
1315
  }
1090
1316
 
1091
1317
  }
1092
1318
  if(order !== 'DESC') order = 'ASC';
1093
-
1319
+
1094
1320
  if(ccolors.length < this.ranges.length) {
1095
1321
  if(this.silent) this.log("[silent mode] " + _t('The number of colors should fit the number of ranges. Exit!'), true);
1096
- else alert(_t('The number of colors should fit the number of ranges. Exit!'));
1322
+ else throw new TypeError(_t('The number of colors should fit the number of ranges. Exit!'));
1097
1323
  return;
1098
1324
  }
1099
-
1325
+
1100
1326
  if(this.is_uniqueValues == false) {
1101
-
1102
- for (i = 0; i < (this.ranges.length); i++) {
1327
+
1328
+ for (var i = 0; i < (this.ranges.length); i++) {
1103
1329
  if(getcounter===true) {
1104
1330
  cnt = ' <span class="geostats-legend-counter">(' + this.counter[i] + ')</span>';
1105
1331
  }
1106
1332
  //console.log("Ranges : " + this.ranges[i]);
1107
-
1108
- // default mode
1333
+
1334
+ // default mode
1109
1335
  var tmp = this.ranges[i].split(this.separator);
1110
-
1336
+
1111
1337
  var start_value = parseFloat(tmp[0]).toFixed(this.precision);
1112
1338
  var end_value = parseFloat(tmp[1]).toFixed(this.precision);
1113
-
1114
-
1339
+
1340
+
1115
1341
  // if mode == 'distinct' and we are not working on the first value
1116
1342
  if(mode == 'distinct' && i != 0) {
1117
1343
 
@@ -1122,34 +1348,34 @@ var geostats = function(a) {
1122
1348
  } else {
1123
1349
 
1124
1350
  start_value = parseFloat(start_value) + (1 / Math.pow(10,this.precision));
1125
- // strangely the formula above return sometimes long decimal values,
1351
+ // strangely the formula above return sometimes long decimal values,
1126
1352
  // the following instruction fix it
1127
1353
  start_value = parseFloat(start_value).toFixed(this.precision);
1128
1354
  }
1129
1355
  }
1130
-
1356
+
1131
1357
  // if mode == 'discontinuous'
1132
1358
  if(mode == 'discontinuous') {
1133
-
1359
+
1134
1360
  var tmp = this.inner_ranges[i].split(this.separator);
1135
1361
  // console.log("Ranges : " + this.inner_ranges[i]);
1136
-
1362
+
1137
1363
  var start_value = parseFloat(tmp[0]).toFixed(this.precision);
1138
1364
  var end_value = parseFloat(tmp[1]).toFixed(this.precision);
1139
-
1365
+
1140
1366
  }
1141
-
1367
+
1142
1368
  // we apply callback function
1143
1369
  var el = fn(start_value) + this.legendSeparator + fn(end_value);
1144
-
1370
+
1145
1371
  var block = '<div><div class="geostats-legend-block" style="background-color:' + ccolors[i] + '"></div> ' + el + cnt + '</div>';
1146
1372
  elements.push(block);
1147
1373
  }
1148
-
1374
+
1149
1375
  } else {
1150
-
1376
+
1151
1377
  // only if classification is done on unique values
1152
- for (i = 0; i < (this.bounds.length); i++) {
1378
+ for (var i = 0; i < (this.bounds.length); i++) {
1153
1379
  if(getcounter===true) {
1154
1380
  cnt = ' <span class="geostats-legend-counter">(' + this.counter[i] + ')</span>';
1155
1381
  }
@@ -1158,31 +1384,31 @@ var geostats = function(a) {
1158
1384
 
1159
1385
  elements.push(block);
1160
1386
  }
1161
-
1387
+
1162
1388
  }
1163
-
1389
+
1164
1390
  // do we reverse the return legend ?
1165
- if(order === 'DESC') elements.reverse();
1166
-
1391
+ if(order === 'DESC') elements.reverse();
1392
+
1167
1393
  // finally we create HTML and return it
1168
1394
  var content = '<div class="geostats-legend"><div class="geostats-legend-title">' + _t(lg) + '</div>';
1169
- for (i = 0; i < (elements.length); i++) {
1395
+ for (var i = 0; i < (elements.length); i++) {
1170
1396
  content += elements[i];
1171
1397
  }
1172
1398
  content += '</div>';
1173
-
1399
+
1174
1400
  return content;
1175
1401
  };
1176
1402
 
1177
-
1178
-
1403
+
1404
+
1179
1405
  // object constructor
1180
1406
  // At the end of script. If not setPrecision() method is not known
1181
-
1407
+
1182
1408
  // we create an object identifier for debugging
1183
1409
  this.objectID = new Date().getUTCMilliseconds();
1184
1410
  this.log('Creating new geostats object');
1185
-
1411
+
1186
1412
  if(typeof a !== 'undefined' && a.length > 0) {
1187
1413
  this.serie = a;
1188
1414
  this.setPrecision();
@@ -1191,9 +1417,10 @@ var geostats = function(a) {
1191
1417
  this.serie = Array();
1192
1418
 
1193
1419
  };
1194
-
1420
+
1195
1421
  // creating aliases on classification function for backward compatibility
1196
1422
  this.getJenks = this.getClassJenks;
1423
+ this.getJenks2 = this.getClassJenks2;
1197
1424
  this.getGeometricProgression = this.getClassGeometricProgression;
1198
1425
  this.getEqInterval = this.getClassEqInterval;
1199
1426
  this.getQuantile = this.getClassQuantile;
@@ -1205,4 +1432,4 @@ var geostats = function(a) {
1205
1432
 
1206
1433
 
1207
1434
  return geostats;
1208
- });
1435
+ });