blacklight_heatmaps 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ });