mapshaper 0.5.95 → 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.94";
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();
@@ -19152,7 +19152,7 @@ ${svg}
19152
19152
  describe: 'value (or color) to use for invalid or missing data'
19153
19153
  })
19154
19154
  .option('method', {
19155
- describe: 'one of: quantile, nice, equal-interval, hybrid'
19155
+ describe: 'quantile, nice, equal-interval, categorical, etc.'
19156
19156
  })
19157
19157
  .option('quantile', {
19158
19158
  //describe: 'shortcut for method=quantile (the default)',
@@ -27734,217 +27734,6 @@ ${svg}
27734
27734
  return [lyr2];
27735
27735
  }
27736
27736
 
27737
- // TODO: support three or more stops
27738
- function getGradientFunction(stops) {
27739
- var min = stops[0] / 100,
27740
- max = stops[1] / 100;
27741
- if (stops.length != 2) {
27742
- stop('Only two stops are currently supported');
27743
- }
27744
- if (!(min >= 0 && max <= 1 && min < max)) {
27745
- stop('Invalid gradient stops:', stops);
27746
- }
27747
- return function(t) {
27748
- return t * (max - min) + min;
27749
- };
27750
- }
27751
-
27752
- function getStoppedValues(values, stops) {
27753
- var interpolate = getInterpolatedValueGetter(values, null);
27754
- var n = values.length;
27755
- var fstop = getGradientFunction(stops);
27756
- var values2 = [];
27757
- var t, val;
27758
- for (var i=0; i<n; i++) {
27759
- t = fstop(i / (n - 1));
27760
- val = interpolate(t * (n - 1));
27761
- values2.push(val);
27762
- }
27763
- return values2;
27764
- }
27765
-
27766
- // convert a continuous index ([0, n-1], -1) to a corresponding interpolated value
27767
- function getInterpolatedValueGetter(values, nullValue) {
27768
- var d3 = require('d3-interpolate');
27769
- var interpolators = [];
27770
- var tmax = values.length - 1;
27771
- for (var i=1; i<values.length; i++) {
27772
- interpolators.push(d3.interpolate(values[i-1], values[i]));
27773
- }
27774
- return function(t) {
27775
- if (t == -1) return nullValue;
27776
- if ((t >= 0 && t <= tmax) === false) {
27777
- error('Range error');
27778
- }
27779
- var i = t == tmax ? tmax - 1 : Math.floor(t);
27780
- var j = t == tmax ? 1 : t % 1;
27781
- return interpolators[i](j);
27782
- };
27783
- }
27784
-
27785
- // return an array of n values
27786
- // assumes that values can be interpolated by d3-interpolate
27787
- // (colors and numbers should work)
27788
- function interpolateValuesToClasses(values, n, stops) {
27789
- if (values.length == n && !stops) return values;
27790
- var d3 = require('d3-interpolate');
27791
- var numPairs = values.length - 1;
27792
- var output = [values[0]];
27793
- var k, j, t, intVal;
27794
- for (var i=1; i<n-1; i++) {
27795
- k = i / (n-1) * numPairs;
27796
- j = Math.floor(k);
27797
- t = k - j;
27798
- // if (convert) t = convert(t);
27799
- intVal = d3.interpolate(values[j], values[j+1])(t);
27800
- output.push(intVal);
27801
- }
27802
- output.push(values[values.length - 1]);
27803
- if (stops) {
27804
- output = getStoppedValues(output, stops);
27805
- }
27806
- return output;
27807
- }
27808
-
27809
- var index = {
27810
- categorical: [],
27811
- sequential: [],
27812
- rainbow: [],
27813
- diverging: []
27814
- };
27815
- var ramps;
27816
-
27817
- function initSchemes() {
27818
- if (ramps) return;
27819
- ramps = {};
27820
- addSchemesFromD3('categorical', 'Category10,Accent,Dark2,Paired,Pastel1,Pastel2,Set1,Set2,Set3,Tableau10');
27821
- addSchemesFromD3('sequential', 'Blues,Greens,Greys,Purples,Reds,Oranges,BuGn,BuPu,GnBu,OrRd,PuBuGn,PuBu,PuRd,RdPu,YlGnBu,YlGn,YlOrBr,YlOrRd');
27822
- addSchemesFromD3('rainbow', 'Cividis,CubehelixDefault,Rainbow,Warm,Cool,Sinebow,Turbo,Viridis,Magma,Inferno,Plasma');
27823
- addSchemesFromD3('diverging', 'BrBG,PRGn,PRGn,PiYG,PuOr,RdBu,RdGy,RdYlBu,RdYlGn,Spectral');
27824
- testLib(); // make sure these schemes are all available
27825
- addCategoricalScheme('Category20',
27826
- '1f77b4aec7e8ff7f0effbb782ca02c98df8ad62728ff98969467bdc5b0d58c564bc49c94e377c2f7b6d27f7f7fc7c7c7bcbd22dbdb8d17becf9edae5');
27827
- addCategoricalScheme('Category20b',
27828
- '393b795254a36b6ecf9c9ede6379398ca252b5cf6bcedb9c8c6d31bd9e39e7ba52e7cb94843c39ad494ad6616be7969c7b4173a55194ce6dbdde9ed6');
27829
- addCategoricalScheme('Category20c',
27830
- '3182bd6baed69ecae1c6dbefe6550dfd8d3cfdae6bfdd0a231a35474c476a1d99bc7e9c0756bb19e9ac8bcbddcdadaeb636363969696bdbdbdd9d9d9');
27831
- addCategoricalScheme('Tableau20',
27832
- '4c78a89ecae9f58518ffbf7954a24b88d27ab79a20f2cf5b43989483bcb6e45756ff9d9879706ebab0acd67195fcbfd2b279a2d6a5c99e765fd8b5a5');
27833
- }
27834
-
27835
- function addSchemesFromD3(type, names) {
27836
- index[type] = index[type].concat(names.split(','));
27837
- }
27838
-
27839
- function addCategoricalScheme(name, str) {
27840
- index.categorical.push(name);
27841
- ramps[name] = unpackRamp(str);
27842
- }
27843
-
27844
- function unpackRamp(str) {
27845
- var colors = [];
27846
- for (var i=0, n=str.length; i<n; i+=6) {
27847
- colors.push('#' + str.substr(i, 6));
27848
- }
27849
- return colors;
27850
- }
27851
-
27852
- function testLib() {
27853
- var lib = require('d3-scale-chromatic');
27854
- schemes(index.categorical);
27855
- schemes(index.sequential);
27856
- schemes(index.diverging);
27857
- interpolators(index.sequential);
27858
- interpolators(index.rainbow);
27859
- interpolators(index.diverging);
27860
-
27861
- function schemes(arr) {
27862
- arr.forEach(function(name) {
27863
- if (!lib['scheme' + name]) {
27864
- message('Warning: missing data for', name);
27865
- }
27866
- });
27867
- }
27868
-
27869
- function interpolators(arr) {
27870
- arr.forEach(function(name) {
27871
- if (!lib['interpolate' + name]) {
27872
- message('Missing interpolator for', name);
27873
- }
27874
- });
27875
- }
27876
- }
27877
-
27878
- function printColorSchemeNames() {
27879
- initSchemes();
27880
- print('Built-in color schemes (from d3):');
27881
- print ('Categorical\n' + formatStringsAsGrid(index.categorical));
27882
- print ('\nSequential\n' + formatStringsAsGrid(index.sequential));
27883
- print ('\nDiverging\n' + formatStringsAsGrid(index.diverging));
27884
- print ('\nMulti-hue/rainbow\n' + formatStringsAsGrid(index.rainbow));
27885
- }
27886
-
27887
- function getCategoricalColorScheme(name, n) {
27888
- var colors;
27889
- initSchemes();
27890
- if (!isColorSchemeName(name)) {
27891
- stop('Unknown color scheme name:', name);
27892
- } else if (isCategoricalColorScheme(name)) {
27893
- colors = ramps[name] || require('d3-scale-chromatic')['scheme' + name];
27894
- } else {
27895
- colors = getColorRamp(name, n);
27896
- }
27897
- if (n > colors.length) {
27898
- stop(name, 'does not contain', n, 'colors');
27899
- }
27900
- return colors.slice(0, n);
27901
- }
27902
-
27903
- function isColorSchemeName(name) {
27904
- initSchemes();
27905
- return index.categorical.includes(name) || index.sequential.includes(name) ||
27906
- index.diverging.includes(name) || index.rainbow.includes(name);
27907
- }
27908
-
27909
- function isCategoricalColorScheme(name) {
27910
- initSchemes();
27911
- return index.categorical.includes(name);
27912
- }
27913
-
27914
- function getColorRamp(name, n, stops) {
27915
- initSchemes();
27916
- var lib = require('d3-scale-chromatic');
27917
- var ramps = lib['scheme' + name];
27918
- var interpolate = lib['interpolate' + name];
27919
- var ramp;
27920
- if (!ramps && !interpolate) {
27921
- stop('Unknown color scheme name:', name);
27922
- }
27923
- if (index.categorical.includes(name)) {
27924
- stop(name, ' is a categorical color scheme (expected a sequential color scheme)');
27925
- }
27926
- if (ramps && ramps[n]) {
27927
- ramp = ramps[n];
27928
- } else {
27929
- ramp = getInterpolatedRamp(interpolate, n);
27930
- }
27931
- if (stops) {
27932
- ramp = getStoppedValues(ramp, stops);
27933
- }
27934
- return ramp;
27935
- }
27936
-
27937
- function getInterpolatedRamp(interpolate, n) {
27938
- if (n > 0 === false || !utils.isInteger(n)) {
27939
- error('Expected a positive integer');
27940
- }
27941
- var ramp = [];
27942
- for (var i=0; i<n; i++) {
27943
- ramp.push(interpolate(i / (n - 1)));
27944
- }
27945
- return ramp;
27946
- }
27947
-
27948
27737
  var scaledIntervals =
27949
27738
  [10,12,15,18,20,22,25,30,35,40,45,50,60,70,80,90,100];
27950
27739
  var precisions =
@@ -28074,6 +27863,78 @@ ${svg}
28074
27863
  return s;
28075
27864
  }
28076
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
+
28077
27938
  // convert an index (0 ... n-1, -1, -2) to a corresponding discreet value
28078
27939
  function getDiscreteValueGetter(values, nullValue, otherValue) {
28079
27940
  var n = values.length;
@@ -28680,35 +28541,6 @@ ${svg}
28680
28541
  };
28681
28542
  }
28682
28543
 
28683
- function getIndexedClassifier(values, nullVal, opts) {
28684
- // TODO: handle continuous classification
28685
- var numBuckets = values.length;
28686
- var classToValue = getOutputFunction(values, nullVal, opts);
28687
-
28688
- return function(val) {
28689
- var idx = utils.isInteger(val) && val >= 0 && val < numBuckets ? val : -1;
28690
- return classToValue(idx);
28691
- };
28692
- }
28693
-
28694
- // returns the number of classes, based on the largest class index found
28695
- function validateClassIndexField(records, name) {
28696
- var invalid = [];
28697
- var maxId = -1;
28698
- records.forEach(function(d) {
28699
- var val = (d || {})[name];
28700
- if (!utils.isInteger(val) || val < -2) {
28701
- invalid.push(val);
28702
- } else {
28703
- maxId = Math.max(maxId, val);
28704
- }
28705
- });
28706
- if (invalid.length > 0) {
28707
- stop(`Class index field contains invalid value(s): ${invalid.slice(0, 5)}`);
28708
- }
28709
- return maxId + 1;
28710
- }
28711
-
28712
28544
  // Returns a function for constructing a query function that accepts an arc id and
28713
28545
  // returns information about the polygon or polygons that use the given arc.
28714
28546
  // TODO: explain this better.
@@ -29005,64 +28837,321 @@ ${svg}
29005
28837
  };
29006
28838
  }
29007
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
+
29008
29121
  cmd.classify = function(lyr, dataset, optsArg) {
29009
29122
  if (!lyr.data) {
29010
29123
  initDataTable(lyr);
29011
29124
  }
29012
29125
  var opts = optsArg || {};
29013
29126
  var records = lyr.data && lyr.data.getRecords();
29014
- var nullValue = opts.null_value || null;
29015
- var valuesAreColors = !!opts.colors || !!opts.color_scheme;
29016
- var colorScheme;
29017
- var values, classifyByValue, classifyById;
29127
+ var valuesAreColors = !!opts.colors;
29128
+ var dataField, dataFieldType, outputField;
29129
+ var values, nullValue;
29130
+ var classifyByValue, classifyByRecordId;
29018
29131
  var numClasses, numValues;
29019
- var dataField, outputField;
29020
29132
  var method;
29021
29133
 
29022
- // validate explicitly set classes
29023
- if (opts.classes) {
29024
- if (!utils.isInteger(opts.classes) || opts.classes > 1 === false) {
29025
- stop('Invalid number of classes:', opts.classes, '(expected a value greater than 1)');
29026
- }
29027
- numClasses = opts.classes;
29028
- }
29029
-
29030
- // TODO: better validation of breaks values
29031
- if (opts.breaks) {
29032
- numClasses = opts.breaks.length + 1;
29134
+ if (opts.color_scheme) {
29135
+ stop('color-scheme is not a valid option, use colors instead');
29033
29136
  }
29034
29137
 
29138
+ // get data field to use for classification
29139
+ //
29035
29140
  if (opts.index_field) {
29036
29141
  dataField = opts.index_field;
29037
- if (numClasses > 0 === false) {
29038
- stop('The index-field= option requires the classes= option to be set');
29039
- }
29040
- // You can't infer the number of classes by looking at index values;
29041
- // this can cause unwanted interpolation if one or more values are
29042
- // not present in the index field
29043
- // numClasses = validateClassIndexField(records, opts.index_field);
29044
-
29045
29142
  } else if (opts.field) {
29046
29143
  dataField = opts.field;
29144
+ dataFieldType = getColumnType(opts.field, records);
29047
29145
  }
29048
-
29049
- // expand categories if value is '*'
29050
- if (dataField && opts.categories && opts.categories.includes('*')) {
29051
- opts.categories = getUniqFieldValues(records, dataField);
29146
+ if (dataField) {
29147
+ requireDataField(lyr.data, dataField);
29052
29148
  }
29053
29149
 
29054
29150
  // get classification method
29055
29151
  //
29056
- if (opts.method) {
29057
- method = opts.method;
29058
- } else if (opts.categories) {
29059
- method = 'categorical';
29060
- } else if (opts.index_field) {
29061
- method = 'indexed';
29062
- } else {
29063
- method = 'quantile'; // TODO: validate data field
29064
- }
29152
+ method = getClassifyMethod(opts, dataFieldType);
29065
29153
 
29154
+ // validate classification method
29066
29155
  if (method == 'non-adjacent') {
29067
29156
  if (lyr.geometry_type != 'polygon') {
29068
29157
  stop('The non-adjacent option requires a polygon layer');
@@ -29072,83 +29161,70 @@ ${svg}
29072
29161
  }
29073
29162
  } else if (!dataField) {
29074
29163
  stop('Missing a data field to classify');
29075
- } else {
29076
- requireDataField(lyr.data, dataField);
29077
29164
  }
29078
29165
 
29079
- if (numClasses) {
29080
- numValues = opts.continuous ? numClasses + 1 : numClasses;
29081
- }
29082
29166
 
29083
- // support both deprecated color-scheme= option and colors=<color-scheme> syntax
29084
- if (opts.color_scheme) {
29085
- if (!isColorSchemeName(opts.color_scheme)) {
29086
- 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);
29087
29173
  }
29088
- colorScheme = opts.color_scheme;
29089
- } else if (opts.colors && isColorSchemeName(opts.colors[0])) {
29090
- colorScheme = opts.colors[0];
29091
- } else if (opts.colors) {
29092
- opts.colors.forEach(parseColor); // validate colors -- error if unparsable
29093
29174
  }
29094
29175
 
29095
- /// get values (usually colors)
29096
- ///
29097
- if (colorScheme) {
29098
- if (method == 'non-adjacent') {
29099
- numClasses = numValues = numClasses || 5;
29100
- values = getCategoricalColorScheme(colorScheme, numValues);
29101
-
29102
- } else if (method == 'categorical') {
29103
- values = getCategoricalColorScheme(colorScheme, opts.categories.length);
29104
- numClasses = numValues = values.length;
29105
-
29106
- } else {
29107
- if (!numClasses) {
29108
- // stop('color-scheme= option requires classes= or breaks=');
29109
- numClasses = 4; // use a default number of classes
29110
- numValues = opts.continuous ? numClasses + 1 : numClasses;
29111
- }
29112
- values = getColorRamp(colorScheme, numValues, opts.stops);
29113
- }
29114
-
29115
- } else if (opts.colors || opts.values) {
29116
- values = opts.values ? parseValues(opts.values) : opts.colors;
29117
- if (!numValues) {
29118
- numValues = values.length;
29119
- }
29120
- if ((values.length != numValues || opts.stops) && numValues > 1) {
29121
- // TODO: handle numValues == 1
29122
- // TODO: check for non-interpolatable value types (e.g. boolean, text)
29123
- 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)');
29124
29179
  }
29125
-
29126
- } else if (numValues > 1) {
29127
- // no values were given: assign indexes for each class
29128
- values = getIndexValues(numValues);
29129
- nullValue = -1;
29130
- }
29131
-
29132
- if (valuesAreColors) {
29133
- 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;
29134
29195
  }
29135
-
29196
+ numValues = opts.continuous ? numClasses + 1 : numClasses;
29136
29197
  if (numValues > 1 === false) {
29137
- stop('Missing a valid number of classes');
29198
+ stop('Missing a valid number of values');
29138
29199
  }
29139
29200
 
29201
+ // get colors or other values
29202
+ //
29203
+ values = getClassValues(method, numValues, opts);
29140
29204
  if (opts.invert) {
29141
29205
  values = values.concat().reverse();
29142
29206
  }
29143
-
29144
29207
  if (valuesAreColors) {
29145
29208
  message('Colors:', formatValuesForLogging(values));
29146
29209
  }
29147
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
+
29148
29224
  // get a function to convert input data to class indexes
29149
29225
  //
29150
29226
  if (method == 'non-adjacent') {
29151
- classifyById = getNonAdjacentClassifier(lyr, dataset, values);
29227
+ classifyByRecordId = getNonAdjacentClassifier(lyr, dataset, values);
29152
29228
  } else if (opts.index_field) {
29153
29229
  // data is pre-classified... just read the index from a field
29154
29230
  classifyByValue = getIndexedClassifier(values, nullValue, opts);
@@ -29159,13 +29235,12 @@ ${svg}
29159
29235
  }
29160
29236
 
29161
29237
  if (classifyByValue) {
29162
- classifyById = function(id) {
29238
+ classifyByRecordId = function(id) {
29163
29239
  var d = records[id] || {};
29164
29240
  return classifyByValue(d[dataField]);
29165
29241
  };
29166
29242
  }
29167
29243
 
29168
-
29169
29244
  // get the name of the output field
29170
29245
  //
29171
29246
  if (valuesAreColors) {
@@ -29181,23 +29256,10 @@ ${svg}
29181
29256
  }
29182
29257
 
29183
29258
  records.forEach(function(d, i) {
29184
- d[outputField] = classifyById(i);
29259
+ d[outputField] = classifyByRecordId(i);
29185
29260
  });
29186
29261
  };
29187
29262
 
29188
-
29189
- // convert strings to numbers if they all parse as numbers
29190
- // arr: an array of strings
29191
- function parseValues(strings) {
29192
- var values = strings;
29193
- if (strings.every(utils.parseNumber)) {
29194
- values = strings.map(function(str) {
29195
- return +str;
29196
- });
29197
- }
29198
- return values;
29199
- }
29200
-
29201
29263
  function formatValuesForLogging(arr) {
29202
29264
  if (arr.some(val => utils.isString(val) && val.indexOf('rgb(') === 0)) {
29203
29265
  return formatColorsAsHex(arr);
@@ -29214,15 +29276,6 @@ ${svg}
29214
29276
  });
29215
29277
  }
29216
29278
 
29217
-
29218
- function getIndexValues(n) {
29219
- var vals = [];
29220
- for (var i=0; i<n; i++) {
29221
- vals.push(i);
29222
- }
29223
- return vals;
29224
- }
29225
-
29226
29279
  // Remove small-area polygon rings (very simple implementation of sliver removal)
29227
29280
  // TODO: more sophisticated sliver detection (e.g. could consider ratio of area to perimeter)
29228
29281
  // TODO: consider merging slivers into adjacent polygons to prevent gaps from forming
@@ -35056,7 +35109,6 @@ ${svg}
35056
35109
  }
35057
35110
 
35058
35111
 
35059
-
35060
35112
  function snapVerticesToPoint(ids, p, arcs, final) {
35061
35113
  ids.forEach(function(idx) {
35062
35114
  setVertexCoords(p[0], p[1], idx, arcs);
@@ -35136,7 +35188,7 @@ ${svg}
35136
35188
  function findNearestVertex(x, y, shp, arcs, spherical) {
35137
35189
  var calcLen = spherical ? geom.greatCircleDistance : geom.distance2D,
35138
35190
  minLen = Infinity,
35139
- minX, minY, vId, dist, iter;
35191
+ minX, minY, dist, iter;
35140
35192
  for (var i=0; i<shp.length; i++) {
35141
35193
  iter = arcs.getShapeIter(shp[i]);
35142
35194
  while (iter.hasNext()) {
@@ -35145,26 +35197,10 @@ ${svg}
35145
35197
  minLen = dist;
35146
35198
  minX = iter.x;
35147
35199
  minY = iter.y;
35148
- vId = iter.i;
35149
35200
  }
35150
35201
  }
35151
35202
  }
35152
- return minLen < Infinity ? {x: minX, y: minY, i: vId} : null;
35153
- }
35154
-
35155
- // v: vertex in {x, y, i} format
35156
- function findAdjacentVertex(v, shp, arcs, offs) {
35157
- var p, i;
35158
- var arcEnd = offs == 1 && vertexIsArcEnd(v.i, arcs);
35159
- var arcStart = offs == -1 && vertexIsArcStart(v.i, arcs);
35160
- if (arcEnd || arcStart) return null;
35161
- i = v.i + offs;
35162
- p = getVertexCoords(i, arcs);
35163
- return {
35164
- i: i,
35165
- x: p[0],
35166
- y: p[1]
35167
- };
35203
+ return minLen < Infinity ? {x: minX, y: minY} : null;
35168
35204
  }
35169
35205
 
35170
35206
  var VertexUtils = /*#__PURE__*/Object.freeze({
@@ -35177,8 +35213,7 @@ ${svg}
35177
35213
  vertexIsArcEnd: vertexIsArcEnd,
35178
35214
  vertexIsArcStart: vertexIsArcStart,
35179
35215
  setVertexCoords: setVertexCoords,
35180
- findNearestVertex: findNearestVertex,
35181
- findAdjacentVertex: findAdjacentVertex
35216
+ findNearestVertex: findNearestVertex
35182
35217
  });
35183
35218
 
35184
35219
  // Returns x,y coordinates of the point that is at the midpoint of each polyline feature