mapshaper 0.5.93 → 0.5.96

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.
package/mapshaper.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
 
3
- var VERSION = "0.5.92";
3
+ var VERSION = "0.5.95";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -4993,7 +4993,7 @@
4993
4993
  this._n = 0;
4994
4994
  this.x = 0;
4995
4995
  this.y = 0;
4996
- this.i = -1;
4996
+ // this.i = -1;
4997
4997
  }
4998
4998
 
4999
4999
  ShapeIter.prototype.hasNext = function() {
@@ -5004,7 +5004,7 @@
5004
5004
  if (arc.hasNext()) {
5005
5005
  this.x = arc.x;
5006
5006
  this.y = arc.y;
5007
- this.i = arc.i;
5007
+ // this.i = arc.i;
5008
5008
  return true;
5009
5009
  }
5010
5010
  this.nextArc();
@@ -9461,6 +9461,9 @@
9461
9461
  return rec && (rec[name] === val);
9462
9462
  });
9463
9463
  };
9464
+ obj.file_exists = function(name) {
9465
+ return cli.isFile(name);
9466
+ };
9464
9467
  return obj;
9465
9468
  }
9466
9469
 
@@ -19149,7 +19152,7 @@ ${svg}
19149
19152
  describe: 'value (or color) to use for invalid or missing data'
19150
19153
  })
19151
19154
  .option('method', {
19152
- describe: 'one of: quantile, nice, equal-interval, hybrid'
19155
+ describe: 'quantile, nice, equal-interval, categorical, etc.'
19153
19156
  })
19154
19157
  .option('quantile', {
19155
19158
  //describe: 'shortcut for method=quantile (the default)',
@@ -27731,217 +27734,6 @@ ${svg}
27731
27734
  return [lyr2];
27732
27735
  }
27733
27736
 
27734
- // TODO: support three or more stops
27735
- function getGradientFunction(stops) {
27736
- var min = stops[0] / 100,
27737
- max = stops[1] / 100;
27738
- if (stops.length != 2) {
27739
- stop('Only two stops are currently supported');
27740
- }
27741
- if (!(min >= 0 && max <= 1 && min < max)) {
27742
- stop('Invalid gradient stops:', stops);
27743
- }
27744
- return function(t) {
27745
- return t * (max - min) + min;
27746
- };
27747
- }
27748
-
27749
- function getStoppedValues(values, stops) {
27750
- var interpolate = getInterpolatedValueGetter(values, null);
27751
- var n = values.length;
27752
- var fstop = getGradientFunction(stops);
27753
- var values2 = [];
27754
- var t, val;
27755
- for (var i=0; i<n; i++) {
27756
- t = fstop(i / (n - 1));
27757
- val = interpolate(t * (n - 1));
27758
- values2.push(val);
27759
- }
27760
- return values2;
27761
- }
27762
-
27763
- // convert a continuous index ([0, n-1], -1) to a corresponding interpolated value
27764
- function getInterpolatedValueGetter(values, nullValue) {
27765
- var d3 = require('d3-interpolate');
27766
- var interpolators = [];
27767
- var tmax = values.length - 1;
27768
- for (var i=1; i<values.length; i++) {
27769
- interpolators.push(d3.interpolate(values[i-1], values[i]));
27770
- }
27771
- return function(t) {
27772
- if (t == -1) return nullValue;
27773
- if ((t >= 0 && t <= tmax) === false) {
27774
- error('Range error');
27775
- }
27776
- var i = t == tmax ? tmax - 1 : Math.floor(t);
27777
- var j = t == tmax ? 1 : t % 1;
27778
- return interpolators[i](j);
27779
- };
27780
- }
27781
-
27782
- // return an array of n values
27783
- // assumes that values can be interpolated by d3-interpolate
27784
- // (colors and numbers should work)
27785
- function interpolateValuesToClasses(values, n, stops) {
27786
- if (values.length == n && !stops) return values;
27787
- var d3 = require('d3-interpolate');
27788
- var numPairs = values.length - 1;
27789
- var output = [values[0]];
27790
- var k, j, t, intVal;
27791
- for (var i=1; i<n-1; i++) {
27792
- k = i / (n-1) * numPairs;
27793
- j = Math.floor(k);
27794
- t = k - j;
27795
- // if (convert) t = convert(t);
27796
- intVal = d3.interpolate(values[j], values[j+1])(t);
27797
- output.push(intVal);
27798
- }
27799
- output.push(values[values.length - 1]);
27800
- if (stops) {
27801
- output = getStoppedValues(output, stops);
27802
- }
27803
- return output;
27804
- }
27805
-
27806
- var index = {
27807
- categorical: [],
27808
- sequential: [],
27809
- rainbow: [],
27810
- diverging: []
27811
- };
27812
- var ramps;
27813
-
27814
- function initSchemes() {
27815
- if (ramps) return;
27816
- ramps = {};
27817
- addSchemesFromD3('categorical', 'Category10,Accent,Dark2,Paired,Pastel1,Pastel2,Set1,Set2,Set3,Tableau10');
27818
- addSchemesFromD3('sequential', 'Blues,Greens,Greys,Purples,Reds,Oranges,BuGn,BuPu,GnBu,OrRd,PuBuGn,PuBu,PuRd,RdPu,YlGnBu,YlGn,YlOrBr,YlOrRd');
27819
- addSchemesFromD3('rainbow', 'Cividis,CubehelixDefault,Rainbow,Warm,Cool,Sinebow,Turbo,Viridis,Magma,Inferno,Plasma');
27820
- addSchemesFromD3('diverging', 'BrBG,PRGn,PRGn,PiYG,PuOr,RdBu,RdGy,RdYlBu,RdYlGn,Spectral');
27821
- testLib(); // make sure these schemes are all available
27822
- addCategoricalScheme('Category20',
27823
- '1f77b4aec7e8ff7f0effbb782ca02c98df8ad62728ff98969467bdc5b0d58c564bc49c94e377c2f7b6d27f7f7fc7c7c7bcbd22dbdb8d17becf9edae5');
27824
- addCategoricalScheme('Category20b',
27825
- '393b795254a36b6ecf9c9ede6379398ca252b5cf6bcedb9c8c6d31bd9e39e7ba52e7cb94843c39ad494ad6616be7969c7b4173a55194ce6dbdde9ed6');
27826
- addCategoricalScheme('Category20c',
27827
- '3182bd6baed69ecae1c6dbefe6550dfd8d3cfdae6bfdd0a231a35474c476a1d99bc7e9c0756bb19e9ac8bcbddcdadaeb636363969696bdbdbdd9d9d9');
27828
- addCategoricalScheme('Tableau20',
27829
- '4c78a89ecae9f58518ffbf7954a24b88d27ab79a20f2cf5b43989483bcb6e45756ff9d9879706ebab0acd67195fcbfd2b279a2d6a5c99e765fd8b5a5');
27830
- }
27831
-
27832
- function addSchemesFromD3(type, names) {
27833
- index[type] = index[type].concat(names.split(','));
27834
- }
27835
-
27836
- function addCategoricalScheme(name, str) {
27837
- index.categorical.push(name);
27838
- ramps[name] = unpackRamp(str);
27839
- }
27840
-
27841
- function unpackRamp(str) {
27842
- var colors = [];
27843
- for (var i=0, n=str.length; i<n; i+=6) {
27844
- colors.push('#' + str.substr(i, 6));
27845
- }
27846
- return colors;
27847
- }
27848
-
27849
- function testLib() {
27850
- var lib = require('d3-scale-chromatic');
27851
- schemes(index.categorical);
27852
- schemes(index.sequential);
27853
- schemes(index.diverging);
27854
- interpolators(index.sequential);
27855
- interpolators(index.rainbow);
27856
- interpolators(index.diverging);
27857
-
27858
- function schemes(arr) {
27859
- arr.forEach(function(name) {
27860
- if (!lib['scheme' + name]) {
27861
- message('Warning: missing data for', name);
27862
- }
27863
- });
27864
- }
27865
-
27866
- function interpolators(arr) {
27867
- arr.forEach(function(name) {
27868
- if (!lib['interpolate' + name]) {
27869
- message('Missing interpolator for', name);
27870
- }
27871
- });
27872
- }
27873
- }
27874
-
27875
- function printColorSchemeNames() {
27876
- initSchemes();
27877
- print('Built-in color schemes (from d3):');
27878
- print ('Categorical\n' + formatStringsAsGrid(index.categorical));
27879
- print ('\nSequential\n' + formatStringsAsGrid(index.sequential));
27880
- print ('\nDiverging\n' + formatStringsAsGrid(index.diverging));
27881
- print ('\nMulti-hue/rainbow\n' + formatStringsAsGrid(index.rainbow));
27882
- }
27883
-
27884
- function getCategoricalColorScheme(name, n) {
27885
- var colors;
27886
- initSchemes();
27887
- if (!isColorSchemeName(name)) {
27888
- stop('Unknown color scheme name:', name);
27889
- } else if (isCategoricalColorScheme(name)) {
27890
- colors = ramps[name] || require('d3-scale-chromatic')['scheme' + name];
27891
- } else {
27892
- colors = getColorRamp(name, n);
27893
- }
27894
- if (n > colors.length) {
27895
- stop(name, 'does not contain', n, 'colors');
27896
- }
27897
- return colors.slice(0, n);
27898
- }
27899
-
27900
- function isColorSchemeName(name) {
27901
- initSchemes();
27902
- return index.categorical.includes(name) || index.sequential.includes(name) ||
27903
- index.diverging.includes(name) || index.rainbow.includes(name);
27904
- }
27905
-
27906
- function isCategoricalColorScheme(name) {
27907
- initSchemes();
27908
- return index.categorical.includes(name);
27909
- }
27910
-
27911
- function getColorRamp(name, n, stops) {
27912
- initSchemes();
27913
- var lib = require('d3-scale-chromatic');
27914
- var ramps = lib['scheme' + name];
27915
- var interpolate = lib['interpolate' + name];
27916
- var ramp;
27917
- if (!ramps && !interpolate) {
27918
- stop('Unknown color scheme name:', name);
27919
- }
27920
- if (index.categorical.includes(name)) {
27921
- stop(name, ' is a categorical color scheme (expected a sequential color scheme)');
27922
- }
27923
- if (ramps && ramps[n]) {
27924
- ramp = ramps[n];
27925
- } else {
27926
- ramp = getInterpolatedRamp(interpolate, n);
27927
- }
27928
- if (stops) {
27929
- ramp = getStoppedValues(ramp, stops);
27930
- }
27931
- return ramp;
27932
- }
27933
-
27934
- function getInterpolatedRamp(interpolate, n) {
27935
- if (n > 0 === false || !utils.isInteger(n)) {
27936
- error('Expected a positive integer');
27937
- }
27938
- var ramp = [];
27939
- for (var i=0; i<n; i++) {
27940
- ramp.push(interpolate(i / (n - 1)));
27941
- }
27942
- return ramp;
27943
- }
27944
-
27945
27737
  var scaledIntervals =
27946
27738
  [10,12,15,18,20,22,25,30,35,40,45,50,60,70,80,90,100];
27947
27739
  var precisions =
@@ -28071,6 +27863,78 @@ ${svg}
28071
27863
  return s;
28072
27864
  }
28073
27865
 
27866
+ // TODO: support three or more stops
27867
+ function getGradientFunction(stops) {
27868
+ var min = stops[0] / 100,
27869
+ max = stops[1] / 100;
27870
+ if (stops.length != 2) {
27871
+ stop('Only two stops are currently supported');
27872
+ }
27873
+ if (!(min >= 0 && max <= 1 && min < max)) {
27874
+ stop('Invalid gradient stops:', stops);
27875
+ }
27876
+ return function(t) {
27877
+ return t * (max - min) + min;
27878
+ };
27879
+ }
27880
+
27881
+ function getStoppedValues(values, stops) {
27882
+ var interpolate = getInterpolatedValueGetter(values, null);
27883
+ var n = values.length;
27884
+ var fstop = getGradientFunction(stops);
27885
+ var values2 = [];
27886
+ var t, val;
27887
+ for (var i=0; i<n; i++) {
27888
+ t = fstop(i / (n - 1));
27889
+ val = interpolate(t * (n - 1));
27890
+ values2.push(val);
27891
+ }
27892
+ return values2;
27893
+ }
27894
+
27895
+ // convert a continuous index ([0, n-1], -1) to a corresponding interpolated value
27896
+ function getInterpolatedValueGetter(values, nullValue) {
27897
+ var d3 = require('d3-interpolate');
27898
+ var interpolators = [];
27899
+ var tmax = values.length - 1;
27900
+ for (var i=1; i<values.length; i++) {
27901
+ interpolators.push(d3.interpolate(values[i-1], values[i]));
27902
+ }
27903
+ return function(t) {
27904
+ if (t == -1) return nullValue;
27905
+ if ((t >= 0 && t <= tmax) === false) {
27906
+ error('Range error');
27907
+ }
27908
+ var i = t == tmax ? tmax - 1 : Math.floor(t);
27909
+ var j = t == tmax ? 1 : t % 1;
27910
+ return interpolators[i](j);
27911
+ };
27912
+ }
27913
+
27914
+ // return an array of n values
27915
+ // assumes that values can be interpolated by d3-interpolate
27916
+ // (colors and numbers should work)
27917
+ function interpolateValuesToClasses(values, n, stops) {
27918
+ if (values.length == n && !stops) return values;
27919
+ var d3 = require('d3-interpolate');
27920
+ var numPairs = values.length - 1;
27921
+ var output = [values[0]];
27922
+ var k, j, t, intVal;
27923
+ for (var i=1; i<n-1; i++) {
27924
+ k = i / (n-1) * numPairs;
27925
+ j = Math.floor(k);
27926
+ t = k - j;
27927
+ // if (convert) t = convert(t);
27928
+ intVal = d3.interpolate(values[j], values[j+1])(t);
27929
+ output.push(intVal);
27930
+ }
27931
+ output.push(values[values.length - 1]);
27932
+ if (stops) {
27933
+ output = getStoppedValues(output, stops);
27934
+ }
27935
+ return output;
27936
+ }
27937
+
28074
27938
  // convert an index (0 ... n-1, -1, -2) to a corresponding discreet value
28075
27939
  function getDiscreteValueGetter(values, nullValue, otherValue) {
28076
27940
  var n = values.length;
@@ -28677,35 +28541,6 @@ ${svg}
28677
28541
  };
28678
28542
  }
28679
28543
 
28680
- function getIndexedClassifier(values, nullVal, opts) {
28681
- // TODO: handle continuous classification
28682
- var numBuckets = values.length;
28683
- var classToValue = getOutputFunction(values, nullVal, opts);
28684
-
28685
- return function(val) {
28686
- var idx = utils.isInteger(val) && val >= 0 && val < numBuckets ? val : -1;
28687
- return classToValue(idx);
28688
- };
28689
- }
28690
-
28691
- // returns the number of classes, based on the largest class index found
28692
- function validateClassIndexField(records, name) {
28693
- var invalid = [];
28694
- var maxId = -1;
28695
- records.forEach(function(d) {
28696
- var val = (d || {})[name];
28697
- if (!utils.isInteger(val) || val < -2) {
28698
- invalid.push(val);
28699
- } else {
28700
- maxId = Math.max(maxId, val);
28701
- }
28702
- });
28703
- if (invalid.length > 0) {
28704
- stop(`Class index field contains invalid value(s): ${invalid.slice(0, 5)}`);
28705
- }
28706
- return maxId + 1;
28707
- }
28708
-
28709
28544
  // Returns a function for constructing a query function that accepts an arc id and
28710
28545
  // returns information about the polygon or polygons that use the given arc.
28711
28546
  // TODO: explain this better.
@@ -29002,64 +28837,321 @@ ${svg}
29002
28837
  };
29003
28838
  }
29004
28839
 
28840
+ function getIndexedClassifier(values, nullVal, opts) {
28841
+ // TODO: handle continuous classification
28842
+ var numBuckets = values.length;
28843
+ var classToValue = getOutputFunction(values, nullVal, opts);
28844
+
28845
+ return function(val) {
28846
+ var idx = utils.isInteger(val) && val >= 0 && val < numBuckets ? val : -1;
28847
+ return classToValue(idx);
28848
+ };
28849
+ }
28850
+
28851
+ // returns the number of classes, based on the largest class index found
28852
+ function getIndexedClassCount(records, name) {
28853
+ var invalid = [];
28854
+ var maxId = -1;
28855
+ records.forEach(function(d) {
28856
+ var val = (d || {})[name];
28857
+ if (!utils.isInteger(val) || val < -2) {
28858
+ invalid.push(val);
28859
+ } else {
28860
+ maxId = Math.max(maxId, val);
28861
+ }
28862
+ });
28863
+ if (invalid.length > 0) {
28864
+ stop(`Class index field contains invalid value(s): ${invalid.slice(0, 5)}`);
28865
+ }
28866
+ return maxId + 1;
28867
+ }
28868
+
28869
+ var index = {
28870
+ categorical: [],
28871
+ sequential: [],
28872
+ rainbow: [],
28873
+ diverging: []
28874
+ };
28875
+ var ramps;
28876
+
28877
+ function initSchemes() {
28878
+ if (ramps) return;
28879
+ ramps = {};
28880
+ addSchemesFromD3('categorical', 'Category10,Accent,Dark2,Paired,Pastel1,Pastel2,Set1,Set2,Set3,Tableau10');
28881
+ addSchemesFromD3('sequential', 'Blues,Greens,Greys,Purples,Reds,Oranges,BuGn,BuPu,GnBu,OrRd,PuBuGn,PuBu,PuRd,RdPu,YlGnBu,YlGn,YlOrBr,YlOrRd');
28882
+ addSchemesFromD3('rainbow', 'Cividis,CubehelixDefault,Rainbow,Warm,Cool,Sinebow,Turbo,Viridis,Magma,Inferno,Plasma');
28883
+ addSchemesFromD3('diverging', 'BrBG,PRGn,PRGn,PiYG,PuOr,RdBu,RdGy,RdYlBu,RdYlGn,Spectral');
28884
+ testLib(); // make sure these schemes are all available
28885
+ addCategoricalScheme('Category20',
28886
+ '1f77b4aec7e8ff7f0effbb782ca02c98df8ad62728ff98969467bdc5b0d58c564bc49c94e377c2f7b6d27f7f7fc7c7c7bcbd22dbdb8d17becf9edae5');
28887
+ addCategoricalScheme('Category20b',
28888
+ '393b795254a36b6ecf9c9ede6379398ca252b5cf6bcedb9c8c6d31bd9e39e7ba52e7cb94843c39ad494ad6616be7969c7b4173a55194ce6dbdde9ed6');
28889
+ addCategoricalScheme('Category20c',
28890
+ '3182bd6baed69ecae1c6dbefe6550dfd8d3cfdae6bfdd0a231a35474c476a1d99bc7e9c0756bb19e9ac8bcbddcdadaeb636363969696bdbdbdd9d9d9');
28891
+ addCategoricalScheme('Tableau20',
28892
+ '4c78a89ecae9f58518ffbf7954a24b88d27ab79a20f2cf5b43989483bcb6e45756ff9d9879706ebab0acd67195fcbfd2b279a2d6a5c99e765fd8b5a5');
28893
+ }
28894
+
28895
+ function addSchemesFromD3(type, names) {
28896
+ index[type] = index[type].concat(names.split(','));
28897
+ }
28898
+
28899
+ function addCategoricalScheme(name, str) {
28900
+ index.categorical.push(name);
28901
+ ramps[name] = unpackRamp(str);
28902
+ }
28903
+
28904
+ function unpackRamp(str) {
28905
+ var colors = [];
28906
+ for (var i=0, n=str.length; i<n; i+=6) {
28907
+ colors.push('#' + str.substr(i, 6));
28908
+ }
28909
+ return colors;
28910
+ }
28911
+
28912
+ function testLib() {
28913
+ var lib = require('d3-scale-chromatic');
28914
+ schemes(index.categorical);
28915
+ schemes(index.sequential);
28916
+ schemes(index.diverging);
28917
+ interpolators(index.sequential);
28918
+ interpolators(index.rainbow);
28919
+ interpolators(index.diverging);
28920
+
28921
+ function schemes(arr) {
28922
+ arr.forEach(function(name) {
28923
+ if (!lib['scheme' + name]) {
28924
+ message('Warning: missing data for', name);
28925
+ }
28926
+ });
28927
+ }
28928
+
28929
+ function interpolators(arr) {
28930
+ arr.forEach(function(name) {
28931
+ if (!lib['interpolate' + name]) {
28932
+ message('Missing interpolator for', name);
28933
+ }
28934
+ });
28935
+ }
28936
+ }
28937
+
28938
+ function printColorSchemeNames() {
28939
+ initSchemes();
28940
+ print('Built-in color schemes (from d3):');
28941
+ print ('Categorical\n' + formatStringsAsGrid(index.categorical));
28942
+ print ('\nSequential\n' + formatStringsAsGrid(index.sequential));
28943
+ print ('\nDiverging\n' + formatStringsAsGrid(index.diverging));
28944
+ print ('\nMulti-hue/rainbow\n' + formatStringsAsGrid(index.rainbow));
28945
+ }
28946
+
28947
+ function getCategoricalColorScheme(name, n) {
28948
+ var colors;
28949
+ initSchemes();
28950
+ if (!isColorSchemeName(name)) {
28951
+ stop('Unknown color scheme name:', name);
28952
+ } else if (isCategoricalColorScheme(name)) {
28953
+ colors = ramps[name] || require('d3-scale-chromatic')['scheme' + name];
28954
+ } else {
28955
+ colors = getColorRamp(name, n);
28956
+ }
28957
+ if (n > colors.length) {
28958
+ // stop(name, 'does not contain', n, 'colors');
28959
+ message('Color scheme has', colors.length, 'colors. Using duplication to match', n, 'categories.');
28960
+ colors = wrapColors(colors, n);
28961
+ } else {
28962
+ colors = colors.slice(0, n);
28963
+ }
28964
+ return colors;
28965
+ }
28966
+
28967
+ function wrapColors(colors, n) {
28968
+ while (colors.length > 0 && colors.length < n) {
28969
+ colors = colors.concat(colors.slice(0, n - colors.length));
28970
+ }
28971
+ return colors;
28972
+ }
28973
+
28974
+ function isColorSchemeName(name) {
28975
+ initSchemes();
28976
+ return index.categorical.includes(name) || index.sequential.includes(name) ||
28977
+ index.diverging.includes(name) || index.rainbow.includes(name);
28978
+ }
28979
+
28980
+ function isCategoricalColorScheme(name) {
28981
+ initSchemes();
28982
+ return index.categorical.includes(name);
28983
+ }
28984
+
28985
+ function getColorRamp(name, n, stops) {
28986
+ initSchemes();
28987
+ var lib = require('d3-scale-chromatic');
28988
+ var ramps = lib['scheme' + name];
28989
+ var interpolate = lib['interpolate' + name];
28990
+ var ramp;
28991
+ if (!ramps && !interpolate) {
28992
+ stop('Unknown color scheme name:', name);
28993
+ }
28994
+ if (index.categorical.includes(name)) {
28995
+ stop(name, ' is a categorical color scheme (expected a sequential color scheme)');
28996
+ }
28997
+ if (ramps && ramps[n]) {
28998
+ ramp = ramps[n];
28999
+ } else {
29000
+ ramp = getInterpolatedRamp(interpolate, n);
29001
+ }
29002
+ if (stops) {
29003
+ ramp = getStoppedValues(ramp, stops);
29004
+ }
29005
+ return ramp;
29006
+ }
29007
+
29008
+ function getInterpolatedRamp(interpolate, n) {
29009
+ if (n > 0 === false || !utils.isInteger(n)) {
29010
+ error('Expected a positive integer');
29011
+ }
29012
+ var ramp = [];
29013
+ for (var i=0; i<n; i++) {
29014
+ ramp.push(interpolate(i / (n - 1)));
29015
+ }
29016
+ return ramp;
29017
+ }
29018
+
29019
+ function getClassValues(method, n, opts) {
29020
+ var categorical = method == 'categorical' || method == 'non-adjacent';
29021
+ var colorArg = opts.colors ? opts.colors[0] : null;
29022
+ var colorScheme;
29023
+
29024
+ if (isColorSchemeName(colorArg)) {
29025
+ colorScheme = colorArg;
29026
+ } else if (colorArg == 'random') {
29027
+ colorScheme = categorical ? 'Tableau20' : 'BuGn'; // TODO: randomize
29028
+ } else if (opts.colors) {
29029
+ // validate colors
29030
+ opts.colors.forEach(parseColor);
29031
+ }
29032
+
29033
+ if (categorical) {
29034
+ if (colorScheme && isCategoricalColorScheme(colorScheme)) {
29035
+ return getCategoricalColorScheme(colorScheme, n);
29036
+ } else if (colorScheme) {
29037
+ // assume we have a sequential ramp
29038
+ return getColorRamp(colorScheme, n, opts.stops);
29039
+ } else if (opts.colors || opts.values) {
29040
+ return getCategoricalValues(opts.colors || opts.values, n);
29041
+ } else {
29042
+ // numerical indexes seem to make sense for non-adjacent and categorical colors
29043
+ return getIndexes(n);
29044
+ }
29045
+ } else {
29046
+ // sequential values
29047
+ if (colorScheme) {
29048
+ return getColorRamp(colorScheme, n, opts.stops);
29049
+ } else if (opts.colors || opts.values) {
29050
+ return getInterpolableValues(opts.colors || opts.values, n, opts);
29051
+ } else {
29052
+ // TODO: rethink this
29053
+ // return getInterpolableValues([0, 1], n, opts);
29054
+ return getIndexes(n);
29055
+ }
29056
+ }
29057
+ }
29058
+
29059
+
29060
+ function getCategoricalValues(values, n) {
29061
+ if (n != values.length) {
29062
+ stop('Mismatch in number of categories and number of values');
29063
+ }
29064
+ return values;
29065
+ }
29066
+
29067
+ function getIndexes(n) {
29068
+ var vals = [];
29069
+ for (var i=0; i<n; i++) {
29070
+ vals.push(i);
29071
+ }
29072
+ return vals;
29073
+ }
29074
+
29075
+ // TODO: check for non-interpolatable value types (e.g. boolean, text)
29076
+ function getInterpolableValues(arr, n, opts) {
29077
+ var values = parseValues(arr);
29078
+ if (n != values.length || opts.stops) {
29079
+ return interpolateValuesToClasses(values, n, opts.stops);
29080
+ }
29081
+ return values;
29082
+ }
29083
+
29084
+ // convert strings to numbers if they all parse as numbers
29085
+ // arr: an array of strings
29086
+ function parseValues(strings) {
29087
+ var values = strings;
29088
+ if (strings.every(utils.parseNumber)) {
29089
+ values = strings.map(function(str) {
29090
+ return +str;
29091
+ });
29092
+ }
29093
+ return values;
29094
+ }
29095
+
29096
+ var sequential = ['quantile', 'nice', 'equal-interval', 'hybrid'];
29097
+ var all = ['non-adjacent', 'indexed', 'categorical'].concat(sequential);
29098
+
29099
+ function getClassifyMethod(opts, dataFieldType) {
29100
+ var method;
29101
+ if (opts.method) {
29102
+ method = opts.method;
29103
+ } else if (opts.index_field) {
29104
+ method = 'indexed';
29105
+ } else if (opts.categories || dataFieldType == 'string') {
29106
+ method = 'categorical';
29107
+ } else if (dataFieldType == 'number') {
29108
+ method = 'quantile'; // TODO: validate data field
29109
+ } else {
29110
+ stop('Unable to determine which classification method to use.');
29111
+ }
29112
+ if (!all.includes(method)) {
29113
+ stop('Not a recognized classification method:', method);
29114
+ }
29115
+ if (sequential.includes(method) && dataFieldType != 'number') {
29116
+ stop('The', method, 'method requires a numerical data field');
29117
+ }
29118
+ return method;
29119
+ }
29120
+
29005
29121
  cmd.classify = function(lyr, dataset, optsArg) {
29006
29122
  if (!lyr.data) {
29007
29123
  initDataTable(lyr);
29008
29124
  }
29009
29125
  var opts = optsArg || {};
29010
29126
  var records = lyr.data && lyr.data.getRecords();
29011
- var nullValue = opts.null_value || null;
29012
- var valuesAreColors = !!opts.colors || !!opts.color_scheme;
29013
- var colorScheme;
29014
- var values, classifyByValue, classifyById;
29127
+ var valuesAreColors = !!opts.colors;
29128
+ var dataField, dataFieldType, outputField;
29129
+ var values, nullValue;
29130
+ var classifyByValue, classifyByRecordId;
29015
29131
  var numClasses, numValues;
29016
- var dataField, outputField;
29017
29132
  var method;
29018
29133
 
29019
- // validate explicitly set classes
29020
- if (opts.classes) {
29021
- if (!utils.isInteger(opts.classes) || opts.classes > 1 === false) {
29022
- stop('Invalid number of classes:', opts.classes, '(expected a value greater than 1)');
29023
- }
29024
- numClasses = opts.classes;
29025
- }
29026
-
29027
- // TODO: better validation of breaks values
29028
- if (opts.breaks) {
29029
- numClasses = opts.breaks.length + 1;
29134
+ if (opts.color_scheme) {
29135
+ stop('color-scheme is not a valid option, use colors instead');
29030
29136
  }
29031
29137
 
29138
+ // get data field to use for classification
29139
+ //
29032
29140
  if (opts.index_field) {
29033
29141
  dataField = opts.index_field;
29034
- if (numClasses > 0 === false) {
29035
- stop('The index-field= option requires the classes= option to be set');
29036
- }
29037
- // You can't infer the number of classes by looking at index values;
29038
- // this can cause unwanted interpolation if one or more values are
29039
- // not present in the index field
29040
- // numClasses = validateClassIndexField(records, opts.index_field);
29041
-
29042
29142
  } else if (opts.field) {
29043
29143
  dataField = opts.field;
29144
+ dataFieldType = getColumnType(opts.field, records);
29044
29145
  }
29045
-
29046
- // expand categories if value is '*'
29047
- if (dataField && opts.categories && opts.categories.includes('*')) {
29048
- opts.categories = getUniqFieldValues(records, dataField);
29146
+ if (dataField) {
29147
+ requireDataField(lyr.data, dataField);
29049
29148
  }
29050
29149
 
29051
29150
  // get classification method
29052
29151
  //
29053
- if (opts.method) {
29054
- method = opts.method;
29055
- } else if (opts.categories) {
29056
- method = 'categorical';
29057
- } else if (opts.index_field) {
29058
- method = 'indexed';
29059
- } else {
29060
- method = 'quantile'; // TODO: validate data field
29061
- }
29152
+ method = getClassifyMethod(opts, dataFieldType);
29062
29153
 
29154
+ // validate classification method
29063
29155
  if (method == 'non-adjacent') {
29064
29156
  if (lyr.geometry_type != 'polygon') {
29065
29157
  stop('The non-adjacent option requires a polygon layer');
@@ -29069,83 +29161,70 @@ ${svg}
29069
29161
  }
29070
29162
  } else if (!dataField) {
29071
29163
  stop('Missing a data field to classify');
29072
- } else {
29073
- requireDataField(lyr.data, dataField);
29074
29164
  }
29075
29165
 
29076
- if (numClasses) {
29077
- numValues = opts.continuous ? numClasses + 1 : numClasses;
29078
- }
29079
29166
 
29080
- // support both deprecated color-scheme= option and colors=<color-scheme> syntax
29081
- if (opts.color_scheme) {
29082
- if (!isColorSchemeName(opts.color_scheme)) {
29083
- stop('Unknown color scheme:', opts.color_scheme);
29167
+ // get the number of classes and the number of values
29168
+ //
29169
+ // expand categories if value is '*'
29170
+ if (method == 'categorical') {
29171
+ if ((!opts.categories || opts.categories.includes('*')) && dataField) {
29172
+ opts.categories = getUniqFieldValues(records, dataField);
29084
29173
  }
29085
- colorScheme = opts.color_scheme;
29086
- } else if (opts.colors && isColorSchemeName(opts.colors[0])) {
29087
- colorScheme = opts.colors[0];
29088
- } else if (opts.colors) {
29089
- opts.colors.forEach(parseColor); // validate colors -- error if unparsable
29090
29174
  }
29091
29175
 
29092
- /// get values (usually colors)
29093
- ///
29094
- if (colorScheme) {
29095
- if (method == 'non-adjacent') {
29096
- numClasses = numValues = numClasses || 5;
29097
- values = getCategoricalColorScheme(colorScheme, numValues);
29098
-
29099
- } else if (method == 'categorical') {
29100
- values = getCategoricalColorScheme(colorScheme, opts.categories.length);
29101
- numClasses = numValues = values.length;
29102
-
29103
- } else {
29104
- if (!numClasses) {
29105
- // stop('color-scheme= option requires classes= or breaks=');
29106
- numClasses = 4; // use a default number of classes
29107
- numValues = opts.continuous ? numClasses + 1 : numClasses;
29108
- }
29109
- values = getColorRamp(colorScheme, numValues, opts.stops);
29110
- }
29111
-
29112
- } else if (opts.colors || opts.values) {
29113
- values = opts.values ? parseValues(opts.values) : opts.colors;
29114
- if (!numValues) {
29115
- numValues = values.length;
29116
- }
29117
- if ((values.length != numValues || opts.stops) && numValues > 1) {
29118
- // TODO: handle numValues == 1
29119
- // TODO: check for non-interpolatable value types (e.g. boolean, text)
29120
- values = interpolateValuesToClasses(values, numValues, opts.stops);
29176
+ if (opts.classes) {
29177
+ if (!utils.isInteger(opts.classes) || opts.classes > 1 === false) {
29178
+ stop('Invalid number of classes:', opts.classes, '(expected a value greater than 1)');
29121
29179
  }
29122
-
29123
- } else if (numValues > 1) {
29124
- // no values were given: assign indexes for each class
29125
- values = getIndexValues(numValues);
29126
- nullValue = -1;
29127
- }
29128
-
29129
- if (valuesAreColors) {
29130
- nullValue = nullValue || '#eee';
29180
+ numClasses = opts.classes;
29181
+ } else if (method == 'indexed' && dataField) {
29182
+ numClasses = getIndexedClassCount(records, dataField);
29183
+ } else if (opts.breaks) {
29184
+ numClasses = opts.breaks.length + 1;
29185
+ } else if (method == 'categorical' && opts.categories) {
29186
+ numClasses = opts.categories.length;
29187
+ } else if (opts.colors && opts.colors.length > 1) {
29188
+ numClasses = opts.colors.length;
29189
+ } else if (opts.values && opts.values.length > 1) {
29190
+ numClasses = opts.values.length;
29191
+ } else if (method == 'non-adjacent') {
29192
+ numClasses = 5;
29193
+ } else {
29194
+ numClasses = 4;
29131
29195
  }
29132
-
29196
+ numValues = opts.continuous ? numClasses + 1 : numClasses;
29133
29197
  if (numValues > 1 === false) {
29134
- stop('Missing a valid number of classes');
29198
+ stop('Missing a valid number of values');
29135
29199
  }
29136
29200
 
29201
+ // get colors or other values
29202
+ //
29203
+ values = getClassValues(method, numValues, opts);
29137
29204
  if (opts.invert) {
29138
29205
  values = values.concat().reverse();
29139
29206
  }
29140
-
29141
29207
  if (valuesAreColors) {
29142
29208
  message('Colors:', formatValuesForLogging(values));
29143
29209
  }
29144
29210
 
29211
+ // get null value
29212
+ //
29213
+ if ('null_value' in opts) {
29214
+ nullValue = opts.null_value;
29215
+ } else if (valuesAreColors) {
29216
+ nullValue = '#eee';
29217
+ } else if (opts.values) {
29218
+ nullValue = null;
29219
+ } else {
29220
+ nullValue = -1; // kludge, to match behavior of getClassValues()
29221
+ }
29222
+
29223
+
29145
29224
  // get a function to convert input data to class indexes
29146
29225
  //
29147
29226
  if (method == 'non-adjacent') {
29148
- classifyById = getNonAdjacentClassifier(lyr, dataset, values);
29227
+ classifyByRecordId = getNonAdjacentClassifier(lyr, dataset, values);
29149
29228
  } else if (opts.index_field) {
29150
29229
  // data is pre-classified... just read the index from a field
29151
29230
  classifyByValue = getIndexedClassifier(values, nullValue, opts);
@@ -29156,13 +29235,12 @@ ${svg}
29156
29235
  }
29157
29236
 
29158
29237
  if (classifyByValue) {
29159
- classifyById = function(id) {
29238
+ classifyByRecordId = function(id) {
29160
29239
  var d = records[id] || {};
29161
29240
  return classifyByValue(d[dataField]);
29162
29241
  };
29163
29242
  }
29164
29243
 
29165
-
29166
29244
  // get the name of the output field
29167
29245
  //
29168
29246
  if (valuesAreColors) {
@@ -29178,23 +29256,10 @@ ${svg}
29178
29256
  }
29179
29257
 
29180
29258
  records.forEach(function(d, i) {
29181
- d[outputField] = classifyById(i);
29259
+ d[outputField] = classifyByRecordId(i);
29182
29260
  });
29183
29261
  };
29184
29262
 
29185
-
29186
- // convert strings to numbers if they all parse as numbers
29187
- // arr: an array of strings
29188
- function parseValues(strings) {
29189
- var values = strings;
29190
- if (strings.every(utils.parseNumber)) {
29191
- values = strings.map(function(str) {
29192
- return +str;
29193
- });
29194
- }
29195
- return values;
29196
- }
29197
-
29198
29263
  function formatValuesForLogging(arr) {
29199
29264
  if (arr.some(val => utils.isString(val) && val.indexOf('rgb(') === 0)) {
29200
29265
  return formatColorsAsHex(arr);
@@ -29211,15 +29276,6 @@ ${svg}
29211
29276
  });
29212
29277
  }
29213
29278
 
29214
-
29215
- function getIndexValues(n) {
29216
- var vals = [];
29217
- for (var i=0; i<n; i++) {
29218
- vals.push(i);
29219
- }
29220
- return vals;
29221
- }
29222
-
29223
29279
  // Remove small-area polygon rings (very simple implementation of sliver removal)
29224
29280
  // TODO: more sophisticated sliver detection (e.g. could consider ratio of area to perimeter)
29225
29281
  // TODO: consider merging slivers into adjacent polygons to prevent gaps from forming
@@ -35053,7 +35109,6 @@ ${svg}
35053
35109
  }
35054
35110
 
35055
35111
 
35056
-
35057
35112
  function snapVerticesToPoint(ids, p, arcs, final) {
35058
35113
  ids.forEach(function(idx) {
35059
35114
  setVertexCoords(p[0], p[1], idx, arcs);
@@ -35133,7 +35188,7 @@ ${svg}
35133
35188
  function findNearestVertex(x, y, shp, arcs, spherical) {
35134
35189
  var calcLen = spherical ? geom.greatCircleDistance : geom.distance2D,
35135
35190
  minLen = Infinity,
35136
- minX, minY, vId, dist, iter;
35191
+ minX, minY, dist, iter;
35137
35192
  for (var i=0; i<shp.length; i++) {
35138
35193
  iter = arcs.getShapeIter(shp[i]);
35139
35194
  while (iter.hasNext()) {
@@ -35142,26 +35197,10 @@ ${svg}
35142
35197
  minLen = dist;
35143
35198
  minX = iter.x;
35144
35199
  minY = iter.y;
35145
- vId = iter.i;
35146
35200
  }
35147
35201
  }
35148
35202
  }
35149
- return minLen < Infinity ? {x: minX, y: minY, i: vId} : null;
35150
- }
35151
-
35152
- // v: vertex in {x, y, i} format
35153
- function findAdjacentVertex(v, shp, arcs, offs) {
35154
- var p, i;
35155
- var arcEnd = offs == 1 && vertexIsArcEnd(v.i, arcs);
35156
- var arcStart = offs == -1 && vertexIsArcStart(v.i, arcs);
35157
- if (arcEnd || arcStart) return null;
35158
- i = v.i + offs;
35159
- p = getVertexCoords(i, arcs);
35160
- return {
35161
- i: i,
35162
- x: p[0],
35163
- y: p[1]
35164
- };
35203
+ return minLen < Infinity ? {x: minX, y: minY} : null;
35165
35204
  }
35166
35205
 
35167
35206
  var VertexUtils = /*#__PURE__*/Object.freeze({
@@ -35174,8 +35213,7 @@ ${svg}
35174
35213
  vertexIsArcEnd: vertexIsArcEnd,
35175
35214
  vertexIsArcStart: vertexIsArcStart,
35176
35215
  setVertexCoords: setVertexCoords,
35177
- findNearestVertex: findNearestVertex,
35178
- findAdjacentVertex: findAdjacentVertex
35216
+ findNearestVertex: findNearestVertex
35179
35217
  });
35180
35218
 
35181
35219
  // Returns x,y coordinates of the point that is at the midpoint of each polyline feature