mapshaper 0.5.95 → 0.5.98

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.94";
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,381 @@ ${svg}
29005
28849
  };
29006
28850
  }
29007
28851
 
29008
- cmd.classify = function(lyr, dataset, optsArg) {
29009
- if (!lyr.data) {
29010
- initDataTable(lyr);
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)}`);
29011
28877
  }
29012
- var opts = optsArg || {};
29013
- 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;
29018
- var numClasses, numValues;
29019
- var dataField, outputField;
29020
- var method;
28878
+ return maxId + 1;
28879
+ }
29021
28880
 
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)');
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];
29026
28916
  }
29027
- numClasses = opts.classes;
29028
28917
  }
28918
+ return null;
28919
+ }
29029
28920
 
29030
- // TODO: better validation of breaks values
29031
- if (opts.breaks) {
29032
- numClasses = opts.breaks.length + 1;
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));
29033
28934
  }
28935
+ return colors;
28936
+ }
29034
28937
 
29035
- if (opts.index_field) {
29036
- dataField = opts.index_field;
29037
- if (numClasses > 0 === false) {
29038
- stop('The index-field= option requires the classes= option to be set');
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 getNullValue(opts) {
29063
+ var nullValue;
29064
+ if ('null_value' in opts) {
29065
+ nullValue = parseNullValue(opts.null_value);
29066
+ } else if (opts.colors) {
29067
+ nullValue = '#eee';
29068
+ } else if (opts.values) {
29069
+ nullValue = null;
29070
+ } else {
29071
+ nullValue = -1; // kludge, to match behavior of getClassValues()
29072
+ }
29073
+ return nullValue;
29074
+ }
29075
+
29076
+ // Parse command line string arguments to the correct data type
29077
+ function parseNullValue(val) {
29078
+ if (utils.isString(val) && !isNaN(+val)) {
29079
+ val = +val;
29080
+ }
29081
+ if (val === 'null') {
29082
+ val = null;
29083
+ }
29084
+ return val;
29085
+ }
29086
+
29087
+ function getClassValues(method, n, opts) {
29088
+ var categorical = method == 'categorical' || method == 'non-adjacent';
29089
+ var colorArg = opts.colors && opts.colors.length == 1 ? opts.colors[0] : null;
29090
+ var colorScheme;
29091
+
29092
+ if (colorArg == 'random') {
29093
+ if (categorical) {
29094
+ return getRandomColors(n);
29039
29095
  }
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);
29096
+ colorScheme = pickRandomColorScheme('sequential');
29097
+ message('Randomly selected color ramp:', colorScheme);
29098
+ } else if (isColorSchemeName(colorArg)) {
29099
+ colorScheme = colorArg;
29100
+ } else if (colorArg && !parseColor(colorArg)) {
29101
+ stop('Unrecognized color scheme name:', colorArg);
29102
+ } else if (opts.colors) {
29103
+ opts.colors.forEach(validateColor);
29104
+ }
29044
29105
 
29045
- } else if (opts.field) {
29046
- dataField = opts.field;
29106
+ if (colorScheme) {
29107
+ if (categorical && isCategoricalColorScheme(colorScheme)) {
29108
+ return getCategoricalColorScheme(colorScheme, n);
29109
+ } else {
29110
+ return getColorRamp(colorScheme, n, opts.stops);
29111
+ }
29112
+ } else if (opts.colors || opts.values) {
29113
+ if (categorical) {
29114
+ return getCategoricalValues(opts.colors || opts.values, n);
29115
+ } else {
29116
+ return getInterpolableValues(opts.colors || opts.values, n, opts);
29117
+ }
29118
+ } else {
29119
+ // use numerical class indexes (0, 1, ...) if no values are given
29120
+ return getIndexes(n);
29047
29121
  }
29122
+ }
29048
29123
 
29049
- // expand categories if value is '*'
29050
- if (dataField && opts.categories && opts.categories.includes('*')) {
29051
- opts.categories = getUniqFieldValues(records, dataField);
29124
+ function getCategoricalValues(values, n) {
29125
+ if (n != values.length) {
29126
+ stop('Mismatch in number of categories and number of values');
29052
29127
  }
29128
+ return values;
29129
+ }
29053
29130
 
29054
- // get classification method
29055
- //
29131
+ function getIndexes(n) {
29132
+ var vals = [];
29133
+ for (var i=0; i<n; i++) {
29134
+ vals.push(i);
29135
+ }
29136
+ return vals;
29137
+ }
29138
+
29139
+ // TODO: check for non-interpolatable value types (e.g. boolean, text)
29140
+ function getInterpolableValues(arr, n, opts) {
29141
+ var values = parseValues(arr);
29142
+ if (n != values.length || opts.stops) {
29143
+ return interpolateValuesToClasses(values, n, opts.stops);
29144
+ }
29145
+ return values;
29146
+ }
29147
+
29148
+ // convert strings to numbers if they all parse as numbers
29149
+ // arr: an array of strings
29150
+ function parseValues(strings) {
29151
+ var values = strings;
29152
+ if (strings.every(utils.parseNumber)) {
29153
+ values = strings.map(function(str) {
29154
+ return +str;
29155
+ });
29156
+ }
29157
+ return values;
29158
+ }
29159
+
29160
+ var sequential = ['quantile', 'nice', 'equal-interval', 'hybrid', 'breaks'];
29161
+ var all = ['non-adjacent', 'indexed', 'categorical'].concat(sequential);
29162
+
29163
+ function getClassifyMethod(opts, dataType) {
29164
+ var method;
29056
29165
  if (opts.method) {
29057
29166
  method = opts.method;
29058
- } else if (opts.categories) {
29059
- method = 'categorical';
29167
+ } else if (opts.breaks) {
29168
+ method = 'breaks';
29060
29169
  } else if (opts.index_field) {
29061
29170
  method = 'indexed';
29062
- } else {
29171
+ } else if (opts.categories || dataType == 'string') {
29172
+ method = 'categorical';
29173
+ } else if (dataType == 'number') {
29063
29174
  method = 'quantile'; // TODO: validate data field
29175
+ } else if (dataType == 'date' || dataType == 'object') {
29176
+ stop('Data type does not support classification:', dataType);
29177
+ } else if (dataType === null) {
29178
+ // data field is empty
29179
+ return null; // kludge
29180
+ } else {
29181
+ stop('Unable to determine which classification method to use.');
29182
+ }
29183
+ if (!all.includes(method)) {
29184
+ stop('Not a recognized classification method:', method);
29064
29185
  }
29186
+ if (sequential.includes(method) && dataType != 'number' && dataType !== null) {
29187
+ stop('The', method, 'method requires a numerical data field');
29188
+ }
29189
+ return method;
29190
+ }
29065
29191
 
29192
+ cmd.classify = function(lyr, dataset, optsArg) {
29193
+ if (!lyr.data) {
29194
+ initDataTable(lyr);
29195
+ }
29196
+ var opts = optsArg || {};
29197
+ var records = lyr.data && lyr.data.getRecords();
29198
+ var valuesAreColors = !!opts.colors;
29199
+ var dataField, fieldType, outputField;
29200
+ var values, nullValue;
29201
+ var classifyByValue, classifyByRecordId;
29202
+ var numClasses, numValues;
29203
+ var method;
29204
+
29205
+ if (opts.color_scheme) {
29206
+ stop('color-scheme is not a valid option, use colors instead');
29207
+ }
29208
+
29209
+ // get data field to use for classification
29210
+ //
29211
+ if (opts.index_field) {
29212
+ dataField = opts.index_field;
29213
+ fieldType = getColumnType(opts.field, records);
29214
+ } else if (opts.field) {
29215
+ dataField = opts.field;
29216
+ fieldType = getColumnType(opts.field, records);
29217
+ }
29218
+ if (dataField) {
29219
+ requireDataField(lyr.data, dataField);
29220
+ }
29221
+
29222
+ // get classification method
29223
+ //
29224
+ method = getClassifyMethod(opts, fieldType);
29225
+
29226
+ // validate classification method
29066
29227
  if (method == 'non-adjacent') {
29067
29228
  if (lyr.geometry_type != 'polygon') {
29068
29229
  stop('The non-adjacent option requires a polygon layer');
@@ -29072,84 +29233,64 @@ ${svg}
29072
29233
  }
29073
29234
  } else if (!dataField) {
29074
29235
  stop('Missing a data field to classify');
29075
- } else {
29076
- requireDataField(lyr.data, dataField);
29077
29236
  }
29078
29237
 
29079
- if (numClasses) {
29080
- numValues = opts.continuous ? numClasses + 1 : numClasses;
29081
- }
29082
29238
 
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);
29239
+ // get the number of classes and the number of values
29240
+ //
29241
+ // expand categories if value is '*'
29242
+ // use all unique values if categories option is missing
29243
+ if (method == 'categorical') {
29244
+ if ((!opts.categories || opts.categories.includes('*')) && dataField) {
29245
+ opts.categories = getUniqFieldValues(records, dataField);
29087
29246
  }
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
29247
  }
29094
29248
 
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);
29249
+ if (opts.classes) {
29250
+ if (!utils.isInteger(opts.classes) || opts.classes > 1 === false) {
29251
+ stop('Invalid number of classes:', opts.classes, '(expected a value greater than 1)');
29124
29252
  }
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';
29253
+ numClasses = opts.classes;
29254
+ } else if (method == 'indexed' && dataField) {
29255
+ numClasses = getIndexedClassCount(records, dataField);
29256
+ } else if (opts.breaks) {
29257
+ numClasses = opts.breaks.length + 1;
29258
+ } else if (method == 'categorical' && opts.categories) {
29259
+ numClasses = opts.categories.length;
29260
+ } else if (opts.colors && opts.colors.length > 1) {
29261
+ numClasses = opts.colors.length;
29262
+ } else if (opts.values && opts.values.length > 1) {
29263
+ numClasses = opts.values.length;
29264
+ } else if (method == 'non-adjacent') {
29265
+ numClasses = 5;
29266
+ } else {
29267
+ numClasses = 4;
29134
29268
  }
29135
-
29269
+ numValues = opts.continuous ? numClasses + 1 : numClasses;
29136
29270
  if (numValues > 1 === false) {
29137
- stop('Missing a valid number of classes');
29271
+ stop('Missing a valid number of values');
29138
29272
  }
29139
29273
 
29274
+ // get colors or other values
29275
+ //
29276
+ values = getClassValues(method, numValues, opts);
29140
29277
  if (opts.invert) {
29141
29278
  values = values.concat().reverse();
29142
29279
  }
29143
-
29144
29280
  if (valuesAreColors) {
29145
29281
  message('Colors:', formatValuesForLogging(values));
29146
29282
  }
29147
29283
 
29284
+ nullValue = getNullValue(opts);
29285
+
29148
29286
  // get a function to convert input data to class indexes
29149
29287
  //
29150
- if (method == 'non-adjacent') {
29151
- classifyById = getNonAdjacentClassifier(lyr, dataset, values);
29152
- } else if (opts.index_field) {
29288
+ if (fieldType === null) {
29289
+ // no valid data -- always return null value
29290
+ classifyByRecordId = function() {return nullValue;};
29291
+ } else if (method == 'non-adjacent') {
29292
+ classifyByRecordId = getNonAdjacentClassifier(lyr, dataset, values);
29293
+ } else if (method == 'indexed') {
29153
29294
  // data is pre-classified... just read the index from a field
29154
29295
  classifyByValue = getIndexedClassifier(values, nullValue, opts);
29155
29296
  } else if (method == 'categorical') {
@@ -29159,13 +29300,12 @@ ${svg}
29159
29300
  }
29160
29301
 
29161
29302
  if (classifyByValue) {
29162
- classifyById = function(id) {
29303
+ classifyByRecordId = function(id) {
29163
29304
  var d = records[id] || {};
29164
29305
  return classifyByValue(d[dataField]);
29165
29306
  };
29166
29307
  }
29167
29308
 
29168
-
29169
29309
  // get the name of the output field
29170
29310
  //
29171
29311
  if (valuesAreColors) {
@@ -29181,23 +29321,10 @@ ${svg}
29181
29321
  }
29182
29322
 
29183
29323
  records.forEach(function(d, i) {
29184
- d[outputField] = classifyById(i);
29324
+ d[outputField] = classifyByRecordId(i);
29185
29325
  });
29186
29326
  };
29187
29327
 
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
29328
  function formatValuesForLogging(arr) {
29202
29329
  if (arr.some(val => utils.isString(val) && val.indexOf('rgb(') === 0)) {
29203
29330
  return formatColorsAsHex(arr);
@@ -29214,15 +29341,6 @@ ${svg}
29214
29341
  });
29215
29342
  }
29216
29343
 
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
29344
  // Remove small-area polygon rings (very simple implementation of sliver removal)
29227
29345
  // TODO: more sophisticated sliver detection (e.g. could consider ratio of area to perimeter)
29228
29346
  // TODO: consider merging slivers into adjacent polygons to prevent gaps from forming
@@ -31087,7 +31205,11 @@ ${svg}
31087
31205
  if (!opts.force && !opts.prefix) {
31088
31206
  // overwrite existing fields if the "force" option is set.
31089
31207
  // prefix also overwrites... TODO: consider changing this
31090
- joinFields = utils.difference(joinFields, destFields);
31208
+ var duplicateFields = utils.intersection(joinFields, destFields);
31209
+ if (duplicateFields.length > 0) {
31210
+ message('Same-named fields not joined without the "force" flag:', duplicateFields);
31211
+ joinFields = utils.difference(joinFields, duplicateFields);
31212
+ }
31091
31213
  }
31092
31214
  return joinFields;
31093
31215
  }
@@ -31819,7 +31941,7 @@ ${svg}
31819
31941
  // stop("Missing required colors parameter");
31820
31942
  // }
31821
31943
  if (Array.isArray(opts.colors)) {
31822
- opts.colors.forEach(parseColor); // validate colors
31944
+ opts.colors.forEach(validateColor);
31823
31945
  }
31824
31946
 
31825
31947
  var records = lyr.data ? lyr.data.getRecords() : [];
@@ -32058,7 +32180,7 @@ ${svg}
32058
32180
  var name = cmdOpts.name;
32059
32181
  var cmdDefn = externalCommands[name];
32060
32182
  if (!cmdDefn) {
32061
- stop('Unsupported command');
32183
+ stop('Unsupported command:', name);
32062
32184
  }
32063
32185
  var targetType = cmdDefn.target;
32064
32186
  var opts = parseExternalCommand(name, cmdDefn, cmdOpts._);
@@ -35056,7 +35178,6 @@ ${svg}
35056
35178
  }
35057
35179
 
35058
35180
 
35059
-
35060
35181
  function snapVerticesToPoint(ids, p, arcs, final) {
35061
35182
  ids.forEach(function(idx) {
35062
35183
  setVertexCoords(p[0], p[1], idx, arcs);
@@ -35136,7 +35257,7 @@ ${svg}
35136
35257
  function findNearestVertex(x, y, shp, arcs, spherical) {
35137
35258
  var calcLen = spherical ? geom.greatCircleDistance : geom.distance2D,
35138
35259
  minLen = Infinity,
35139
- minX, minY, vId, dist, iter;
35260
+ minX, minY, dist, iter;
35140
35261
  for (var i=0; i<shp.length; i++) {
35141
35262
  iter = arcs.getShapeIter(shp[i]);
35142
35263
  while (iter.hasNext()) {
@@ -35145,26 +35266,10 @@ ${svg}
35145
35266
  minLen = dist;
35146
35267
  minX = iter.x;
35147
35268
  minY = iter.y;
35148
- vId = iter.i;
35149
35269
  }
35150
35270
  }
35151
35271
  }
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
- };
35272
+ return minLen < Infinity ? {x: minX, y: minY} : null;
35168
35273
  }
35169
35274
 
35170
35275
  var VertexUtils = /*#__PURE__*/Object.freeze({
@@ -35177,8 +35282,7 @@ ${svg}
35177
35282
  vertexIsArcEnd: vertexIsArcEnd,
35178
35283
  vertexIsArcStart: vertexIsArcStart,
35179
35284
  setVertexCoords: setVertexCoords,
35180
- findNearestVertex: findNearestVertex,
35181
- findAdjacentVertex: findAdjacentVertex
35285
+ findNearestVertex: findNearestVertex
35182
35286
  });
35183
35287
 
35184
35288
  // Returns x,y coordinates of the point that is at the midpoint of each polyline feature
@@ -36856,8 +36960,12 @@ ${svg}
36856
36960
  // support multiline string of commands pasted into console
36857
36961
  str = str.split(/\n+/g).map(function(str) {
36858
36962
  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
36963
+ //if (match && parser.isCommandName(match[0])) {
36964
+ if (match) {
36965
+ // add hyphen prefix to bare command
36966
+ // also add hyphen to non-command strings, for a better error message
36967
+ // ("unsupported command" instead of "The -i command cannot be run in the browser")
36968
+ str = '-' + str;
36861
36969
  }
36862
36970
  return str;
36863
36971
  }).join(' ');