mapshaper 0.5.75 → 0.5.79
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 +15 -0
- package/bin/mapshaper-gui +1 -1
- package/mapshaper.js +673 -234
- package/package.json +2 -2
- package/www/mapshaper-gui.js +11 -10
- package/www/mapshaper.js +673 -234
- package/www/modules.js +21 -2
package/www/mapshaper.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
|
|
3
|
-
var VERSION = "0.5.
|
|
3
|
+
var VERSION = "0.5.78";
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
var utils = /*#__PURE__*/Object.freeze({
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
get initializeArray () { return initializeArray; },
|
|
53
53
|
get replaceArray () { return replaceArray; },
|
|
54
54
|
get repeatString () { return repeatString; },
|
|
55
|
+
get splitLines () { return splitLines; },
|
|
55
56
|
get pluralSuffix () { return pluralSuffix; },
|
|
56
57
|
get endsWith () { return endsWith; },
|
|
57
58
|
get lpad () { return lpad; },
|
|
@@ -61,7 +62,9 @@
|
|
|
61
62
|
get rtrim () { return rtrim; },
|
|
62
63
|
get addThousandsSep () { return addThousandsSep; },
|
|
63
64
|
get numToStr () { return numToStr; },
|
|
64
|
-
get formatNumber () { return formatNumber
|
|
65
|
+
get formatNumber () { return formatNumber; },
|
|
66
|
+
get formatIntlNumber () { return formatIntlNumber; },
|
|
67
|
+
get formatNumberForDisplay () { return formatNumberForDisplay; },
|
|
65
68
|
get shuffle () { return shuffle; },
|
|
66
69
|
get sortOn () { return sortOn; },
|
|
67
70
|
get genericSort () { return genericSort; },
|
|
@@ -532,6 +535,10 @@
|
|
|
532
535
|
return str;
|
|
533
536
|
}
|
|
534
537
|
|
|
538
|
+
function splitLines(str) {
|
|
539
|
+
return str.split(/\r?\n/);
|
|
540
|
+
}
|
|
541
|
+
|
|
535
542
|
function pluralSuffix(count) {
|
|
536
543
|
return count != 1 ? 's' : '';
|
|
537
544
|
}
|
|
@@ -584,7 +591,16 @@
|
|
|
584
591
|
return decimals >= 0 ? num.toFixed(decimals) : String(num);
|
|
585
592
|
}
|
|
586
593
|
|
|
587
|
-
function formatNumber
|
|
594
|
+
function formatNumber(val) {
|
|
595
|
+
return val + '';
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function formatIntlNumber(val) {
|
|
599
|
+
var str = formatNumber(val);
|
|
600
|
+
return '"' + str.replace('.', ',') + '"'; // need to quote if comma-delimited
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function formatNumberForDisplay(num, decimals, nullStr, showPos) {
|
|
588
604
|
var fmt;
|
|
589
605
|
if (isNaN(num)) {
|
|
590
606
|
fmt = nullStr || '-';
|
|
@@ -862,7 +878,8 @@
|
|
|
862
878
|
str = str.toUpperCase();
|
|
863
879
|
}
|
|
864
880
|
else if (isNumber) {
|
|
865
|
-
str =
|
|
881
|
+
// str = formatNumberForDisplay(val, isInt ? 0 : decimals);
|
|
882
|
+
str = numToStr(val, decimals);
|
|
866
883
|
if (str[0] == '-') {
|
|
867
884
|
isNeg = true;
|
|
868
885
|
str = str.substr(1);
|
|
@@ -1313,9 +1330,10 @@
|
|
|
1313
1330
|
}
|
|
1314
1331
|
|
|
1315
1332
|
function logArgs(args) {
|
|
1316
|
-
if (LOGGING
|
|
1317
|
-
|
|
1318
|
-
|
|
1333
|
+
if (!LOGGING || getStateVar('QUIET') || !utils.isArrayLike(args)) return;
|
|
1334
|
+
var msg = formatLogArgs(args);
|
|
1335
|
+
if (STDOUT) console.log(msg);
|
|
1336
|
+
else console.error(msg);
|
|
1319
1337
|
}
|
|
1320
1338
|
|
|
1321
1339
|
var Logging = /*#__PURE__*/Object.freeze({
|
|
@@ -2967,12 +2985,33 @@
|
|
|
2967
2985
|
orient2D(cx, cy, dx, dy, bx, by) <= 0;
|
|
2968
2986
|
}
|
|
2969
2987
|
|
|
2988
|
+
// Useful for determining if a segment that intersects another segment is
|
|
2989
|
+
// entering or leaving an enclosed buffer area
|
|
2990
|
+
// returns -1 if angle of p1p2 -> p3p4 is counter-clockwise (left turn)
|
|
2991
|
+
// returns 1 if angle is clockwise
|
|
2992
|
+
// return 0 if segments are collinear
|
|
2993
|
+
function segmentTurn(p1, p2, p3, p4) {
|
|
2994
|
+
var ax = p1[0],
|
|
2995
|
+
ay = p1[1],
|
|
2996
|
+
bx = p2[0],
|
|
2997
|
+
by = p2[1],
|
|
2998
|
+
// shift p3p4 segment to start at p2
|
|
2999
|
+
dx = bx - p3[0],
|
|
3000
|
+
dy = by - p3[1],
|
|
3001
|
+
cx = p4[0] + dx,
|
|
3002
|
+
cy = p4[1] + dy,
|
|
3003
|
+
orientation = orient2D(ax, ay, bx, by, cx, cy);
|
|
3004
|
+
if (!orientation) return 0;
|
|
3005
|
+
return orientation < 0 ? 1 : -1;
|
|
3006
|
+
}
|
|
3007
|
+
|
|
2970
3008
|
var SegmentGeom = /*#__PURE__*/Object.freeze({
|
|
2971
3009
|
__proto__: null,
|
|
2972
3010
|
segmentIntersection: segmentIntersection,
|
|
2973
3011
|
findClosestPointOnSeg: findClosestPointOnSeg,
|
|
2974
3012
|
orient2D: orient2D,
|
|
2975
|
-
segmentHit: segmentHit
|
|
3013
|
+
segmentHit: segmentHit,
|
|
3014
|
+
segmentTurn: segmentTurn
|
|
2976
3015
|
});
|
|
2977
3016
|
|
|
2978
3017
|
var geom = Object.assign({}, Geom, PolygonGeom, PathGeom, SegmentGeom, PolygonCentroid);
|
|
@@ -8136,10 +8175,14 @@
|
|
|
8136
8175
|
}
|
|
8137
8176
|
|
|
8138
8177
|
function roundToDigits(n, d) {
|
|
8139
|
-
return +n.toFixed(d);
|
|
8178
|
+
return +n.toFixed(d); // string conversion makes this slow
|
|
8140
8179
|
}
|
|
8141
8180
|
|
|
8142
|
-
|
|
8181
|
+
function roundToTenths(n) {
|
|
8182
|
+
return (Math.round(n * 10)) / 10;
|
|
8183
|
+
}
|
|
8184
|
+
|
|
8185
|
+
// inc: Rounding increment (e.g. 0.001 rounds to thousandths)
|
|
8143
8186
|
function getRoundingFunction(inc) {
|
|
8144
8187
|
if (!utils.isNumber(inc) || inc === 0) {
|
|
8145
8188
|
error("Rounding increment must be a non-zero number.");
|
|
@@ -8209,6 +8252,7 @@
|
|
|
8209
8252
|
__proto__: null,
|
|
8210
8253
|
roundToSignificantDigits: roundToSignificantDigits,
|
|
8211
8254
|
roundToDigits: roundToDigits,
|
|
8255
|
+
roundToTenths: roundToTenths,
|
|
8212
8256
|
getRoundingFunction: getRoundingFunction,
|
|
8213
8257
|
getBoundsPrecisionForDisplay: getBoundsPrecisionForDisplay,
|
|
8214
8258
|
getRoundedCoordString: getRoundedCoordString,
|
|
@@ -10270,6 +10314,12 @@
|
|
|
10270
10314
|
};
|
|
10271
10315
|
}
|
|
10272
10316
|
|
|
10317
|
+
/*
|
|
10318
|
+
|
|
10319
|
+
|
|
10320
|
+
|
|
10321
|
+
*/
|
|
10322
|
+
|
|
10273
10323
|
// TODO: use polygon pathfinder shared code
|
|
10274
10324
|
function collectPolylineArcs(ids, nodes, testArc, useArc) {
|
|
10275
10325
|
var parts = [];
|
|
@@ -11839,7 +11889,6 @@
|
|
|
11839
11889
|
});
|
|
11840
11890
|
|
|
11841
11891
|
// Map positive or negative integer ids to non-negative integer ids
|
|
11842
|
-
|
|
11843
11892
|
function IdLookupIndex(n, clearable) {
|
|
11844
11893
|
var fwdIndex = new Int32Array(n);
|
|
11845
11894
|
var revIndex = new Int32Array(n);
|
|
@@ -13811,7 +13860,7 @@
|
|
|
13811
13860
|
var symbolRenderers = {};
|
|
13812
13861
|
|
|
13813
13862
|
function getTransform(xy, scale) {
|
|
13814
|
-
var str = 'translate(' + xy[0] + ' ' + xy[1] + ')';
|
|
13863
|
+
var str = 'translate(' + roundToTenths(xy[0]) + ' ' + roundToTenths(xy[1]) + ')';
|
|
13815
13864
|
if (scale && scale != 1) {
|
|
13816
13865
|
str += ' scale(' + scale + ')';
|
|
13817
13866
|
}
|
|
@@ -15539,6 +15588,139 @@ ${svg}
|
|
|
15539
15588
|
}];
|
|
15540
15589
|
}
|
|
15541
15590
|
|
|
15591
|
+
function exportRecordsAsFixedWidthString(fields, records, opts) {
|
|
15592
|
+
var rows = [], col;
|
|
15593
|
+
for (var i=0; i<fields.length; i++) {
|
|
15594
|
+
col = formatFixedWidthColumn(fields[i], records, opts);
|
|
15595
|
+
if (i === 0) {
|
|
15596
|
+
rows = col;
|
|
15597
|
+
} else for (var j=0; j<rows.length; j++) {
|
|
15598
|
+
rows[j] += ' ' + col[j];
|
|
15599
|
+
}
|
|
15600
|
+
}
|
|
15601
|
+
return rows.join('\n');
|
|
15602
|
+
}
|
|
15603
|
+
|
|
15604
|
+
function formatFixedWidthColumn(field, records, opts) {
|
|
15605
|
+
var arr = [],
|
|
15606
|
+
maxLen = field.length,
|
|
15607
|
+
n = records.length,
|
|
15608
|
+
i, val;
|
|
15609
|
+
arr.push(field);
|
|
15610
|
+
for (i=0; i<n; i++) {
|
|
15611
|
+
val = formatFixedWidthValue(records[i][field], opts);
|
|
15612
|
+
maxLen = Math.max(maxLen, val.length);
|
|
15613
|
+
arr.push(val);
|
|
15614
|
+
}
|
|
15615
|
+
for (i=0; i<arr.length; i++) {
|
|
15616
|
+
arr[i] = arr[i].padEnd(maxLen, ' ');
|
|
15617
|
+
}
|
|
15618
|
+
return arr;
|
|
15619
|
+
}
|
|
15620
|
+
|
|
15621
|
+
function formatFixedWidthValue(val, opts) {
|
|
15622
|
+
// TODO: remove duplication with mapshaper-delim-export.js
|
|
15623
|
+
var s;
|
|
15624
|
+
if (val == null) {
|
|
15625
|
+
s = '';
|
|
15626
|
+
} else if (utils.isString(val)) {
|
|
15627
|
+
s = val; // TODO: handle wide characters, newlines etc.
|
|
15628
|
+
} else if (utils.isNumber(val)) {
|
|
15629
|
+
s = opts.decimal_comma ? utils.formatIntlNumber(val) : utils.formatNumber(val);
|
|
15630
|
+
} else if (utils.isObject(val)) {
|
|
15631
|
+
s = JSON.stringify(val);
|
|
15632
|
+
} else {
|
|
15633
|
+
s = val + '';
|
|
15634
|
+
}
|
|
15635
|
+
return s;
|
|
15636
|
+
}
|
|
15637
|
+
|
|
15638
|
+
|
|
15639
|
+
function readFixedWidthRecords(reader, opts) {
|
|
15640
|
+
var str = reader.toString(opts.encoding || 'ascii');
|
|
15641
|
+
return readFixedWidthRecordsFromString(str, opts);
|
|
15642
|
+
}
|
|
15643
|
+
|
|
15644
|
+
function readFixedWidthRecordsFromString(str, ops) {
|
|
15645
|
+
var fields = parseFixedWidthInfo(str.substring(0, 2000));
|
|
15646
|
+
if (!fields) return [];
|
|
15647
|
+
var lines = utils.splitLines(str);
|
|
15648
|
+
if (lines[lines.length - 1] === '') lines.pop(); // handle newline at end of string
|
|
15649
|
+
var records = [];
|
|
15650
|
+
for (var i=1; i<lines.length; i++) {
|
|
15651
|
+
records.push(parseFixedWidthLine(lines[i], fields));
|
|
15652
|
+
}
|
|
15653
|
+
return records;
|
|
15654
|
+
}
|
|
15655
|
+
|
|
15656
|
+
function parseFixedWidthInfo(sample) {
|
|
15657
|
+
var lines = utils.splitLines(sample);
|
|
15658
|
+
if (lines.length > 2) lines.pop(); // remove possible partial line
|
|
15659
|
+
var n = getMaxLineLength(lines);
|
|
15660
|
+
var headerLine = lines[0];
|
|
15661
|
+
var colInfo = [];
|
|
15662
|
+
var colStart = 0;
|
|
15663
|
+
var inContent = false;
|
|
15664
|
+
var inHeader = false;
|
|
15665
|
+
var isContentChar, isHeaderChar, isColStart, colEnd;
|
|
15666
|
+
for (var i=0; i<=n; i++) {
|
|
15667
|
+
isHeaderChar = testContentChar(headerLine, i);
|
|
15668
|
+
isContentChar = !testEmptyCol(lines, i);
|
|
15669
|
+
isColStart = isHeaderChar && !inHeader;
|
|
15670
|
+
if (isColStart && inContent) {
|
|
15671
|
+
// all lines should have a space char in the position right before a header starts
|
|
15672
|
+
return null;
|
|
15673
|
+
}
|
|
15674
|
+
if (i == n || i > 0 && isColStart) {
|
|
15675
|
+
colEnd = i == n ? undefined : i-1;
|
|
15676
|
+
colInfo.push({
|
|
15677
|
+
name: readValue$1(headerLine, colStart, colEnd),
|
|
15678
|
+
end: colEnd,
|
|
15679
|
+
start: colStart
|
|
15680
|
+
});
|
|
15681
|
+
colStart = i;
|
|
15682
|
+
}
|
|
15683
|
+
inContent = isContentChar;
|
|
15684
|
+
inHeader = isHeaderChar;
|
|
15685
|
+
}
|
|
15686
|
+
return colInfo.length > 0 ? colInfo : null;
|
|
15687
|
+
}
|
|
15688
|
+
|
|
15689
|
+
function getMaxLineLength(lines) {
|
|
15690
|
+
var max = 0;
|
|
15691
|
+
for (var i=0; i<lines.length; i++) {
|
|
15692
|
+
max = Math.max(max, lines[i].length);
|
|
15693
|
+
}
|
|
15694
|
+
return max;
|
|
15695
|
+
}
|
|
15696
|
+
|
|
15697
|
+
function readValue$1(line, start, end) {
|
|
15698
|
+
return line.substring(start, end).trim();
|
|
15699
|
+
}
|
|
15700
|
+
|
|
15701
|
+
function parseFixedWidthLine(str, fields) {
|
|
15702
|
+
var obj = {}, field;
|
|
15703
|
+
for (var i=0; i<fields.length; i++) {
|
|
15704
|
+
field = fields[i];
|
|
15705
|
+
obj[field.name] = readValue$1(str, field.start, field.end);
|
|
15706
|
+
}
|
|
15707
|
+
return obj;
|
|
15708
|
+
}
|
|
15709
|
+
|
|
15710
|
+
function testContentChar(str, i) {
|
|
15711
|
+
return i < str.length && str[i] !== ' ';
|
|
15712
|
+
}
|
|
15713
|
+
|
|
15714
|
+
// return true iff all samples are blank at index i
|
|
15715
|
+
function testEmptyCol(samples, i) {
|
|
15716
|
+
var line;
|
|
15717
|
+
for (var j=0; j<samples.length; j++) {
|
|
15718
|
+
line = samples[j];
|
|
15719
|
+
if (testContentChar(line, i)) return false;
|
|
15720
|
+
}
|
|
15721
|
+
return true;
|
|
15722
|
+
}
|
|
15723
|
+
|
|
15542
15724
|
// Generate output content from a dataset object
|
|
15543
15725
|
function exportDelim(dataset, opts) {
|
|
15544
15726
|
var delim = getExportDelimiter(dataset.info, opts),
|
|
@@ -15560,6 +15742,9 @@ ${svg}
|
|
|
15560
15742
|
var encoding = opts.encoding || 'utf8';
|
|
15561
15743
|
var records = lyr.data.getRecords();
|
|
15562
15744
|
var fields = findFieldNames(records, opts.field_order);
|
|
15745
|
+
if (delim == ' ') {
|
|
15746
|
+
return exportRecordsAsFixedWidthString(fields, records, opts);
|
|
15747
|
+
}
|
|
15563
15748
|
var formatRow = getDelimRowFormatter(fields, delim, opts);
|
|
15564
15749
|
// exporting utf8 and ascii text as string by default (for now)
|
|
15565
15750
|
var exportAsString = encodingIsUtf8(encoding) && !opts.to_buffer &&
|
|
@@ -15617,15 +15802,6 @@ ${svg}
|
|
|
15617
15802
|
};
|
|
15618
15803
|
}
|
|
15619
15804
|
|
|
15620
|
-
function formatNumber$1(val) {
|
|
15621
|
-
return val + '';
|
|
15622
|
-
}
|
|
15623
|
-
|
|
15624
|
-
function formatIntlNumber(val) {
|
|
15625
|
-
var str = formatNumber$1(val);
|
|
15626
|
-
return '"' + str.replace('.', ',') + '"'; // need to quote if comma-delimited
|
|
15627
|
-
}
|
|
15628
|
-
|
|
15629
15805
|
function getDelimValueFormatter(delim, opts) {
|
|
15630
15806
|
var dquoteRxp = new RegExp('["\n\r' + delim + ']');
|
|
15631
15807
|
var decimalComma = opts && opts.decimal_comma || false;
|
|
@@ -15642,7 +15818,7 @@ ${svg}
|
|
|
15642
15818
|
} else if (utils.isString(val)) {
|
|
15643
15819
|
s = formatString(val);
|
|
15644
15820
|
} else if (utils.isNumber(val)) {
|
|
15645
|
-
s = decimalComma ? formatIntlNumber(val) : formatNumber
|
|
15821
|
+
s = decimalComma ? utils.formatIntlNumber(val) : utils.formatNumber(val);
|
|
15646
15822
|
} else if (utils.isObject(val)) {
|
|
15647
15823
|
s = formatString(JSON.stringify(val));
|
|
15648
15824
|
} else {
|
|
@@ -15685,8 +15861,6 @@ ${svg}
|
|
|
15685
15861
|
__proto__: null,
|
|
15686
15862
|
exportDelim: exportDelim,
|
|
15687
15863
|
exportLayerAsDSV: exportLayerAsDSV,
|
|
15688
|
-
formatNumber: formatNumber$1,
|
|
15689
|
-
formatIntlNumber: formatIntlNumber,
|
|
15690
15864
|
getDelimValueFormatter: getDelimValueFormatter
|
|
15691
15865
|
});
|
|
15692
15866
|
|
|
@@ -17050,8 +17224,9 @@ ${svg}
|
|
|
17050
17224
|
//
|
|
17051
17225
|
// TODO: confirm compatibility with all supported encodings
|
|
17052
17226
|
function readDelimRecords(reader, delim, optsArg) {
|
|
17227
|
+
var opts = optsArg || {};
|
|
17228
|
+
if (delim == ' ') return readFixedWidthRecords(reader, opts);
|
|
17053
17229
|
var reader2 = new Reader2(reader),
|
|
17054
|
-
opts = optsArg || {},
|
|
17055
17230
|
headerStr = readLinesAsString(reader2, getDelimHeaderLines(opts), opts.encoding),
|
|
17056
17231
|
header = parseDelimHeaderSection(headerStr, delim, opts),
|
|
17057
17232
|
convertRowArr = getRowConverter(header.import_fields),
|
|
@@ -17074,6 +17249,7 @@ ${svg}
|
|
|
17074
17249
|
// for delimiter characters and newlines. Input size is limited by the maximum
|
|
17075
17250
|
// string size.
|
|
17076
17251
|
function readDelimRecordsFromString(str, delim, opts) {
|
|
17252
|
+
if (delim == ' ') return readFixedWidthRecordsFromString(str, opts);
|
|
17077
17253
|
var header = parseDelimHeaderSection(str, delim, opts);
|
|
17078
17254
|
if (header.import_fields.length === 0 || !header.remainder) return [];
|
|
17079
17255
|
var convert = getRowConverter(header.import_fields);
|
|
@@ -17477,7 +17653,7 @@ ${svg}
|
|
|
17477
17653
|
};
|
|
17478
17654
|
}
|
|
17479
17655
|
|
|
17480
|
-
var supportedDelimiters = ['|', '\t', ',', ';'];
|
|
17656
|
+
var supportedDelimiters = ['|', '\t', ',', ';', ' '];
|
|
17481
17657
|
|
|
17482
17658
|
function isSupportedDelimiter(d) {
|
|
17483
17659
|
return utils.contains(supportedDelimiters, d);
|
|
@@ -17838,9 +18014,21 @@ ${svg}
|
|
|
17838
18014
|
}
|
|
17839
18015
|
|
|
17840
18016
|
function cleanArgv(argv) {
|
|
17841
|
-
|
|
18017
|
+
// Note: original trim caused some quoted spaces to be removed
|
|
18018
|
+
// (e.g. bash shell seems to convert [delimiter=" "] to [delimiter= ],
|
|
18019
|
+
// which then got trimmed to [delimiter=] below)
|
|
18020
|
+
//// argv = argv.map(function(s) {return s.trim();}); // trim whitespace
|
|
18021
|
+
|
|
18022
|
+
// Updated: don't trim space from tokens like [delimeter= ]
|
|
18023
|
+
argv = argv.map(function(s) {
|
|
18024
|
+
if (!/= $/.test(s)) {
|
|
18025
|
+
s = s.trimEnd();
|
|
18026
|
+
}
|
|
18027
|
+
s = s.trimStart();
|
|
18028
|
+
return s;
|
|
18029
|
+
});
|
|
17842
18030
|
argv = argv.filter(function(s) {return s !== '';}); // remove empty tokens
|
|
17843
|
-
// removing trimQuotes() call... now, strings like 'name="Meg"' will no longer
|
|
18031
|
+
// Note: removing trimQuotes() call... now, strings like 'name="Meg"' will no longer
|
|
17844
18032
|
// be parsed the same way as name=Meg and name="Meg"
|
|
17845
18033
|
//// argv = argv.map(utils.trimQuotes); // remove one level of single or dbl quotes
|
|
17846
18034
|
return argv;
|
|
@@ -18947,6 +19135,28 @@ ${svg}
|
|
|
18947
19135
|
'$ mapshaper data.json -colorizer name=getColor nodata=#eee breaks=20,40 \\\n' +
|
|
18948
19136
|
' colors=#e0f3db,#a8ddb5,#43a2ca -each \'fill = getColor(RATING)\' -o output.json');
|
|
18949
19137
|
|
|
19138
|
+
parser.command('dashlines')
|
|
19139
|
+
.describe('split lines into sections, with or without a gap')
|
|
19140
|
+
.oldAlias('split-lines')
|
|
19141
|
+
.option('dash-length', {
|
|
19142
|
+
type: 'distance',
|
|
19143
|
+
describe: 'length of split-apart lines (e.g. 200km)'
|
|
19144
|
+
})
|
|
19145
|
+
.option('gap-length', {
|
|
19146
|
+
type: 'distance',
|
|
19147
|
+
describe: 'length of gaps between dashes (default is 0)'
|
|
19148
|
+
})
|
|
19149
|
+
.option('scaled', {
|
|
19150
|
+
type: 'flag',
|
|
19151
|
+
describe: 'scale dashes and gaps to prevent partial dashes'
|
|
19152
|
+
})
|
|
19153
|
+
.option('planar', {
|
|
19154
|
+
type: 'flag',
|
|
19155
|
+
describe: 'use planar geometry'
|
|
19156
|
+
})
|
|
19157
|
+
.option('where', whereOpt)
|
|
19158
|
+
.option('target', targetOpt);
|
|
19159
|
+
|
|
18950
19160
|
parser.command('define')
|
|
18951
19161
|
// .describe('define expression variables')
|
|
18952
19162
|
.option('expression', {
|
|
@@ -19143,6 +19353,10 @@ ${svg}
|
|
|
19143
19353
|
.option('keep-shapes', {
|
|
19144
19354
|
type: 'flag'
|
|
19145
19355
|
})
|
|
19356
|
+
.option('ids', {
|
|
19357
|
+
// describe: 'filter on a list of feature ids',
|
|
19358
|
+
type: 'numbers'
|
|
19359
|
+
})
|
|
19146
19360
|
.option('cleanup', {type: 'flag'}) // TODO: document
|
|
19147
19361
|
.option('name', nameOpt)
|
|
19148
19362
|
.option('target', targetOpt)
|
|
@@ -19711,6 +19925,11 @@ ${svg}
|
|
|
19711
19925
|
DEFAULT: true,
|
|
19712
19926
|
describe: 'expression or field for grouping features and naming split layers'
|
|
19713
19927
|
})
|
|
19928
|
+
.option('ids', {
|
|
19929
|
+
// used by gui history to split on selected features
|
|
19930
|
+
// describe: 'split on a list of feature ids',
|
|
19931
|
+
type: 'numbers'
|
|
19932
|
+
})
|
|
19714
19933
|
.option('apart', {
|
|
19715
19934
|
describe: 'save output layers to independent datasets',
|
|
19716
19935
|
type: 'flag'
|
|
@@ -20122,6 +20341,16 @@ ${svg}
|
|
|
20122
20341
|
})
|
|
20123
20342
|
.option('name', nameOpt);
|
|
20124
20343
|
|
|
20344
|
+
// parser.command('shapes')
|
|
20345
|
+
// .describe('convert points to shapes')
|
|
20346
|
+
// .option('type', {
|
|
20347
|
+
// })
|
|
20348
|
+
// .option('size', {
|
|
20349
|
+
// })
|
|
20350
|
+
// .option('rotation', {
|
|
20351
|
+
// })
|
|
20352
|
+
|
|
20353
|
+
|
|
20125
20354
|
parser.command('subdivide')
|
|
20126
20355
|
.describe('recursively split a layer using a JS expression')
|
|
20127
20356
|
.validate(validateExpressionOpt)
|
|
@@ -20990,7 +21219,7 @@ ${svg}
|
|
|
20990
21219
|
var header = parseHeader(shpFile.readToBinArray(0, 100));
|
|
20991
21220
|
var shpSize = shpFile.size();
|
|
20992
21221
|
var RecordClass = new ShpRecordClass(header.type);
|
|
20993
|
-
var shpOffset, recordCount
|
|
21222
|
+
var shpOffset, recordCount;
|
|
20994
21223
|
var shxBin, shxFile;
|
|
20995
21224
|
|
|
20996
21225
|
if (shxSrc) {
|
|
@@ -21017,61 +21246,36 @@ ${svg}
|
|
|
21017
21246
|
|
|
21018
21247
|
// Iterator interface for reading shape records
|
|
21019
21248
|
this.nextShape = function() {
|
|
21020
|
-
var shape = readNextShape();
|
|
21021
|
-
if (
|
|
21022
|
-
|
|
21023
|
-
|
|
21024
|
-
// ne_10m_admin_0_boundary_lines_land.shp
|
|
21025
|
-
// ne_110m_admin_0_scale_rank.shp
|
|
21026
|
-
verbose("Skipped over " + skippedBytes + " non-data bytes in the .shp file.");
|
|
21027
|
-
}
|
|
21249
|
+
var shape = readNextShape(recordCount);
|
|
21250
|
+
if (shape) {
|
|
21251
|
+
recordCount++;
|
|
21252
|
+
} else {
|
|
21028
21253
|
shpFile.close();
|
|
21029
21254
|
reset();
|
|
21030
21255
|
}
|
|
21031
21256
|
return shape;
|
|
21032
21257
|
};
|
|
21033
21258
|
|
|
21034
|
-
|
|
21035
|
-
|
|
21259
|
+
// Returns a shape record or null if no more shapes can be read
|
|
21260
|
+
//
|
|
21261
|
+
function readNextShape(i) {
|
|
21262
|
+
var expectedId = i + 1; // Shapefile ids are 1-based
|
|
21036
21263
|
var shape, offset;
|
|
21037
|
-
if (done()) return null;
|
|
21038
21264
|
if (shxBin) {
|
|
21039
|
-
|
|
21265
|
+
if (shxFile.size() <= 100 + i * 8) return null; // done
|
|
21266
|
+
shxBin.position(100 + i * 8);
|
|
21040
21267
|
offset = shxBin.readUint32() * 2;
|
|
21041
|
-
|
|
21042
|
-
skippedBytes += offset - shpOffset;
|
|
21043
|
-
}
|
|
21268
|
+
shape = readIndexedShape(shpFile, offset, expectedId);
|
|
21044
21269
|
} else {
|
|
21270
|
+
// Reading without a .shx file (returns null at end-of-file)
|
|
21045
21271
|
offset = shpOffset;
|
|
21046
|
-
|
|
21047
|
-
shape = readShapeAtOffset(offset);
|
|
21048
|
-
if (!shape) {
|
|
21049
|
-
// Some in-the-wild .shp files contain junk bytes between records. This
|
|
21050
|
-
// is a problem if the .shx index file is not present.
|
|
21051
|
-
// Here, we try to scan past the junk to find the next record.
|
|
21052
|
-
shape = huntForNextShape(offset, expectedId);
|
|
21053
|
-
}
|
|
21054
|
-
if (shape) {
|
|
21055
|
-
if (shape.id < expectedId) {
|
|
21056
|
-
message("Found a Shapefile record with the same id as a previous record (" + shape.id + ") -- skipping.");
|
|
21057
|
-
return readNextShape();
|
|
21058
|
-
} else if (shape.id > expectedId) {
|
|
21059
|
-
stop("Shapefile contains an out-of-sequence record. Possible data corruption -- bailing.");
|
|
21060
|
-
}
|
|
21061
|
-
recordCount++;
|
|
21272
|
+
shape = readNonIndexedShape(shpFile, offset, expectedId);
|
|
21062
21273
|
}
|
|
21063
21274
|
return shape || null;
|
|
21064
21275
|
}
|
|
21065
21276
|
|
|
21066
|
-
function done() {
|
|
21067
|
-
if (shxFile && shxFile.size() <= 100 + recordCount * 8) return true;
|
|
21068
|
-
if (shpOffset + 12 > shpSize) return true;
|
|
21069
|
-
return false;
|
|
21070
|
-
}
|
|
21071
|
-
|
|
21072
21277
|
function reset() {
|
|
21073
21278
|
shpOffset = 100;
|
|
21074
|
-
skippedBytes = 0;
|
|
21075
21279
|
recordCount = 0;
|
|
21076
21280
|
}
|
|
21077
21281
|
|
|
@@ -21101,7 +21305,8 @@ ${svg}
|
|
|
21101
21305
|
return header;
|
|
21102
21306
|
}
|
|
21103
21307
|
|
|
21104
|
-
|
|
21308
|
+
|
|
21309
|
+
function readShapeAtOffset(shpFile, offset) {
|
|
21105
21310
|
var shape = null,
|
|
21106
21311
|
recordSize, recordType, recordId, goodSize, goodType, bin;
|
|
21107
21312
|
|
|
@@ -21116,32 +21321,62 @@ ${svg}
|
|
|
21116
21321
|
if (goodSize && goodType) {
|
|
21117
21322
|
bin = shpFile.readToBinArray(offset, recordSize);
|
|
21118
21323
|
shape = new RecordClass(bin, recordSize);
|
|
21119
|
-
shpOffset = offset + shape.byteLength; // advance read position
|
|
21120
21324
|
}
|
|
21121
21325
|
}
|
|
21122
21326
|
return shape;
|
|
21123
21327
|
}
|
|
21124
21328
|
|
|
21125
|
-
|
|
21126
|
-
|
|
21127
|
-
|
|
21128
|
-
|
|
21329
|
+
function readIndexedShape(shpFile, offset, expectedId) {
|
|
21330
|
+
var shape = readShapeAtOffset(shpFile, offset);
|
|
21331
|
+
if (!shape) {
|
|
21332
|
+
stop('Index of Shapefile record', expectedId, 'in the .shx file is invalid.');
|
|
21333
|
+
}
|
|
21334
|
+
if (shape.id != expectedId) {
|
|
21335
|
+
// stop("Found a Shapefile record with an out-of-sequence id (" + shape.id + ") -- bailing.");
|
|
21336
|
+
message(`Warning: A feature has a different record number in .shx (${expectedId}) and .shp (${shape.id}).`);
|
|
21337
|
+
}
|
|
21338
|
+
// TODO: consider printing verbose message if a .shp file contains garbage bytes
|
|
21339
|
+
// example files:
|
|
21340
|
+
// ne_10m_admin_0_boundary_lines_land.shp
|
|
21341
|
+
// ne_110m_admin_0_scale_rank.shp
|
|
21342
|
+
return shape;
|
|
21343
|
+
}
|
|
21344
|
+
|
|
21345
|
+
// The Shapefile specification does not require records to be densely packed or
|
|
21346
|
+
// in consecutive sequence in the .shp file. This is a problem when the .shx
|
|
21347
|
+
// index file is not present.
|
|
21348
|
+
//
|
|
21349
|
+
// Here, we try to scan past invalid content to find the next record.
|
|
21350
|
+
// Records are required to be in sequential order.
|
|
21351
|
+
//
|
|
21352
|
+
function readNonIndexedShape(shpFile, start, expectedId) {
|
|
21353
|
+
var offset = start,
|
|
21354
|
+
fileSize = shpFile.size(),
|
|
21129
21355
|
shape = null,
|
|
21130
|
-
bin, recordId, recordType,
|
|
21131
|
-
while (offset + 12 <=
|
|
21356
|
+
bin, recordId, recordType, isValidType;
|
|
21357
|
+
while (offset + 12 <= fileSize) {
|
|
21132
21358
|
bin = shpFile.readToBinArray(offset, 12);
|
|
21133
21359
|
recordId = bin.bigEndian().readUint32();
|
|
21134
21360
|
recordType = bin.littleEndian().skipBytes(4).readUint32();
|
|
21135
|
-
|
|
21136
|
-
|
|
21137
|
-
|
|
21138
|
-
|
|
21361
|
+
isValidType = recordType == header.type || recordType === 0;
|
|
21362
|
+
if (!isValidType || recordId != expectedId && recordType === 0) {
|
|
21363
|
+
offset += 4; // keep scanning -- try next integer position
|
|
21364
|
+
continue;
|
|
21139
21365
|
}
|
|
21140
|
-
|
|
21366
|
+
shape = readShapeAtOffset(shpFile, offset);
|
|
21367
|
+
if (!shape) break; // probably ran into end of file
|
|
21368
|
+
shpOffset = offset + shape.byteLength; // update
|
|
21369
|
+
if (recordId == expectedId) break; // found an apparently valid shape
|
|
21370
|
+
if (recordId < expectedId) {
|
|
21371
|
+
message("Found a Shapefile record with the same id as a previous record (" + shape.id + ") -- skipping.");
|
|
21372
|
+
offset += shape.byteLength;
|
|
21373
|
+
} else {
|
|
21374
|
+
stop("Shapefile contains an out-of-sequence record. Possible data corruption -- bailing.");
|
|
21375
|
+
}
|
|
21376
|
+
}
|
|
21377
|
+
if (shape && offset > start) {
|
|
21378
|
+
verbose("Skipped over " + (offset - start) + " non-data bytes in the .shp file.");
|
|
21141
21379
|
}
|
|
21142
|
-
count = shape ? offset - start : shpSize - start;
|
|
21143
|
-
// debug('Skipped', count, 'bytes', shape ? 'before record ' + id : 'at the end of the file');
|
|
21144
|
-
skippedBytes += count;
|
|
21145
21380
|
return shape;
|
|
21146
21381
|
}
|
|
21147
21382
|
}
|
|
@@ -26263,97 +26498,6 @@ ${svg}
|
|
|
26263
26498
|
geom.segmentIntersection(a[0], a[1], b[0], b[1], bb.xmax, bb.ymin, bb.xmin, bb.ymin));
|
|
26264
26499
|
}
|
|
26265
26500
|
|
|
26266
|
-
function getGeodesic(P) {
|
|
26267
|
-
if (!isLatLngCRS(P)) error('Expected an unprojected CRS');
|
|
26268
|
-
var f = P.es / (1 + Math.sqrt(P.one_es));
|
|
26269
|
-
var GeographicLib = require('mproj').internal.GeographicLib;
|
|
26270
|
-
return new GeographicLib.Geodesic.Geodesic(P.a, f);
|
|
26271
|
-
}
|
|
26272
|
-
|
|
26273
|
-
function getPlanarSegmentEndpoint(x, y, bearing, meterDist) {
|
|
26274
|
-
var rad = bearing / 180 * Math.PI;
|
|
26275
|
-
var dx = Math.sin(rad) * meterDist;
|
|
26276
|
-
var dy = Math.cos(rad) * meterDist;
|
|
26277
|
-
return [x + dx, y + dy];
|
|
26278
|
-
}
|
|
26279
|
-
|
|
26280
|
-
// source: https://github.com/mapbox/cheap-ruler/blob/master/index.js
|
|
26281
|
-
function fastGeodeticSegmentFunction(lng, lat, bearing, meterDist) {
|
|
26282
|
-
var D2R = Math.PI / 180;
|
|
26283
|
-
var cos = Math.cos(lat * D2R);
|
|
26284
|
-
var cos2 = 2 * cos * cos - 1;
|
|
26285
|
-
var cos3 = 2 * cos * cos2 - cos;
|
|
26286
|
-
var cos4 = 2 * cos * cos3 - cos2;
|
|
26287
|
-
var cos5 = 2 * cos * cos4 - cos3;
|
|
26288
|
-
var kx = (111.41513 * cos - 0.09455 * cos3 + 0.00012 * cos5) * 1000;
|
|
26289
|
-
var ky = (111.13209 - 0.56605 * cos2 + 0.0012 * cos4) * 1000;
|
|
26290
|
-
var bearingRad = bearing * D2R;
|
|
26291
|
-
var lat2 = lat + Math.cos(bearingRad) * meterDist / ky;
|
|
26292
|
-
var lng2 = lng + Math.sin(bearingRad) * meterDist / kx;
|
|
26293
|
-
return [lng2, lat2];
|
|
26294
|
-
}
|
|
26295
|
-
|
|
26296
|
-
function getGeodeticSegmentFunction(P) {
|
|
26297
|
-
if (!isLatLngCRS(P)) {
|
|
26298
|
-
return getPlanarSegmentEndpoint;
|
|
26299
|
-
}
|
|
26300
|
-
var g = getGeodesic(P);
|
|
26301
|
-
return function(lng, lat, bearing, meterDist) {
|
|
26302
|
-
var o = g.Direct(lat, lng, bearing, meterDist);
|
|
26303
|
-
var p = [o.lon2, o.lat2];
|
|
26304
|
-
return p;
|
|
26305
|
-
};
|
|
26306
|
-
}
|
|
26307
|
-
|
|
26308
|
-
function getFastGeodeticSegmentFunction(P) {
|
|
26309
|
-
// CAREFUL: this function has higher error at very large distances and at the poles
|
|
26310
|
-
// also, it wouldn't work for other planets than Earth
|
|
26311
|
-
return isLatLngCRS(P) ? fastGeodeticSegmentFunction : getPlanarSegmentEndpoint;
|
|
26312
|
-
}
|
|
26313
|
-
|
|
26314
|
-
// Useful for determining if a segment that intersects another segment is
|
|
26315
|
-
// entering or leaving an enclosed buffer area
|
|
26316
|
-
// returns -1 if angle of p1p2 -> p3p4 is counter-clockwise (left turn)
|
|
26317
|
-
// returns 1 if angle is clockwise
|
|
26318
|
-
// return 0 if segments are collinear
|
|
26319
|
-
function segmentTurn(p1, p2, p3, p4) {
|
|
26320
|
-
var ax = p1[0],
|
|
26321
|
-
ay = p1[1],
|
|
26322
|
-
bx = p2[0],
|
|
26323
|
-
by = p2[1],
|
|
26324
|
-
// shift p3p4 segment to start at p2
|
|
26325
|
-
dx = bx - p3[0],
|
|
26326
|
-
dy = by - p3[1],
|
|
26327
|
-
cx = p4[0] + dx,
|
|
26328
|
-
cy = p4[1] + dy,
|
|
26329
|
-
orientation = geom.orient2D(ax, ay, bx, by, cx, cy);
|
|
26330
|
-
if (!orientation) return 0;
|
|
26331
|
-
return orientation < 0 ? 1 : -1;
|
|
26332
|
-
}
|
|
26333
|
-
|
|
26334
|
-
function bearingDegrees(a, b, c, d) {
|
|
26335
|
-
return geom.bearing(a, b, c, d) * 180 / Math.PI;
|
|
26336
|
-
}
|
|
26337
|
-
|
|
26338
|
-
function bearingDegrees2D(a, b, c, d) {
|
|
26339
|
-
return geom.bearing2D(a, b, c, d) * 180 / Math.PI;
|
|
26340
|
-
}
|
|
26341
|
-
|
|
26342
|
-
// return function to calculate bearing of a segment in degrees
|
|
26343
|
-
function getBearingFunction(dataset) {
|
|
26344
|
-
var P = getDatasetCRS(dataset);
|
|
26345
|
-
return isLatLngCRS(P) ? bearingDegrees : bearingDegrees2D;
|
|
26346
|
-
}
|
|
26347
|
-
|
|
26348
|
-
var Geodesic = /*#__PURE__*/Object.freeze({
|
|
26349
|
-
__proto__: null,
|
|
26350
|
-
getPlanarSegmentEndpoint: getPlanarSegmentEndpoint,
|
|
26351
|
-
getGeodeticSegmentFunction: getGeodeticSegmentFunction,
|
|
26352
|
-
getFastGeodeticSegmentFunction: getFastGeodeticSegmentFunction,
|
|
26353
|
-
segmentTurn: segmentTurn,
|
|
26354
|
-
getBearingFunction: getBearingFunction
|
|
26355
|
-
});
|
|
26356
|
-
|
|
26357
26501
|
function getPolylineBufferMaker2(arcs, geod, getBearing, opts) {
|
|
26358
26502
|
var makeLeftBuffer = getPathBufferMaker2(arcs, geod, getBearing, opts);
|
|
26359
26503
|
var geomType = opts.geometry_type;
|
|
@@ -26647,6 +26791,101 @@ ${svg}
|
|
|
26647
26791
|
};
|
|
26648
26792
|
}
|
|
26649
26793
|
|
|
26794
|
+
// GeographicLib docs: https://geographiclib.sourceforge.io/html/js/
|
|
26795
|
+
// https://geographiclib.sourceforge.io/html/js/module-GeographicLib_Geodesic.Geodesic.html
|
|
26796
|
+
// https://geographiclib.sourceforge.io/html/js/tutorial-2-interface.html
|
|
26797
|
+
function getGeodesic(P) {
|
|
26798
|
+
if (!isLatLngCRS(P)) error('Expected an unprojected CRS');
|
|
26799
|
+
var f = P.es / (1 + Math.sqrt(P.one_es));
|
|
26800
|
+
var GeographicLib = require('mproj').internal.GeographicLib;
|
|
26801
|
+
return new GeographicLib.Geodesic.Geodesic(P.a, f);
|
|
26802
|
+
}
|
|
26803
|
+
|
|
26804
|
+
function interpolatePoint2D(ax, ay, bx, by, k) {
|
|
26805
|
+
var j = 1 - k;
|
|
26806
|
+
return [ax * j + bx * k, ay * j + by * k];
|
|
26807
|
+
}
|
|
26808
|
+
|
|
26809
|
+
function getInterpolationFunction(P) {
|
|
26810
|
+
var spherical = P && isLatLngCRS(P);
|
|
26811
|
+
if (!spherical) return interpolatePoint2D;
|
|
26812
|
+
var geod = getGeodesic(P);
|
|
26813
|
+
return function(lng, lat, lng2, lat2, k) {
|
|
26814
|
+
var r = geod.Inverse(lat, lng, lat2, lng2);
|
|
26815
|
+
var dist = r.s12 * k;
|
|
26816
|
+
var r2 = geod.Direct(lat, lng, r.azi1, dist);
|
|
26817
|
+
return [r2.lon2, r2.lat2];
|
|
26818
|
+
};
|
|
26819
|
+
}
|
|
26820
|
+
|
|
26821
|
+
function getPlanarSegmentEndpoint(x, y, bearing, meterDist) {
|
|
26822
|
+
var rad = bearing / 180 * Math.PI;
|
|
26823
|
+
var dx = Math.sin(rad) * meterDist;
|
|
26824
|
+
var dy = Math.cos(rad) * meterDist;
|
|
26825
|
+
return [x + dx, y + dy];
|
|
26826
|
+
}
|
|
26827
|
+
|
|
26828
|
+
// source: https://github.com/mapbox/cheap-ruler/blob/master/index.js
|
|
26829
|
+
function fastGeodeticSegmentFunction(lng, lat, bearing, meterDist) {
|
|
26830
|
+
var D2R = Math.PI / 180;
|
|
26831
|
+
var cos = Math.cos(lat * D2R);
|
|
26832
|
+
var cos2 = 2 * cos * cos - 1;
|
|
26833
|
+
var cos3 = 2 * cos * cos2 - cos;
|
|
26834
|
+
var cos4 = 2 * cos * cos3 - cos2;
|
|
26835
|
+
var cos5 = 2 * cos * cos4 - cos3;
|
|
26836
|
+
var kx = (111.41513 * cos - 0.09455 * cos3 + 0.00012 * cos5) * 1000;
|
|
26837
|
+
var ky = (111.13209 - 0.56605 * cos2 + 0.0012 * cos4) * 1000;
|
|
26838
|
+
var bearingRad = bearing * D2R;
|
|
26839
|
+
var lat2 = lat + Math.cos(bearingRad) * meterDist / ky;
|
|
26840
|
+
var lng2 = lng + Math.sin(bearingRad) * meterDist / kx;
|
|
26841
|
+
return [lng2, lat2];
|
|
26842
|
+
}
|
|
26843
|
+
|
|
26844
|
+
function getGeodeticSegmentFunction(P) {
|
|
26845
|
+
if (!isLatLngCRS(P)) {
|
|
26846
|
+
return getPlanarSegmentEndpoint;
|
|
26847
|
+
}
|
|
26848
|
+
var g = getGeodesic(P);
|
|
26849
|
+
return function(lng, lat, bearing, meterDist) {
|
|
26850
|
+
var o = g.Direct(lat, lng, bearing, meterDist);
|
|
26851
|
+
var p = [o.lon2, o.lat2];
|
|
26852
|
+
return p;
|
|
26853
|
+
};
|
|
26854
|
+
}
|
|
26855
|
+
|
|
26856
|
+
function getFastGeodeticSegmentFunction(P) {
|
|
26857
|
+
// CAREFUL: this function has higher error at very large distances and at the poles
|
|
26858
|
+
// also, it wouldn't work for other planets than Earth
|
|
26859
|
+
return isLatLngCRS(P) ? fastGeodeticSegmentFunction : getPlanarSegmentEndpoint;
|
|
26860
|
+
}
|
|
26861
|
+
|
|
26862
|
+
|
|
26863
|
+
function bearingDegrees(a, b, c, d) {
|
|
26864
|
+
return geom.bearing(a, b, c, d) * 180 / Math.PI;
|
|
26865
|
+
}
|
|
26866
|
+
|
|
26867
|
+
function bearingDegrees2D(a, b, c, d) {
|
|
26868
|
+
return geom.bearing2D(a, b, c, d) * 180 / Math.PI;
|
|
26869
|
+
}
|
|
26870
|
+
|
|
26871
|
+
// return function to calculate bearing of a segment in degrees
|
|
26872
|
+
function getBearingFunction(dataset) {
|
|
26873
|
+
var P = getDatasetCRS(dataset);
|
|
26874
|
+
return isLatLngCRS(P) ? bearingDegrees : bearingDegrees2D;
|
|
26875
|
+
}
|
|
26876
|
+
|
|
26877
|
+
var Geodesic = /*#__PURE__*/Object.freeze({
|
|
26878
|
+
__proto__: null,
|
|
26879
|
+
interpolatePoint2D: interpolatePoint2D,
|
|
26880
|
+
getInterpolationFunction: getInterpolationFunction,
|
|
26881
|
+
getPlanarSegmentEndpoint: getPlanarSegmentEndpoint,
|
|
26882
|
+
getGeodeticSegmentFunction: getGeodeticSegmentFunction,
|
|
26883
|
+
getFastGeodeticSegmentFunction: getFastGeodeticSegmentFunction,
|
|
26884
|
+
bearingDegrees: bearingDegrees,
|
|
26885
|
+
bearingDegrees2D: bearingDegrees2D,
|
|
26886
|
+
getBearingFunction: getBearingFunction
|
|
26887
|
+
});
|
|
26888
|
+
|
|
26650
26889
|
function makePolylineBuffer(lyr, dataset, opts) {
|
|
26651
26890
|
var geojson = makeShapeBufferGeoJSON(lyr, dataset, opts);
|
|
26652
26891
|
var dataset2 = importGeoJSON(geojson, {});
|
|
@@ -29682,6 +29921,178 @@ ${svg}
|
|
|
29682
29921
|
getColorizerFunction: getColorizerFunction
|
|
29683
29922
|
});
|
|
29684
29923
|
|
|
29924
|
+
function expressionUsesGeoJSON(exp) {
|
|
29925
|
+
return exp.includes('this.geojson');
|
|
29926
|
+
}
|
|
29927
|
+
|
|
29928
|
+
function getFeatureEditor(lyr, dataset) {
|
|
29929
|
+
var changed = false;
|
|
29930
|
+
var api = {};
|
|
29931
|
+
// need to copy attribute to avoid circular references if geojson is assigned
|
|
29932
|
+
// to a data property.
|
|
29933
|
+
var copy = copyLayer(lyr);
|
|
29934
|
+
var features = exportLayerAsGeoJSON(copy, dataset, {}, true);
|
|
29935
|
+
|
|
29936
|
+
api.get = function(i) {
|
|
29937
|
+
return features[i];
|
|
29938
|
+
};
|
|
29939
|
+
|
|
29940
|
+
api.set = function(feat, i) {
|
|
29941
|
+
changed = true;
|
|
29942
|
+
if (utils.isString(feat)) {
|
|
29943
|
+
feat = JSON.parse(feat);
|
|
29944
|
+
}
|
|
29945
|
+
features[i] = GeoJSON.toFeature(feat); // TODO: validate
|
|
29946
|
+
};
|
|
29947
|
+
|
|
29948
|
+
api.done = function() {
|
|
29949
|
+
if (!changed) return; // read-only expression
|
|
29950
|
+
// TODO: validate number of features, etc.
|
|
29951
|
+
var geojson = {
|
|
29952
|
+
type: 'FeatureCollection',
|
|
29953
|
+
features: features
|
|
29954
|
+
};
|
|
29955
|
+
|
|
29956
|
+
// console.log(JSON.stringify(geojson, null, 2))
|
|
29957
|
+
return importGeoJSON(geojson);
|
|
29958
|
+
};
|
|
29959
|
+
return api;
|
|
29960
|
+
}
|
|
29961
|
+
|
|
29962
|
+
cmd.dashlines = function(lyr, dataset, opts) {
|
|
29963
|
+
var crs = getDatasetCRS(dataset);
|
|
29964
|
+
var defs = getStateVar('defs');
|
|
29965
|
+
var exp = `this.geojson = splitFeature(this.geojson)`;
|
|
29966
|
+
requirePolylineLayer(lyr);
|
|
29967
|
+
defs.splitFeature = getSplitFeatureFunction(crs, opts);
|
|
29968
|
+
cmd.evaluateEachFeature(lyr, dataset, exp, opts);
|
|
29969
|
+
delete defs.splitFeature;
|
|
29970
|
+
};
|
|
29971
|
+
|
|
29972
|
+
function getSplitFeatureFunction(crs, opts) {
|
|
29973
|
+
var dashLen = opts.dash_length ? convertDistanceParam(opts.dash_length, crs) : 0;
|
|
29974
|
+
var gapLen = opts.gap_length ? convertDistanceParam(opts.gap_length, crs) : 0;
|
|
29975
|
+
if (dashLen > 0 === false) {
|
|
29976
|
+
stop('Missing required dash-length parameter');
|
|
29977
|
+
}
|
|
29978
|
+
if (gapLen >= 0 == false) {
|
|
29979
|
+
stop('Invalid gap-length option');
|
|
29980
|
+
}
|
|
29981
|
+
var splitLine = getSplitLineFunction(crs, dashLen, gapLen, opts);
|
|
29982
|
+
return function(feat) {
|
|
29983
|
+
var geom = feat.geometry;
|
|
29984
|
+
if (!geom) return feat;
|
|
29985
|
+
if (geom.type == 'LineString') {
|
|
29986
|
+
geom.type = 'MultiLineString';
|
|
29987
|
+
geom.coordinates = [geom.coordinates];
|
|
29988
|
+
}
|
|
29989
|
+
if (geom.type != 'MultiLineString') {
|
|
29990
|
+
error('Unexpected geometry:', geom.type);
|
|
29991
|
+
}
|
|
29992
|
+
geom.coordinates = geom.coordinates.reduce(function(memo, coords) {
|
|
29993
|
+
try {
|
|
29994
|
+
var parts = splitLine(coords);
|
|
29995
|
+
memo = memo.concat(parts);
|
|
29996
|
+
} catch(e) {
|
|
29997
|
+
console.error(e);
|
|
29998
|
+
throw e;
|
|
29999
|
+
}
|
|
30000
|
+
return memo;
|
|
30001
|
+
}, []);
|
|
30002
|
+
|
|
30003
|
+
return feat;
|
|
30004
|
+
};
|
|
30005
|
+
}
|
|
30006
|
+
|
|
30007
|
+
function getSplitLineFunction(crs, dashLen, gapLen, opts) {
|
|
30008
|
+
var planar = !!opts.planar;
|
|
30009
|
+
var interpolate = getInterpolationFunction(planar ? null : crs);
|
|
30010
|
+
var distance = isLatLngCRS(crs) ? greatCircleDistance : distance2D;
|
|
30011
|
+
var inDash, parts2, interval, scale;
|
|
30012
|
+
function addPart(coords) {
|
|
30013
|
+
if (inDash) parts2.push(coords);
|
|
30014
|
+
if (gapLen > 0) {
|
|
30015
|
+
inDash = !inDash;
|
|
30016
|
+
interval = scale * (inDash ? dashLen : gapLen);
|
|
30017
|
+
}
|
|
30018
|
+
}
|
|
30019
|
+
|
|
30020
|
+
return function splitLineString(coords) {
|
|
30021
|
+
var elapsedDist = 0;
|
|
30022
|
+
var p = coords[0];
|
|
30023
|
+
var coords2 = [p];
|
|
30024
|
+
var segLen, pct, prev;
|
|
30025
|
+
if (opts.scaled) {
|
|
30026
|
+
scale = scaleDashes(dashLen, gapLen, getLineLength(coords, distance));
|
|
30027
|
+
} else {
|
|
30028
|
+
scale = 1;
|
|
30029
|
+
}
|
|
30030
|
+
// init this LineString
|
|
30031
|
+
inDash = gapLen > 0 ? false : true;
|
|
30032
|
+
interval = scale * (inDash ? dashLen : gapLen);
|
|
30033
|
+
if (!inDash) {
|
|
30034
|
+
// start gapped lines with a half-gap
|
|
30035
|
+
// (a half-gap or a half-dash is probably better for rings and intersecting lines)
|
|
30036
|
+
interval *= 0.5;
|
|
30037
|
+
}
|
|
30038
|
+
parts2 = [];
|
|
30039
|
+
for (var i=1, n=coords.length; i<n; i++) {
|
|
30040
|
+
prev = p;
|
|
30041
|
+
p = coords[i];
|
|
30042
|
+
segLen = distance(prev[0], prev[1], p[0], p[1]);
|
|
30043
|
+
if (segLen <= 0) continue;
|
|
30044
|
+
while (elapsedDist + segLen >= interval) {
|
|
30045
|
+
// this segment contains a break either within it or at the far endpoint
|
|
30046
|
+
pct = (interval - elapsedDist) / segLen;
|
|
30047
|
+
if (pct > 0.999 && i == n - 1) {
|
|
30048
|
+
// snap to endpoint (so fp rounding errors don't result in a tiny
|
|
30049
|
+
// last segment)
|
|
30050
|
+
pct = 1;
|
|
30051
|
+
}
|
|
30052
|
+
if (pct < 1) {
|
|
30053
|
+
prev = interpolate(prev[0], prev[1], p[0], p[1], pct);
|
|
30054
|
+
} else {
|
|
30055
|
+
prev = p;
|
|
30056
|
+
}
|
|
30057
|
+
coords2.push(prev);
|
|
30058
|
+
addPart(coords2);
|
|
30059
|
+
// start a new part
|
|
30060
|
+
coords2 = pct < 1 ? [prev] : [];
|
|
30061
|
+
elapsedDist = 0;
|
|
30062
|
+
segLen = (1 - pct) * segLen;
|
|
30063
|
+
}
|
|
30064
|
+
coords2.push(p);
|
|
30065
|
+
elapsedDist += segLen;
|
|
30066
|
+
}
|
|
30067
|
+
if (elapsedDist > 0 && coords2.length > 1) {
|
|
30068
|
+
addPart(coords2);
|
|
30069
|
+
}
|
|
30070
|
+
return parts2;
|
|
30071
|
+
};
|
|
30072
|
+
}
|
|
30073
|
+
|
|
30074
|
+
function getLineLength(coords, distance) {
|
|
30075
|
+
var len = 0;
|
|
30076
|
+
for (var i=1, n=coords.length; i<n; i++) {
|
|
30077
|
+
len += distance(coords[i-1][0], coords[i-1][1], coords[i][0], coords[i][1]);
|
|
30078
|
+
}
|
|
30079
|
+
return len;
|
|
30080
|
+
}
|
|
30081
|
+
|
|
30082
|
+
function scaleDashes(dash, gap, len) {
|
|
30083
|
+
var dash2, gap2;
|
|
30084
|
+
var n = len / (dash + gap); // number of dashes
|
|
30085
|
+
var n1 = Math.floor(n);
|
|
30086
|
+
var n2 = Math.ceil(n);
|
|
30087
|
+
var k1 = len / (n1 * (dash + gap)); // scaled-up dashes, >1
|
|
30088
|
+
var k2 = len / (n2 * (dash + gap)); // scaled-down dashes <1
|
|
30089
|
+
var k = k2;
|
|
30090
|
+
if (k1 < 1/k2 && n1 > 0) {
|
|
30091
|
+
k = k1; // pick the smaller of the two scales
|
|
30092
|
+
}
|
|
30093
|
+
return k;
|
|
30094
|
+
}
|
|
30095
|
+
|
|
29685
30096
|
// This function creates a continuous mosaic of data values in a
|
|
29686
30097
|
// given field by assigning data from adjacent polygon features to polygons
|
|
29687
30098
|
// that contain null values.
|
|
@@ -31206,43 +31617,6 @@ ${svg}
|
|
|
31206
31617
|
}
|
|
31207
31618
|
};
|
|
31208
31619
|
|
|
31209
|
-
function expressionUsesGeoJSON(exp) {
|
|
31210
|
-
return exp.includes('this.geojson');
|
|
31211
|
-
}
|
|
31212
|
-
|
|
31213
|
-
function getFeatureEditor(lyr, dataset) {
|
|
31214
|
-
var changed = false;
|
|
31215
|
-
var api = {};
|
|
31216
|
-
// need to copy attribute to avoid circular references if geojson is assigned
|
|
31217
|
-
// to a data property.
|
|
31218
|
-
var copy = copyLayer(lyr);
|
|
31219
|
-
var features = exportLayerAsGeoJSON(copy, dataset, {}, true);
|
|
31220
|
-
|
|
31221
|
-
api.get = function(i) {
|
|
31222
|
-
return features[i];
|
|
31223
|
-
};
|
|
31224
|
-
|
|
31225
|
-
api.set = function(feat, i) {
|
|
31226
|
-
changed = true;
|
|
31227
|
-
if (utils.isString(feat)) {
|
|
31228
|
-
feat = JSON.parse(feat);
|
|
31229
|
-
}
|
|
31230
|
-
features[i] = GeoJSON.toFeature(feat); // TODO: validate
|
|
31231
|
-
};
|
|
31232
|
-
|
|
31233
|
-
api.done = function() {
|
|
31234
|
-
if (!changed) return; // read-only expression
|
|
31235
|
-
// TODO: validate number of features, etc.
|
|
31236
|
-
var geojson = {
|
|
31237
|
-
type: 'FeatureCollection',
|
|
31238
|
-
features: features
|
|
31239
|
-
};
|
|
31240
|
-
// console.log(JSON.stringify(geojson, null, 2))
|
|
31241
|
-
return importGeoJSON(geojson);
|
|
31242
|
-
};
|
|
31243
|
-
return api;
|
|
31244
|
-
}
|
|
31245
|
-
|
|
31246
31620
|
cmd.evaluateEachFeature = function(lyr, dataset, exp, opts) {
|
|
31247
31621
|
var n = getFeatureCount(lyr),
|
|
31248
31622
|
arcs = dataset.arcs,
|
|
@@ -31485,6 +31859,10 @@ ${svg}
|
|
|
31485
31859
|
filter = compileValueExpression(opts.expression, lyr, arcs);
|
|
31486
31860
|
}
|
|
31487
31861
|
|
|
31862
|
+
if (opts.ids) {
|
|
31863
|
+
filter = combineFilters(filter, getIdFilter(opts.ids));
|
|
31864
|
+
}
|
|
31865
|
+
|
|
31488
31866
|
if (opts.remove_empty) {
|
|
31489
31867
|
filter = combineFilters(filter, getNullGeometryFilter(lyr, arcs));
|
|
31490
31868
|
}
|
|
@@ -31543,6 +31921,13 @@ ${svg}
|
|
|
31543
31921
|
lyr.data = filteredRecords ? new DataTable(filteredRecords) : null;
|
|
31544
31922
|
}
|
|
31545
31923
|
|
|
31924
|
+
function getIdFilter(ids) {
|
|
31925
|
+
var set = new Set(ids);
|
|
31926
|
+
return function(i) {
|
|
31927
|
+
return set.has(i);
|
|
31928
|
+
};
|
|
31929
|
+
}
|
|
31930
|
+
|
|
31546
31931
|
function getNullGeometryFilter(lyr, arcs) {
|
|
31547
31932
|
var shapes = lyr.shapes;
|
|
31548
31933
|
if (lyr.geometry_type == 'polygon') {
|
|
@@ -31848,7 +32233,6 @@ ${svg}
|
|
|
31848
32233
|
return joinTableToLayer(targetLyr, polygonLyr.data, joinFunction, opts);
|
|
31849
32234
|
}
|
|
31850
32235
|
|
|
31851
|
-
|
|
31852
32236
|
function prepJoinLayers(targetLyr, srcLyr) {
|
|
31853
32237
|
if (!targetLyr.data) {
|
|
31854
32238
|
// create an empty data table if target layer is missing attributes
|
|
@@ -33816,10 +34200,6 @@ ${svg}
|
|
|
33816
34200
|
|
|
33817
34201
|
|
|
33818
34202
|
|
|
33819
|
-
function formatNumber(val) {
|
|
33820
|
-
return val + '';
|
|
33821
|
-
}
|
|
33822
|
-
|
|
33823
34203
|
function maxChars(arr) {
|
|
33824
34204
|
return arr.reduce(function(memo, str) {
|
|
33825
34205
|
var w = stringDisplayWidth(str);
|
|
@@ -33843,14 +34223,14 @@ ${svg}
|
|
|
33843
34223
|
}
|
|
33844
34224
|
|
|
33845
34225
|
function countIntegralChars(val) {
|
|
33846
|
-
return utils.isNumber(val) ? (formatNumber(val) + '.').indexOf('.') : 0;
|
|
34226
|
+
return utils.isNumber(val) ? (utils.formatNumber(val) + '.').indexOf('.') : 0;
|
|
33847
34227
|
}
|
|
33848
34228
|
|
|
33849
34229
|
function formatTableValue(val, integralChars) {
|
|
33850
34230
|
var str;
|
|
33851
34231
|
if (utils.isNumber(val)) {
|
|
33852
34232
|
str = utils.lpad("", integralChars - countIntegralChars(val), ' ') +
|
|
33853
|
-
formatNumber(val);
|
|
34233
|
+
utils.formatNumber(val);
|
|
33854
34234
|
} else if (utils.isString(val)) {
|
|
33855
34235
|
str = formatString(val);
|
|
33856
34236
|
} else if (utils.isDate(val)) {
|
|
@@ -34403,11 +34783,6 @@ ${svg}
|
|
|
34403
34783
|
});
|
|
34404
34784
|
}
|
|
34405
34785
|
|
|
34406
|
-
function interpolatePoint2D(ax, ay, bx, by, k) {
|
|
34407
|
-
var j = 1 - k;
|
|
34408
|
-
return [ax * j + bx * k, ay * j + by * k];
|
|
34409
|
-
}
|
|
34410
|
-
|
|
34411
34786
|
function interpolatePointsAlongArc(ids, arcs, interval) {
|
|
34412
34787
|
var iter = arcs.getShapeIter(ids);
|
|
34413
34788
|
var distance = arcs.isPlanar() ? geom.distance2D : geom.greatCircleDistance;
|
|
@@ -34659,6 +35034,38 @@ ${svg}
|
|
|
34659
35034
|
}
|
|
34660
35035
|
}
|
|
34661
35036
|
|
|
35037
|
+
function pointsFromPolylinesForJoin(lyr, dataset) {
|
|
35038
|
+
var shapes = lyr.shapes.map(function(shp) {
|
|
35039
|
+
return polylineToMidpoints(shp, dataset.arcs);
|
|
35040
|
+
});
|
|
35041
|
+
return {
|
|
35042
|
+
geometry_type: 'point',
|
|
35043
|
+
shapes: shapes,
|
|
35044
|
+
data: lyr.data // TODO copy if needed
|
|
35045
|
+
};
|
|
35046
|
+
}
|
|
35047
|
+
|
|
35048
|
+
function validateOpts(opts) {
|
|
35049
|
+
if (!opts.point_method) {
|
|
35050
|
+
stop('The "point-method" flag is required for polyline-polygon joins');
|
|
35051
|
+
}
|
|
35052
|
+
}
|
|
35053
|
+
|
|
35054
|
+
function joinPolylinesToPolygons(targetLyr, targetDataset, source, opts) {
|
|
35055
|
+
validateOpts(opts);
|
|
35056
|
+
var pointLyr = pointsFromPolylinesForJoin(source.layer, source.dataset);
|
|
35057
|
+
var retn = joinPointsToPolygons(targetLyr, targetDataset.arcs, pointLyr, opts);
|
|
35058
|
+
return retn;
|
|
35059
|
+
}
|
|
35060
|
+
|
|
35061
|
+
function joinPolygonsToPolylines(targetLyr, targetDataset, source, opts) {
|
|
35062
|
+
validateOpts(opts);
|
|
35063
|
+
var pointLyr = pointsFromPolylinesForJoin(targetLyr, targetDataset);
|
|
35064
|
+
var retn = joinPolygonsToPoints(pointLyr, source.layer, source.dataset.arcs, opts);
|
|
35065
|
+
targetLyr.data = pointLyr.data;
|
|
35066
|
+
return retn;
|
|
35067
|
+
}
|
|
35068
|
+
|
|
34662
35069
|
class TinyQueue {
|
|
34663
35070
|
constructor(data = [], compare = defaultCompare) {
|
|
34664
35071
|
this.data = data;
|
|
@@ -34982,7 +35389,7 @@ ${svg}
|
|
|
34982
35389
|
|
|
34983
35390
|
cmd.join = function(targetLyr, targetDataset, src, opts) {
|
|
34984
35391
|
var srcType, targetType, retn;
|
|
34985
|
-
if (!src || !src.
|
|
35392
|
+
if (!src || !src.dataset) {
|
|
34986
35393
|
stop("Missing a joinable data source");
|
|
34987
35394
|
}
|
|
34988
35395
|
if (opts.keys) {
|
|
@@ -34990,9 +35397,18 @@ ${svg}
|
|
|
34990
35397
|
if (opts.keys.length != 2) {
|
|
34991
35398
|
stop("Expected two key fields: a target field and a source field");
|
|
34992
35399
|
}
|
|
35400
|
+
if (!src.layer.data) {
|
|
35401
|
+
stop("Source layer is missing attribute data");
|
|
35402
|
+
}
|
|
34993
35403
|
retn = joinAttributesToFeatures(targetLyr, src.layer.data, opts);
|
|
34994
35404
|
} else {
|
|
34995
35405
|
// spatial join
|
|
35406
|
+
if (!src.layer.data) {
|
|
35407
|
+
// KLUDGE -- users might want to join a layer without attributes
|
|
35408
|
+
// to test for intersection... the simplest way to support this is
|
|
35409
|
+
// to add an empty data table to the source layer
|
|
35410
|
+
initDataTable(src.layer);
|
|
35411
|
+
}
|
|
34996
35412
|
requireDatasetsHaveCompatibleCRS([targetDataset, src.dataset]);
|
|
34997
35413
|
srcType = src.layer.geometry_type;
|
|
34998
35414
|
targetType = targetLyr.geometry_type;
|
|
@@ -35004,6 +35420,10 @@ ${svg}
|
|
|
35004
35420
|
retn = joinPointsToPoints(targetLyr, src.layer, getDatasetCRS(targetDataset), opts);
|
|
35005
35421
|
} else if (srcType == 'polygon' && targetType == 'polygon') {
|
|
35006
35422
|
retn = joinPolygonsToPolygons(targetLyr, targetDataset, src, opts);
|
|
35423
|
+
} else if (srcType == 'polyline' && targetType == 'polygon') {
|
|
35424
|
+
retn = joinPolylinesToPolygons(targetLyr, targetDataset, src, opts);
|
|
35425
|
+
} else if (srcType == 'polygon' && targetType == 'polyline') {
|
|
35426
|
+
retn = joinPolygonsToPolylines(targetLyr, targetDataset, src, opts);
|
|
35007
35427
|
} else {
|
|
35008
35428
|
stop(utils.format("Unable to join %s geometry to %s geometry",
|
|
35009
35429
|
srcType || 'null', targetType || 'null'));
|
|
@@ -37182,13 +37602,20 @@ ${svg}
|
|
|
37182
37602
|
|
|
37183
37603
|
// @expression: optional field name or expression
|
|
37184
37604
|
//
|
|
37185
|
-
cmd.splitLayer = function(src, expression,
|
|
37186
|
-
var
|
|
37605
|
+
cmd.splitLayer = function(src, expression, optsArg) {
|
|
37606
|
+
var opts = optsArg || {},
|
|
37607
|
+
lyr0 = opts.no_replace ? copyLayer(src) : src,
|
|
37187
37608
|
properties = lyr0.data ? lyr0.data.getRecords() : null,
|
|
37188
37609
|
shapes = lyr0.shapes,
|
|
37189
37610
|
index = {},
|
|
37190
37611
|
splitLayers = [],
|
|
37191
|
-
namer
|
|
37612
|
+
namer;
|
|
37613
|
+
|
|
37614
|
+
if (opts.ids) {
|
|
37615
|
+
namer = getIdSplitFunction(opts.ids);
|
|
37616
|
+
} else {
|
|
37617
|
+
namer = getSplitNameFunction(lyr0, expression);
|
|
37618
|
+
}
|
|
37192
37619
|
|
|
37193
37620
|
// if (splitField) {
|
|
37194
37621
|
// internal.requireDataField(lyr0, splitField);
|
|
@@ -37220,15 +37647,24 @@ ${svg}
|
|
|
37220
37647
|
return splitLayers;
|
|
37221
37648
|
};
|
|
37222
37649
|
|
|
37650
|
+
function getIdSplitFunction(ids) {
|
|
37651
|
+
var set = new Set(ids);
|
|
37652
|
+
return function(i) {
|
|
37653
|
+
return set.has(i) ? '1' : '2';
|
|
37654
|
+
};
|
|
37655
|
+
}
|
|
37656
|
+
|
|
37657
|
+
function getDefaultSplitFunction(lyr) {
|
|
37658
|
+
// if not splitting on an expression and layer is unnamed, name split-apart layers
|
|
37659
|
+
// like: split-1, split-2, ...
|
|
37660
|
+
return function(i) {
|
|
37661
|
+
return (lyr && lyr.name || 'split') + '-' + (i + 1);
|
|
37662
|
+
};
|
|
37663
|
+
}
|
|
37664
|
+
|
|
37223
37665
|
function getSplitNameFunction(lyr, exp) {
|
|
37224
37666
|
var compiled;
|
|
37225
|
-
if (!exp)
|
|
37226
|
-
// if not splitting on an expression and layer is unnamed, name split-apart layers
|
|
37227
|
-
// like: split-1, split-2, ...
|
|
37228
|
-
return function(i) {
|
|
37229
|
-
return (lyr && lyr.name || 'split') + '-' + (i + 1);
|
|
37230
|
-
};
|
|
37231
|
-
}
|
|
37667
|
+
if (!exp) return getDefaultSplitFunction(lyr);
|
|
37232
37668
|
lyr = {name: lyr.name, data: lyr.data}; // remove shape info
|
|
37233
37669
|
compiled = compileValueExpression(exp, lyr, null);
|
|
37234
37670
|
return function(i) {
|
|
@@ -38028,6 +38464,9 @@ ${svg}
|
|
|
38028
38464
|
} else if (name == 'colorizer') {
|
|
38029
38465
|
outputLayers = cmd.colorizer(opts);
|
|
38030
38466
|
|
|
38467
|
+
} else if (name == 'dashlines') {
|
|
38468
|
+
applyCommandToEachLayer(cmd.dashlines, targetLayers, targetDataset, opts);
|
|
38469
|
+
|
|
38031
38470
|
} else if (name == 'define') {
|
|
38032
38471
|
cmd.define(opts);
|
|
38033
38472
|
|