mapshaper 0.7.56 → 0.7.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/mapshaper.js +948 -303
- package/package.json +1 -1
- package/www/mapshaper-gui.js +38 -3
- package/www/mapshaper.js +948 -303
- package/www/sponsor.html +2 -0
package/www/mapshaper.js
CHANGED
|
@@ -351,7 +351,7 @@
|
|
|
351
351
|
// Calc sum, skip falsy and NaN values
|
|
352
352
|
// Assumes: no other non-numeric objects in array
|
|
353
353
|
//
|
|
354
|
-
function sum$
|
|
354
|
+
function sum$2(arr, info) {
|
|
355
355
|
if (!isArrayLike(arr)) error$1 ("sum() expects an array, received:", arr);
|
|
356
356
|
var tot = 0,
|
|
357
357
|
nan = 0,
|
|
@@ -1117,7 +1117,7 @@
|
|
|
1117
1117
|
quicksort: quicksort$1, quicksortPartition,
|
|
1118
1118
|
range, regexEscape, reorderArray: reorderArray$1, repeat: repeat$1, repeatString,
|
|
1119
1119
|
replaceArray, rpad, rtrim,
|
|
1120
|
-
shuffle, some, sortArrayIndex, sortOn, splitLines, sum: sum$
|
|
1120
|
+
shuffle, some, sortArrayIndex, sortOn, splitLines, sum: sum$2,
|
|
1121
1121
|
toArray: toArray$1, toBuffer, trim, trimQuotes,
|
|
1122
1122
|
uniq, uniqifyNames,
|
|
1123
1123
|
wildcardToRegExp
|
|
@@ -9880,7 +9880,7 @@
|
|
|
9880
9880
|
id: piece.id,
|
|
9881
9881
|
source_region: piece.source_region,
|
|
9882
9882
|
copy: piece.copy,
|
|
9883
|
-
projected_boundary: closeRing$
|
|
9883
|
+
projected_boundary: closeRing$2(piece.boundary)
|
|
9884
9884
|
};
|
|
9885
9885
|
}
|
|
9886
9886
|
|
|
@@ -9888,7 +9888,7 @@
|
|
|
9888
9888
|
return {
|
|
9889
9889
|
id: piece.id,
|
|
9890
9890
|
source_region: piece.source_region,
|
|
9891
|
-
projected_boundary: closeRing$
|
|
9891
|
+
projected_boundary: closeRing$2(piece.boundary)
|
|
9892
9892
|
};
|
|
9893
9893
|
}
|
|
9894
9894
|
|
|
@@ -9970,7 +9970,7 @@
|
|
|
9970
9970
|
return paths;
|
|
9971
9971
|
}
|
|
9972
9972
|
|
|
9973
|
-
function closeRing$
|
|
9973
|
+
function closeRing$2(ring) {
|
|
9974
9974
|
var copy = ring.map(function(p) { return p.concat(); });
|
|
9975
9975
|
if (copy.length && !pointsEqual$2(copy[0], copy[copy.length - 1])) {
|
|
9976
9976
|
copy.push(copy[0].concat());
|
|
@@ -14955,6 +14955,27 @@
|
|
|
14955
14955
|
return fflatePostprocess(obj);
|
|
14956
14956
|
}
|
|
14957
14957
|
|
|
14958
|
+
// Return the entry names in a zip archive, without decompressing them.
|
|
14959
|
+
// Used to tell a KMZ from an ordinary zip when there is no filename to go by.
|
|
14960
|
+
// input: A file path or a buffer
|
|
14961
|
+
function listZipEntryNames(input) {
|
|
14962
|
+
if (input instanceof ArrayBuffer) {
|
|
14963
|
+
input = new Uint8Array(input);
|
|
14964
|
+
}
|
|
14965
|
+
if (!runningInBrowser()) {
|
|
14966
|
+
var AdmZip = require('adm-zip');
|
|
14967
|
+
return new AdmZip(input).getEntries().map(function(entry) {
|
|
14968
|
+
return entry.entryName;
|
|
14969
|
+
});
|
|
14970
|
+
}
|
|
14971
|
+
var names = [];
|
|
14972
|
+
unzipSync$1(input, {filter: function(file) {
|
|
14973
|
+
names.push(file.name);
|
|
14974
|
+
return false; // collect names only
|
|
14975
|
+
}});
|
|
14976
|
+
return names;
|
|
14977
|
+
}
|
|
14978
|
+
|
|
14958
14979
|
function unzipAsync(buf, cb) {
|
|
14959
14980
|
if (!runningInBrowser()) {
|
|
14960
14981
|
error('Async unzipping only supported in the browser');
|
|
@@ -15049,6 +15070,7 @@
|
|
|
15049
15070
|
var Zip = /*#__PURE__*/Object.freeze({
|
|
15050
15071
|
__proto__: null,
|
|
15051
15072
|
isImportableZipPath: isImportableZipPath,
|
|
15073
|
+
listZipEntryNames: listZipEntryNames,
|
|
15052
15074
|
unzipAsync: unzipAsync,
|
|
15053
15075
|
unzipSync: unzipSync,
|
|
15054
15076
|
zipAsync: zipAsync,
|
|
@@ -18629,14 +18651,15 @@
|
|
|
18629
18651
|
function parseHexColor(str) {
|
|
18630
18652
|
var hex = hexRxp.exec(str)[1];
|
|
18631
18653
|
if (hex.length == 3 || hex.length == 4) {
|
|
18632
|
-
|
|
18654
|
+
// expand shorthand, e.g. #f90 -> #ffcc00
|
|
18655
|
+
hex = hex.split('').map(function(c) { return c + c; }).join('');
|
|
18633
18656
|
}
|
|
18634
18657
|
if (hex.length != 6 && hex.length != 8) return null;
|
|
18635
18658
|
return {
|
|
18636
18659
|
r: parseInt(hex.substr(0, 2), 16),
|
|
18637
18660
|
g: parseInt(hex.substr(2, 2), 16),
|
|
18638
18661
|
b: parseInt(hex.substr(4, 2), 16),
|
|
18639
|
-
a: hex.length == 8 ? parseInt(hex.substr(
|
|
18662
|
+
a: hex.length == 8 ? parseInt(hex.substr(6, 2), 16) / 255 : 1
|
|
18640
18663
|
};
|
|
18641
18664
|
}
|
|
18642
18665
|
|
|
@@ -25744,6 +25767,173 @@
|
|
|
25744
25767
|
return text || text === 0; // accept numerical 0 as label text
|
|
25745
25768
|
}
|
|
25746
25769
|
|
|
25770
|
+
var assignmentRxp = /^([a-z0-9_+-]+)=(?!=)(.*)$/i; // exclude ==
|
|
25771
|
+
|
|
25772
|
+
function splitShellTokens(str) {
|
|
25773
|
+
var BAREWORD = `([^'"\\s])+`;
|
|
25774
|
+
var DOUBLE_QUOTE = `"((\\\\"|[^"])*?)"`;
|
|
25775
|
+
var SINGLE_QUOTE = `'((\\\\'|[^'])*?)'`;
|
|
25776
|
+
var rxp = new RegExp('(' + BAREWORD + '|' + SINGLE_QUOTE + '|' + DOUBLE_QUOTE + ')*', 'g');
|
|
25777
|
+
var matches = str.match(rxp) || [];
|
|
25778
|
+
var chunks = matches.filter(function(chunk) {
|
|
25779
|
+
// single backslashes may be present in multiline commands pasted from a makefile, e.g.
|
|
25780
|
+
return !!chunk && chunk != '\\';
|
|
25781
|
+
}).map(utils.trimQuotes);
|
|
25782
|
+
return chunks;
|
|
25783
|
+
}
|
|
25784
|
+
|
|
25785
|
+
function parsePercent(o) {
|
|
25786
|
+
var str = String(o);
|
|
25787
|
+
var isPct = str.indexOf('%') > 0;
|
|
25788
|
+
var pct;
|
|
25789
|
+
if (isPct) {
|
|
25790
|
+
pct = Number(str.replace('%', '')) / 100;
|
|
25791
|
+
} else {
|
|
25792
|
+
pct = Number(str);
|
|
25793
|
+
}
|
|
25794
|
+
if (!(pct >= 0 && pct <= 1)) {
|
|
25795
|
+
stop$1(utils.format("Invalid percentage: %s", str));
|
|
25796
|
+
}
|
|
25797
|
+
return pct;
|
|
25798
|
+
}
|
|
25799
|
+
|
|
25800
|
+
function parseNumberList(token) {
|
|
25801
|
+
return token.split(',').map(parseFloat);
|
|
25802
|
+
}
|
|
25803
|
+
|
|
25804
|
+
// Split comma-delimited list, trim quotes from entire list and
|
|
25805
|
+
// individual members
|
|
25806
|
+
function parseStringList(token) {
|
|
25807
|
+
var delim = ',';
|
|
25808
|
+
var list = splitOptionList(token, delim);
|
|
25809
|
+
if (list.length == 1) {
|
|
25810
|
+
list = splitOptionList(list[0], delim);
|
|
25811
|
+
}
|
|
25812
|
+
return list;
|
|
25813
|
+
}
|
|
25814
|
+
|
|
25815
|
+
// Accept spaces and/or commas as delimiters
|
|
25816
|
+
function parseColorList(token) {
|
|
25817
|
+
var delim = ', ';
|
|
25818
|
+
// accept rgb(0 0 0) rgb(0,0,0) rgb(0, 0, 0)
|
|
25819
|
+
var token2 = token.replace(/[ ,] *(?=[^(]*\))/g, '~~~'); // kludge: protect rgba() functions from being split apart
|
|
25820
|
+
var list = splitOptionList(token2, delim);
|
|
25821
|
+
if (list.length == 1) {
|
|
25822
|
+
list = splitOptionList(list[0], delim);
|
|
25823
|
+
}
|
|
25824
|
+
list = list.map(function(str) {
|
|
25825
|
+
return str.replace(/~~~/g, ',');
|
|
25826
|
+
});
|
|
25827
|
+
return list;
|
|
25828
|
+
}
|
|
25829
|
+
|
|
25830
|
+
// Splits a comma-delimited list into items, for the list-valued options of the
|
|
25831
|
+
// -symbols command (radii=, values=, fills=). Unlike parseStringList(), empty
|
|
25832
|
+
// items are kept, so that a blank item in one list still lines up with its
|
|
25833
|
+
// counterpart in another. Commas nested inside parentheses, brackets, braces
|
|
25834
|
+
// or quotes are not delimiters, so items like rgba(0,0,0,0.5), Math.max(A,B)
|
|
25835
|
+
// and [R, R * 0.5] survive as single items.
|
|
25836
|
+
function splitListItems(str) {
|
|
25837
|
+
var items = [];
|
|
25838
|
+
var start = 0;
|
|
25839
|
+
var depth = 0;
|
|
25840
|
+
var quote = '';
|
|
25841
|
+
var c;
|
|
25842
|
+
for (var i=0, n=str.length; i<n; i++) {
|
|
25843
|
+
c = str[i];
|
|
25844
|
+
if (quote) {
|
|
25845
|
+
if (c === quote) quote = '';
|
|
25846
|
+
} else if (c === '"' || c === "'") {
|
|
25847
|
+
quote = c;
|
|
25848
|
+
} else if (c === '(' || c === '[' || c === '{') {
|
|
25849
|
+
depth++;
|
|
25850
|
+
} else if (c === ')' || c === ']' || c === '}') {
|
|
25851
|
+
depth--;
|
|
25852
|
+
} else if (c === ',' && depth <= 0) {
|
|
25853
|
+
items.push(str.slice(start, i));
|
|
25854
|
+
start = i + 1;
|
|
25855
|
+
}
|
|
25856
|
+
}
|
|
25857
|
+
items.push(str.slice(start));
|
|
25858
|
+
return items.map(function(item) {
|
|
25859
|
+
return utils.trimQuotes(item.trim());
|
|
25860
|
+
});
|
|
25861
|
+
}
|
|
25862
|
+
|
|
25863
|
+
function cleanArgv(argv) {
|
|
25864
|
+
// Note: original trim caused some quoted spaces to be removed
|
|
25865
|
+
// (e.g. bash shell seems to convert [delimiter=" "] to [delimiter= ],
|
|
25866
|
+
// which then got trimmed to [delimiter=] below)
|
|
25867
|
+
//// argv = argv.map(function(s) {return s.trim();}); // trim whitespace
|
|
25868
|
+
|
|
25869
|
+
// Updated: don't trim space from tokens like [delimeter= ]
|
|
25870
|
+
argv = argv.map(function(s) {
|
|
25871
|
+
if (!/= $/.test(s)) {
|
|
25872
|
+
s = utils.rtrim(s);
|
|
25873
|
+
}
|
|
25874
|
+
s = utils.ltrim(s);
|
|
25875
|
+
return s;
|
|
25876
|
+
});
|
|
25877
|
+
argv = argv.filter(function(s) {return s !== '';}); // remove empty tokens
|
|
25878
|
+
// Note: removing trimQuotes() call... now, strings like 'name="Meg"' will no longer
|
|
25879
|
+
// be parsed the same way as name=Meg and name="Meg"
|
|
25880
|
+
//// argv = argv.map(utils.trimQuotes); // remove one level of single or dbl quotes
|
|
25881
|
+
return argv;
|
|
25882
|
+
}
|
|
25883
|
+
|
|
25884
|
+
function splitOptionList(str, delimChars) {
|
|
25885
|
+
var BAREWORD = '([^' + delimChars + '\'"][^' + delimChars + ']*)'; // TODO: make safer
|
|
25886
|
+
var DOUBLE_QUOTE = '"((\\\\"|[^"])*?)"';
|
|
25887
|
+
var SINGLE_QUOTE = '\'((\\\\\'|[^\'])*?)\'';
|
|
25888
|
+
var rxp = new RegExp('^(' + BAREWORD + '|' + SINGLE_QUOTE + '|' + DOUBLE_QUOTE + ')([' + delimChars + ']+|$)');
|
|
25889
|
+
var chunks = [];
|
|
25890
|
+
var match;
|
|
25891
|
+
while ((match = rxp.exec(str)) !== null) {
|
|
25892
|
+
chunks.push(match[1]);
|
|
25893
|
+
str = str.substr(match[0].length);
|
|
25894
|
+
}
|
|
25895
|
+
return chunks.filter(function(chunk) {
|
|
25896
|
+
return !!chunk && chunk != '\\';
|
|
25897
|
+
}).map(utils.trimQuotes);
|
|
25898
|
+
}
|
|
25899
|
+
|
|
25900
|
+
// Prepare a value to be used as an option value.
|
|
25901
|
+
// Places quotes around strings containing spaces.
|
|
25902
|
+
// e.g. converts Layer 1 -> "Layer 1"
|
|
25903
|
+
// for use in contexts like: name="Layer 1"
|
|
25904
|
+
function formatOptionValue(val) {
|
|
25905
|
+
val = String(val);
|
|
25906
|
+
if (val.indexOf(' ') > -1) {
|
|
25907
|
+
val = JSON.stringify(val); // quote ids with spaces
|
|
25908
|
+
}
|
|
25909
|
+
return val;
|
|
25910
|
+
}
|
|
25911
|
+
|
|
25912
|
+
function isAssignment(token) {
|
|
25913
|
+
return assignmentRxp.test(token);
|
|
25914
|
+
}
|
|
25915
|
+
|
|
25916
|
+
function splitAssignment(token) {
|
|
25917
|
+
var match = assignmentRxp.exec(token),
|
|
25918
|
+
name = match[1],
|
|
25919
|
+
val = utils.trimQuotes(match[2]);
|
|
25920
|
+
return [name, val];
|
|
25921
|
+
}
|
|
25922
|
+
|
|
25923
|
+
var OptionParsingUtils = /*#__PURE__*/Object.freeze({
|
|
25924
|
+
__proto__: null,
|
|
25925
|
+
cleanArgv: cleanArgv,
|
|
25926
|
+
formatOptionValue: formatOptionValue,
|
|
25927
|
+
isAssignment: isAssignment,
|
|
25928
|
+
parseColorList: parseColorList,
|
|
25929
|
+
parseNumberList: parseNumberList,
|
|
25930
|
+
parsePercent: parsePercent,
|
|
25931
|
+
parseStringList: parseStringList,
|
|
25932
|
+
splitAssignment: splitAssignment,
|
|
25933
|
+
splitListItems: splitListItems,
|
|
25934
|
+
splitShellTokens: splitShellTokens
|
|
25935
|
+
});
|
|
25936
|
+
|
|
25747
25937
|
/* example patterns
|
|
25748
25938
|
hatches 1px black 1px red 1px white
|
|
25749
25939
|
1px black 1px red 1px white // same as above (hatches is default)
|
|
@@ -26035,7 +26225,10 @@
|
|
|
26035
26225
|
length: 'number', // e.g. arrow length
|
|
26036
26226
|
rotation: 'number',
|
|
26037
26227
|
radius: 'number',
|
|
26038
|
-
radii: null, //
|
|
26228
|
+
radii: null, // (ring) list, see symbolListProperties
|
|
26229
|
+
hole: 'number', // (pie) radius of the hole in a donut
|
|
26230
|
+
values: null, // (pie) list, see symbolListProperties
|
|
26231
|
+
fills: null, // (pie) list, see symbolListProperties
|
|
26039
26232
|
flipped: 'boolean',
|
|
26040
26233
|
rotated: 'boolean',
|
|
26041
26234
|
direction: 'number',
|
|
@@ -26054,6 +26247,18 @@
|
|
|
26054
26247
|
effect: null // e.g. "fade"
|
|
26055
26248
|
}, stylePropertyTypes);
|
|
26056
26249
|
|
|
26250
|
+
// Options that take a list of values -- one for each part of a multi-part
|
|
26251
|
+
// symbol -- instead of a single value. Each item in the list is resolved
|
|
26252
|
+
// separately, so an item can be a literal value, a field name or an
|
|
26253
|
+
// expression. The type hint for fills= is null on purpose: an item that isn't
|
|
26254
|
+
// a recognizable color is passed along as a literal string, so that a symbol
|
|
26255
|
+
// can leave that part unfilled instead of the command failing.
|
|
26256
|
+
var symbolListProperties = {
|
|
26257
|
+
radii: 'number',
|
|
26258
|
+
values: 'number',
|
|
26259
|
+
fills: null
|
|
26260
|
+
};
|
|
26261
|
+
|
|
26057
26262
|
var commonProperties = 'css,class,opacity,stroke,stroke-width,stroke-dasharray,stroke-opacity,fill-opacity,vector-effect'.split(',');
|
|
26058
26263
|
|
|
26059
26264
|
var propertiesBySymbolType = {
|
|
@@ -26129,7 +26334,9 @@
|
|
|
26129
26334
|
return;
|
|
26130
26335
|
}
|
|
26131
26336
|
var val = opts[optName];
|
|
26132
|
-
functions[svgName] =
|
|
26337
|
+
functions[svgName] = svgName in symbolListProperties ?
|
|
26338
|
+
getSymbolListAccessor(val, svgName, lyr) :
|
|
26339
|
+
getSymbolPropertyAccessor(val, svgName, lyr);
|
|
26133
26340
|
properties.push(svgName);
|
|
26134
26341
|
});
|
|
26135
26342
|
|
|
@@ -26159,6 +26366,21 @@
|
|
|
26159
26366
|
return getPropertyAccessor(val, symbolPropertyTypes[svgName], lyr, svgName);
|
|
26160
26367
|
}
|
|
26161
26368
|
|
|
26369
|
+
// Returns a function that maps a feature id to an array of values, for options
|
|
26370
|
+
// like values= and fills= that describe the parts of a multi-part symbol.
|
|
26371
|
+
// The option may be given as an array or as a comma-separated list.
|
|
26372
|
+
function getSymbolListAccessor(val, svgName, lyr) {
|
|
26373
|
+
var typeHint = symbolListProperties[svgName];
|
|
26374
|
+
var items = Array.isArray(val) ? val : splitListItems(String(val));
|
|
26375
|
+
var accessors = items.map(function(item) {
|
|
26376
|
+
return getPropertyAccessor(item, typeHint, lyr, svgName);
|
|
26377
|
+
});
|
|
26378
|
+
return function(id) {
|
|
26379
|
+
return accessors.map(function(accessor) {
|
|
26380
|
+
return accessor(id);
|
|
26381
|
+
});
|
|
26382
|
+
};
|
|
26383
|
+
}
|
|
26162
26384
|
// Returns a function that maps a feature id to a property value. The value may
|
|
26163
26385
|
// be given as a literal, as the name of a data field, or as a JS expression
|
|
26164
26386
|
// evaluated against each feature.
|
|
@@ -26295,6 +26517,7 @@
|
|
|
26295
26517
|
getLabelPositionStyle: getLabelPositionStyle,
|
|
26296
26518
|
getPropertyAccessor: getPropertyAccessor,
|
|
26297
26519
|
getSymbolDataAccessor: getSymbolDataAccessor,
|
|
26520
|
+
getSymbolListAccessor: getSymbolListAccessor,
|
|
26298
26521
|
getSymbolPropertyAccessor: getSymbolPropertyAccessor,
|
|
26299
26522
|
isSupportedSvgStyleProperty: isSupportedSvgStyleProperty,
|
|
26300
26523
|
isSvgClassName: isSvgClassName,
|
|
@@ -37978,139 +38201,6 @@ ${svg}
|
|
|
37978
38201
|
}
|
|
37979
38202
|
}
|
|
37980
38203
|
|
|
37981
|
-
var assignmentRxp = /^([a-z0-9_+-]+)=(?!=)(.*)$/i; // exclude ==
|
|
37982
|
-
|
|
37983
|
-
function splitShellTokens(str) {
|
|
37984
|
-
var BAREWORD = `([^'"\\s])+`;
|
|
37985
|
-
var DOUBLE_QUOTE = `"((\\\\"|[^"])*?)"`;
|
|
37986
|
-
var SINGLE_QUOTE = `'((\\\\'|[^'])*?)'`;
|
|
37987
|
-
var rxp = new RegExp('(' + BAREWORD + '|' + SINGLE_QUOTE + '|' + DOUBLE_QUOTE + ')*', 'g');
|
|
37988
|
-
var matches = str.match(rxp) || [];
|
|
37989
|
-
var chunks = matches.filter(function(chunk) {
|
|
37990
|
-
// single backslashes may be present in multiline commands pasted from a makefile, e.g.
|
|
37991
|
-
return !!chunk && chunk != '\\';
|
|
37992
|
-
}).map(utils.trimQuotes);
|
|
37993
|
-
return chunks;
|
|
37994
|
-
}
|
|
37995
|
-
|
|
37996
|
-
function parsePercent(o) {
|
|
37997
|
-
var str = String(o);
|
|
37998
|
-
var isPct = str.indexOf('%') > 0;
|
|
37999
|
-
var pct;
|
|
38000
|
-
if (isPct) {
|
|
38001
|
-
pct = Number(str.replace('%', '')) / 100;
|
|
38002
|
-
} else {
|
|
38003
|
-
pct = Number(str);
|
|
38004
|
-
}
|
|
38005
|
-
if (!(pct >= 0 && pct <= 1)) {
|
|
38006
|
-
stop$1(utils.format("Invalid percentage: %s", str));
|
|
38007
|
-
}
|
|
38008
|
-
return pct;
|
|
38009
|
-
}
|
|
38010
|
-
|
|
38011
|
-
function parseNumberList(token) {
|
|
38012
|
-
return token.split(',').map(parseFloat);
|
|
38013
|
-
}
|
|
38014
|
-
|
|
38015
|
-
// Split comma-delimited list, trim quotes from entire list and
|
|
38016
|
-
// individual members
|
|
38017
|
-
function parseStringList(token) {
|
|
38018
|
-
var delim = ',';
|
|
38019
|
-
var list = splitOptionList(token, delim);
|
|
38020
|
-
if (list.length == 1) {
|
|
38021
|
-
list = splitOptionList(list[0], delim);
|
|
38022
|
-
}
|
|
38023
|
-
return list;
|
|
38024
|
-
}
|
|
38025
|
-
|
|
38026
|
-
// Accept spaces and/or commas as delimiters
|
|
38027
|
-
function parseColorList(token) {
|
|
38028
|
-
var delim = ', ';
|
|
38029
|
-
// accept rgb(0 0 0) rgb(0,0,0) rgb(0, 0, 0)
|
|
38030
|
-
var token2 = token.replace(/[ ,] *(?=[^(]*\))/g, '~~~'); // kludge: protect rgba() functions from being split apart
|
|
38031
|
-
var list = splitOptionList(token2, delim);
|
|
38032
|
-
if (list.length == 1) {
|
|
38033
|
-
list = splitOptionList(list[0], delim);
|
|
38034
|
-
}
|
|
38035
|
-
list = list.map(function(str) {
|
|
38036
|
-
return str.replace(/~~~/g, ',');
|
|
38037
|
-
});
|
|
38038
|
-
return list;
|
|
38039
|
-
}
|
|
38040
|
-
|
|
38041
|
-
function cleanArgv(argv) {
|
|
38042
|
-
// Note: original trim caused some quoted spaces to be removed
|
|
38043
|
-
// (e.g. bash shell seems to convert [delimiter=" "] to [delimiter= ],
|
|
38044
|
-
// which then got trimmed to [delimiter=] below)
|
|
38045
|
-
//// argv = argv.map(function(s) {return s.trim();}); // trim whitespace
|
|
38046
|
-
|
|
38047
|
-
// Updated: don't trim space from tokens like [delimeter= ]
|
|
38048
|
-
argv = argv.map(function(s) {
|
|
38049
|
-
if (!/= $/.test(s)) {
|
|
38050
|
-
s = utils.rtrim(s);
|
|
38051
|
-
}
|
|
38052
|
-
s = utils.ltrim(s);
|
|
38053
|
-
return s;
|
|
38054
|
-
});
|
|
38055
|
-
argv = argv.filter(function(s) {return s !== '';}); // remove empty tokens
|
|
38056
|
-
// Note: removing trimQuotes() call... now, strings like 'name="Meg"' will no longer
|
|
38057
|
-
// be parsed the same way as name=Meg and name="Meg"
|
|
38058
|
-
//// argv = argv.map(utils.trimQuotes); // remove one level of single or dbl quotes
|
|
38059
|
-
return argv;
|
|
38060
|
-
}
|
|
38061
|
-
|
|
38062
|
-
function splitOptionList(str, delimChars) {
|
|
38063
|
-
var BAREWORD = '([^' + delimChars + '\'"][^' + delimChars + ']*)'; // TODO: make safer
|
|
38064
|
-
var DOUBLE_QUOTE = '"((\\\\"|[^"])*?)"';
|
|
38065
|
-
var SINGLE_QUOTE = '\'((\\\\\'|[^\'])*?)\'';
|
|
38066
|
-
var rxp = new RegExp('^(' + BAREWORD + '|' + SINGLE_QUOTE + '|' + DOUBLE_QUOTE + ')([' + delimChars + ']+|$)');
|
|
38067
|
-
var chunks = [];
|
|
38068
|
-
var match;
|
|
38069
|
-
while ((match = rxp.exec(str)) !== null) {
|
|
38070
|
-
chunks.push(match[1]);
|
|
38071
|
-
str = str.substr(match[0].length);
|
|
38072
|
-
}
|
|
38073
|
-
return chunks.filter(function(chunk) {
|
|
38074
|
-
return !!chunk && chunk != '\\';
|
|
38075
|
-
}).map(utils.trimQuotes);
|
|
38076
|
-
}
|
|
38077
|
-
|
|
38078
|
-
// Prepare a value to be used as an option value.
|
|
38079
|
-
// Places quotes around strings containing spaces.
|
|
38080
|
-
// e.g. converts Layer 1 -> "Layer 1"
|
|
38081
|
-
// for use in contexts like: name="Layer 1"
|
|
38082
|
-
function formatOptionValue(val) {
|
|
38083
|
-
val = String(val);
|
|
38084
|
-
if (val.indexOf(' ') > -1) {
|
|
38085
|
-
val = JSON.stringify(val); // quote ids with spaces
|
|
38086
|
-
}
|
|
38087
|
-
return val;
|
|
38088
|
-
}
|
|
38089
|
-
|
|
38090
|
-
function isAssignment(token) {
|
|
38091
|
-
return assignmentRxp.test(token);
|
|
38092
|
-
}
|
|
38093
|
-
|
|
38094
|
-
function splitAssignment(token) {
|
|
38095
|
-
var match = assignmentRxp.exec(token),
|
|
38096
|
-
name = match[1],
|
|
38097
|
-
val = utils.trimQuotes(match[2]);
|
|
38098
|
-
return [name, val];
|
|
38099
|
-
}
|
|
38100
|
-
|
|
38101
|
-
var OptionParsingUtils = /*#__PURE__*/Object.freeze({
|
|
38102
|
-
__proto__: null,
|
|
38103
|
-
cleanArgv: cleanArgv,
|
|
38104
|
-
formatOptionValue: formatOptionValue,
|
|
38105
|
-
isAssignment: isAssignment,
|
|
38106
|
-
parseColorList: parseColorList,
|
|
38107
|
-
parseNumberList: parseNumberList,
|
|
38108
|
-
parsePercent: parsePercent,
|
|
38109
|
-
parseStringList: parseStringList,
|
|
38110
|
-
splitAssignment: splitAssignment,
|
|
38111
|
-
splitShellTokens: splitShellTokens
|
|
38112
|
-
});
|
|
38113
|
-
|
|
38114
38204
|
function CommandParser() {
|
|
38115
38205
|
var commandRxp = /^--?([a-z][\w-]*)$/i,
|
|
38116
38206
|
invalidCommandRxp = /^--?[a-z][\w-]*[=]/i, // e.g. -target=A // could be more general
|
|
@@ -38290,6 +38380,7 @@ ${svg}
|
|
|
38290
38380
|
// Try to read an option for command @cmdDef from @argv
|
|
38291
38381
|
function readOption(cmd, argv, cmdDef) {
|
|
38292
38382
|
var token = argv.shift(),
|
|
38383
|
+
assignedValue = null,
|
|
38293
38384
|
optName, optDef, parts;
|
|
38294
38385
|
|
|
38295
38386
|
if (isAssignment(token)) {
|
|
@@ -38299,7 +38390,10 @@ ${svg}
|
|
|
38299
38390
|
if (!optDef) ; else if (optDef.type == 'flag' || optDef.assign_to) {
|
|
38300
38391
|
stop$1("-" + cmdDef.name + " " + parts[0] + " option doesn't take a value");
|
|
38301
38392
|
} else {
|
|
38302
|
-
argv
|
|
38393
|
+
// Keep the value out of argv: it was written as part of the token,
|
|
38394
|
+
// so it is a value even when it looks like a command name
|
|
38395
|
+
// (e.g. hole=-size, dx=-OFFSET).
|
|
38396
|
+
assignedValue = parts[1];
|
|
38303
38397
|
}
|
|
38304
38398
|
} else {
|
|
38305
38399
|
// try to parse as a flag option,
|
|
@@ -38324,6 +38418,8 @@ ${svg}
|
|
|
38324
38418
|
cmd.options[optDef.assign_to] = optDef.name;
|
|
38325
38419
|
} else if (optDef.type == 'flag') {
|
|
38326
38420
|
cmd.options[optName] = true;
|
|
38421
|
+
} else if (assignedValue !== null) {
|
|
38422
|
+
cmd.options[optName] = parseOptionValue(assignedValue, optDef);
|
|
38327
38423
|
} else {
|
|
38328
38424
|
cmd.options[optName] = readOptionValue(argv, optDef);
|
|
38329
38425
|
}
|
|
@@ -40287,9 +40383,9 @@ ${svg}
|
|
|
40287
40383
|
.option('no-replace', noReplaceOpt);
|
|
40288
40384
|
|
|
40289
40385
|
parser.command('grid')
|
|
40290
|
-
.describe('create a grid of square, hexagonal, rhombus or
|
|
40386
|
+
.describe('create a grid of square, hexagonal, rhombus, triangle or cairo polygons')
|
|
40291
40387
|
.option('type', {
|
|
40292
|
-
describe: 'square, square2, hex, hex2, rhombus, rhombus2, triangle or
|
|
40388
|
+
describe: 'square, square2, hex, hex2, rhombus, rhombus2, triangle, triangle2 or cairo (default is square)'
|
|
40293
40389
|
})
|
|
40294
40390
|
.option('interval', {
|
|
40295
40391
|
describe: 'side length (e.g. 500m, 12km)',
|
|
@@ -40311,6 +40407,10 @@ ${svg}
|
|
|
40311
40407
|
describe: 'scale factor for cells, between 0 and 2',
|
|
40312
40408
|
type: 'number'
|
|
40313
40409
|
})
|
|
40410
|
+
.option('rotate', {
|
|
40411
|
+
describe: 'rotation angle in degrees (default is 0)',
|
|
40412
|
+
type: 'number'
|
|
40413
|
+
})
|
|
40314
40414
|
// .option('bbox', {
|
|
40315
40415
|
// type: 'bbox',
|
|
40316
40416
|
// describe: 'xmin,ymin,xmax,ymax (default is bbox of data)'
|
|
@@ -41028,7 +41128,7 @@ ${svg}
|
|
|
41028
41128
|
.option('target', targetOpt);
|
|
41029
41129
|
|
|
41030
41130
|
parser.command('repel')
|
|
41031
|
-
.describe('move overlapping
|
|
41131
|
+
.describe('move overlapping point symbols apart')
|
|
41032
41132
|
.option('width', {
|
|
41033
41133
|
describe: 'display width of the layer in pixels (required if no frame)',
|
|
41034
41134
|
type: 'number'
|
|
@@ -41039,6 +41139,9 @@ ${svg}
|
|
|
41039
41139
|
.option('padding', {
|
|
41040
41140
|
describe: 'pixels of clearance around each symbol; may vary (default is 0)'
|
|
41041
41141
|
})
|
|
41142
|
+
.option('margin', {
|
|
41143
|
+
describe: 'pixels of clearance between two symbols; may vary (default is 0)'
|
|
41144
|
+
})
|
|
41042
41145
|
.option('ticks', {
|
|
41043
41146
|
describe: 'number of solver passes (default is 100)',
|
|
41044
41147
|
type: 'number'
|
|
@@ -41058,7 +41161,7 @@ ${svg}
|
|
|
41058
41161
|
parser.command('symbols')
|
|
41059
41162
|
.describe('symbolize points as arrows, circles, stars, polygons, etc.')
|
|
41060
41163
|
.option('type', {
|
|
41061
|
-
describe: 'types: arrow, circle, square, star, polygon, ring'
|
|
41164
|
+
describe: 'types: arrow, circle, square, star, polygon, ring, pie'
|
|
41062
41165
|
})
|
|
41063
41166
|
.option('stroke', {})
|
|
41064
41167
|
.option('stroke-width', {})
|
|
@@ -41117,6 +41220,15 @@ ${svg}
|
|
|
41117
41220
|
.option('radii', {
|
|
41118
41221
|
describe: '(ring) comma-sep. list of concentric radii, ascending order'
|
|
41119
41222
|
})
|
|
41223
|
+
.option('hole', {
|
|
41224
|
+
describe: '(pie) radius of the hole in a donut symbol'
|
|
41225
|
+
})
|
|
41226
|
+
.option('values', {
|
|
41227
|
+
describe: '(pie) comma-sep. list of wedge values (numbers or fields)'
|
|
41228
|
+
})
|
|
41229
|
+
.option('fills', {
|
|
41230
|
+
describe: '(pie) comma-sep. list of wedge fill colors'
|
|
41231
|
+
})
|
|
41120
41232
|
.option('arrow-style', {
|
|
41121
41233
|
describe: '(arrow) options: stick, standard (default is standard)'
|
|
41122
41234
|
})
|
|
@@ -44368,9 +44480,10 @@ ${svg}
|
|
|
44368
44480
|
({gpkg, tmpPath} = await openGeoPackage(input, geopackage));
|
|
44369
44481
|
var availableLayers;
|
|
44370
44482
|
var filterApplied;
|
|
44483
|
+
var unreadableLayers;
|
|
44371
44484
|
try {
|
|
44372
44485
|
try {
|
|
44373
|
-
({datasets, availableLayers, filterApplied} = readFeatureTableDatasets(gpkg, opts));
|
|
44486
|
+
({datasets, availableLayers, filterApplied, unreadableLayers} = readFeatureTableDatasets(gpkg, opts));
|
|
44374
44487
|
} catch (e) {
|
|
44375
44488
|
if (!runningInBrowser() && isLocalCsProjError(e)) {
|
|
44376
44489
|
gpkg.close();
|
|
@@ -44383,7 +44496,7 @@ ${svg}
|
|
|
44383
44496
|
throw e;
|
|
44384
44497
|
}
|
|
44385
44498
|
gpkg = await geopackage.GeoPackageAPI.open(tmpPath);
|
|
44386
|
-
({datasets, availableLayers, filterApplied} = readFeatureTableDatasets(gpkg, opts));
|
|
44499
|
+
({datasets, availableLayers, filterApplied, unreadableLayers} = readFeatureTableDatasets(gpkg, opts));
|
|
44387
44500
|
} else {
|
|
44388
44501
|
throw e;
|
|
44389
44502
|
}
|
|
@@ -44398,6 +44511,7 @@ ${svg}
|
|
|
44398
44511
|
layers: [{name: '', data: null}],
|
|
44399
44512
|
info: {
|
|
44400
44513
|
_gpkg_available_layers: availableLayers,
|
|
44514
|
+
_gpkg_unreadable_layers: unreadableLayers,
|
|
44401
44515
|
_gpkg_placeholder: !!filterApplied
|
|
44402
44516
|
}
|
|
44403
44517
|
};
|
|
@@ -44409,6 +44523,7 @@ ${svg}
|
|
|
44409
44523
|
mergedArr.forEach(function(ds) {
|
|
44410
44524
|
ds.info = ds.info || {};
|
|
44411
44525
|
ds.info._gpkg_available_layers = availableLayers;
|
|
44526
|
+
ds.info._gpkg_unreadable_layers = unreadableLayers;
|
|
44412
44527
|
});
|
|
44413
44528
|
return merged;
|
|
44414
44529
|
}
|
|
@@ -44534,10 +44649,38 @@ ${svg}
|
|
|
44534
44649
|
var availableLayers = gpkg.getFeatureTables() || [];
|
|
44535
44650
|
var tables = filterGeoPackageTables(availableLayers, selected);
|
|
44536
44651
|
var filterApplied = Array.isArray(selected) && selected.length > 0;
|
|
44537
|
-
var
|
|
44538
|
-
|
|
44652
|
+
var unreadableLayers = [];
|
|
44653
|
+
var datasets = [];
|
|
44654
|
+
tables.forEach(function(table) {
|
|
44655
|
+
try {
|
|
44656
|
+
datasets.push(readFeatureTable(gpkg, table, opts));
|
|
44657
|
+
} catch (e) {
|
|
44658
|
+
// The caller recovers from a CRS parsing failure by rewriting the file's
|
|
44659
|
+
// CRS metadata and re-reading, so that error must not be swallowed here.
|
|
44660
|
+
if (isLocalCsProjError(e)) throw e;
|
|
44661
|
+
unreadableLayers.push(table);
|
|
44662
|
+
warn(formatUnreadableTableMessage(table, e));
|
|
44663
|
+
}
|
|
44539
44664
|
});
|
|
44540
|
-
return {
|
|
44665
|
+
return {
|
|
44666
|
+
datasets: datasets,
|
|
44667
|
+
availableLayers: availableLayers,
|
|
44668
|
+
filterApplied: filterApplied,
|
|
44669
|
+
unreadableLayers: unreadableLayers
|
|
44670
|
+
};
|
|
44671
|
+
}
|
|
44672
|
+
|
|
44673
|
+
// A GeoPackage can advertise feature tables that aren't readable. USGS National
|
|
44674
|
+
// Map vector products, for instance, list a CLIPPOLY table in gpkg_contents and
|
|
44675
|
+
// gpkg_geometry_columns after dropping the table itself from the file. GDAL
|
|
44676
|
+
// skips such layers with a warning; do the same rather than failing the whole
|
|
44677
|
+
// import over one bad table.
|
|
44678
|
+
function formatUnreadableTableMessage(table, err) {
|
|
44679
|
+
var msg = err && err.message ? String(err.message) : '';
|
|
44680
|
+
var reason = /^Table does not exist:/.test(msg) ?
|
|
44681
|
+
'the table is listed in the GeoPackage metadata but is missing from the file' :
|
|
44682
|
+
msg || 'unknown error';
|
|
44683
|
+
return 'Skipping GeoPackage layer "' + table + '": ' + reason;
|
|
44541
44684
|
}
|
|
44542
44685
|
|
|
44543
44686
|
function getSelectedGeoPackageLayers(opts) {
|
|
@@ -46510,12 +46653,12 @@ ${svg}
|
|
|
46510
46653
|
var coords = path.coords;
|
|
46511
46654
|
if (coords.length < 2) return;
|
|
46512
46655
|
if (forcePolylinePaths) {
|
|
46513
|
-
lines.push(path.closed || pointsEqual$1(coords[0], coords[coords.length - 1]) ? closeRing(coords) : coords);
|
|
46656
|
+
lines.push(path.closed || pointsEqual$1(coords[0], coords[coords.length - 1]) ? closeRing$1(coords) : coords);
|
|
46514
46657
|
return;
|
|
46515
46658
|
}
|
|
46516
46659
|
if (path.closed || pointsEqual$1(coords[0], coords[coords.length - 1])) {
|
|
46517
46660
|
if (coords.length >= 3) {
|
|
46518
|
-
rings.push(closeRing(coords));
|
|
46661
|
+
rings.push(closeRing$1(coords));
|
|
46519
46662
|
}
|
|
46520
46663
|
} else {
|
|
46521
46664
|
lines.push(coords);
|
|
@@ -46819,7 +46962,7 @@ ${svg}
|
|
|
46819
46962
|
return /translate\(/i.test(String(str || ''));
|
|
46820
46963
|
}
|
|
46821
46964
|
|
|
46822
|
-
function closeRing(coords) {
|
|
46965
|
+
function closeRing$1(coords) {
|
|
46823
46966
|
var ring = coords.map(function(p) { return [p[0], p[1]]; });
|
|
46824
46967
|
if (!pointsEqual$1(ring[0], ring[ring.length - 1])) {
|
|
46825
46968
|
ring.push([ring[0][0], ring[0][1]]);
|
|
@@ -47296,15 +47439,12 @@ ${svg}
|
|
|
47296
47439
|
var files = opts.files || [];
|
|
47297
47440
|
var dataset, datasets, target;
|
|
47298
47441
|
|
|
47442
|
+
// copy opts, so parameters can be modified within this command
|
|
47443
|
+
opts = Object.assign({}, opts);
|
|
47444
|
+
opts.input = Object.assign({}, opts.input); // make sure we have a cache
|
|
47445
|
+
|
|
47299
47446
|
if (opts.stdin) {
|
|
47300
|
-
|
|
47301
|
-
catalog.addDatasets(datasets);
|
|
47302
|
-
if (datasets.length > 1) {
|
|
47303
|
-
catalog.setDefaultTargets(datasets.map(function(ds) {
|
|
47304
|
-
return {dataset: ds, layers: ds.layers};
|
|
47305
|
-
}));
|
|
47306
|
-
}
|
|
47307
|
-
return normalizeImportedTarget(datasets);
|
|
47447
|
+
files = [readStdinAsFile(opts.input)];
|
|
47308
47448
|
}
|
|
47309
47449
|
|
|
47310
47450
|
if (files.length > 0 === false) {
|
|
@@ -47313,10 +47453,6 @@ ${svg}
|
|
|
47313
47453
|
|
|
47314
47454
|
verbose("Importing: " + files.join(' '));
|
|
47315
47455
|
|
|
47316
|
-
// copy opts, so parameters can be modified within this command
|
|
47317
|
-
opts = Object.assign({}, opts);
|
|
47318
|
-
opts.input = Object.assign({}, opts.input); // make sure we have a cache
|
|
47319
|
-
|
|
47320
47456
|
convertDataObjects(files, opts.input);
|
|
47321
47457
|
|
|
47322
47458
|
files = expandFiles(files, opts.input);
|
|
@@ -47397,6 +47533,41 @@ ${svg}
|
|
|
47397
47533
|
return obj.target;
|
|
47398
47534
|
}
|
|
47399
47535
|
|
|
47536
|
+
// Read the stdin stream into @cache and return the filename to import it as.
|
|
47537
|
+
// A zipped dataset is given a .zip or .kmz name, so that it is unpacked by
|
|
47538
|
+
// expandFiles() like an archive passed as a file argument. Stdin carries no
|
|
47539
|
+
// filename to identify its contents, so an archive is detected from the ZIP
|
|
47540
|
+
// magic number, and a KMZ is told from an ordinary zip by its doc.kml entry.
|
|
47541
|
+
function readStdinAsFile(cache) {
|
|
47542
|
+
var path = '/dev/stdin';
|
|
47543
|
+
var content = cli.readFile(path, null, cache);
|
|
47544
|
+
var name = path;
|
|
47545
|
+
if (contentLooksZipped(content)) {
|
|
47546
|
+
name = zipContainsDocKml(content) ? 'stdin.kmz' : 'stdin.zip';
|
|
47547
|
+
}
|
|
47548
|
+
cache[name] = content;
|
|
47549
|
+
return name;
|
|
47550
|
+
}
|
|
47551
|
+
|
|
47552
|
+
// Detect the ZIP local file header signature ("PK\x03\x04")
|
|
47553
|
+
function contentLooksZipped(content) {
|
|
47554
|
+
var bytes = content instanceof ArrayBuffer ? new Uint8Array(content) : content;
|
|
47555
|
+
if (utils.isString(bytes) || !bytes || bytes.length < 4) return false;
|
|
47556
|
+
return bytes[0] == 0x50 && bytes[1] == 0x4b && bytes[2] == 0x03 && bytes[3] == 0x04;
|
|
47557
|
+
}
|
|
47558
|
+
|
|
47559
|
+
function zipContainsDocKml(content) {
|
|
47560
|
+
var names;
|
|
47561
|
+
try {
|
|
47562
|
+
names = listZipEntryNames(content);
|
|
47563
|
+
} catch(e) {
|
|
47564
|
+
return false; // let the unzipping step report a malformed archive
|
|
47565
|
+
}
|
|
47566
|
+
return names.some(function(name) {
|
|
47567
|
+
return parseLocalPath(name).filename.toLowerCase() == 'doc.kml';
|
|
47568
|
+
});
|
|
47569
|
+
}
|
|
47570
|
+
|
|
47400
47571
|
function expandFiles(files, cache) {
|
|
47401
47572
|
var files2 = [];
|
|
47402
47573
|
files.forEach(function(file) {
|
|
@@ -47528,6 +47699,7 @@ ${svg}
|
|
|
47528
47699
|
content = cli.readFile(path, encoding || 'utf-8', cache);
|
|
47529
47700
|
fileType = guessInputContentType(content);
|
|
47530
47701
|
if (fileType == 'text' && content.indexOf('\ufffd') > -1) {
|
|
47702
|
+
// invalidate string data that contains the 'replacement character'
|
|
47531
47703
|
fileType = null;
|
|
47532
47704
|
}
|
|
47533
47705
|
}
|
|
@@ -47536,7 +47708,7 @@ ${svg}
|
|
|
47536
47708
|
stop$1(getUnsupportedFileMessage(path));
|
|
47537
47709
|
}
|
|
47538
47710
|
input[fileType] = {filename: path, content: content};
|
|
47539
|
-
content = null;
|
|
47711
|
+
content = null; // for g.c.
|
|
47540
47712
|
if (fileType == 'shp' || fileType == 'dbf') {
|
|
47541
47713
|
readShapefileAuxFiles(path, input, cache);
|
|
47542
47714
|
} else if (isRasterImageInputType(fileType)) {
|
|
@@ -47581,12 +47753,14 @@ ${svg}
|
|
|
47581
47753
|
function validateAndCleanGpkgSelection(datasets, opts) {
|
|
47582
47754
|
var availableSet = new Set();
|
|
47583
47755
|
var importedSet = new Set();
|
|
47756
|
+
var unreadableSet = new Set();
|
|
47584
47757
|
var sawGpkg = false;
|
|
47585
47758
|
datasets.forEach(function(ds) {
|
|
47586
47759
|
var info = ds && ds.info;
|
|
47587
47760
|
if (!info || !Array.isArray(info._gpkg_available_layers)) return;
|
|
47588
47761
|
sawGpkg = true;
|
|
47589
47762
|
info._gpkg_available_layers.forEach(function(name) { availableSet.add(name); });
|
|
47763
|
+
(info._gpkg_unreadable_layers || []).forEach(function(name) { unreadableSet.add(name); });
|
|
47590
47764
|
if (!info._gpkg_placeholder) {
|
|
47591
47765
|
(ds.layers || []).forEach(function(lyr) {
|
|
47592
47766
|
if (lyr && lyr.name) importedSet.add(lyr.name);
|
|
@@ -47594,9 +47768,17 @@ ${svg}
|
|
|
47594
47768
|
}
|
|
47595
47769
|
});
|
|
47596
47770
|
if (sawGpkg && Array.isArray(opts.layers) && opts.layers.length > 0) {
|
|
47597
|
-
var
|
|
47771
|
+
var absent = opts.layers.filter(function(name) {
|
|
47598
47772
|
return !importedSet.has(name);
|
|
47599
47773
|
});
|
|
47774
|
+
// A layer that the file advertises but that can't be parsed is skipped with
|
|
47775
|
+
// a warning during import; report it separately here, because saying it is
|
|
47776
|
+
// missing while also listing it as an existing layer is contradictory.
|
|
47777
|
+
var unreadable = absent.filter(function(name) { return unreadableSet.has(name); });
|
|
47778
|
+
var missing = absent.filter(function(name) { return !unreadableSet.has(name); });
|
|
47779
|
+
if (unreadable.length > 0) {
|
|
47780
|
+
stop$1('Unable to read GeoPackage layer(s): ' + unreadable.join(', '));
|
|
47781
|
+
}
|
|
47600
47782
|
if (missing.length > 0) {
|
|
47601
47783
|
stop$1(
|
|
47602
47784
|
'Missing GeoPackage layer(s): ' + missing.join(', ') + '\n' +
|
|
@@ -47610,6 +47792,7 @@ ${svg}
|
|
|
47610
47792
|
cleaned.forEach(function(ds) {
|
|
47611
47793
|
if (ds && ds.info) {
|
|
47612
47794
|
delete ds.info._gpkg_available_layers;
|
|
47795
|
+
delete ds.info._gpkg_unreadable_layers;
|
|
47613
47796
|
delete ds.info._gpkg_placeholder;
|
|
47614
47797
|
}
|
|
47615
47798
|
});
|
|
@@ -48420,7 +48603,7 @@ ${svg}
|
|
|
48420
48603
|
const resulterrbound = (3 + 8 * epsilon) * epsilon;
|
|
48421
48604
|
|
|
48422
48605
|
// fast_expansion_sum_zeroelim routine from oritinal code
|
|
48423
|
-
function sum(elen, e, flen, f, h) {
|
|
48606
|
+
function sum$1(elen, e, flen, f, h) {
|
|
48424
48607
|
let Q, Qnew, hh, bvirt;
|
|
48425
48608
|
let enow = e[0];
|
|
48426
48609
|
let fnow = f[0];
|
|
@@ -48603,7 +48786,7 @@ ${svg}
|
|
|
48603
48786
|
bvirt = u3 - _j;
|
|
48604
48787
|
u[2] = _j - (u3 - bvirt) + (_i - bvirt);
|
|
48605
48788
|
u[3] = u3;
|
|
48606
|
-
const C1len = sum(4, B$2, 4, u, C1);
|
|
48789
|
+
const C1len = sum$1(4, B$2, 4, u, C1);
|
|
48607
48790
|
|
|
48608
48791
|
s1 = acx * bcytail;
|
|
48609
48792
|
c = splitter * acx;
|
|
@@ -48634,7 +48817,7 @@ ${svg}
|
|
|
48634
48817
|
bvirt = u3 - _j;
|
|
48635
48818
|
u[2] = _j - (u3 - bvirt) + (_i - bvirt);
|
|
48636
48819
|
u[3] = u3;
|
|
48637
|
-
const C2len = sum(C1len, C1, 4, u, C2);
|
|
48820
|
+
const C2len = sum$1(C1len, C1, 4, u, C2);
|
|
48638
48821
|
|
|
48639
48822
|
s1 = acxtail * bcytail;
|
|
48640
48823
|
c = splitter * acxtail;
|
|
@@ -48665,7 +48848,7 @@ ${svg}
|
|
|
48665
48848
|
bvirt = u3 - _j;
|
|
48666
48849
|
u[2] = _j - (u3 - bvirt) + (_i - bvirt);
|
|
48667
48850
|
u[3] = u3;
|
|
48668
|
-
const Dlen = sum(C2len, C2, 4, u, D$1);
|
|
48851
|
+
const Dlen = sum$1(C2len, C2, 4, u, D$1);
|
|
48669
48852
|
|
|
48670
48853
|
return D$1[Dlen - 1];
|
|
48671
48854
|
}
|
|
@@ -74376,7 +74559,7 @@ ${svg}
|
|
|
74376
74559
|
cmd.polygonGrid = function(targetLayers, targetDataset, opts) {
|
|
74377
74560
|
requireProjectedDataset(targetDataset);
|
|
74378
74561
|
var params = getGridParams(targetLayers, targetDataset, opts);
|
|
74379
|
-
var gridDataset = makeGridDataset(params); // grid is a new dataset
|
|
74562
|
+
var gridDataset = makeGridDataset(params, opts); // grid is a new dataset
|
|
74380
74563
|
gridDataset.info = copyDatasetInfo(targetDataset.info);
|
|
74381
74564
|
setOutputLayerName(gridDataset.layers[0], null, 'grid', opts);
|
|
74382
74565
|
if (opts.debug) gridDataset.layers.push(cmd.pointGrid2(targetLayers, targetDataset, opts));
|
|
@@ -74440,35 +74623,67 @@ ${svg}
|
|
|
74440
74623
|
|
|
74441
74624
|
function makeGridDataset(params, opts) {
|
|
74442
74625
|
var geojson, dataset;
|
|
74626
|
+
var rotation = opts.rotate || 0;
|
|
74627
|
+
var genParams = rotation ? getRotatedGridParams(params, rotation) : params;
|
|
74443
74628
|
if (params.type == 'square') {
|
|
74444
|
-
geojson = getSquareGridGeoJSON(getSquareGridCoordinates(
|
|
74629
|
+
geojson = getSquareGridGeoJSON(getSquareGridCoordinates(genParams));
|
|
74445
74630
|
} else if (params.type == 'square2') {
|
|
74446
|
-
geojson = getRotatedSquareGridGeoJSON(
|
|
74631
|
+
geojson = getRotatedSquareGridGeoJSON(genParams);
|
|
74447
74632
|
} else if (params.type == 'hex') {
|
|
74448
|
-
geojson = getHexGridGeoJSON(getHexGridCoordinates(
|
|
74633
|
+
geojson = getHexGridGeoJSON(getHexGridCoordinates(genParams));
|
|
74449
74634
|
} else if (params.type == 'hex2') {
|
|
74450
74635
|
// use rotated grid
|
|
74451
|
-
geojson = getHexGridGeoJSON(getHexGridCoordinates(swapGridParams(
|
|
74636
|
+
geojson = getHexGridGeoJSON(getHexGridCoordinates(swapGridParams(genParams)));
|
|
74452
74637
|
swapPolygonCoords(geojson);
|
|
74453
74638
|
} else if (params.type == 'rhombus') {
|
|
74454
|
-
geojson = getRhombusGridGeoJSON(
|
|
74639
|
+
geojson = getRhombusGridGeoJSON(genParams, false);
|
|
74455
74640
|
} else if (params.type == 'rhombus2') {
|
|
74456
|
-
geojson = getRhombusGridGeoJSON(
|
|
74641
|
+
geojson = getRhombusGridGeoJSON(genParams, true);
|
|
74457
74642
|
} else if (params.type == 'triangle') {
|
|
74458
|
-
geojson = getTriangleGridGeoJSON(
|
|
74643
|
+
geojson = getTriangleGridGeoJSON(genParams, false);
|
|
74459
74644
|
} else if (params.type == 'triangle2') {
|
|
74460
|
-
geojson = getTriangleGridGeoJSON(
|
|
74645
|
+
geojson = getTriangleGridGeoJSON(genParams, true);
|
|
74646
|
+
} else if (params.type == 'cairo') {
|
|
74647
|
+
geojson = getCairoGridGeoJSON(genParams);
|
|
74461
74648
|
} else {
|
|
74462
74649
|
stop$1('Unsupported grid type');
|
|
74463
74650
|
}
|
|
74464
74651
|
scaleGridCells(geojson, params.cellScale);
|
|
74465
74652
|
alignGridToBounds(geojson, params.bbox);
|
|
74653
|
+
if (rotation) {
|
|
74654
|
+
rotateGridCells(geojson, rotation, params.bbox);
|
|
74655
|
+
}
|
|
74466
74656
|
cullGridCells(geojson, params.bbox);
|
|
74467
74657
|
dataset = importGeoJSON(geojson, {});
|
|
74468
74658
|
buildTopology(dataset);
|
|
74469
74659
|
return dataset;
|
|
74470
74660
|
}
|
|
74471
74661
|
|
|
74662
|
+
// Expand the generation rectangle so that, after rotation around the shared
|
|
74663
|
+
// center, the grid still covers the original target bbox.
|
|
74664
|
+
function getRotatedGridParams(params, rotation) {
|
|
74665
|
+
var angle = rotation * Math.PI / 180;
|
|
74666
|
+
var cos = Math.abs(Math.cos(angle));
|
|
74667
|
+
var sin = Math.abs(Math.sin(angle));
|
|
74668
|
+
return utils.defaults({
|
|
74669
|
+
width: params.width * cos + params.height * sin,
|
|
74670
|
+
height: params.width * sin + params.height * cos
|
|
74671
|
+
}, params);
|
|
74672
|
+
}
|
|
74673
|
+
|
|
74674
|
+
function rotateGridCells(geojson, rotation, bbox) {
|
|
74675
|
+
var cx = (bbox[0] + bbox[2]) / 2;
|
|
74676
|
+
var cy = (bbox[1] + bbox[3]) / 2;
|
|
74677
|
+
var transform = getAffineTransform(rotation, 1, [0, 0], [cx, cy]);
|
|
74678
|
+
geojson.geometries.forEach(function(geom) {
|
|
74679
|
+
if (geom.type == 'Polygon') {
|
|
74680
|
+
geom.coordinates[0] = geom.coordinates[0].map(function(xy) {
|
|
74681
|
+
return transform(xy[0], xy[1]);
|
|
74682
|
+
});
|
|
74683
|
+
}
|
|
74684
|
+
});
|
|
74685
|
+
}
|
|
74686
|
+
|
|
74472
74687
|
function swapGridParams(params) {
|
|
74473
74688
|
var bbox = params.bbox;
|
|
74474
74689
|
return utils.defaults({
|
|
@@ -74550,6 +74765,7 @@ ${svg}
|
|
|
74550
74765
|
if (type == 'hex' || type == 'hex2') return 3 * Math.sqrt(3) / 2;
|
|
74551
74766
|
if (type == 'rhombus' || type == 'rhombus2') return Math.sqrt(3) / 2;
|
|
74552
74767
|
if (type == 'triangle' || type == 'triangle2') return Math.sqrt(3) / 4;
|
|
74768
|
+
if (type == 'cairo') return (4 + Math.sqrt(7)) / 4;
|
|
74553
74769
|
stop$1('Unsupported grid type');
|
|
74554
74770
|
}
|
|
74555
74771
|
|
|
@@ -74703,6 +74919,95 @@ ${svg}
|
|
|
74703
74919
|
return geojson;
|
|
74704
74920
|
}
|
|
74705
74921
|
|
|
74922
|
+
// Equilateral type-4 Cairo tessellation. Four-valent (90°) vertices sit on a
|
|
74923
|
+
// square lattice of spacing S = interval * (√7+1)/2; each lattice square
|
|
74924
|
+
// contains a central bar of length `interval`, alternating horizontal and
|
|
74925
|
+
// vertical. Two pentagons share each bar.
|
|
74926
|
+
//
|
|
74927
|
+
// Vertices are computed once and reused. Algebraically equal expressions
|
|
74928
|
+
// such as i*S and (i-1)*S + S are not bit-identical, and independently
|
|
74929
|
+
// computed copies of the same 4-way junction become T-intersections.
|
|
74930
|
+
function getCairoGridGeoJSON(params) {
|
|
74931
|
+
var side = params.interval;
|
|
74932
|
+
var spacing = side * (Math.sqrt(7) + 1) / 2;
|
|
74933
|
+
var halfBar = side / 2;
|
|
74934
|
+
var iMin = -1;
|
|
74935
|
+
var jMin = -1;
|
|
74936
|
+
var iMax = Math.ceil(params.width / spacing) + 1;
|
|
74937
|
+
var jMax = Math.ceil(params.height / spacing) + 1;
|
|
74938
|
+
var lattice = [];
|
|
74939
|
+
var bars = [];
|
|
74940
|
+
var geometries = [];
|
|
74941
|
+
var i, j, y, cx, cy;
|
|
74942
|
+
|
|
74943
|
+
for (j = jMin - 1; j <= jMax + 1; j++) {
|
|
74944
|
+
y = j * spacing;
|
|
74945
|
+
lattice[j] = [];
|
|
74946
|
+
for (i = iMin - 1; i <= iMax + 1; i++) {
|
|
74947
|
+
lattice[j][i] = [i * spacing, y];
|
|
74948
|
+
}
|
|
74949
|
+
}
|
|
74950
|
+
|
|
74951
|
+
for (j = jMin - 1; j < jMax + 1; j++) {
|
|
74952
|
+
bars[j] = [];
|
|
74953
|
+
for (i = iMin - 1; i < iMax + 1; i++) {
|
|
74954
|
+
cx = (i + 0.5) * spacing;
|
|
74955
|
+
cy = (j + 0.5) * spacing;
|
|
74956
|
+
bars[j][i] = (i + j) % 2 === 0 ? {
|
|
74957
|
+
a: [cx - halfBar, cy],
|
|
74958
|
+
b: [cx + halfBar, cy]
|
|
74959
|
+
} : {
|
|
74960
|
+
a: [cx, cy - halfBar],
|
|
74961
|
+
b: [cx, cy + halfBar]
|
|
74962
|
+
};
|
|
74963
|
+
}
|
|
74964
|
+
}
|
|
74965
|
+
|
|
74966
|
+
for (j = jMin; j < jMax; j++) {
|
|
74967
|
+
for (i = iMin; i < iMax; i++) {
|
|
74968
|
+
if ((i + j) % 2 === 0) {
|
|
74969
|
+
appendCairoPentagon(geometries, [
|
|
74970
|
+
lattice[j][i],
|
|
74971
|
+
bars[j - 1][i].b,
|
|
74972
|
+
lattice[j][i + 1],
|
|
74973
|
+
bars[j][i].b,
|
|
74974
|
+
bars[j][i].a
|
|
74975
|
+
]);
|
|
74976
|
+
appendCairoPentagon(geometries, [
|
|
74977
|
+
lattice[j + 1][i],
|
|
74978
|
+
bars[j][i].a,
|
|
74979
|
+
bars[j][i].b,
|
|
74980
|
+
lattice[j + 1][i + 1],
|
|
74981
|
+
bars[j + 1][i].a
|
|
74982
|
+
]);
|
|
74983
|
+
} else {
|
|
74984
|
+
appendCairoPentagon(geometries, [
|
|
74985
|
+
lattice[j][i],
|
|
74986
|
+
bars[j][i].a,
|
|
74987
|
+
bars[j][i].b,
|
|
74988
|
+
lattice[j + 1][i],
|
|
74989
|
+
bars[j][i - 1].b
|
|
74990
|
+
]);
|
|
74991
|
+
appendCairoPentagon(geometries, [
|
|
74992
|
+
lattice[j][i + 1],
|
|
74993
|
+
bars[j][i + 1].a,
|
|
74994
|
+
lattice[j + 1][i + 1],
|
|
74995
|
+
bars[j][i].b,
|
|
74996
|
+
bars[j][i].a
|
|
74997
|
+
]);
|
|
74998
|
+
}
|
|
74999
|
+
}
|
|
75000
|
+
}
|
|
75001
|
+
return {type: 'GeometryCollection', geometries: geometries};
|
|
75002
|
+
}
|
|
75003
|
+
|
|
75004
|
+
function appendCairoPentagon(geometries, verts) {
|
|
75005
|
+
geometries.push({
|
|
75006
|
+
type: 'Polygon',
|
|
75007
|
+
coordinates: [verts.concat([verts[0]])]
|
|
75008
|
+
});
|
|
75009
|
+
}
|
|
75010
|
+
|
|
74706
75011
|
function subdivideHexagon(coords) {
|
|
74707
75012
|
var center = getPolygonRingCenter(coords);
|
|
74708
75013
|
return [
|
|
@@ -75425,16 +75730,180 @@ ${svg}
|
|
|
75425
75730
|
};
|
|
75426
75731
|
}
|
|
75427
75732
|
|
|
75733
|
+
var roundCoord$1 = getRoundingFunction(0.01);
|
|
75734
|
+
|
|
75735
|
+
function getSymbolFillColor(d) {
|
|
75736
|
+
return d.fill || 'magenta';
|
|
75737
|
+
}
|
|
75738
|
+
|
|
75739
|
+
function getSymbolStrokeColor(d) {
|
|
75740
|
+
return d.stroke || d.fill || 'magenta';
|
|
75741
|
+
}
|
|
75742
|
+
|
|
75743
|
+
function applySymbolStyles(sym, d) {
|
|
75744
|
+
if (sym.type == 'polyline') {
|
|
75745
|
+
sym.stroke = getSymbolStrokeColor(d);
|
|
75746
|
+
} else {
|
|
75747
|
+
sym.fill = getSymbolFillColor(d);
|
|
75748
|
+
}
|
|
75749
|
+
if (d.opacity) {
|
|
75750
|
+
sym.opacity = d.opacity;
|
|
75751
|
+
}
|
|
75752
|
+
return sym;
|
|
75753
|
+
}
|
|
75754
|
+
|
|
75755
|
+
function getSymbolRadius(d) {
|
|
75756
|
+
if (d.radius === 0 || d.length === 0 || d.r === 0) return 0;
|
|
75757
|
+
return d.radius || d.length || d.r || 5; // use a default value
|
|
75758
|
+
}
|
|
75759
|
+
|
|
75760
|
+
// Converts the value of a list-valued option into an array of items. The value
|
|
75761
|
+
// arrives either as a whole string (from a direct call, rather than from the
|
|
75762
|
+
// -symbols option accessor) or as an array of items already resolved one by
|
|
75763
|
+
// one. An item that holds an array of its own, as an expression like
|
|
75764
|
+
// [R, R * 0.5] does, is flattened into the list. Items themselves are left
|
|
75765
|
+
// unsplit, because a single item can contain a comma, as rgba() colors do.
|
|
75766
|
+
function toItemList(val) {
|
|
75767
|
+
var items = utils.isString(val) ? splitListItems(val) : [].concat(val);
|
|
75768
|
+
var list = [];
|
|
75769
|
+
for (var i=0; i<items.length; i++) {
|
|
75770
|
+
if (Array.isArray(items[i])) {
|
|
75771
|
+
list = list.concat(items[i]);
|
|
75772
|
+
} else {
|
|
75773
|
+
list.push(items[i]);
|
|
75774
|
+
}
|
|
75775
|
+
}
|
|
75776
|
+
return list;
|
|
75777
|
+
}
|
|
75778
|
+
|
|
75779
|
+
// Like toItemList(), for options that take numbers. An item that is a string
|
|
75780
|
+
// containing commas is split, so that a data field holding a value like "2,4"
|
|
75781
|
+
// works as a list. Values that aren't numbers become NaN, for the caller to
|
|
75782
|
+
// report or ignore.
|
|
75783
|
+
function toNumberList(val) {
|
|
75784
|
+
var list = [];
|
|
75785
|
+
toItemList(val).forEach(function(item) {
|
|
75786
|
+
if (utils.isString(item) && item.indexOf(',') > -1) {
|
|
75787
|
+
list = list.concat(parseNumberList(item));
|
|
75788
|
+
} else {
|
|
75789
|
+
list.push(Number(item));
|
|
75790
|
+
}
|
|
75791
|
+
});
|
|
75792
|
+
return list;
|
|
75793
|
+
}
|
|
75794
|
+
|
|
75795
|
+
// Returns the radius of the smallest circle centered on a symbol's point that
|
|
75796
|
+
// covers the symbol, or null if the symbol contains a part whose reach can't be
|
|
75797
|
+
// measured.
|
|
75798
|
+
//
|
|
75799
|
+
// Only a symbol drawn around its point can be described this way: a circle, or
|
|
75800
|
+
// a group of the circles and polygons that make up ring, pie and donut symbols.
|
|
75801
|
+
// An arrow is drawn from its point outward, so the circle around its point that
|
|
75802
|
+
// covers it says nothing useful about the space it occupies -- arrows are plain
|
|
75803
|
+
// polygon symbols, which reach this function only as group parts, which they
|
|
75804
|
+
// never are.
|
|
75805
|
+
function getSymbolBoundingRadius(sym) {
|
|
75806
|
+
var max = 0;
|
|
75807
|
+
var parts, r, i;
|
|
75808
|
+
if (!sym) return null;
|
|
75809
|
+
if (sym.type == 'circle') {
|
|
75810
|
+
// Half of a stroke lies outside the circle it follows, which is how a ring
|
|
75811
|
+
// symbol paints its bands.
|
|
75812
|
+
return getPositiveNumber(sym.r) + getStrokeOutset(sym);
|
|
75813
|
+
}
|
|
75814
|
+
if (sym.type == 'polygon') {
|
|
75815
|
+
forEachSymbolCoord(sym.coordinates || [], function(p) {
|
|
75816
|
+
var dist = Math.sqrt(p[0] * p[0] + p[1] * p[1]);
|
|
75817
|
+
if (dist > max) max = dist;
|
|
75818
|
+
});
|
|
75819
|
+
return max + getStrokeOutset(sym);
|
|
75820
|
+
}
|
|
75821
|
+
if (sym.type == 'group') {
|
|
75822
|
+
parts = sym.parts || [];
|
|
75823
|
+
for (i=0; i<parts.length; i++) {
|
|
75824
|
+
r = getSymbolBoundingRadius(parts[i]);
|
|
75825
|
+
if (r === null) return null;
|
|
75826
|
+
if (r > max) max = r;
|
|
75827
|
+
}
|
|
75828
|
+
return max;
|
|
75829
|
+
}
|
|
75830
|
+
return null;
|
|
75831
|
+
}
|
|
75832
|
+
|
|
75833
|
+
// A stroke straddles the path it is drawn on, so half of its width extends
|
|
75834
|
+
// beyond the shape.
|
|
75835
|
+
function getStrokeOutset(sym) {
|
|
75836
|
+
var width = sym['stroke-width'];
|
|
75837
|
+
if (!sym.stroke || sym.stroke == 'none') return 0;
|
|
75838
|
+
// an SVG stroke is one pixel wide unless stroke-width says otherwise
|
|
75839
|
+
if (width === undefined || width === null || width === '') return 0.5;
|
|
75840
|
+
return getPositiveNumber(width) / 2;
|
|
75841
|
+
}
|
|
75842
|
+
|
|
75843
|
+
function getPositiveNumber(val) {
|
|
75844
|
+
var num = +val;
|
|
75845
|
+
return num > 0 ? num : 0;
|
|
75846
|
+
}
|
|
75847
|
+
|
|
75848
|
+
function forEachSymbolCoord(coords, cb) {
|
|
75849
|
+
var isPoint = coords && utils.isNumber(coords[0]);
|
|
75850
|
+
var isNested = !isPoint && coords && Array.isArray(coords[0]);
|
|
75851
|
+
if (isPoint) return cb(coords);
|
|
75852
|
+
for (var i=0; i<coords.length; i++) {
|
|
75853
|
+
if (isNested) forEachSymbolCoord(coords[i], cb);
|
|
75854
|
+
}
|
|
75855
|
+
}
|
|
75856
|
+
|
|
75857
|
+
function flipY(coords) {
|
|
75858
|
+
forEachSymbolCoord(coords, function(p) {
|
|
75859
|
+
p[1] = -p[1];
|
|
75860
|
+
});
|
|
75861
|
+
}
|
|
75862
|
+
|
|
75863
|
+
function scaleAndShiftCoords(coords, scale, shift) {
|
|
75864
|
+
forEachSymbolCoord(coords, function(xy) {
|
|
75865
|
+
xy[0] = xy[0] * scale + shift[0];
|
|
75866
|
+
xy[1] = xy[1] * scale + shift[1];
|
|
75867
|
+
});
|
|
75868
|
+
}
|
|
75869
|
+
|
|
75870
|
+
function roundCoordsForSVG(coords) {
|
|
75871
|
+
forEachSymbolCoord(coords, function(p) {
|
|
75872
|
+
p[0] = roundCoord$1(p[0]);
|
|
75873
|
+
p[1] = roundCoord$1(p[1]);
|
|
75874
|
+
});
|
|
75875
|
+
}
|
|
75876
|
+
|
|
75877
|
+
function rotateCoords(coords, rotation) {
|
|
75878
|
+
if (!rotation) return;
|
|
75879
|
+
var f = getAffineTransform(rotation, 1, [0, 0], [0, 0]);
|
|
75880
|
+
forEachSymbolCoord(coords, function(p) {
|
|
75881
|
+
var p2 = f(p[0], p[1]);
|
|
75882
|
+
p[0] = p2[0];
|
|
75883
|
+
p[1] = p2[1];
|
|
75884
|
+
});
|
|
75885
|
+
}
|
|
75886
|
+
|
|
75428
75887
|
// Collision reduction for circular symbols, in the spirit of d3-force's
|
|
75429
75888
|
// forceCollide. Overlapping symbols are pushed apart, but each one is kept
|
|
75430
75889
|
// within a fixed distance of its true position.
|
|
75431
75890
|
//
|
|
75432
75891
|
// Nodes are laid out in pixel space; converting to and from map coordinates is
|
|
75433
|
-
// the caller's job. A node is {i, x, y, x0, y0, r, maxShift}, where
|
|
75434
|
-
// the anchor (the symbol's true position), r includes any padding and
|
|
75435
|
-
// is the furthest this symbol may be displaced.
|
|
75436
|
-
// per-node, so the caller can vary them from symbol to
|
|
75437
|
-
// maxShift of 0 never moves and so acts as a fixed
|
|
75892
|
+
// the caller's job. A node is {i, x, y, x0, y0, r, maxShift, margin}, where
|
|
75893
|
+
// x0,y0 is the anchor (the symbol's true position), r includes any padding and
|
|
75894
|
+
// maxShift is the furthest this symbol may be displaced. All three of r,
|
|
75895
|
+
// maxShift and margin are per-node, so the caller can vary them from symbol to
|
|
75896
|
+
// symbol; a node with a maxShift of 0 never moves and so acts as a fixed
|
|
75897
|
+
// obstacle.
|
|
75898
|
+
//
|
|
75899
|
+
// A pair of nodes is kept a.r + b.r + max(a.margin, b.margin) apart, so a
|
|
75900
|
+
// margin is a gap that one symbol claims from every neighbor, whatever the
|
|
75901
|
+
// neighbor asks for, and the larger of the two claims wins. Padding is the
|
|
75902
|
+
// alternative already folded into r, where a pair ends up with the sum of the
|
|
75903
|
+
// two symbols' padding between them. margin is optional; treated as 0 if absent.
|
|
75904
|
+
//
|
|
75905
|
+
// A negative margin lets a pair overlap, but only where both nodes permit it:
|
|
75906
|
+
// max() means one node's 0 outranks another's -5.
|
|
75438
75907
|
|
|
75439
75908
|
// max_shift is not read by the solver (it is a per-node property); it is here
|
|
75440
75909
|
// as the default for callers to apply when building nodes.
|
|
@@ -75481,55 +75950,91 @@ ${svg}
|
|
|
75481
75950
|
return moved;
|
|
75482
75951
|
}
|
|
75483
75952
|
|
|
75484
|
-
//
|
|
75485
|
-
// leave
|
|
75486
|
-
// pixel
|
|
75487
|
-
//
|
|
75488
|
-
// So a threshold of a pixel is where this
|
|
75489
|
-
// genuine unresolved crowding begins, as
|
|
75490
|
-
//
|
|
75491
|
-
var
|
|
75492
|
-
|
|
75493
|
-
// Number of
|
|
75494
|
-
// done. Deliberately not a count of
|
|
75495
|
-
//
|
|
75496
|
-
// makes a layout that is visually finished
|
|
75953
|
+
// Collisions shallower than this are not reported. A converged layout doesn't
|
|
75954
|
+
// leave pairs at exactly the separation they need, it leaves a tail of pairs
|
|
75955
|
+
// short of it by a fraction of a pixel -- on a solved 435-symbol layout, 55
|
|
75956
|
+
// pairs fall short by more than a hundredth of a pixel, 6 by more than half a
|
|
75957
|
+
// pixel, and none by more than one. So a threshold of a pixel is where this
|
|
75958
|
+
// solver's convergence residue ends and genuine unresolved crowding begins, as
|
|
75959
|
+
// well as being about the point where the shortfall becomes visible.
|
|
75960
|
+
var VISIBLE_COLLISION = 1; // pixels
|
|
75961
|
+
|
|
75962
|
+
// Number of pairs that fall visibly short of the separation they need, for
|
|
75963
|
+
// reporting how much of the job got done. Deliberately not a count of every
|
|
75964
|
+
// collision: falling short by a 100th of a pixel and by 15 pixels are not the
|
|
75965
|
+
// same result, and treating them alike makes a layout that is visually finished
|
|
75966
|
+
// look like a failure.
|
|
75967
|
+
//
|
|
75968
|
+
// Note this counts collisions, not overlaps, and padding or a margin can put
|
|
75969
|
+
// the two apart: symbols asked to keep 4px between them and left 3px apart are
|
|
75970
|
+
// colliding, in that the layout hasn't done what was asked, but not overlapping.
|
|
75497
75971
|
function countSymbolCollisions(nodes) {
|
|
75498
75972
|
var count = 0;
|
|
75499
75973
|
forEachCollision(nodes, function(a, b, dx, dy, distSq, sum) {
|
|
75500
|
-
if (sum - Math.sqrt(distSq) >
|
|
75974
|
+
if (sum - Math.sqrt(distSq) > VISIBLE_COLLISION) count++;
|
|
75501
75975
|
});
|
|
75502
75976
|
return count;
|
|
75503
75977
|
}
|
|
75504
75978
|
|
|
75505
|
-
// Push
|
|
75506
|
-
// less than smaller ones.
|
|
75979
|
+
// Push colliding nodes apart, in proportion to how far short of their required
|
|
75980
|
+
// separation they are. Larger nodes move less than smaller ones. Which of the
|
|
75981
|
+
// two yields is decided by their radii alone, so asking for a wide margin does
|
|
75982
|
+
// not also make a symbol harder to push.
|
|
75507
75983
|
function resolveCollisions(nodes, strength) {
|
|
75508
75984
|
forEachCollision(nodes, function(a, b, dx, dy, distSq, sum) {
|
|
75509
75985
|
separate(a, b, dx, dy, distSq, sum, strength);
|
|
75510
75986
|
});
|
|
75511
75987
|
}
|
|
75512
75988
|
|
|
75513
|
-
// Sweep along the x axis, using the
|
|
75514
|
-
// node to know when to stop looking for neighbors.
|
|
75989
|
+
// Sweep along the x axis, using the greatest reach still ahead of the current
|
|
75990
|
+
// node to know when to stop looking for neighbors. A node's reach is its radius
|
|
75991
|
+
// plus its margin if that margin is positive: only a positive margin widens the
|
|
75992
|
+
// area a symbol needs, and clamping is what makes the bound below safe.
|
|
75993
|
+
//
|
|
75994
|
+
// Breaking out of the inner loop is valid because the separation a pair needs,
|
|
75995
|
+
// a.r + b.r + max(a.margin, b.margin), never exceeds a.reach + b.reach. With
|
|
75996
|
+
// margins clamped at 0 that holds, since max(ma, mb) <= max(ma, 0) + max(mb, 0).
|
|
75997
|
+
// Without the clamp it fails for two negative margins -- max(-3, -3) is -3, not
|
|
75998
|
+
// -6 -- and the sweep quietly stops short of pairs that are still colliding.
|
|
75999
|
+
//
|
|
76000
|
+
// Margins are collected into an array in the same pass that builds the bound,
|
|
76001
|
+
// which normalizes a missing margin to 0 once per node instead of guarding every
|
|
76002
|
+
// comparison in the loop below.
|
|
76003
|
+
//
|
|
76004
|
+
// Note the sort happens once per pass while the callback moves nodes as it goes,
|
|
76005
|
+
// so the bound is applied to positions that have already shifted and a pair that
|
|
76006
|
+
// drifts into range mid-pass can be missed until the next pass re-sorts. A
|
|
76007
|
+
// consequence is that padding and an equivalent margin, which ask for the same
|
|
76008
|
+
// separation, don't produce identical output: the margin gives a node a longer
|
|
76009
|
+
// reach, which prunes less and catches a few more of those pairs.
|
|
75515
76010
|
function forEachCollision(nodes, cb) {
|
|
75516
76011
|
var n = nodes.length,
|
|
75517
76012
|
maxAhead = new Float64Array(n),
|
|
76013
|
+
margins = new Float64Array(n),
|
|
75518
76014
|
max = 0,
|
|
75519
|
-
a, b, dx, dy, sum, distSq, i, j;
|
|
76015
|
+
a, b, dx, dy, sum, distSq, reach, ma, mb, i, j;
|
|
75520
76016
|
nodes.sort(compareX);
|
|
75521
76017
|
for (i=n-1; i>=0; i--) {
|
|
75522
|
-
|
|
76018
|
+
ma = nodes[i].margin || 0;
|
|
76019
|
+
margins[i] = ma;
|
|
76020
|
+
reach = nodes[i].r + (ma > 0 ? ma : 0);
|
|
76021
|
+
if (reach > max) max = reach;
|
|
75523
76022
|
maxAhead[i] = max;
|
|
75524
76023
|
}
|
|
75525
76024
|
for (i=0; i<n; i++) {
|
|
75526
76025
|
a = nodes[i];
|
|
76026
|
+
ma = margins[i];
|
|
76027
|
+
reach = a.r + (ma > 0 ? ma : 0);
|
|
75527
76028
|
for (j=i+1; j<n; j++) {
|
|
75528
76029
|
b = nodes[j];
|
|
75529
76030
|
dx = b.x - a.x;
|
|
75530
|
-
if (dx >
|
|
76031
|
+
if (dx > reach + maxAhead[j]) break;
|
|
75531
76032
|
dy = b.y - a.y;
|
|
75532
|
-
|
|
76033
|
+
mb = margins[j];
|
|
76034
|
+
sum = a.r + b.r + (ma > mb ? ma : mb);
|
|
76035
|
+
// Margins can cancel the pair's radii entirely, which separate() would
|
|
76036
|
+
// read as an overlap and answer by pulling the two nodes together.
|
|
76037
|
+
if (sum <= 0) continue;
|
|
75533
76038
|
if (dy > sum || dy < -sum) continue;
|
|
75534
76039
|
distSq = dx * dx + dy * dy;
|
|
75535
76040
|
if (distSq >= sum * sum) continue;
|
|
@@ -75600,14 +76105,16 @@ ${svg}
|
|
|
75600
76105
|
|
|
75601
76106
|
var SymbolCollisions = /*#__PURE__*/Object.freeze({
|
|
75602
76107
|
__proto__: null,
|
|
75603
|
-
|
|
76108
|
+
VISIBLE_COLLISION: VISIBLE_COLLISION,
|
|
75604
76109
|
countSymbolCollisions: countSymbolCollisions,
|
|
75605
76110
|
resolveSymbolCollisions: resolveSymbolCollisions,
|
|
75606
76111
|
symbolCollisionDefaults: symbolCollisionDefaults
|
|
75607
76112
|
});
|
|
75608
76113
|
|
|
75609
|
-
// Move
|
|
75610
|
-
//
|
|
76114
|
+
// Move symbols apart to reduce overlaps, keeping each one within a fixed pixel
|
|
76115
|
+
// distance of its true position. Each symbol is laid out as the circle that
|
|
76116
|
+
// covers it, so the command works on the symbol types that are drawn around
|
|
76117
|
+
// their point (see getSymbolLayoutRadius()).
|
|
75611
76118
|
//
|
|
75612
76119
|
// The layout runs in the pixel space of the map that the symbols will be
|
|
75613
76120
|
// rendered in, so the command needs to know the display scale: either from a
|
|
@@ -75619,9 +76126,17 @@ ${svg}
|
|
|
75619
76126
|
// An optional polygons= layer confines each symbol to the polygon it started
|
|
75620
76127
|
// in, so that a symbol standing for one area can't drift into a neighboring
|
|
75621
76128
|
// one. Symbols in an area too small to hold them barely move as a result, and
|
|
75622
|
-
// their
|
|
75623
|
-
// mean fewer
|
|
76129
|
+
// their collisions go unresolved -- that is the point of the option, but it does
|
|
76130
|
+
// mean fewer collisions get fixed.
|
|
76131
|
+
//
|
|
76132
|
+
// Clearance between symbols can be asked for in two ways. padding= surrounds
|
|
76133
|
+
// each symbol, so a pair ends up with the sum of their two paddings between
|
|
76134
|
+
// them; margin= is a gap a symbol claims from every neighbor, so a pair ends up
|
|
76135
|
+
// with the greater of their two margins. Use padding= to make a symbol keep its
|
|
76136
|
+
// distance in proportion to how much of it there is, and margin= to set a gap
|
|
76137
|
+
// that holds however the neighbor is configured.
|
|
75624
76138
|
var DEFAULT_PADDING = 0; // pixels
|
|
76139
|
+
var DEFAULT_MARGIN = 0; // pixels
|
|
75625
76140
|
|
|
75626
76141
|
cmd.repel = function(targetLayers, dataset, catalog, polygonSource, opts) {
|
|
75627
76142
|
var solverOpts = {
|
|
@@ -75641,7 +76156,7 @@ ${svg}
|
|
|
75641
76156
|
addLayerNodes(nodes, lyr, pixelsPerUnit, opts);
|
|
75642
76157
|
});
|
|
75643
76158
|
if (nodes.length === 0) {
|
|
75644
|
-
stop$1('Targeted layer(s) contain no
|
|
76159
|
+
stop$1('Targeted layer(s) contain no symbols to move.');
|
|
75645
76160
|
}
|
|
75646
76161
|
var unconstrained = addContainment(nodes, dataset, polygonSource, pixelsPerUnit, solverOpts);
|
|
75647
76162
|
|
|
@@ -75702,17 +76217,19 @@ ${svg}
|
|
|
75702
76217
|
function reportResults(nodes, moved, before, after, contained) {
|
|
75703
76218
|
var total = nodes.length;
|
|
75704
76219
|
var msg = utils.format('Moved %d of %d symbol%s', moved, total, total == 1 ? '' : 's');
|
|
75705
|
-
// Counts are of visible
|
|
75706
|
-
//
|
|
75707
|
-
// advice to raise max-shift= is not given when nothing is
|
|
76220
|
+
// Counts are of visible collisions only, so a layout whose symbols end up at
|
|
76221
|
+
// or a fraction of a pixel short of the separation they need is reported as
|
|
76222
|
+
// finished, and the advice to raise max-shift= is not given when nothing is
|
|
76223
|
+
// left to fix. A collision is not the same as an overlap: two symbols asked to
|
|
76224
|
+
// keep a gap between them collide as soon as the gap closes.
|
|
75708
76225
|
if (after > 0) {
|
|
75709
|
-
msg += utils.format('; %d of %d visible
|
|
76226
|
+
msg += utils.format('; %d of %d visible collision%s remain (%s)',
|
|
75710
76227
|
after, before, before == 1 ? '' : 's',
|
|
75711
76228
|
contained ? getMaxShiftAdvice(nodes) + ', or drop polygons=' : getMaxShiftAdvice(nodes));
|
|
75712
76229
|
} else if (before > 0) {
|
|
75713
|
-
msg += utils.format('; resolved %d visible
|
|
76230
|
+
msg += utils.format('; resolved %d visible collision%s', before, before == 1 ? '' : 's');
|
|
75714
76231
|
} else {
|
|
75715
|
-
msg += ' (no visible
|
|
76232
|
+
msg += ' (no visible collisions found)';
|
|
75716
76233
|
}
|
|
75717
76234
|
message(msg);
|
|
75718
76235
|
}
|
|
@@ -75733,19 +76250,20 @@ ${svg}
|
|
|
75733
76250
|
function requireRepelTarget(lyr, opts) {
|
|
75734
76251
|
requireSinglePointLayer(lyr,
|
|
75735
76252
|
'-repel requires single points; layer contains multi-point features.');
|
|
75736
|
-
if (!opts.radius && !
|
|
75737
|
-
stop$1('-repel requires a layer containing
|
|
76253
|
+
if (!opts.radius && !layerHasSymbols(lyr)) {
|
|
76254
|
+
stop$1('-repel requires a layer containing symbols ' +
|
|
75738
76255
|
'(see the -symbols and -style commands), or a radius= option.');
|
|
75739
76256
|
}
|
|
75740
76257
|
}
|
|
75741
76258
|
|
|
75742
|
-
function
|
|
76259
|
+
function layerHasSymbols(lyr) {
|
|
75743
76260
|
return !!lyr.data && (lyr.data.fieldExists('svg-symbol') || lyr.data.fieldExists('r'));
|
|
75744
76261
|
}
|
|
75745
76262
|
|
|
75746
76263
|
function addLayerNodes(nodes, lyr, pixelsPerUnit, opts) {
|
|
75747
76264
|
var getRadius = getRadiusAccessor(lyr, opts);
|
|
75748
76265
|
var getPadding = getPixelValueAccessor(opts.padding, 'padding', DEFAULT_PADDING, lyr);
|
|
76266
|
+
var getMargin = getPixelValueAccessor(opts.margin, 'margin', DEFAULT_MARGIN, lyr, true);
|
|
75749
76267
|
var getMaxShift = getPixelValueAccessor(opts.max_shift, 'max-shift',
|
|
75750
76268
|
symbolCollisionDefaults.max_shift, lyr);
|
|
75751
76269
|
var i, shp, p, r;
|
|
@@ -75764,23 +76282,32 @@ ${svg}
|
|
|
75764
76282
|
x0: p[0] * pixelsPerUnit,
|
|
75765
76283
|
y0: p[1] * pixelsPerUnit,
|
|
75766
76284
|
// Padding is added to the radius, so a pair of symbols ends up with
|
|
75767
|
-
// (padding a + padding b) pixels of clearance between them.
|
|
76285
|
+
// (padding a + padding b) pixels of clearance between them. A margin is
|
|
76286
|
+
// kept separate, because the solver has to compare the two symbols'
|
|
76287
|
+
// margins to find the gap a pair needs.
|
|
75768
76288
|
r: r + getPadding(i),
|
|
76289
|
+
margin: getMargin(i),
|
|
75769
76290
|
maxShift: getMaxShift(i)
|
|
75770
76291
|
});
|
|
75771
76292
|
}
|
|
75772
76293
|
}
|
|
75773
76294
|
|
|
75774
|
-
// padding= and max-shift= accept a number, a field name or an
|
|
75775
|
-
// so are resolved for each symbol.
|
|
75776
|
-
|
|
76295
|
+
// padding=, margin= and max-shift= accept a number, a field name or an
|
|
76296
|
+
// expression, and so are resolved for each symbol.
|
|
76297
|
+
//
|
|
76298
|
+
// @allowNegative is for margin=, where a negative value asks that a pair of
|
|
76299
|
+
// symbols be allowed to overlap. Elsewhere a negative value is an error: it
|
|
76300
|
+
// would shrink a radius or a shift limit, neither of which is a coherent
|
|
76301
|
+
// request. A margin still has to be a finite number, so NaN is rejected either
|
|
76302
|
+
// way.
|
|
76303
|
+
function getPixelValueAccessor(optVal, name, defaultVal, lyr, allowNegative) {
|
|
75777
76304
|
if (optVal === undefined || optVal === null || optVal === '') {
|
|
75778
76305
|
return function(i) {return defaultVal;};
|
|
75779
76306
|
}
|
|
75780
76307
|
var accessor = getPropertyAccessor(optVal, 'number', lyr, name);
|
|
75781
76308
|
return function(i) {
|
|
75782
76309
|
var val = +accessor(i);
|
|
75783
|
-
if (!(val >= 0)) {
|
|
76310
|
+
if (allowNegative ? !isFinite(val) : !(val >= 0)) {
|
|
75784
76311
|
stop$1(utils.format('Invalid %s= value: %s', name, accessor(i)));
|
|
75785
76312
|
}
|
|
75786
76313
|
return val;
|
|
@@ -75799,12 +76326,17 @@ ${svg}
|
|
|
75799
76326
|
return function(i) {
|
|
75800
76327
|
var rec = records[i];
|
|
75801
76328
|
if (!rec) return 0;
|
|
75802
|
-
if (rec['svg-symbol']) return
|
|
76329
|
+
if (rec['svg-symbol']) return getSymbolLayoutRadius(rec['svg-symbol']);
|
|
75803
76330
|
return rec.r > 0 ? +rec.r : 0;
|
|
75804
76331
|
};
|
|
75805
76332
|
}
|
|
75806
76333
|
|
|
75807
|
-
|
|
76334
|
+
// A symbol is laid out as the circle that covers it, so a symbol type is
|
|
76335
|
+
// supported if it is drawn around its point: a circle, or a group of parts,
|
|
76336
|
+
// which is how ring, pie and donut symbols are made. A symbol drawn from its
|
|
76337
|
+
// point outward, like an arrow, has no meaningful radius and needs radius=.
|
|
76338
|
+
function getSymbolLayoutRadius(sym) {
|
|
76339
|
+
var r;
|
|
75808
76340
|
if (utils.isString(sym)) {
|
|
75809
76341
|
try {
|
|
75810
76342
|
sym = JSON.parse(sym);
|
|
@@ -75812,11 +76344,14 @@ ${svg}
|
|
|
75812
76344
|
return 0;
|
|
75813
76345
|
}
|
|
75814
76346
|
}
|
|
75815
|
-
if (!sym
|
|
75816
|
-
|
|
75817
|
-
|
|
76347
|
+
if (!sym) return 0;
|
|
76348
|
+
r = sym.type == 'circle' || sym.type == 'group' ?
|
|
76349
|
+
getSymbolBoundingRadius(sym) : null;
|
|
76350
|
+
if (r === null) {
|
|
76351
|
+
stop$1('-repel is unable to size a ' + (sym.type || 'symbol') +
|
|
76352
|
+
' symbol; use a radius= option to give it a radius.');
|
|
75818
76353
|
}
|
|
75819
|
-
return
|
|
76354
|
+
return r;
|
|
75820
76355
|
}
|
|
75821
76356
|
|
|
75822
76357
|
// Returns the number of display pixels per map coordinate unit.
|
|
@@ -77864,72 +78399,6 @@ ${svg}
|
|
|
77864
78399
|
});
|
|
77865
78400
|
}
|
|
77866
78401
|
|
|
77867
|
-
var roundCoord$1 = getRoundingFunction(0.01);
|
|
77868
|
-
|
|
77869
|
-
function getSymbolFillColor(d) {
|
|
77870
|
-
return d.fill || 'magenta';
|
|
77871
|
-
}
|
|
77872
|
-
|
|
77873
|
-
function getSymbolStrokeColor(d) {
|
|
77874
|
-
return d.stroke || d.fill || 'magenta';
|
|
77875
|
-
}
|
|
77876
|
-
|
|
77877
|
-
function applySymbolStyles(sym, d) {
|
|
77878
|
-
if (sym.type == 'polyline') {
|
|
77879
|
-
sym.stroke = getSymbolStrokeColor(d);
|
|
77880
|
-
} else {
|
|
77881
|
-
sym.fill = getSymbolFillColor(d);
|
|
77882
|
-
}
|
|
77883
|
-
if (d.opacity) {
|
|
77884
|
-
sym.opacity = d.opacity;
|
|
77885
|
-
}
|
|
77886
|
-
return sym;
|
|
77887
|
-
}
|
|
77888
|
-
|
|
77889
|
-
function getSymbolRadius(d) {
|
|
77890
|
-
if (d.radius === 0 || d.length === 0 || d.r === 0) return 0;
|
|
77891
|
-
return d.radius || d.length || d.r || 5; // use a default value
|
|
77892
|
-
}
|
|
77893
|
-
|
|
77894
|
-
function forEachSymbolCoord(coords, cb) {
|
|
77895
|
-
var isPoint = coords && utils.isNumber(coords[0]);
|
|
77896
|
-
var isNested = !isPoint && coords && Array.isArray(coords[0]);
|
|
77897
|
-
if (isPoint) return cb(coords);
|
|
77898
|
-
for (var i=0; i<coords.length; i++) {
|
|
77899
|
-
if (isNested) forEachSymbolCoord(coords[i], cb);
|
|
77900
|
-
}
|
|
77901
|
-
}
|
|
77902
|
-
|
|
77903
|
-
function flipY(coords) {
|
|
77904
|
-
forEachSymbolCoord(coords, function(p) {
|
|
77905
|
-
p[1] = -p[1];
|
|
77906
|
-
});
|
|
77907
|
-
}
|
|
77908
|
-
|
|
77909
|
-
function scaleAndShiftCoords(coords, scale, shift) {
|
|
77910
|
-
forEachSymbolCoord(coords, function(xy) {
|
|
77911
|
-
xy[0] = xy[0] * scale + shift[0];
|
|
77912
|
-
xy[1] = xy[1] * scale + shift[1];
|
|
77913
|
-
});
|
|
77914
|
-
}
|
|
77915
|
-
|
|
77916
|
-
function roundCoordsForSVG(coords) {
|
|
77917
|
-
forEachSymbolCoord(coords, function(p) {
|
|
77918
|
-
p[0] = roundCoord$1(p[0]);
|
|
77919
|
-
p[1] = roundCoord$1(p[1]);
|
|
77920
|
-
});
|
|
77921
|
-
}
|
|
77922
|
-
|
|
77923
|
-
function rotateCoords(coords, rotation) {
|
|
77924
|
-
if (!rotation) return;
|
|
77925
|
-
var f = getAffineTransform(rotation, 1, [0, 0], [0, 0]);
|
|
77926
|
-
forEachSymbolCoord(coords, function(p) {
|
|
77927
|
-
var p2 = f(p[0], p[1]);
|
|
77928
|
-
p[0] = p2[0];
|
|
77929
|
-
p[1] = p2[1];
|
|
77930
|
-
});
|
|
77931
|
-
}
|
|
77932
|
-
|
|
77933
78402
|
function getStickArrowCoords(d) {
|
|
77934
78403
|
return getArrowCoords(d, 'stick');
|
|
77935
78404
|
}
|
|
@@ -78267,11 +78736,176 @@ ${svg}
|
|
|
78267
78736
|
}
|
|
78268
78737
|
|
|
78269
78738
|
function parseRings(arg) {
|
|
78270
|
-
var arr =
|
|
78739
|
+
var arr = toNumberList(arg);
|
|
78271
78740
|
utils.genericSort(arr, true);
|
|
78272
78741
|
return utils.uniq(arr);
|
|
78273
78742
|
}
|
|
78274
78743
|
|
|
78744
|
+
// Matches the vertex density of the 72-sided circle made by getPolygonCoords()
|
|
78745
|
+
var DEGREES_PER_SEGMENT = 5;
|
|
78746
|
+
|
|
78747
|
+
// Returns an svg-symbol object containing one polygon part per wedge, or null
|
|
78748
|
+
// if the symbol has nothing to draw.
|
|
78749
|
+
function makePieSymbol(d, opts) {
|
|
78750
|
+
var wedges = getPieWedges(d, +opts.scale || 1);
|
|
78751
|
+
if (wedges.length === 0) return null;
|
|
78752
|
+
var parts = wedges.map(function(wedge) {
|
|
78753
|
+
flipY(wedge.coordinates); // the SVG y-axis points down
|
|
78754
|
+
roundCoordsForSVG(wedge.coordinates);
|
|
78755
|
+
var part = {
|
|
78756
|
+
type: 'polygon',
|
|
78757
|
+
coordinates: wedge.coordinates,
|
|
78758
|
+
fill: wedge.fill
|
|
78759
|
+
};
|
|
78760
|
+
if (d.stroke) part.stroke = d.stroke;
|
|
78761
|
+
if (d['stroke-width']) part['stroke-width'] = d['stroke-width'];
|
|
78762
|
+
// opacity is applied per-part, not to the group: renderComplexSymbol()
|
|
78763
|
+
// assumes a group symbol has a properties object, which group() only
|
|
78764
|
+
// provides for single-part groups.
|
|
78765
|
+
if (d.opacity) part.opacity = d.opacity;
|
|
78766
|
+
return part;
|
|
78767
|
+
});
|
|
78768
|
+
return {type: 'group', parts: parts};
|
|
78769
|
+
}
|
|
78770
|
+
|
|
78771
|
+
// Returns an array of {fill, coordinates} objects, in the y-up coordinate space
|
|
78772
|
+
// shared by the symbol generators, centered on [0, 0]. Coordinates are an array
|
|
78773
|
+
// of rings, like flattened GeoJSON MultiPolygon coordinates.
|
|
78774
|
+
//
|
|
78775
|
+
// The first wedge starts at the top of the symbol and the rest follow in
|
|
78776
|
+
// clockwise order. A wedge with no value, or without a usable fill color, is
|
|
78777
|
+
// left out -- an unfilled wedge becomes a gap in the pie without shifting the
|
|
78778
|
+
// wedges that follow it.
|
|
78779
|
+
function getPieWedges(d, scale) {
|
|
78780
|
+
var values = getPieValues(d);
|
|
78781
|
+
var fills = getPieFills(d);
|
|
78782
|
+
var radii = getPieRadii(d, scale);
|
|
78783
|
+
var rotation = +d.rotation || 0;
|
|
78784
|
+
var total = sum(values);
|
|
78785
|
+
var wedges = [];
|
|
78786
|
+
var start = rotation;
|
|
78787
|
+
var fill, end;
|
|
78788
|
+
if (total > 0 === false || radii.outer > 0 === false) return wedges;
|
|
78789
|
+
for (var i=0; i<values.length; i++) {
|
|
78790
|
+
end = start + values[i] / total * 360;
|
|
78791
|
+
fill = getWedgeFill(fills[i]);
|
|
78792
|
+
if (fill && end > start) {
|
|
78793
|
+
wedges.push({
|
|
78794
|
+
fill: fill,
|
|
78795
|
+
coordinates: getWedgeCoords(start, end, radii.inner, radii.outer)
|
|
78796
|
+
});
|
|
78797
|
+
}
|
|
78798
|
+
start = end;
|
|
78799
|
+
}
|
|
78800
|
+
return wedges;
|
|
78801
|
+
}
|
|
78802
|
+
|
|
78803
|
+
// Returns an array of rings outlining a wedge spanning two bearings.
|
|
78804
|
+
function getWedgeCoords(startAngle, endAngle, inner, outer) {
|
|
78805
|
+
var ring;
|
|
78806
|
+
if (endAngle - startAngle >= 360) {
|
|
78807
|
+
// A single non-zero value covers the whole symbol: a circle, or a pair of
|
|
78808
|
+
// concentric rings if the symbol is a donut.
|
|
78809
|
+
ring = closeRing(getArcCoords(startAngle, startAngle + 360, outer));
|
|
78810
|
+
if (inner > 0) {
|
|
78811
|
+
// wind the hole in the opposite direction from the outer ring
|
|
78812
|
+
return [ring, closeRing(getArcCoords(startAngle + 360, startAngle, inner))];
|
|
78813
|
+
}
|
|
78814
|
+
return [ring];
|
|
78815
|
+
}
|
|
78816
|
+
ring = getArcCoords(startAngle, endAngle, outer);
|
|
78817
|
+
if (inner > 0) {
|
|
78818
|
+
// A wedge of a donut is a simple polygon, so the hole needs no ring of
|
|
78819
|
+
// its own -- the inner arc doubles back inside the outer one.
|
|
78820
|
+
ring = ring.concat(getArcCoords(endAngle, startAngle, inner));
|
|
78821
|
+
} else {
|
|
78822
|
+
ring.push([0, 0]);
|
|
78823
|
+
}
|
|
78824
|
+
ring.push(ring[0].concat());
|
|
78825
|
+
return [ring];
|
|
78826
|
+
}
|
|
78827
|
+
|
|
78828
|
+
// A 360 degree arc starts and ends at the same angle, but rounding leaves its
|
|
78829
|
+
// endpoints a hair apart, so the ring is closed explicitly.
|
|
78830
|
+
function closeRing(ring) {
|
|
78831
|
+
ring[ring.length - 1] = ring[0].concat();
|
|
78832
|
+
return ring;
|
|
78833
|
+
}
|
|
78834
|
+
|
|
78835
|
+
// Returns points along an arc, including both endpoints. Angles are bearings,
|
|
78836
|
+
// which run clockwise from the top of the symbol -- the same convention as
|
|
78837
|
+
// rotateCoords(), so the rotation= option is just an offset to the first angle.
|
|
78838
|
+
function getArcCoords(startAngle, endAngle, radius) {
|
|
78839
|
+
var sweep = endAngle - startAngle;
|
|
78840
|
+
var segments = Math.max(1, Math.ceil(Math.abs(sweep) / DEGREES_PER_SEGMENT));
|
|
78841
|
+
var coords = [];
|
|
78842
|
+
for (var i=0; i<=segments; i++) {
|
|
78843
|
+
coords.push(getPlanarSegmentEndpoint(0, 0, startAngle + sweep * i / segments, radius));
|
|
78844
|
+
}
|
|
78845
|
+
return coords;
|
|
78846
|
+
}
|
|
78847
|
+
|
|
78848
|
+
// Missing, non-numeric and negative values all count as zero.
|
|
78849
|
+
function getPieValues(d) {
|
|
78850
|
+
if (!d.values) {
|
|
78851
|
+
stop$1('A pie symbol requires a values= option.');
|
|
78852
|
+
}
|
|
78853
|
+
return toNumberList(d.values).map(function(num) {
|
|
78854
|
+
return num > 0 ? num : 0;
|
|
78855
|
+
});
|
|
78856
|
+
}
|
|
78857
|
+
|
|
78858
|
+
function getPieFills(d) {
|
|
78859
|
+
if (!d.fills) {
|
|
78860
|
+
stop$1('A pie symbol requires a fills= option.');
|
|
78861
|
+
}
|
|
78862
|
+
return toItemList(d.fills);
|
|
78863
|
+
}
|
|
78864
|
+
|
|
78865
|
+
// Returns the color to fill a wedge with, or null if the wedge should be left
|
|
78866
|
+
// out of the symbol.
|
|
78867
|
+
function getWedgeFill(val) {
|
|
78868
|
+
var str = val === null || val === undefined ? '' : String(val).trim();
|
|
78869
|
+
var name = str.toLowerCase();
|
|
78870
|
+
var rgb;
|
|
78871
|
+
if (!str || name == 'none' || name == 'transparent') {
|
|
78872
|
+
return null;
|
|
78873
|
+
}
|
|
78874
|
+
rgb = parseColor(str);
|
|
78875
|
+
if (!rgb || rgb.a === 0) return null;
|
|
78876
|
+
return str;
|
|
78877
|
+
}
|
|
78878
|
+
|
|
78879
|
+
// Returns {inner, outer} radii in pixels. The hole= option makes a donut. A
|
|
78880
|
+
// negative hole is measured inward from the outer radius, which gives a band
|
|
78881
|
+
// of wedges of a fixed width without having to repeat a data-driven radius=
|
|
78882
|
+
// expression. A hole as wide as the symbol leaves a plain pie, and a
|
|
78883
|
+
// non-positive outer radius is left for the caller to skip, like radius=0 on
|
|
78884
|
+
// the other symbol types.
|
|
78885
|
+
function getPieRadii(d, scale) {
|
|
78886
|
+
var outer = getSymbolRadius(d);
|
|
78887
|
+
var hole = +d.hole;
|
|
78888
|
+
var inner = 0;
|
|
78889
|
+
if (hole > 0) {
|
|
78890
|
+
inner = hole;
|
|
78891
|
+
} else if (hole < 0) {
|
|
78892
|
+
inner = outer + hole;
|
|
78893
|
+
}
|
|
78894
|
+
if (!(inner > 0) || inner >= outer) inner = 0;
|
|
78895
|
+
return {
|
|
78896
|
+
inner: inner * scale,
|
|
78897
|
+
outer: outer * scale
|
|
78898
|
+
};
|
|
78899
|
+
}
|
|
78900
|
+
|
|
78901
|
+
function sum(arr) {
|
|
78902
|
+
var total = 0;
|
|
78903
|
+
for (var i=0; i<arr.length; i++) {
|
|
78904
|
+
total += arr[i];
|
|
78905
|
+
}
|
|
78906
|
+
return total;
|
|
78907
|
+
}
|
|
78908
|
+
|
|
78275
78909
|
// Returns an svg-symbol data object for one symbol
|
|
78276
78910
|
function makePathSymbol(coords, properties, geojsonType) {
|
|
78277
78911
|
var sym;
|
|
@@ -78328,6 +78962,17 @@ ${svg}
|
|
|
78328
78962
|
var d = getSymbolData(i);
|
|
78329
78963
|
var rec = records[i] || {};
|
|
78330
78964
|
|
|
78965
|
+
// A pie has a different fill for each of its wedges, which the single
|
|
78966
|
+
// fill field of a generated shape can't express.
|
|
78967
|
+
if (d.type == 'pie') {
|
|
78968
|
+
if (shapeMode) {
|
|
78969
|
+
stop$1('The pie symbol type does not support the geographic option.');
|
|
78970
|
+
}
|
|
78971
|
+
var pie = makePieSymbol(d, opts);
|
|
78972
|
+
if (pie) rec['svg-symbol'] = pie;
|
|
78973
|
+
return;
|
|
78974
|
+
}
|
|
78975
|
+
|
|
78331
78976
|
// non-polygon symbols
|
|
78332
78977
|
if (!shapeMode && d.type == 'circle') {
|
|
78333
78978
|
rec['svg-symbol'] = makeCircleSymbol(d, opts);
|
|
@@ -79565,7 +80210,7 @@ ${svg}
|
|
|
79565
80210
|
return name == 'rectangle' || name == 'rectangles' || name == 'filter' && opts.cleanup;
|
|
79566
80211
|
}
|
|
79567
80212
|
|
|
79568
|
-
var version = "0.7.
|
|
80213
|
+
var version = "0.7.59";
|
|
79569
80214
|
|
|
79570
80215
|
// Parse command line args into commands and run them
|
|
79571
80216
|
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.
|