mapshaper 0.5.94 → 0.5.97

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/www/mapshaper.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
 
3
- var VERSION = "0.5.92";
3
+ var VERSION = "0.5.96";
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();
@@ -8877,12 +8877,24 @@
8877
8877
  arg = arg ? String(arg) : '';
8878
8878
  var hexStr = hexRxp.test(arg) ? arg : lookupColorName(arg);
8879
8879
  var rgb = null;
8880
- if (hexStr) rgb = parseHexColor(hexStr);
8881
- else if (rgbaRxp.test(arg)) rgb = parseRGBA(arg);
8882
- if (!testRGB(rgb)) stop("Unsupported color:", arg);
8880
+ if (hexStr) {
8881
+ rgb = parseHexColor(hexStr);
8882
+ } else if (rgbaRxp.test(arg)) {
8883
+ rgb = parseRGBA(arg);
8884
+ }
8885
+ if (rgb && !testRGB(rgb)) {
8886
+ rgb = null;
8887
+ }
8883
8888
  return rgb;
8884
8889
  }
8885
8890
 
8891
+ function validateColor(arg) {
8892
+ if (!parseColor(arg)) {
8893
+ stop("Unsupported color:", arg);
8894
+ }
8895
+ return true;
8896
+ }
8897
+
8886
8898
  function testRGB(o) {
8887
8899
  return !!o && testChannel(o.r) && testChannel(o.g) && testChannel(o.b) &&
8888
8900
  testAlpha(o.a);
@@ -9028,7 +9040,7 @@
9028
9040
  weights = normalizeWeights(weights);
9029
9041
  if (!weights) return '#eee';
9030
9042
  var blended = colors.reduce(function(memo, col, i) {
9031
- var rgb = parseColor(col);
9043
+ var rgb = validateColor(col) && parseColor(col);
9032
9044
  var w = +weights[i] || 0;
9033
9045
  memo.r += rgb.r * w;
9034
9046
  memo.g += rgb.g * w;
@@ -19152,7 +19164,7 @@ ${svg}
19152
19164
  describe: 'value (or color) to use for invalid or missing data'
19153
19165
  })
19154
19166
  .option('method', {
19155
- describe: 'one of: quantile, nice, equal-interval, hybrid'
19167
+ describe: 'quantile, nice, equal-interval, categorical, etc.'
19156
19168
  })
19157
19169
  .option('quantile', {
19158
19170
  //describe: 'shortcut for method=quantile (the default)',
@@ -27734,217 +27746,6 @@ ${svg}
27734
27746
  return [lyr2];
27735
27747
  }
27736
27748
 
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
27749
  var scaledIntervals =
27949
27750
  [10,12,15,18,20,22,25,30,35,40,45,50,60,70,80,90,100];
27950
27751
  var precisions =
@@ -28074,6 +27875,78 @@ ${svg}
28074
27875
  return s;
28075
27876
  }
28076
27877
 
27878
+ // TODO: support three or more stops
27879
+ function getGradientFunction(stops) {
27880
+ var min = stops[0] / 100,
27881
+ max = stops[1] / 100;
27882
+ if (stops.length != 2) {
27883
+ stop('Only two stops are currently supported');
27884
+ }
27885
+ if (!(min >= 0 && max <= 1 && min < max)) {
27886
+ stop('Invalid gradient stops:', stops);
27887
+ }
27888
+ return function(t) {
27889
+ return t * (max - min) + min;
27890
+ };
27891
+ }
27892
+
27893
+ function getStoppedValues(values, stops) {
27894
+ var interpolate = getInterpolatedValueGetter(values, null);
27895
+ var n = values.length;
27896
+ var fstop = getGradientFunction(stops);
27897
+ var values2 = [];
27898
+ var t, val;
27899
+ for (var i=0; i<n; i++) {
27900
+ t = fstop(i / (n - 1));
27901
+ val = interpolate(t * (n - 1));
27902
+ values2.push(val);
27903
+ }
27904
+ return values2;
27905
+ }
27906
+
27907
+ // convert a continuous index ([0, n-1], -1) to a corresponding interpolated value
27908
+ function getInterpolatedValueGetter(values, nullValue) {
27909
+ var d3 = require('d3-interpolate');
27910
+ var interpolators = [];
27911
+ var tmax = values.length - 1;
27912
+ for (var i=1; i<values.length; i++) {
27913
+ interpolators.push(d3.interpolate(values[i-1], values[i]));
27914
+ }
27915
+ return function(t) {
27916
+ if (t == -1) return nullValue;
27917
+ if ((t >= 0 && t <= tmax) === false) {
27918
+ error('Range error');
27919
+ }
27920
+ var i = t == tmax ? tmax - 1 : Math.floor(t);
27921
+ var j = t == tmax ? 1 : t % 1;
27922
+ return interpolators[i](j);
27923
+ };
27924
+ }
27925
+
27926
+ // return an array of n values
27927
+ // assumes that values can be interpolated by d3-interpolate
27928
+ // (colors and numbers should work)
27929
+ function interpolateValuesToClasses(values, n, stops) {
27930
+ if (values.length == n && !stops) return values;
27931
+ var d3 = require('d3-interpolate');
27932
+ var numPairs = values.length - 1;
27933
+ var output = [values[0]];
27934
+ var k, j, t, intVal;
27935
+ for (var i=1; i<n-1; i++) {
27936
+ k = i / (n-1) * numPairs;
27937
+ j = Math.floor(k);
27938
+ t = k - j;
27939
+ // if (convert) t = convert(t);
27940
+ intVal = d3.interpolate(values[j], values[j+1])(t);
27941
+ output.push(intVal);
27942
+ }
27943
+ output.push(values[values.length - 1]);
27944
+ if (stops) {
27945
+ output = getStoppedValues(output, stops);
27946
+ }
27947
+ return output;
27948
+ }
27949
+
28077
27950
  // convert an index (0 ... n-1, -1, -2) to a corresponding discreet value
28078
27951
  function getDiscreteValueGetter(values, nullValue, otherValue) {
28079
27952
  var n = values.length;
@@ -28680,35 +28553,6 @@ ${svg}
28680
28553
  };
28681
28554
  }
28682
28555
 
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
28556
  // Returns a function for constructing a query function that accepts an arc id and
28713
28557
  // returns information about the polygon or polygons that use the given arc.
28714
28558
  // TODO: explain this better.
@@ -29005,64 +28849,349 @@ ${svg}
29005
28849
  };
29006
28850
  }
29007
28851
 
28852
+ function getIndexedClassifier(values, nullVal, opts) {
28853
+ // TODO: handle continuous classification
28854
+ var numBuckets = values.length;
28855
+ var classToValue = getOutputFunction(values, nullVal, opts);
28856
+
28857
+ return function(val) {
28858
+ var idx = utils.isInteger(val) && val >= 0 && val < numBuckets ? val : -1;
28859
+ return classToValue(idx);
28860
+ };
28861
+ }
28862
+
28863
+ // returns the number of classes, based on the largest class index found
28864
+ function getIndexedClassCount(records, name) {
28865
+ var invalid = [];
28866
+ var maxId = -1;
28867
+ records.forEach(function(d) {
28868
+ var val = (d || {})[name];
28869
+ if (!utils.isInteger(val) || val < -2) {
28870
+ invalid.push(val);
28871
+ } else {
28872
+ maxId = Math.max(maxId, val);
28873
+ }
28874
+ });
28875
+ if (invalid.length > 0) {
28876
+ stop(`Class index field contains invalid value(s): ${invalid.slice(0, 5)}`);
28877
+ }
28878
+ return maxId + 1;
28879
+ }
28880
+
28881
+ var index = {
28882
+ categorical: [],
28883
+ sequential: [],
28884
+ rainbow: [],
28885
+ diverging: [],
28886
+ all: []
28887
+ };
28888
+ var ramps;
28889
+
28890
+ function initSchemes() {
28891
+ if (ramps) return;
28892
+ ramps = {};
28893
+ addSchemesFromD3('categorical', 'Category10,Accent,Dark2,Paired,Pastel1,Pastel2,Set1,Set2,Set3,Tableau10');
28894
+ addSchemesFromD3('sequential', 'Blues,Greens,Greys,Purples,Reds,Oranges,BuGn,BuPu,GnBu,OrRd,PuBuGn,PuBu,PuRd,RdPu,YlGnBu,YlGn,YlOrBr,YlOrRd');
28895
+ addSchemesFromD3('rainbow', 'Cividis,CubehelixDefault,Rainbow,Warm,Cool,Sinebow,Turbo,Viridis,Magma,Inferno,Plasma');
28896
+ addSchemesFromD3('diverging', 'BrBG,PRGn,PRGn,PiYG,PuOr,RdBu,RdGy,RdYlBu,RdYlGn,Spectral');
28897
+ testLib(); // make sure these schemes are all available
28898
+ addCategoricalScheme('Category20',
28899
+ '1f77b4aec7e8ff7f0effbb782ca02c98df8ad62728ff98969467bdc5b0d58c564bc49c94e377c2f7b6d27f7f7fc7c7c7bcbd22dbdb8d17becf9edae5');
28900
+ addCategoricalScheme('Category20b',
28901
+ '393b795254a36b6ecf9c9ede6379398ca252b5cf6bcedb9c8c6d31bd9e39e7ba52e7cb94843c39ad494ad6616be7969c7b4173a55194ce6dbdde9ed6');
28902
+ addCategoricalScheme('Category20c',
28903
+ '3182bd6baed69ecae1c6dbefe6550dfd8d3cfdae6bfdd0a231a35474c476a1d99bc7e9c0756bb19e9ac8bcbddcdadaeb636363969696bdbdbdd9d9d9');
28904
+ addCategoricalScheme('Tableau20',
28905
+ '4c78a89ecae9f58518ffbf7954a24b88d27ab79a20f2cf5b43989483bcb6e45756ff9d9879706ebab0acd67195fcbfd2b279a2d6a5c99e765fd8b5a5');
28906
+ index.all = [].concat(index.sequential, index.rainbow, index.diverging, index.categorical);
28907
+
28908
+ }
28909
+
28910
+ function standardName(name) {
28911
+ if (!name) return null;
28912
+ var lcname = name.toLowerCase();
28913
+ for (var i=0; i<index.all.length; i++) {
28914
+ if (index.all[i].toLowerCase() == lcname) {
28915
+ return index.all[i];
28916
+ }
28917
+ }
28918
+ return null;
28919
+ }
28920
+
28921
+ function addSchemesFromD3(type, names) {
28922
+ index[type] = index[type].concat(names.split(','));
28923
+ }
28924
+
28925
+ function addCategoricalScheme(name, str) {
28926
+ index.categorical.push(name);
28927
+ ramps[name] = unpackRamp(str);
28928
+ }
28929
+
28930
+ function unpackRamp(str) {
28931
+ var colors = [];
28932
+ for (var i=0, n=str.length; i<n; i+=6) {
28933
+ colors.push('#' + str.substr(i, 6));
28934
+ }
28935
+ return colors;
28936
+ }
28937
+
28938
+ function testLib() {
28939
+ var lib = require('d3-scale-chromatic');
28940
+ schemes(index.categorical);
28941
+ schemes(index.sequential);
28942
+ schemes(index.diverging);
28943
+ interpolators(index.sequential);
28944
+ interpolators(index.rainbow);
28945
+ interpolators(index.diverging);
28946
+
28947
+ function schemes(arr) {
28948
+ arr.forEach(function(name) {
28949
+ if (!lib['scheme' + name]) {
28950
+ message('Warning: missing data for', name);
28951
+ }
28952
+ });
28953
+ }
28954
+
28955
+ function interpolators(arr) {
28956
+ arr.forEach(function(name) {
28957
+ if (!lib['interpolate' + name]) {
28958
+ message('Missing interpolator for', name);
28959
+ }
28960
+ });
28961
+ }
28962
+ }
28963
+
28964
+ function printColorSchemeNames() {
28965
+ initSchemes();
28966
+ print('Built-in color schemes (from d3):');
28967
+ print ('Categorical\n' + formatStringsAsGrid(index.categorical));
28968
+ print ('\nSequential\n' + formatStringsAsGrid(index.sequential));
28969
+ print ('\nDiverging\n' + formatStringsAsGrid(index.diverging));
28970
+ print ('\nMulti-hue/rainbow\n' + formatStringsAsGrid(index.rainbow));
28971
+ }
28972
+
28973
+ function pickRandomColorScheme(type) {
28974
+ initSchemes();
28975
+ var names = index[type];
28976
+ if (!names) error('Unknown color scheme type:', type);
28977
+ var i = Math.floor(Math.random() * names.length);
28978
+ return names[i];
28979
+ }
28980
+
28981
+ function getRandomColors(n) {
28982
+ initSchemes();
28983
+ var colors = getCategoricalColorScheme('Tableau20', 20);
28984
+ utils.shuffle(colors);
28985
+ colors = wrapColors(colors, n);
28986
+ return colors.slice(0, n);
28987
+ }
28988
+
28989
+ function getCategoricalColorScheme(name, n) {
28990
+ var colors;
28991
+ initSchemes();
28992
+ name = standardName(name);
28993
+ if (!isColorSchemeName(name)) {
28994
+ stop('Unknown color scheme name:', name);
28995
+ } else if (isCategoricalColorScheme(name)) {
28996
+ colors = ramps[name] || require('d3-scale-chromatic')['scheme' + name];
28997
+ } else {
28998
+ colors = getColorRamp(name, n);
28999
+ }
29000
+ if (n > colors.length) {
29001
+ // stop(name, 'does not contain', n, 'colors');
29002
+ message('Color scheme has', colors.length, 'colors. Using duplication to match', n, 'categories.');
29003
+ colors = wrapColors(colors, n);
29004
+ } else {
29005
+ colors = colors.slice(0, n);
29006
+ }
29007
+ return colors;
29008
+ }
29009
+
29010
+ function wrapColors(colors, n) {
29011
+ while (colors.length > 0 && colors.length < n) {
29012
+ colors = colors.concat(colors.slice(0, n - colors.length));
29013
+ }
29014
+ return colors;
29015
+ }
29016
+
29017
+ function isColorSchemeName(name) {
29018
+ initSchemes();
29019
+ return index.all.includes(standardName(name));
29020
+ }
29021
+
29022
+ function isCategoricalColorScheme(name) {
29023
+ initSchemes();
29024
+ return index.categorical.includes(standardName(name));
29025
+ }
29026
+
29027
+ function getColorRamp(name, n, stops) {
29028
+ initSchemes();
29029
+ name = standardName(name);
29030
+ var lib = require('d3-scale-chromatic');
29031
+ var ramps = lib['scheme' + name];
29032
+ var interpolate = lib['interpolate' + name];
29033
+ var ramp;
29034
+ if (!ramps && !interpolate) {
29035
+ stop('Unknown color scheme name:', name);
29036
+ }
29037
+ if (index.categorical.includes(name)) {
29038
+ stop(name, ' is a categorical color scheme (expected a sequential color scheme)');
29039
+ }
29040
+ if (ramps && ramps[n]) {
29041
+ ramp = ramps[n];
29042
+ } else {
29043
+ ramp = getInterpolatedRamp(interpolate, n);
29044
+ }
29045
+ if (stops) {
29046
+ ramp = getStoppedValues(ramp, stops);
29047
+ }
29048
+ return ramp;
29049
+ }
29050
+
29051
+ function getInterpolatedRamp(interpolate, n) {
29052
+ if (n > 0 === false || !utils.isInteger(n)) {
29053
+ error('Expected a positive integer');
29054
+ }
29055
+ var ramp = [];
29056
+ for (var i=0; i<n; i++) {
29057
+ ramp.push(interpolate(i / (n - 1)));
29058
+ }
29059
+ return ramp;
29060
+ }
29061
+
29062
+ function getClassValues(method, n, opts) {
29063
+ var categorical = method == 'categorical' || method == 'non-adjacent';
29064
+ var colorArg = opts.colors && opts.colors.length == 1 ? opts.colors[0] : null;
29065
+ var colorScheme;
29066
+
29067
+ if (colorArg == 'random') {
29068
+ if (categorical) {
29069
+ return getRandomColors(n);
29070
+ }
29071
+ colorScheme = pickRandomColorScheme('sequential');
29072
+ message('Randomly selected color ramp:', colorScheme);
29073
+ } else if (isColorSchemeName(colorArg)) {
29074
+ colorScheme = colorArg;
29075
+ } else if (colorArg && !parseColor(colorArg)) {
29076
+ stop('Unrecognized color scheme name:', colorArg);
29077
+ } else if (opts.colors) {
29078
+ opts.colors.forEach(validateColor);
29079
+ }
29080
+
29081
+ if (colorScheme) {
29082
+ if (categorical && isCategoricalColorScheme(colorScheme)) {
29083
+ return getCategoricalColorScheme(colorScheme, n);
29084
+ } else {
29085
+ return getColorRamp(colorScheme, n, opts.stops);
29086
+ }
29087
+ } else if (opts.colors || opts.values) {
29088
+ if (categorical) {
29089
+ return getCategoricalValues(opts.colors || opts.values, n);
29090
+ } else {
29091
+ return getInterpolableValues(opts.colors || opts.values, n, opts);
29092
+ }
29093
+ } else {
29094
+ // use numerical class indexes (0, 1, ...) if no values are given
29095
+ return getIndexes(n);
29096
+ }
29097
+ }
29098
+
29099
+ function getCategoricalValues(values, n) {
29100
+ if (n != values.length) {
29101
+ stop('Mismatch in number of categories and number of values');
29102
+ }
29103
+ return values;
29104
+ }
29105
+
29106
+ function getIndexes(n) {
29107
+ var vals = [];
29108
+ for (var i=0; i<n; i++) {
29109
+ vals.push(i);
29110
+ }
29111
+ return vals;
29112
+ }
29113
+
29114
+ // TODO: check for non-interpolatable value types (e.g. boolean, text)
29115
+ function getInterpolableValues(arr, n, opts) {
29116
+ var values = parseValues(arr);
29117
+ if (n != values.length || opts.stops) {
29118
+ return interpolateValuesToClasses(values, n, opts.stops);
29119
+ }
29120
+ return values;
29121
+ }
29122
+
29123
+ // convert strings to numbers if they all parse as numbers
29124
+ // arr: an array of strings
29125
+ function parseValues(strings) {
29126
+ var values = strings;
29127
+ if (strings.every(utils.parseNumber)) {
29128
+ values = strings.map(function(str) {
29129
+ return +str;
29130
+ });
29131
+ }
29132
+ return values;
29133
+ }
29134
+
29135
+ var sequential = ['quantile', 'nice', 'equal-interval', 'hybrid'];
29136
+ var all = ['non-adjacent', 'indexed', 'categorical'].concat(sequential);
29137
+
29138
+ function getClassifyMethod(opts, dataFieldType) {
29139
+ var method;
29140
+ if (opts.method) {
29141
+ method = opts.method;
29142
+ } else if (opts.index_field) {
29143
+ method = 'indexed';
29144
+ } else if (opts.categories || dataFieldType == 'string') {
29145
+ method = 'categorical';
29146
+ } else if (dataFieldType == 'number') {
29147
+ method = 'quantile'; // TODO: validate data field
29148
+ } else {
29149
+ // stop('Unable to determine which classification method to use.');
29150
+ stop('Missing a data field and/or classification method');
29151
+ }
29152
+ if (!all.includes(method)) {
29153
+ stop('Not a recognized classification method:', method);
29154
+ }
29155
+ if (sequential.includes(method) && dataFieldType != 'number') {
29156
+ stop('The', method, 'method requires a numerical data field');
29157
+ }
29158
+ return method;
29159
+ }
29160
+
29008
29161
  cmd.classify = function(lyr, dataset, optsArg) {
29009
29162
  if (!lyr.data) {
29010
29163
  initDataTable(lyr);
29011
29164
  }
29012
29165
  var opts = optsArg || {};
29013
29166
  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;
29167
+ var valuesAreColors = !!opts.colors;
29168
+ var dataField, dataFieldType, outputField;
29169
+ var values, nullValue;
29170
+ var classifyByValue, classifyByRecordId;
29018
29171
  var numClasses, numValues;
29019
- var dataField, outputField;
29020
29172
  var method;
29021
29173
 
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;
29174
+ if (opts.color_scheme) {
29175
+ stop('color-scheme is not a valid option, use colors instead');
29033
29176
  }
29034
29177
 
29178
+ // get data field to use for classification
29179
+ //
29035
29180
  if (opts.index_field) {
29036
29181
  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
29182
  } else if (opts.field) {
29046
29183
  dataField = opts.field;
29184
+ dataFieldType = getColumnType(opts.field, records);
29047
29185
  }
29048
-
29049
- // expand categories if value is '*'
29050
- if (dataField && opts.categories && opts.categories.includes('*')) {
29051
- opts.categories = getUniqFieldValues(records, dataField);
29186
+ if (dataField) {
29187
+ requireDataField(lyr.data, dataField);
29052
29188
  }
29053
29189
 
29054
29190
  // get classification method
29055
29191
  //
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
- }
29192
+ method = getClassifyMethod(opts, dataFieldType);
29065
29193
 
29194
+ // validate classification method
29066
29195
  if (method == 'non-adjacent') {
29067
29196
  if (lyr.geometry_type != 'polygon') {
29068
29197
  stop('The non-adjacent option requires a polygon layer');
@@ -29072,83 +29201,70 @@ ${svg}
29072
29201
  }
29073
29202
  } else if (!dataField) {
29074
29203
  stop('Missing a data field to classify');
29075
- } else {
29076
- requireDataField(lyr.data, dataField);
29077
29204
  }
29078
29205
 
29079
- if (numClasses) {
29080
- numValues = opts.continuous ? numClasses + 1 : numClasses;
29081
- }
29082
29206
 
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);
29207
+ // get the number of classes and the number of values
29208
+ //
29209
+ // expand categories if value is '*'
29210
+ if (method == 'categorical') {
29211
+ if ((!opts.categories || opts.categories.includes('*')) && dataField) {
29212
+ opts.categories = getUniqFieldValues(records, dataField);
29087
29213
  }
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
29214
  }
29094
29215
 
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);
29216
+ if (opts.classes) {
29217
+ if (!utils.isInteger(opts.classes) || opts.classes > 1 === false) {
29218
+ stop('Invalid number of classes:', opts.classes, '(expected a value greater than 1)');
29124
29219
  }
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';
29220
+ numClasses = opts.classes;
29221
+ } else if (method == 'indexed' && dataField) {
29222
+ numClasses = getIndexedClassCount(records, dataField);
29223
+ } else if (opts.breaks) {
29224
+ numClasses = opts.breaks.length + 1;
29225
+ } else if (method == 'categorical' && opts.categories) {
29226
+ numClasses = opts.categories.length;
29227
+ } else if (opts.colors && opts.colors.length > 1) {
29228
+ numClasses = opts.colors.length;
29229
+ } else if (opts.values && opts.values.length > 1) {
29230
+ numClasses = opts.values.length;
29231
+ } else if (method == 'non-adjacent') {
29232
+ numClasses = 5;
29233
+ } else {
29234
+ numClasses = 4;
29134
29235
  }
29135
-
29236
+ numValues = opts.continuous ? numClasses + 1 : numClasses;
29136
29237
  if (numValues > 1 === false) {
29137
- stop('Missing a valid number of classes');
29238
+ stop('Missing a valid number of values');
29138
29239
  }
29139
29240
 
29241
+ // get colors or other values
29242
+ //
29243
+ values = getClassValues(method, numValues, opts);
29140
29244
  if (opts.invert) {
29141
29245
  values = values.concat().reverse();
29142
29246
  }
29143
-
29144
29247
  if (valuesAreColors) {
29145
29248
  message('Colors:', formatValuesForLogging(values));
29146
29249
  }
29147
29250
 
29251
+ // get null value
29252
+ //
29253
+ if ('null_value' in opts) {
29254
+ nullValue = opts.null_value;
29255
+ } else if (valuesAreColors) {
29256
+ nullValue = '#eee';
29257
+ } else if (opts.values) {
29258
+ nullValue = null;
29259
+ } else {
29260
+ nullValue = -1; // kludge, to match behavior of getClassValues()
29261
+ }
29262
+
29263
+
29148
29264
  // get a function to convert input data to class indexes
29149
29265
  //
29150
29266
  if (method == 'non-adjacent') {
29151
- classifyById = getNonAdjacentClassifier(lyr, dataset, values);
29267
+ classifyByRecordId = getNonAdjacentClassifier(lyr, dataset, values);
29152
29268
  } else if (opts.index_field) {
29153
29269
  // data is pre-classified... just read the index from a field
29154
29270
  classifyByValue = getIndexedClassifier(values, nullValue, opts);
@@ -29159,13 +29275,12 @@ ${svg}
29159
29275
  }
29160
29276
 
29161
29277
  if (classifyByValue) {
29162
- classifyById = function(id) {
29278
+ classifyByRecordId = function(id) {
29163
29279
  var d = records[id] || {};
29164
29280
  return classifyByValue(d[dataField]);
29165
29281
  };
29166
29282
  }
29167
29283
 
29168
-
29169
29284
  // get the name of the output field
29170
29285
  //
29171
29286
  if (valuesAreColors) {
@@ -29181,23 +29296,10 @@ ${svg}
29181
29296
  }
29182
29297
 
29183
29298
  records.forEach(function(d, i) {
29184
- d[outputField] = classifyById(i);
29299
+ d[outputField] = classifyByRecordId(i);
29185
29300
  });
29186
29301
  };
29187
29302
 
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
29303
  function formatValuesForLogging(arr) {
29202
29304
  if (arr.some(val => utils.isString(val) && val.indexOf('rgb(') === 0)) {
29203
29305
  return formatColorsAsHex(arr);
@@ -29214,15 +29316,6 @@ ${svg}
29214
29316
  });
29215
29317
  }
29216
29318
 
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
29319
  // Remove small-area polygon rings (very simple implementation of sliver removal)
29227
29320
  // TODO: more sophisticated sliver detection (e.g. could consider ratio of area to perimeter)
29228
29321
  // TODO: consider merging slivers into adjacent polygons to prevent gaps from forming
@@ -31087,7 +31180,11 @@ ${svg}
31087
31180
  if (!opts.force && !opts.prefix) {
31088
31181
  // overwrite existing fields if the "force" option is set.
31089
31182
  // prefix also overwrites... TODO: consider changing this
31090
- joinFields = utils.difference(joinFields, destFields);
31183
+ var duplicateFields = utils.intersection(joinFields, destFields);
31184
+ if (duplicateFields.length > 0) {
31185
+ message('Same-named fields not joined without the "force" flag:', duplicateFields);
31186
+ joinFields = utils.difference(joinFields, duplicateFields);
31187
+ }
31091
31188
  }
31092
31189
  return joinFields;
31093
31190
  }
@@ -31819,7 +31916,7 @@ ${svg}
31819
31916
  // stop("Missing required colors parameter");
31820
31917
  // }
31821
31918
  if (Array.isArray(opts.colors)) {
31822
- opts.colors.forEach(parseColor); // validate colors
31919
+ opts.colors.forEach(validateColor);
31823
31920
  }
31824
31921
 
31825
31922
  var records = lyr.data ? lyr.data.getRecords() : [];
@@ -32058,7 +32155,7 @@ ${svg}
32058
32155
  var name = cmdOpts.name;
32059
32156
  var cmdDefn = externalCommands[name];
32060
32157
  if (!cmdDefn) {
32061
- stop('Unsupported command');
32158
+ stop('Unsupported command:', name);
32062
32159
  }
32063
32160
  var targetType = cmdDefn.target;
32064
32161
  var opts = parseExternalCommand(name, cmdDefn, cmdOpts._);
@@ -35056,7 +35153,6 @@ ${svg}
35056
35153
  }
35057
35154
 
35058
35155
 
35059
-
35060
35156
  function snapVerticesToPoint(ids, p, arcs, final) {
35061
35157
  ids.forEach(function(idx) {
35062
35158
  setVertexCoords(p[0], p[1], idx, arcs);
@@ -35136,7 +35232,7 @@ ${svg}
35136
35232
  function findNearestVertex(x, y, shp, arcs, spherical) {
35137
35233
  var calcLen = spherical ? geom.greatCircleDistance : geom.distance2D,
35138
35234
  minLen = Infinity,
35139
- minX, minY, vId, dist, iter;
35235
+ minX, minY, dist, iter;
35140
35236
  for (var i=0; i<shp.length; i++) {
35141
35237
  iter = arcs.getShapeIter(shp[i]);
35142
35238
  while (iter.hasNext()) {
@@ -35145,26 +35241,10 @@ ${svg}
35145
35241
  minLen = dist;
35146
35242
  minX = iter.x;
35147
35243
  minY = iter.y;
35148
- vId = iter.i;
35149
35244
  }
35150
35245
  }
35151
35246
  }
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
- };
35247
+ return minLen < Infinity ? {x: minX, y: minY} : null;
35168
35248
  }
35169
35249
 
35170
35250
  var VertexUtils = /*#__PURE__*/Object.freeze({
@@ -35177,8 +35257,7 @@ ${svg}
35177
35257
  vertexIsArcEnd: vertexIsArcEnd,
35178
35258
  vertexIsArcStart: vertexIsArcStart,
35179
35259
  setVertexCoords: setVertexCoords,
35180
- findNearestVertex: findNearestVertex,
35181
- findAdjacentVertex: findAdjacentVertex
35260
+ findNearestVertex: findNearestVertex
35182
35261
  });
35183
35262
 
35184
35263
  // Returns x,y coordinates of the point that is at the midpoint of each polyline feature
@@ -36856,8 +36935,12 @@ ${svg}
36856
36935
  // support multiline string of commands pasted into console
36857
36936
  str = str.split(/\n+/g).map(function(str) {
36858
36937
  var match = /^[a-z][\w-]*/.exec(str = str.trim());
36859
- if (match && parser.isCommandName(match[0])) {
36860
- str = '-' + str; // add hyphen prefix to bare command
36938
+ //if (match && parser.isCommandName(match[0])) {
36939
+ if (match) {
36940
+ // add hyphen prefix to bare command
36941
+ // also add hyphen to non-command strings, for a better error message
36942
+ // ("unsupported command" instead of "The -i command cannot be run in the browser")
36943
+ str = '-' + str;
36861
36944
  }
36862
36945
  return str;
36863
36946
  }).join(' ');