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/CHANGELOG.md +10 -0
- package/mapshaper.js +503 -395
- package/package.json +1 -1
- package/www/mapshaper-gui.js +63 -47
- package/www/mapshaper.js +503 -395
package/mapshaper.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
|
|
3
|
-
var VERSION = "0.5.
|
|
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)
|
|
8881
|
-
|
|
8882
|
-
if (
|
|
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: '
|
|
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
|
-
|
|
29009
|
-
|
|
29010
|
-
|
|
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
|
-
|
|
29013
|
-
|
|
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
|
-
|
|
29023
|
-
|
|
29024
|
-
|
|
29025
|
-
|
|
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
|
-
|
|
29031
|
-
|
|
29032
|
-
|
|
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
|
-
|
|
29036
|
-
|
|
29037
|
-
|
|
29038
|
-
|
|
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
|
-
|
|
29041
|
-
|
|
29042
|
-
|
|
29043
|
-
|
|
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
|
-
|
|
29046
|
-
|
|
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
|
-
|
|
29050
|
-
if (
|
|
29051
|
-
|
|
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
|
-
|
|
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.
|
|
29059
|
-
method = '
|
|
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
|
-
//
|
|
29084
|
-
|
|
29085
|
-
|
|
29086
|
-
|
|
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
|
-
|
|
29096
|
-
|
|
29097
|
-
|
|
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 (
|
|
29127
|
-
|
|
29128
|
-
|
|
29129
|
-
|
|
29130
|
-
}
|
|
29131
|
-
|
|
29132
|
-
if (
|
|
29133
|
-
|
|
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
|
|
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 (
|
|
29151
|
-
|
|
29152
|
-
|
|
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
|
-
|
|
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] =
|
|
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
|
-
|
|
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(
|
|
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,
|
|
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
|
|
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
|
-
|
|
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(' ');
|