mapshaper 0.5.74 → 0.5.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/www/mapshaper.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
 
3
- var VERSION = "0.5.72";
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$2; },
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$2(num, decimals, nullStr, showPos) {
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 = numToStr(val, isInt ? 0 : decimals);
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);
@@ -2967,12 +2984,33 @@
2967
2984
  orient2D(cx, cy, dx, dy, bx, by) <= 0;
2968
2985
  }
2969
2986
 
2987
+ // Useful for determining if a segment that intersects another segment is
2988
+ // entering or leaving an enclosed buffer area
2989
+ // returns -1 if angle of p1p2 -> p3p4 is counter-clockwise (left turn)
2990
+ // returns 1 if angle is clockwise
2991
+ // return 0 if segments are collinear
2992
+ function segmentTurn(p1, p2, p3, p4) {
2993
+ var ax = p1[0],
2994
+ ay = p1[1],
2995
+ bx = p2[0],
2996
+ by = p2[1],
2997
+ // shift p3p4 segment to start at p2
2998
+ dx = bx - p3[0],
2999
+ dy = by - p3[1],
3000
+ cx = p4[0] + dx,
3001
+ cy = p4[1] + dy,
3002
+ orientation = orient2D(ax, ay, bx, by, cx, cy);
3003
+ if (!orientation) return 0;
3004
+ return orientation < 0 ? 1 : -1;
3005
+ }
3006
+
2970
3007
  var SegmentGeom = /*#__PURE__*/Object.freeze({
2971
3008
  __proto__: null,
2972
3009
  segmentIntersection: segmentIntersection,
2973
3010
  findClosestPointOnSeg: findClosestPointOnSeg,
2974
3011
  orient2D: orient2D,
2975
- segmentHit: segmentHit
3012
+ segmentHit: segmentHit,
3013
+ segmentTurn: segmentTurn
2976
3014
  });
2977
3015
 
2978
3016
  var geom = Object.assign({}, Geom, PolygonGeom, PathGeom, SegmentGeom, PolygonCentroid);
@@ -8136,10 +8174,14 @@
8136
8174
  }
8137
8175
 
8138
8176
  function roundToDigits(n, d) {
8139
- return +n.toFixed(d);
8177
+ return +n.toFixed(d); // string conversion makes this slow
8140
8178
  }
8141
8179
 
8142
- // inc: Rounding incrememnt (e.g. 0.001 rounds to thousandths)
8180
+ function roundToTenths(n) {
8181
+ return (Math.round(n * 10)) / 10;
8182
+ }
8183
+
8184
+ // inc: Rounding increment (e.g. 0.001 rounds to thousandths)
8143
8185
  function getRoundingFunction(inc) {
8144
8186
  if (!utils.isNumber(inc) || inc === 0) {
8145
8187
  error("Rounding increment must be a non-zero number.");
@@ -8209,6 +8251,7 @@
8209
8251
  __proto__: null,
8210
8252
  roundToSignificantDigits: roundToSignificantDigits,
8211
8253
  roundToDigits: roundToDigits,
8254
+ roundToTenths: roundToTenths,
8212
8255
  getRoundingFunction: getRoundingFunction,
8213
8256
  getBoundsPrecisionForDisplay: getBoundsPrecisionForDisplay,
8214
8257
  getRoundedCoordString: getRoundedCoordString,
@@ -10270,6 +10313,12 @@
10270
10313
  };
10271
10314
  }
10272
10315
 
10316
+ /*
10317
+
10318
+
10319
+
10320
+ */
10321
+
10273
10322
  // TODO: use polygon pathfinder shared code
10274
10323
  function collectPolylineArcs(ids, nodes, testArc, useArc) {
10275
10324
  var parts = [];
@@ -11839,7 +11888,6 @@
11839
11888
  });
11840
11889
 
11841
11890
  // Map positive or negative integer ids to non-negative integer ids
11842
-
11843
11891
  function IdLookupIndex(n, clearable) {
11844
11892
  var fwdIndex = new Int32Array(n);
11845
11893
  var revIndex = new Int32Array(n);
@@ -13811,7 +13859,7 @@
13811
13859
  var symbolRenderers = {};
13812
13860
 
13813
13861
  function getTransform(xy, scale) {
13814
- var str = 'translate(' + xy[0] + ' ' + xy[1] + ')';
13862
+ var str = 'translate(' + roundToTenths(xy[0]) + ' ' + roundToTenths(xy[1]) + ')';
13815
13863
  if (scale && scale != 1) {
13816
13864
  str += ' scale(' + scale + ')';
13817
13865
  }
@@ -15539,6 +15587,139 @@ ${svg}
15539
15587
  }];
15540
15588
  }
15541
15589
 
15590
+ function exportRecordsAsFixedWidthString(fields, records, opts) {
15591
+ var rows = [], col;
15592
+ for (var i=0; i<fields.length; i++) {
15593
+ col = formatFixedWidthColumn(fields[i], records, opts);
15594
+ if (i === 0) {
15595
+ rows = col;
15596
+ } else for (var j=0; j<rows.length; j++) {
15597
+ rows[j] += ' ' + col[j];
15598
+ }
15599
+ }
15600
+ return rows.join('\n');
15601
+ }
15602
+
15603
+ function formatFixedWidthColumn(field, records, opts) {
15604
+ var arr = [],
15605
+ maxLen = field.length,
15606
+ n = records.length,
15607
+ i, val;
15608
+ arr.push(field);
15609
+ for (i=0; i<n; i++) {
15610
+ val = formatFixedWidthValue(records[i][field], opts);
15611
+ maxLen = Math.max(maxLen, val.length);
15612
+ arr.push(val);
15613
+ }
15614
+ for (i=0; i<arr.length; i++) {
15615
+ arr[i] = arr[i].padEnd(maxLen, ' ');
15616
+ }
15617
+ return arr;
15618
+ }
15619
+
15620
+ function formatFixedWidthValue(val, opts) {
15621
+ // TODO: remove duplication with mapshaper-delim-export.js
15622
+ var s;
15623
+ if (val == null) {
15624
+ s = '';
15625
+ } else if (utils.isString(val)) {
15626
+ s = val; // TODO: handle wide characters, newlines etc.
15627
+ } else if (utils.isNumber(val)) {
15628
+ s = opts.decimal_comma ? utils.formatIntlNumber(val) : utils.formatNumber(val);
15629
+ } else if (utils.isObject(val)) {
15630
+ s = JSON.stringify(val);
15631
+ } else {
15632
+ s = val + '';
15633
+ }
15634
+ return s;
15635
+ }
15636
+
15637
+
15638
+ function readFixedWidthRecords(reader, opts) {
15639
+ var str = reader.toString(opts.encoding || 'ascii');
15640
+ return readFixedWidthRecordsFromString(str, opts);
15641
+ }
15642
+
15643
+ function readFixedWidthRecordsFromString(str, ops) {
15644
+ var fields = parseFixedWidthInfo(str.substring(0, 2000));
15645
+ if (!fields) return [];
15646
+ var lines = utils.splitLines(str);
15647
+ if (lines[lines.length - 1] === '') lines.pop(); // handle newline at end of string
15648
+ var records = [];
15649
+ for (var i=1; i<lines.length; i++) {
15650
+ records.push(parseFixedWidthLine(lines[i], fields));
15651
+ }
15652
+ return records;
15653
+ }
15654
+
15655
+ function parseFixedWidthInfo(sample) {
15656
+ var lines = utils.splitLines(sample);
15657
+ if (lines.length > 2) lines.pop(); // remove possible partial line
15658
+ var n = getMaxLineLength(lines);
15659
+ var headerLine = lines[0];
15660
+ var colInfo = [];
15661
+ var colStart = 0;
15662
+ var inContent = false;
15663
+ var inHeader = false;
15664
+ var isContentChar, isHeaderChar, isColStart, colEnd;
15665
+ for (var i=0; i<=n; i++) {
15666
+ isHeaderChar = testContentChar(headerLine, i);
15667
+ isContentChar = !testEmptyCol(lines, i);
15668
+ isColStart = isHeaderChar && !inHeader;
15669
+ if (isColStart && inContent) {
15670
+ // all lines should have a space char in the position right before a header starts
15671
+ return null;
15672
+ }
15673
+ if (i == n || i > 0 && isColStart) {
15674
+ colEnd = i == n ? undefined : i-1;
15675
+ colInfo.push({
15676
+ name: readValue$1(headerLine, colStart, colEnd),
15677
+ end: colEnd,
15678
+ start: colStart
15679
+ });
15680
+ colStart = i;
15681
+ }
15682
+ inContent = isContentChar;
15683
+ inHeader = isHeaderChar;
15684
+ }
15685
+ return colInfo.length > 0 ? colInfo : null;
15686
+ }
15687
+
15688
+ function getMaxLineLength(lines) {
15689
+ var max = 0;
15690
+ for (var i=0; i<lines.length; i++) {
15691
+ max = Math.max(max, lines[i].length);
15692
+ }
15693
+ return max;
15694
+ }
15695
+
15696
+ function readValue$1(line, start, end) {
15697
+ return line.substring(start, end).trim();
15698
+ }
15699
+
15700
+ function parseFixedWidthLine(str, fields) {
15701
+ var obj = {}, field;
15702
+ for (var i=0; i<fields.length; i++) {
15703
+ field = fields[i];
15704
+ obj[field.name] = readValue$1(str, field.start, field.end);
15705
+ }
15706
+ return obj;
15707
+ }
15708
+
15709
+ function testContentChar(str, i) {
15710
+ return i < str.length && str[i] !== ' ';
15711
+ }
15712
+
15713
+ // return true iff all samples are blank at index i
15714
+ function testEmptyCol(samples, i) {
15715
+ var line;
15716
+ for (var j=0; j<samples.length; j++) {
15717
+ line = samples[j];
15718
+ if (testContentChar(line, i)) return false;
15719
+ }
15720
+ return true;
15721
+ }
15722
+
15542
15723
  // Generate output content from a dataset object
15543
15724
  function exportDelim(dataset, opts) {
15544
15725
  var delim = getExportDelimiter(dataset.info, opts),
@@ -15560,6 +15741,9 @@ ${svg}
15560
15741
  var encoding = opts.encoding || 'utf8';
15561
15742
  var records = lyr.data.getRecords();
15562
15743
  var fields = findFieldNames(records, opts.field_order);
15744
+ if (delim == ' ') {
15745
+ return exportRecordsAsFixedWidthString(fields, records, opts);
15746
+ }
15563
15747
  var formatRow = getDelimRowFormatter(fields, delim, opts);
15564
15748
  // exporting utf8 and ascii text as string by default (for now)
15565
15749
  var exportAsString = encodingIsUtf8(encoding) && !opts.to_buffer &&
@@ -15617,15 +15801,6 @@ ${svg}
15617
15801
  };
15618
15802
  }
15619
15803
 
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
15804
  function getDelimValueFormatter(delim, opts) {
15630
15805
  var dquoteRxp = new RegExp('["\n\r' + delim + ']');
15631
15806
  var decimalComma = opts && opts.decimal_comma || false;
@@ -15642,7 +15817,7 @@ ${svg}
15642
15817
  } else if (utils.isString(val)) {
15643
15818
  s = formatString(val);
15644
15819
  } else if (utils.isNumber(val)) {
15645
- s = decimalComma ? formatIntlNumber(val) : formatNumber$1(val);
15820
+ s = decimalComma ? utils.formatIntlNumber(val) : utils.formatNumber(val);
15646
15821
  } else if (utils.isObject(val)) {
15647
15822
  s = formatString(JSON.stringify(val));
15648
15823
  } else {
@@ -15685,8 +15860,6 @@ ${svg}
15685
15860
  __proto__: null,
15686
15861
  exportDelim: exportDelim,
15687
15862
  exportLayerAsDSV: exportLayerAsDSV,
15688
- formatNumber: formatNumber$1,
15689
- formatIntlNumber: formatIntlNumber,
15690
15863
  getDelimValueFormatter: getDelimValueFormatter
15691
15864
  });
15692
15865
 
@@ -17050,8 +17223,9 @@ ${svg}
17050
17223
  //
17051
17224
  // TODO: confirm compatibility with all supported encodings
17052
17225
  function readDelimRecords(reader, delim, optsArg) {
17226
+ var opts = optsArg || {};
17227
+ if (delim == ' ') return readFixedWidthRecords(reader, opts);
17053
17228
  var reader2 = new Reader2(reader),
17054
- opts = optsArg || {},
17055
17229
  headerStr = readLinesAsString(reader2, getDelimHeaderLines(opts), opts.encoding),
17056
17230
  header = parseDelimHeaderSection(headerStr, delim, opts),
17057
17231
  convertRowArr = getRowConverter(header.import_fields),
@@ -17074,6 +17248,7 @@ ${svg}
17074
17248
  // for delimiter characters and newlines. Input size is limited by the maximum
17075
17249
  // string size.
17076
17250
  function readDelimRecordsFromString(str, delim, opts) {
17251
+ if (delim == ' ') return readFixedWidthRecordsFromString(str, opts);
17077
17252
  var header = parseDelimHeaderSection(str, delim, opts);
17078
17253
  if (header.import_fields.length === 0 || !header.remainder) return [];
17079
17254
  var convert = getRowConverter(header.import_fields);
@@ -17477,7 +17652,7 @@ ${svg}
17477
17652
  };
17478
17653
  }
17479
17654
 
17480
- var supportedDelimiters = ['|', '\t', ',', ';'];
17655
+ var supportedDelimiters = ['|', '\t', ',', ';', ' '];
17481
17656
 
17482
17657
  function isSupportedDelimiter(d) {
17483
17658
  return utils.contains(supportedDelimiters, d);
@@ -17838,9 +18013,21 @@ ${svg}
17838
18013
  }
17839
18014
 
17840
18015
  function cleanArgv(argv) {
17841
- argv = argv.map(function(s) {return s.trim();}); // trim whitespace
18016
+ // Note: original trim caused some quoted spaces to be removed
18017
+ // (e.g. bash shell seems to convert [delimiter=" "] to [delimiter= ],
18018
+ // which then got trimmed to [delimiter=] below)
18019
+ //// argv = argv.map(function(s) {return s.trim();}); // trim whitespace
18020
+
18021
+ // Updated: don't trim space from tokens like [delimeter= ]
18022
+ argv = argv.map(function(s) {
18023
+ if (!/= $/.test(s)) {
18024
+ s = s.trimEnd();
18025
+ }
18026
+ s = s.trimStart();
18027
+ return s;
18028
+ });
17842
18029
  argv = argv.filter(function(s) {return s !== '';}); // remove empty tokens
17843
- // removing trimQuotes() call... now, strings like 'name="Meg"' will no longer
18030
+ // Note: removing trimQuotes() call... now, strings like 'name="Meg"' will no longer
17844
18031
  // be parsed the same way as name=Meg and name="Meg"
17845
18032
  //// argv = argv.map(utils.trimQuotes); // remove one level of single or dbl quotes
17846
18033
  return argv;
@@ -18947,6 +19134,28 @@ ${svg}
18947
19134
  '$ mapshaper data.json -colorizer name=getColor nodata=#eee breaks=20,40 \\\n' +
18948
19135
  ' colors=#e0f3db,#a8ddb5,#43a2ca -each \'fill = getColor(RATING)\' -o output.json');
18949
19136
 
19137
+ parser.command('dashlines')
19138
+ .describe('split lines into sections, with or without a gap')
19139
+ .oldAlias('split-lines')
19140
+ .option('dash-length', {
19141
+ type: 'distance',
19142
+ describe: 'length of split-apart lines (e.g. 200km)'
19143
+ })
19144
+ .option('gap-length', {
19145
+ type: 'distance',
19146
+ describe: 'length of gaps between dashes (default is 0)'
19147
+ })
19148
+ .option('scaled', {
19149
+ type: 'flag',
19150
+ describe: 'scale dashes and gaps to prevent partial dashes'
19151
+ })
19152
+ .option('planar', {
19153
+ type: 'flag',
19154
+ describe: 'use planar geometry'
19155
+ })
19156
+ .option('where', whereOpt)
19157
+ .option('target', targetOpt);
19158
+
18950
19159
  parser.command('define')
18951
19160
  // .describe('define expression variables')
18952
19161
  .option('expression', {
@@ -19143,6 +19352,10 @@ ${svg}
19143
19352
  .option('keep-shapes', {
19144
19353
  type: 'flag'
19145
19354
  })
19355
+ .option('ids', {
19356
+ // describe: 'filter on a list of feature ids',
19357
+ type: 'numbers'
19358
+ })
19146
19359
  .option('cleanup', {type: 'flag'}) // TODO: document
19147
19360
  .option('name', nameOpt)
19148
19361
  .option('target', targetOpt)
@@ -19711,6 +19924,11 @@ ${svg}
19711
19924
  DEFAULT: true,
19712
19925
  describe: 'expression or field for grouping features and naming split layers'
19713
19926
  })
19927
+ .option('ids', {
19928
+ // used by gui history to split on selected features
19929
+ // describe: 'split on a list of feature ids',
19930
+ type: 'numbers'
19931
+ })
19714
19932
  .option('apart', {
19715
19933
  describe: 'save output layers to independent datasets',
19716
19934
  type: 'flag'
@@ -26263,97 +26481,6 @@ ${svg}
26263
26481
  geom.segmentIntersection(a[0], a[1], b[0], b[1], bb.xmax, bb.ymin, bb.xmin, bb.ymin));
26264
26482
  }
26265
26483
 
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
26484
  function getPolylineBufferMaker2(arcs, geod, getBearing, opts) {
26358
26485
  var makeLeftBuffer = getPathBufferMaker2(arcs, geod, getBearing, opts);
26359
26486
  var geomType = opts.geometry_type;
@@ -26647,6 +26774,101 @@ ${svg}
26647
26774
  };
26648
26775
  }
26649
26776
 
26777
+ // GeographicLib docs: https://geographiclib.sourceforge.io/html/js/
26778
+ // https://geographiclib.sourceforge.io/html/js/module-GeographicLib_Geodesic.Geodesic.html
26779
+ // https://geographiclib.sourceforge.io/html/js/tutorial-2-interface.html
26780
+ function getGeodesic(P) {
26781
+ if (!isLatLngCRS(P)) error('Expected an unprojected CRS');
26782
+ var f = P.es / (1 + Math.sqrt(P.one_es));
26783
+ var GeographicLib = require('mproj').internal.GeographicLib;
26784
+ return new GeographicLib.Geodesic.Geodesic(P.a, f);
26785
+ }
26786
+
26787
+ function interpolatePoint2D(ax, ay, bx, by, k) {
26788
+ var j = 1 - k;
26789
+ return [ax * j + bx * k, ay * j + by * k];
26790
+ }
26791
+
26792
+ function getInterpolationFunction(P) {
26793
+ var spherical = P && isLatLngCRS(P);
26794
+ if (!spherical) return interpolatePoint2D;
26795
+ var geod = getGeodesic(P);
26796
+ return function(lng, lat, lng2, lat2, k) {
26797
+ var r = geod.Inverse(lat, lng, lat2, lng2);
26798
+ var dist = r.s12 * k;
26799
+ var r2 = geod.Direct(lat, lng, r.azi1, dist);
26800
+ return [r2.lon2, r2.lat2];
26801
+ };
26802
+ }
26803
+
26804
+ function getPlanarSegmentEndpoint(x, y, bearing, meterDist) {
26805
+ var rad = bearing / 180 * Math.PI;
26806
+ var dx = Math.sin(rad) * meterDist;
26807
+ var dy = Math.cos(rad) * meterDist;
26808
+ return [x + dx, y + dy];
26809
+ }
26810
+
26811
+ // source: https://github.com/mapbox/cheap-ruler/blob/master/index.js
26812
+ function fastGeodeticSegmentFunction(lng, lat, bearing, meterDist) {
26813
+ var D2R = Math.PI / 180;
26814
+ var cos = Math.cos(lat * D2R);
26815
+ var cos2 = 2 * cos * cos - 1;
26816
+ var cos3 = 2 * cos * cos2 - cos;
26817
+ var cos4 = 2 * cos * cos3 - cos2;
26818
+ var cos5 = 2 * cos * cos4 - cos3;
26819
+ var kx = (111.41513 * cos - 0.09455 * cos3 + 0.00012 * cos5) * 1000;
26820
+ var ky = (111.13209 - 0.56605 * cos2 + 0.0012 * cos4) * 1000;
26821
+ var bearingRad = bearing * D2R;
26822
+ var lat2 = lat + Math.cos(bearingRad) * meterDist / ky;
26823
+ var lng2 = lng + Math.sin(bearingRad) * meterDist / kx;
26824
+ return [lng2, lat2];
26825
+ }
26826
+
26827
+ function getGeodeticSegmentFunction(P) {
26828
+ if (!isLatLngCRS(P)) {
26829
+ return getPlanarSegmentEndpoint;
26830
+ }
26831
+ var g = getGeodesic(P);
26832
+ return function(lng, lat, bearing, meterDist) {
26833
+ var o = g.Direct(lat, lng, bearing, meterDist);
26834
+ var p = [o.lon2, o.lat2];
26835
+ return p;
26836
+ };
26837
+ }
26838
+
26839
+ function getFastGeodeticSegmentFunction(P) {
26840
+ // CAREFUL: this function has higher error at very large distances and at the poles
26841
+ // also, it wouldn't work for other planets than Earth
26842
+ return isLatLngCRS(P) ? fastGeodeticSegmentFunction : getPlanarSegmentEndpoint;
26843
+ }
26844
+
26845
+
26846
+ function bearingDegrees(a, b, c, d) {
26847
+ return geom.bearing(a, b, c, d) * 180 / Math.PI;
26848
+ }
26849
+
26850
+ function bearingDegrees2D(a, b, c, d) {
26851
+ return geom.bearing2D(a, b, c, d) * 180 / Math.PI;
26852
+ }
26853
+
26854
+ // return function to calculate bearing of a segment in degrees
26855
+ function getBearingFunction(dataset) {
26856
+ var P = getDatasetCRS(dataset);
26857
+ return isLatLngCRS(P) ? bearingDegrees : bearingDegrees2D;
26858
+ }
26859
+
26860
+ var Geodesic = /*#__PURE__*/Object.freeze({
26861
+ __proto__: null,
26862
+ interpolatePoint2D: interpolatePoint2D,
26863
+ getInterpolationFunction: getInterpolationFunction,
26864
+ getPlanarSegmentEndpoint: getPlanarSegmentEndpoint,
26865
+ getGeodeticSegmentFunction: getGeodeticSegmentFunction,
26866
+ getFastGeodeticSegmentFunction: getFastGeodeticSegmentFunction,
26867
+ bearingDegrees: bearingDegrees,
26868
+ bearingDegrees2D: bearingDegrees2D,
26869
+ getBearingFunction: getBearingFunction
26870
+ });
26871
+
26650
26872
  function makePolylineBuffer(lyr, dataset, opts) {
26651
26873
  var geojson = makeShapeBufferGeoJSON(lyr, dataset, opts);
26652
26874
  var dataset2 = importGeoJSON(geojson, {});
@@ -29682,6 +29904,178 @@ ${svg}
29682
29904
  getColorizerFunction: getColorizerFunction
29683
29905
  });
29684
29906
 
29907
+ function expressionUsesGeoJSON(exp) {
29908
+ return exp.includes('this.geojson');
29909
+ }
29910
+
29911
+ function getFeatureEditor(lyr, dataset) {
29912
+ var changed = false;
29913
+ var api = {};
29914
+ // need to copy attribute to avoid circular references if geojson is assigned
29915
+ // to a data property.
29916
+ var copy = copyLayer(lyr);
29917
+ var features = exportLayerAsGeoJSON(copy, dataset, {}, true);
29918
+
29919
+ api.get = function(i) {
29920
+ return features[i];
29921
+ };
29922
+
29923
+ api.set = function(feat, i) {
29924
+ changed = true;
29925
+ if (utils.isString(feat)) {
29926
+ feat = JSON.parse(feat);
29927
+ }
29928
+ features[i] = GeoJSON.toFeature(feat); // TODO: validate
29929
+ };
29930
+
29931
+ api.done = function() {
29932
+ if (!changed) return; // read-only expression
29933
+ // TODO: validate number of features, etc.
29934
+ var geojson = {
29935
+ type: 'FeatureCollection',
29936
+ features: features
29937
+ };
29938
+
29939
+ // console.log(JSON.stringify(geojson, null, 2))
29940
+ return importGeoJSON(geojson);
29941
+ };
29942
+ return api;
29943
+ }
29944
+
29945
+ cmd.dashlines = function(lyr, dataset, opts) {
29946
+ var crs = getDatasetCRS(dataset);
29947
+ var defs = getStateVar('defs');
29948
+ var exp = `this.geojson = splitFeature(this.geojson)`;
29949
+ requirePolylineLayer(lyr);
29950
+ defs.splitFeature = getSplitFeatureFunction(crs, opts);
29951
+ cmd.evaluateEachFeature(lyr, dataset, exp, opts);
29952
+ delete defs.splitFeature;
29953
+ };
29954
+
29955
+ function getSplitFeatureFunction(crs, opts) {
29956
+ var dashLen = opts.dash_length ? convertDistanceParam(opts.dash_length, crs) : 0;
29957
+ var gapLen = opts.gap_length ? convertDistanceParam(opts.gap_length, crs) : 0;
29958
+ if (dashLen > 0 === false) {
29959
+ stop('Missing required dash-length parameter');
29960
+ }
29961
+ if (gapLen >= 0 == false) {
29962
+ stop('Invalid gap-length option');
29963
+ }
29964
+ var splitLine = getSplitLineFunction(crs, dashLen, gapLen, opts);
29965
+ return function(feat) {
29966
+ var geom = feat.geometry;
29967
+ if (!geom) return feat;
29968
+ if (geom.type == 'LineString') {
29969
+ geom.type = 'MultiLineString';
29970
+ geom.coordinates = [geom.coordinates];
29971
+ }
29972
+ if (geom.type != 'MultiLineString') {
29973
+ error('Unexpected geometry:', geom.type);
29974
+ }
29975
+ geom.coordinates = geom.coordinates.reduce(function(memo, coords) {
29976
+ try {
29977
+ var parts = splitLine(coords);
29978
+ memo = memo.concat(parts);
29979
+ } catch(e) {
29980
+ console.error(e);
29981
+ throw e;
29982
+ }
29983
+ return memo;
29984
+ }, []);
29985
+
29986
+ return feat;
29987
+ };
29988
+ }
29989
+
29990
+ function getSplitLineFunction(crs, dashLen, gapLen, opts) {
29991
+ var planar = !!opts.planar;
29992
+ var interpolate = getInterpolationFunction(planar ? null : crs);
29993
+ var distance = isLatLngCRS(crs) ? greatCircleDistance : distance2D;
29994
+ var inDash, parts2, interval, scale;
29995
+ function addPart(coords) {
29996
+ if (inDash) parts2.push(coords);
29997
+ if (gapLen > 0) {
29998
+ inDash = !inDash;
29999
+ interval = scale * (inDash ? dashLen : gapLen);
30000
+ }
30001
+ }
30002
+
30003
+ return function splitLineString(coords) {
30004
+ var elapsedDist = 0;
30005
+ var p = coords[0];
30006
+ var coords2 = [p];
30007
+ var segLen, pct, prev;
30008
+ if (opts.scaled) {
30009
+ scale = scaleDashes(dashLen, gapLen, getLineLength(coords, distance));
30010
+ } else {
30011
+ scale = 1;
30012
+ }
30013
+ // init this LineString
30014
+ inDash = gapLen > 0 ? false : true;
30015
+ interval = scale * (inDash ? dashLen : gapLen);
30016
+ if (!inDash) {
30017
+ // start gapped lines with a half-gap
30018
+ // (a half-gap or a half-dash is probably better for rings and intersecting lines)
30019
+ interval *= 0.5;
30020
+ }
30021
+ parts2 = [];
30022
+ for (var i=1, n=coords.length; i<n; i++) {
30023
+ prev = p;
30024
+ p = coords[i];
30025
+ segLen = distance(prev[0], prev[1], p[0], p[1]);
30026
+ if (segLen <= 0) continue;
30027
+ while (elapsedDist + segLen >= interval) {
30028
+ // this segment contains a break either within it or at the far endpoint
30029
+ pct = (interval - elapsedDist) / segLen;
30030
+ if (pct > 0.999 && i == n - 1) {
30031
+ // snap to endpoint (so fp rounding errors don't result in a tiny
30032
+ // last segment)
30033
+ pct = 1;
30034
+ }
30035
+ if (pct < 1) {
30036
+ prev = interpolate(prev[0], prev[1], p[0], p[1], pct);
30037
+ } else {
30038
+ prev = p;
30039
+ }
30040
+ coords2.push(prev);
30041
+ addPart(coords2);
30042
+ // start a new part
30043
+ coords2 = pct < 1 ? [prev] : [];
30044
+ elapsedDist = 0;
30045
+ segLen = (1 - pct) * segLen;
30046
+ }
30047
+ coords2.push(p);
30048
+ elapsedDist += segLen;
30049
+ }
30050
+ if (elapsedDist > 0 && coords2.length > 1) {
30051
+ addPart(coords2);
30052
+ }
30053
+ return parts2;
30054
+ };
30055
+ }
30056
+
30057
+ function getLineLength(coords, distance) {
30058
+ var len = 0;
30059
+ for (var i=1, n=coords.length; i<n; i++) {
30060
+ len += distance(coords[i-1][0], coords[i-1][1], coords[i][0], coords[i][1]);
30061
+ }
30062
+ return len;
30063
+ }
30064
+
30065
+ function scaleDashes(dash, gap, len) {
30066
+ var dash2, gap2;
30067
+ var n = len / (dash + gap); // number of dashes
30068
+ var n1 = Math.floor(n);
30069
+ var n2 = Math.ceil(n);
30070
+ var k1 = len / (n1 * (dash + gap)); // scaled-up dashes, >1
30071
+ var k2 = len / (n2 * (dash + gap)); // scaled-down dashes <1
30072
+ var k = k2;
30073
+ if (k1 < 1/k2 && n1 > 0) {
30074
+ k = k1; // pick the smaller of the two scales
30075
+ }
30076
+ return k;
30077
+ }
30078
+
29685
30079
  // This function creates a continuous mosaic of data values in a
29686
30080
  // given field by assigning data from adjacent polygon features to polygons
29687
30081
  // that contain null values.
@@ -31206,43 +31600,6 @@ ${svg}
31206
31600
  }
31207
31601
  };
31208
31602
 
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
31603
  cmd.evaluateEachFeature = function(lyr, dataset, exp, opts) {
31247
31604
  var n = getFeatureCount(lyr),
31248
31605
  arcs = dataset.arcs,
@@ -31485,6 +31842,10 @@ ${svg}
31485
31842
  filter = compileValueExpression(opts.expression, lyr, arcs);
31486
31843
  }
31487
31844
 
31845
+ if (opts.ids) {
31846
+ filter = combineFilters(filter, getIdFilter(opts.ids));
31847
+ }
31848
+
31488
31849
  if (opts.remove_empty) {
31489
31850
  filter = combineFilters(filter, getNullGeometryFilter(lyr, arcs));
31490
31851
  }
@@ -31543,6 +31904,13 @@ ${svg}
31543
31904
  lyr.data = filteredRecords ? new DataTable(filteredRecords) : null;
31544
31905
  }
31545
31906
 
31907
+ function getIdFilter(ids) {
31908
+ var set = new Set(ids);
31909
+ return function(i) {
31910
+ return set.has(i);
31911
+ };
31912
+ }
31913
+
31546
31914
  function getNullGeometryFilter(lyr, arcs) {
31547
31915
  var shapes = lyr.shapes;
31548
31916
  if (lyr.geometry_type == 'polygon') {
@@ -31848,7 +32216,6 @@ ${svg}
31848
32216
  return joinTableToLayer(targetLyr, polygonLyr.data, joinFunction, opts);
31849
32217
  }
31850
32218
 
31851
-
31852
32219
  function prepJoinLayers(targetLyr, srcLyr) {
31853
32220
  if (!targetLyr.data) {
31854
32221
  // create an empty data table if target layer is missing attributes
@@ -33816,10 +34183,6 @@ ${svg}
33816
34183
 
33817
34184
 
33818
34185
 
33819
- function formatNumber(val) {
33820
- return val + '';
33821
- }
33822
-
33823
34186
  function maxChars(arr) {
33824
34187
  return arr.reduce(function(memo, str) {
33825
34188
  var w = stringDisplayWidth(str);
@@ -33843,14 +34206,14 @@ ${svg}
33843
34206
  }
33844
34207
 
33845
34208
  function countIntegralChars(val) {
33846
- return utils.isNumber(val) ? (formatNumber(val) + '.').indexOf('.') : 0;
34209
+ return utils.isNumber(val) ? (utils.formatNumber(val) + '.').indexOf('.') : 0;
33847
34210
  }
33848
34211
 
33849
34212
  function formatTableValue(val, integralChars) {
33850
34213
  var str;
33851
34214
  if (utils.isNumber(val)) {
33852
34215
  str = utils.lpad("", integralChars - countIntegralChars(val), ' ') +
33853
- formatNumber(val);
34216
+ utils.formatNumber(val);
33854
34217
  } else if (utils.isString(val)) {
33855
34218
  str = formatString(val);
33856
34219
  } else if (utils.isDate(val)) {
@@ -34176,6 +34539,32 @@ ${svg}
34176
34539
  return d;
34177
34540
  }
34178
34541
 
34542
+ function findNearestVertices(p, shp, arcs) {
34543
+ var p2 = findNearestVertex(p[0], p[1], shp, arcs);
34544
+ return findVertexIds(p2.x, p2.y, arcs);
34545
+ }
34546
+
34547
+ // p: point to snap
34548
+ // ids: ids of nearby vertices, possibly including an arc endpoint
34549
+ function snapPointToArcEndpoint(p, ids, arcs) {
34550
+ var p2, p3, dx, dy;
34551
+ ids.forEach(function(idx) {
34552
+ if (vertexIsArcStart(idx, arcs)) {
34553
+ p2 = getVertexCoords(idx + 1, arcs);
34554
+ } else if (vertexIsArcEnd(idx, arcs)) {
34555
+ p2 = getVertexCoords(idx - 1, arcs);
34556
+ }
34557
+ });
34558
+ if (!p2) return;
34559
+ dx = p2[0] - p[0];
34560
+ dy = p2[1] - p[1];
34561
+ if (Math.abs(dx) > Math.abs(dy)) {
34562
+ p[1] = p2[1]; // snap y coord
34563
+ } else {
34564
+ p[0] = p2[0];
34565
+ }
34566
+ }
34567
+
34179
34568
  // Find ids of vertices with identical coordinates to x,y in an ArcCollection
34180
34569
  // Caveat: does not exclude vertices that are not visible at the
34181
34570
  // current level of simplification.
@@ -34238,6 +34627,18 @@ ${svg}
34238
34627
  return minLen < Infinity ? {x: minX, y: minY} : null;
34239
34628
  }
34240
34629
 
34630
+ var VertexUtils = /*#__PURE__*/Object.freeze({
34631
+ __proto__: null,
34632
+ findNearestVertices: findNearestVertices,
34633
+ snapPointToArcEndpoint: snapPointToArcEndpoint,
34634
+ findVertexIds: findVertexIds,
34635
+ getVertexCoords: getVertexCoords,
34636
+ vertexIsArcEnd: vertexIsArcEnd,
34637
+ vertexIsArcStart: vertexIsArcStart,
34638
+ setVertexCoords: setVertexCoords,
34639
+ findNearestVertex: findNearestVertex
34640
+ });
34641
+
34241
34642
  // Returns x,y coordinates of the point that is at the midpoint of each polyline feature
34242
34643
  // Uses 2d cartesian geometry
34243
34644
  // TODO: optionally use spherical geometry
@@ -34365,11 +34766,6 @@ ${svg}
34365
34766
  });
34366
34767
  }
34367
34768
 
34368
- function interpolatePoint2D(ax, ay, bx, by, k) {
34369
- var j = 1 - k;
34370
- return [ax * j + bx * k, ay * j + by * k];
34371
- }
34372
-
34373
34769
  function interpolatePointsAlongArc(ids, arcs, interval) {
34374
34770
  var iter = arcs.getShapeIter(ids);
34375
34771
  var distance = arcs.isPlanar() ? geom.distance2D : geom.greatCircleDistance;
@@ -34621,6 +35017,38 @@ ${svg}
34621
35017
  }
34622
35018
  }
34623
35019
 
35020
+ function pointsFromPolylinesForJoin(lyr, dataset) {
35021
+ var shapes = lyr.shapes.map(function(shp) {
35022
+ return polylineToMidpoints(shp, dataset.arcs);
35023
+ });
35024
+ return {
35025
+ geometry_type: 'point',
35026
+ shapes: shapes,
35027
+ data: lyr.data // TODO copy if needed
35028
+ };
35029
+ }
35030
+
35031
+ function validateOpts(opts) {
35032
+ if (!opts.point_method) {
35033
+ stop('The "point-method" flag is required for polyline-polygon joins');
35034
+ }
35035
+ }
35036
+
35037
+ function joinPolylinesToPolygons(targetLyr, targetDataset, source, opts) {
35038
+ validateOpts(opts);
35039
+ var pointLyr = pointsFromPolylinesForJoin(source.layer, source.dataset);
35040
+ var retn = joinPointsToPolygons(targetLyr, targetDataset.arcs, pointLyr, opts);
35041
+ return retn;
35042
+ }
35043
+
35044
+ function joinPolygonsToPolylines(targetLyr, targetDataset, source, opts) {
35045
+ validateOpts(opts);
35046
+ var pointLyr = pointsFromPolylinesForJoin(targetLyr, targetDataset);
35047
+ var retn = joinPolygonsToPoints(pointLyr, source.layer, source.dataset.arcs, opts);
35048
+ targetLyr.data = pointLyr.data;
35049
+ return retn;
35050
+ }
35051
+
34624
35052
  class TinyQueue {
34625
35053
  constructor(data = [], compare = defaultCompare) {
34626
35054
  this.data = data;
@@ -34944,7 +35372,7 @@ ${svg}
34944
35372
 
34945
35373
  cmd.join = function(targetLyr, targetDataset, src, opts) {
34946
35374
  var srcType, targetType, retn;
34947
- if (!src || !src.layer.data || !src.dataset) {
35375
+ if (!src || !src.dataset) {
34948
35376
  stop("Missing a joinable data source");
34949
35377
  }
34950
35378
  if (opts.keys) {
@@ -34952,9 +35380,18 @@ ${svg}
34952
35380
  if (opts.keys.length != 2) {
34953
35381
  stop("Expected two key fields: a target field and a source field");
34954
35382
  }
35383
+ if (!src.layer.data) {
35384
+ stop("Source layer is missing attribute data");
35385
+ }
34955
35386
  retn = joinAttributesToFeatures(targetLyr, src.layer.data, opts);
34956
35387
  } else {
34957
35388
  // spatial join
35389
+ if (!src.layer.data) {
35390
+ // KLUDGE -- users might want to join a layer without attributes
35391
+ // to test for intersection... the simplest way to support this is
35392
+ // to add an empty data table to the source layer
35393
+ initDataTable(src.layer);
35394
+ }
34958
35395
  requireDatasetsHaveCompatibleCRS([targetDataset, src.dataset]);
34959
35396
  srcType = src.layer.geometry_type;
34960
35397
  targetType = targetLyr.geometry_type;
@@ -34966,6 +35403,10 @@ ${svg}
34966
35403
  retn = joinPointsToPoints(targetLyr, src.layer, getDatasetCRS(targetDataset), opts);
34967
35404
  } else if (srcType == 'polygon' && targetType == 'polygon') {
34968
35405
  retn = joinPolygonsToPolygons(targetLyr, targetDataset, src, opts);
35406
+ } else if (srcType == 'polyline' && targetType == 'polygon') {
35407
+ retn = joinPolylinesToPolygons(targetLyr, targetDataset, src, opts);
35408
+ } else if (srcType == 'polygon' && targetType == 'polyline') {
35409
+ retn = joinPolygonsToPolylines(targetLyr, targetDataset, src, opts);
34969
35410
  } else {
34970
35411
  stop(utils.format("Unable to join %s geometry to %s geometry",
34971
35412
  srcType || 'null', targetType || 'null'));
@@ -37144,13 +37585,20 @@ ${svg}
37144
37585
 
37145
37586
  // @expression: optional field name or expression
37146
37587
  //
37147
- cmd.splitLayer = function(src, expression, opts) {
37148
- var lyr0 = opts && opts.no_replace ? copyLayer(src) : src,
37588
+ cmd.splitLayer = function(src, expression, optsArg) {
37589
+ var opts = optsArg || {},
37590
+ lyr0 = opts.no_replace ? copyLayer(src) : src,
37149
37591
  properties = lyr0.data ? lyr0.data.getRecords() : null,
37150
37592
  shapes = lyr0.shapes,
37151
37593
  index = {},
37152
37594
  splitLayers = [],
37153
- namer = getSplitNameFunction(lyr0, expression);
37595
+ namer;
37596
+
37597
+ if (opts.ids) {
37598
+ namer = getIdSplitFunction(opts.ids);
37599
+ } else {
37600
+ namer = getSplitNameFunction(lyr0, expression);
37601
+ }
37154
37602
 
37155
37603
  // if (splitField) {
37156
37604
  // internal.requireDataField(lyr0, splitField);
@@ -37182,15 +37630,24 @@ ${svg}
37182
37630
  return splitLayers;
37183
37631
  };
37184
37632
 
37633
+ function getIdSplitFunction(ids) {
37634
+ var set = new Set(ids);
37635
+ return function(i) {
37636
+ return set.has(i) ? '1' : '2';
37637
+ };
37638
+ }
37639
+
37640
+ function getDefaultSplitFunction(lyr) {
37641
+ // if not splitting on an expression and layer is unnamed, name split-apart layers
37642
+ // like: split-1, split-2, ...
37643
+ return function(i) {
37644
+ return (lyr && lyr.name || 'split') + '-' + (i + 1);
37645
+ };
37646
+ }
37647
+
37185
37648
  function getSplitNameFunction(lyr, exp) {
37186
37649
  var compiled;
37187
- if (!exp) {
37188
- // if not splitting on an expression and layer is unnamed, name split-apart layers
37189
- // like: split-1, split-2, ...
37190
- return function(i) {
37191
- return (lyr && lyr.name || 'split') + '-' + (i + 1);
37192
- };
37193
- }
37650
+ if (!exp) return getDefaultSplitFunction(lyr);
37194
37651
  lyr = {name: lyr.name, data: lyr.data}; // remove shape info
37195
37652
  compiled = compileValueExpression(exp, lyr, null);
37196
37653
  return function(i) {
@@ -37990,6 +38447,9 @@ ${svg}
37990
38447
  } else if (name == 'colorizer') {
37991
38448
  outputLayers = cmd.colorizer(opts);
37992
38449
 
38450
+ } else if (name == 'dashlines') {
38451
+ applyCommandToEachLayer(cmd.dashlines, targetLayers, targetDataset, opts);
38452
+
37993
38453
  } else if (name == 'define') {
37994
38454
  cmd.define(opts);
37995
38455
 
@@ -39097,7 +39557,8 @@ ${svg}
39097
39557
  TopojsonImport,
39098
39558
  Topology,
39099
39559
  Units,
39100
- SvgHatch
39560
+ SvgHatch,
39561
+ VertexUtils
39101
39562
  );
39102
39563
 
39103
39564
  // The entry point for the core mapshaper module