mapshaper 0.6.63 → 0.6.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/mapshaper.js CHANGED
@@ -4010,6 +4010,59 @@
4010
4010
  return applyFieldOrder(names, order);
4011
4011
  }
4012
4012
 
4013
+
4014
+ function parseUnknownType(str) {
4015
+ var val = getInputParser('number')(str);
4016
+ if (val !== null) return val;
4017
+ val = getInputParser('object')(str);
4018
+ if (val !== null) return val;
4019
+ return str;
4020
+ }
4021
+
4022
+ // used by GUI data editing
4023
+ function getInputParser(type) {
4024
+ return inputParsers[type || 'multiple'];
4025
+ }
4026
+
4027
+ var inputParsers = {
4028
+ date: function(raw) {
4029
+ var d = new Date(raw);
4030
+ return isNaN(+d) ? null : d;
4031
+ },
4032
+ string: function(raw) {
4033
+ return raw;
4034
+ },
4035
+ number: function(raw) {
4036
+ var val = Number(raw);
4037
+ if (raw == 'NaN') {
4038
+ val = NaN;
4039
+ } else if (isNaN(val)) {
4040
+ val = null;
4041
+ }
4042
+ return val;
4043
+ },
4044
+ object: function(raw) {
4045
+ var val = null;
4046
+ try {
4047
+ val = JSON.parse(raw);
4048
+ } catch(e) {}
4049
+ return val;
4050
+ },
4051
+ boolean: function(raw) {
4052
+ var val = null;
4053
+ if (raw == 'true') {
4054
+ val = true;
4055
+ } else if (raw == 'false') {
4056
+ val = false;
4057
+ }
4058
+ return val;
4059
+ },
4060
+ multiple: function(raw) {
4061
+ var val = Number(raw);
4062
+ return isNaN(val) ? raw : val;
4063
+ }
4064
+ };
4065
+
4013
4066
  var DataUtils = /*#__PURE__*/Object.freeze({
4014
4067
  __proto__: null,
4015
4068
  copyRecord: copyRecord,
@@ -4024,7 +4077,9 @@
4024
4077
  getUniqFieldValues: getUniqFieldValues,
4025
4078
  applyFieldOrder: applyFieldOrder,
4026
4079
  getFirstNonEmptyRecord: getFirstNonEmptyRecord,
4027
- findFieldNames: findFieldNames
4080
+ findFieldNames: findFieldNames,
4081
+ parseUnknownType: parseUnknownType,
4082
+ getInputParser: getInputParser
4028
4083
  });
4029
4084
 
4030
4085
  function DataTable(obj) {
@@ -4122,6 +4177,30 @@
4122
4177
  }
4123
4178
  };
4124
4179
 
4180
+ // TODO: make this stricter (could give false positive on some degenerate paths)
4181
+ function pathIsRectangle(ids, arcs) {
4182
+ var bbox = arcs.getSimpleShapeBounds(ids).toArray();
4183
+ var iter = arcs.getShapeIter(ids);
4184
+ while (iter.hasNext()) {
4185
+ if (iter.x != bbox[0] && iter.x != bbox[2] ||
4186
+ iter.y != bbox[1] && iter.y != bbox[3]) {
4187
+ return false;
4188
+ }
4189
+ }
4190
+ return true;
4191
+ }
4192
+
4193
+ function bboxToCoords(bbox) {
4194
+ return [[bbox[0], bbox[1]], [bbox[0], bbox[3]], [bbox[2], bbox[3]],
4195
+ [bbox[2], bbox[1]], [bbox[0], bbox[1]]];
4196
+ }
4197
+
4198
+ var RectangleUtils = /*#__PURE__*/Object.freeze({
4199
+ __proto__: null,
4200
+ pathIsRectangle: pathIsRectangle,
4201
+ bboxToCoords: bboxToCoords
4202
+ });
4203
+
4125
4204
  // Insert a column of values into a (new or existing) data field
4126
4205
  function insertFieldValues(lyr, fieldName, values) {
4127
4206
  var size = getFeatureCount(lyr) || values.length,
@@ -4162,6 +4241,19 @@
4162
4241
  return lyr.geometry_type == 'point' && layerHasNonNullShapes(lyr);
4163
4242
  }
4164
4243
 
4244
+ function layerIsRectangle(lyr, arcs) {
4245
+ return layerOnlyHasRectangles(lyr, arcs) && lyr.shapes.length == 1;
4246
+ }
4247
+
4248
+ function layerOnlyHasRectangles(lyr, arcs) {
4249
+ if (!layerHasPaths(lyr)) return false;
4250
+ if (countMultiPartFeatures(lyr) > 0) return false;
4251
+ return lyr.shapes.every(function(shp) {
4252
+ if (!shp) return true;
4253
+ return pathIsRectangle(shp[0], arcs);
4254
+ });
4255
+ }
4256
+
4165
4257
  function layerHasNonNullShapes(lyr) {
4166
4258
  return utils.some(lyr.shapes || [], function(shp) {
4167
4259
  return !!shp;
@@ -4405,6 +4497,8 @@
4405
4497
  layerHasGeometry: layerHasGeometry,
4406
4498
  layerHasPaths: layerHasPaths,
4407
4499
  layerHasPoints: layerHasPoints,
4500
+ layerIsRectangle: layerIsRectangle,
4501
+ layerOnlyHasRectangles: layerOnlyHasRectangles,
4408
4502
  layerHasNonNullShapes: layerHasNonNullShapes,
4409
4503
  deleteFeatureById: deleteFeatureById,
4410
4504
  transformPointsInLayer: transformPointsInLayer,
@@ -5002,10 +5096,10 @@
5002
5096
  // x, y: a point location in projected coordinates
5003
5097
  // Returns k, the ratio of coordinate distance to distance on the ground
5004
5098
  function getScaleFactorAtXY(x, y, crs) {
5005
- var dist = 1;
5099
+ var dist = 1 / crs.to_meter;
5006
5100
  var lp = mproj.pj_inv_deg({x: x, y: y}, crs);
5007
5101
  var lp2 = mproj.pj_inv_deg({x: x + dist, y: y}, crs);
5008
- var k = dist / geom.greatCircleDistance(lp.lam, lp.phi, lp2.lam, lp2.phi);
5102
+ var k = 1 / geom.greatCircleDistance(lp.lam, lp.phi, lp2.lam, lp2.phi);
5009
5103
  return k;
5010
5104
  }
5011
5105
 
@@ -11058,7 +11152,7 @@
11058
11152
  // File looks like an importable file type
11059
11153
  // name: filename or path
11060
11154
  function looksLikeImportableFile(name) {
11061
- return !!guessInputFileType(name);
11155
+ return !!guessInputFileType(name) || isImportableAsBinary(name);
11062
11156
  }
11063
11157
 
11064
11158
  // File looks like a directly readable data file type
@@ -13301,6 +13395,18 @@
13301
13395
  miles: 1609.344 // International Statute Mile
13302
13396
  };
13303
13397
 
13398
+ // str: display size in px, pt or in
13399
+ // using: 72pt per inch, 1pt per pixel.
13400
+ function parseSizeParam(str) {
13401
+ var num = parseFloat(str),
13402
+ units = /px$/.test(str) && 'px' || /pt$/.test(str) && 'pt' ||
13403
+ /in$/.test(str) && 'in' || !isNaN(+str) && 'px' || null;
13404
+ if (isNaN(num) || !units) {
13405
+ stop('Invalid size:', str);
13406
+ }
13407
+ return units == 'in' && num * 72 || num;
13408
+ }
13409
+
13304
13410
  // Return coeff. for converting a distance measure to dataset coordinates
13305
13411
  // @paramUnits: units code of distance param, or null if units are not specified
13306
13412
  // @crs: Proj.4 CRS object, or null (unknown latlong CRS);
@@ -13444,6 +13550,7 @@
13444
13550
 
13445
13551
  var Units = /*#__PURE__*/Object.freeze({
13446
13552
  __proto__: null,
13553
+ parseSizeParam: parseSizeParam,
13447
13554
  getIntervalConversionFactor: getIntervalConversionFactor,
13448
13555
  parseMeasure: parseMeasure,
13449
13556
  parseMeasure2: parseMeasure2,
@@ -18121,7 +18228,7 @@
18121
18228
  var frameLyr = findFrameLayerInDataset(dataset);
18122
18229
  var frameData;
18123
18230
  if (frameLyr) {
18124
- frameData = Object.assign({}, getFurnitureLayerData(frameLyr));
18231
+ frameData = getFrameLayerData(frameLyr, dataset);
18125
18232
  } else {
18126
18233
  frameData = calcFrameData(dataset, exportOpts);
18127
18234
  }
@@ -18129,6 +18236,25 @@
18129
18236
  return frameData;
18130
18237
  }
18131
18238
 
18239
+
18240
+ function getFrameLayerData(lyr, dataset) {
18241
+ var bounds = getLayerBounds(lyr, dataset.arcs);
18242
+ var d = lyr.data.getReadOnlyRecordAt(0);
18243
+ var w = d.width || 800;
18244
+ var h = w * bounds.height() / bounds.width();
18245
+ return {
18246
+ type: 'frame',
18247
+ width: w,
18248
+ height: h,
18249
+ bbox: bounds.toArray()
18250
+ };
18251
+ }
18252
+
18253
+ // export function getFrameLayerData(lyr) {
18254
+ // return lyr.data && lyr.data.getReadOnlyRecordAt(0);
18255
+ // }
18256
+
18257
+
18132
18258
  function calcFrameData(dataset, opts) {
18133
18259
  var bounds;
18134
18260
  if (opts.svg_bbox) {
@@ -18146,10 +18272,6 @@
18146
18272
  };
18147
18273
  }
18148
18274
 
18149
- function getFrameLayerData(lyr) {
18150
- return lyr.data && lyr.data.getReadOnlyRecordAt(0);
18151
- }
18152
-
18153
18275
  // Used by mapshaper-frame and mapshaper-pixel-transform. TODO: refactor
18154
18276
  function getFrameSize(bounds, opts) {
18155
18277
  var aspectRatio = bounds.width() / bounds.height();
@@ -18165,26 +18287,27 @@
18165
18287
 
18166
18288
 
18167
18289
  // @lyr dataset layer
18168
- function isFrameLayer(lyr) {
18169
- return getFurnitureLayerType(lyr) == 'frame';
18290
+ function isFrameLayer(lyr, dataset) {
18291
+ return getFurnitureLayerType(lyr) == 'frame' &&
18292
+ layerIsRectangle(lyr, dataset.arcs);
18170
18293
  }
18171
18294
 
18172
18295
  function findFrameLayerInDataset(dataset) {
18173
18296
  return utils.find(dataset.layers, function(lyr) {
18174
- return isFrameLayer(lyr);
18297
+ return isFrameLayer(lyr, dataset);
18175
18298
  });
18176
18299
  }
18177
18300
 
18178
18301
  function findFrameDataset(catalog) {
18179
18302
  var target = utils.find(catalog.getLayers(), function(o) {
18180
- return isFrameLayer(o.layer);
18303
+ return isFrameLayer(o.layer, o.dataset);
18181
18304
  });
18182
18305
  return target ? target.dataset : null;
18183
18306
  }
18184
18307
 
18185
18308
  function findFrameLayer(catalog) {
18186
18309
  var target = utils.find(catalog.getLayers(), function(o) {
18187
- return isFrameLayer(o.layer);
18310
+ return isFrameLayer(o.layer, o.dataset);
18188
18311
  });
18189
18312
  return target && target.layer || null;
18190
18313
  }
@@ -18322,7 +18445,6 @@
18322
18445
  var FrameData = /*#__PURE__*/Object.freeze({
18323
18446
  __proto__: null,
18324
18447
  getFrameData: getFrameData,
18325
- getFrameLayerData: getFrameLayerData,
18326
18448
  getFrameSize: getFrameSize,
18327
18449
  findFrameLayerInDataset: findFrameLayerInDataset,
18328
18450
  findFrameDataset: findFrameDataset,
@@ -18825,6 +18947,10 @@
18825
18947
  if (filter && !filter(fields[i])) continue;
18826
18948
  setAttribute(svgObj, fields[i], rec[fields[i]]);
18827
18949
  }
18950
+ // kludge to prevent default black fill on polygons with stroke styles
18951
+ if ((symType == 'polygon' || symType == 'circle') && rec.stroke && !rec.fill) {
18952
+ setAttribute(svgObj, 'fill', 'none');
18953
+ }
18828
18954
  }
18829
18955
 
18830
18956
  function setAttribute(obj, k, v) {
@@ -19407,99 +19533,136 @@
19407
19533
  }
19408
19534
 
19409
19535
  // TODO: generalize to other kinds of furniture as they are developed
19410
- function getScalebarPosition(d) {
19411
- var opts = { // defaults
19412
- valign: 'top',
19413
- halign: 'left',
19414
- voffs: 10,
19415
- hoffs: 10
19536
+ function getScalebarPosition(opts) {
19537
+ var pos = opts.position || 'top-left';
19538
+ return {
19539
+ valign: pos.includes('top') ? 'top' : 'bottom',
19540
+ halign: pos.includes('left') ? 'left' : 'right'
19416
19541
  };
19417
- if (+d.left > 0) {
19418
- opts.hoffs = +d.left;
19542
+ }
19543
+
19544
+ var styleOpts = {
19545
+ a: {
19546
+ bar_width: 3,
19547
+ tic_length: 0
19548
+ },
19549
+ b: {
19550
+ bar_width: 1,
19551
+ tic_length: 5
19419
19552
  }
19420
- if (+d.top > 0) {
19421
- opts.voffs = +d.top;
19553
+ };
19554
+
19555
+ var defaultOpts = {
19556
+ position: 'top-left',
19557
+ label_position: 'top',
19558
+ label_offset: 4,
19559
+ font_size: 12,
19560
+ margin: 12
19561
+ };
19562
+
19563
+ function getScalebarOpts(d) {
19564
+ var style = d.style == 'b' || d.style == 'B' ? 'b' : 'a';
19565
+ return Object.assign({}, defaultOpts, styleOpts[style], d, {style: style});
19566
+ }
19567
+
19568
+ // approximate pixel height of the scalebar
19569
+ function getScalebarHeight(opts) {
19570
+ return Math.round(opts.bar_width + opts.label_offset +
19571
+ opts.tic_length + opts.font_size * 0.8);
19572
+ }
19573
+
19574
+ function renderAsSvg(length, text, opts) {
19575
+ // label part
19576
+ var xOff = opts.style == 'b' ? Math.round(opts.font_size / 4) : 0;
19577
+ var alignLeft = opts.style == 'a' && opts.position.includes('left');
19578
+ var anchorX = alignLeft ? -xOff : length + xOff;
19579
+ var anchorY = opts.bar_width + + opts.tic_length + opts.label_offset;
19580
+ if (opts.label_position == 'top') {
19581
+ anchorY = -opts.label_offset - opts.tic_length;
19422
19582
  }
19423
- if (+d.right > 0) {
19424
- opts.hoffs = +d.right;
19425
- opts.halign = 'right';
19583
+ var labelOpts = {
19584
+ 'label-text': text,
19585
+ 'font-size': opts.font_size,
19586
+ 'text-anchor': alignLeft ? 'start': 'end',
19587
+ 'dominant-baseline': opts.label_position == 'top' ? 'auto' : 'hanging'
19588
+ //// 'dominant-baseline': labelPos == 'top' ? 'text-after-edge' : 'text-before-edge'
19589
+ // 'text-after-edge' is buggy in Safari and unsupported by Illustrator,
19590
+ // so I'm using 'hanging' and 'auto', which seem to be well supported.
19591
+ // downside: requires a kludgy multiplier to calculate scalebar height (see above)
19592
+ };
19593
+ var labelPart = symbolRenderers.label(labelOpts, anchorX, anchorY);
19594
+ var zeroOpts = Object.assign({}, labelOpts, {'label-text': '0', 'text-anchor': 'start'});
19595
+ var zeroLabel = symbolRenderers.label(zeroOpts, -xOff, anchorY);
19596
+
19597
+ // bar part
19598
+ var y = 0;
19599
+ var y2 = opts.tic_length + opts.bar_width / 2;
19600
+ var coords;
19601
+ if (opts.label_position == "top") {
19602
+ y2 = -y2;
19426
19603
  }
19427
- if (+d.bottom > 0) {
19428
- opts.voffs = +d.bottom;
19429
- opts.valign = 'bottom';
19604
+ if (opts.tic_length > 0) {
19605
+ coords = [[0, y2], [0, y], [length, y], [length, y2]];
19606
+ } else {
19607
+ coords = [[0, y], [length, y]];
19430
19608
  }
19431
- return opts;
19609
+ var barPart = importLineString(coords);
19610
+ Object.assign(barPart.properties, {
19611
+ stroke: 'black',
19612
+ fill: 'none',
19613
+ 'stroke-width': opts.bar_width,
19614
+ 'stroke-linecap': 'butt',
19615
+ 'stroke-linejoin': 'miter'
19616
+ });
19617
+ var parts = opts.style == 'b' ? [zeroLabel, labelPart, barPart] : [labelPart, barPart];
19618
+ return {
19619
+ tag: 'g',
19620
+ children: parts
19621
+ };
19432
19622
  }
19433
19623
 
19434
19624
  function renderScalebar(d, frame) {
19435
- var pos = getScalebarPosition(d);
19625
+ if (!frame.crs) {
19626
+ message('Unable to render scalebar: unknown CRS.');
19627
+ return [];
19628
+ }
19629
+ if (frame.width > 0 === false) {
19630
+ return [];
19631
+ }
19632
+
19633
+ var opts = getScalebarOpts(d);
19436
19634
  var metersPerPx = getMapFrameMetersPerPixel(frame);
19437
19635
  var frameWidthPx = frame.width;
19438
19636
  var unit = d.label ? parseScalebarUnits(d.label) : 'mile';
19439
19637
  var number = d.label ? parseScalebarNumber(d.label) : null;
19440
19638
  var label = number && unit ? d.label : getAutoScalebarLabel(frameWidthPx, metersPerPx, unit);
19441
- var scalebarKm = parseScalebarLabelToKm(label);
19442
- var barHeight = 3;
19443
- var labelOffs = 4;
19444
- var fontSize = +d.font_size || 12;
19445
- var width = Math.round(scalebarKm / metersPerPx * 1000);
19446
- var height = Math.round(barHeight + labelOffs + fontSize * 0.8);
19447
- var labelPos = d.label_position == 'top' ? 'top' : 'bottom';
19448
- var anchorX = pos.halign == 'left' ? 0 : width;
19449
- var anchorY = barHeight + labelOffs;
19450
- var dx = pos.halign == 'right' ? frameWidthPx - width - pos.hoffs : pos.hoffs;
19451
- var dy = pos.valign == 'bottom' ? frame.height - height - pos.voffs : pos.voffs;
19452
19639
 
19640
+ var scalebarKm = parseScalebarLabelToKm(label);
19453
19641
  if (scalebarKm > 0 === false) {
19454
19642
  message('Unusable scalebar label:', label);
19455
19643
  return [];
19456
19644
  }
19457
19645
 
19458
- if (frameWidthPx > 0 === false) {
19459
- return [];
19460
- }
19461
-
19462
- if (!frame.crs) {
19463
- message('Unable to render scalebar: unknown CRS.');
19464
- return [];
19646
+ var width = Math.round(scalebarKm / metersPerPx * 1000);
19647
+ if (width > 0 === false) {
19648
+ stop("Null scalebar length");
19465
19649
  }
19466
19650
 
19467
- if (labelPos == 'top') {
19468
- anchorY = -labelOffs;
19469
- dy += Math.round(labelOffs + fontSize * 0.8);
19651
+ var pos = getScalebarPosition(opts);
19652
+ var height = getScalebarHeight(opts);
19653
+ var dx = pos.halign == 'right' ? frameWidthPx - width - opts.margin : opts.margin;
19654
+ var dy = pos.valign == 'bottom' ? frame.height - height - opts.margin : opts.margin;
19655
+ if (opts.label_position == 'top') {
19656
+ dy += Math.round(opts.label_offset + opts.tic_length + opts.font_size * 0.8 + opts.bar_width / 2);
19657
+ } else {
19658
+ dy += Math.round(opts.bar_width / 2);
19470
19659
  }
19471
19660
 
19472
- if (width > 0 === false) {
19473
- stop("Null scalebar length");
19474
- }
19475
- var barObj = {
19476
- tag: 'rect',
19477
- properties: {
19478
- fill: 'black',
19479
- x: 0,
19480
- y: 0,
19481
- width: width,
19482
- height: barHeight
19483
- }
19484
- };
19485
- var labelOpts = {
19486
- 'label-text': label,
19487
- 'font-size': fontSize,
19488
- 'text-anchor': pos.halign == 'left' ? 'start': 'end',
19489
- 'dominant-baseline': labelPos == 'top' ? 'auto' : 'hanging'
19490
- //// 'dominant-baseline': labelPos == 'top' ? 'text-after-edge' : 'text-before-edge'
19491
- // 'text-after-edge' is buggy in Safari and unsupported by Illustrator,
19492
- // so I'm using 'hanging' and 'auto', which seem to be well supported.
19493
- // downside: requires a kludgy multiplier to calculate scalebar height (see above)
19494
- };
19495
- var labelObj = symbolRenderers.label(labelOpts, anchorX, anchorY);
19496
- var g = {
19497
- tag: 'g',
19498
- children: [barObj, labelObj],
19499
- properties: {
19500
- transform: 'translate(' + dx + ' ' + dy + ')'
19501
- }
19661
+ var g = renderAsSvg(width, label, opts);
19662
+ g.properties = {
19663
+ transform: 'translate(' + dx + ' ' + dy + ')'
19502
19664
  };
19665
+
19503
19666
  return [g];
19504
19667
  }
19505
19668
 
@@ -19615,6 +19778,7 @@
19615
19778
  }).reverse().join(',');
19616
19779
  }
19617
19780
 
19781
+
19618
19782
  function renderFrame(d) {
19619
19783
  var lineWidth = 1,
19620
19784
  // inset stroke by half of line width
@@ -21986,7 +22150,13 @@ ${svg}
21986
22150
 
21987
22151
  // @targets - non-empty output from Catalog#findCommandTargets()
21988
22152
  //
21989
- async function exportTargetLayers(targets, opts) {
22153
+ async function exportTargetLayers(catalog, targets, opts) {
22154
+ // kludge: get extent of 'fit-extent' layer (if given)
22155
+ if (opts.fit_extent) {
22156
+ var target = catalog.findSingleLayer(opts.fit_extent);
22157
+ var bounds = getLayerBounds(target.layer, target.dataset.arcs);
22158
+ opts = Object.assign({svg_bbox: bounds.toArray()}, opts);
22159
+ }
21990
22160
  // convert target fmt to dataset fmt
21991
22161
  var datasets = targets.map(function(target) {
21992
22162
  return utils.defaults({layers: target.layers}, target.dataset);
@@ -24068,6 +24238,9 @@ ${svg}
24068
24238
  describe: '[SVG] bounding box of SVG map in projected map units',
24069
24239
  type: 'bbox'
24070
24240
  })
24241
+ .option('fit-extent', {
24242
+ describe: '[SVG] layer to use for the map extent'
24243
+ })
24071
24244
  .option('point-symbol', {
24072
24245
  describe: '[SVG] circle or square (default is circle)'
24073
24246
  })
@@ -25018,6 +25191,10 @@ ${svg}
25018
25191
  type: 'bbox'
25019
25192
  })
25020
25193
  .option('offset', offsetOpt)
25194
+ .option('width', {
25195
+ // describe: 'set viewport width in pixels',
25196
+ type: 'number'
25197
+ })
25021
25198
  .option('aspect-ratio', aspectRatioOpt)
25022
25199
  .option('source', {
25023
25200
  describe: 'name of layer to enclose'
@@ -25691,13 +25868,33 @@ ${svg}
25691
25868
  DEFAULT: true,
25692
25869
  describe: 'distance label, e.g. "35 miles"'
25693
25870
  })
25694
- .option('top', {})
25695
- .option('right', {})
25696
- .option('bottom', {})
25697
- .option('left', {})
25698
- .option('font-size', {})
25699
- // .option('font-family', {})
25700
- .option('label-position', {}); // top or bottom
25871
+ .option('style', {
25872
+ describe: 'two options: a or b'
25873
+ })
25874
+ .option('font-size', {
25875
+ type: 'number'
25876
+ })
25877
+ .option('tic-length', {
25878
+ describe: 'length of tic marks (style b)',
25879
+ type: 'number'
25880
+ })
25881
+ .option('bar-width', {
25882
+ describe: 'line width of bar',
25883
+ type: 'number'
25884
+ })
25885
+ .option('label-offset', {
25886
+ type: 'number'
25887
+ })
25888
+ .option('position', {
25889
+ describe: 'e.g. bottom-right (default is top-left)'
25890
+ })
25891
+ .option('label-position', {
25892
+ describe: 'top or bottom'
25893
+ })
25894
+ .option('margin', {
25895
+ describe: 'offset in pixels from edge of map',
25896
+ type: 'number'
25897
+ });
25701
25898
 
25702
25899
  parser.command('shape')
25703
25900
  .describe('create a polyline or polygon from coordinates')
@@ -35673,11 +35870,285 @@ ${svg}
35673
35870
  };
35674
35871
  }
35675
35872
 
35873
+ var MAX_RULE_LEN = 50;
35874
+
35875
+ cmd.info = function(targets, opts) {
35876
+ var layers = expandCommandTargets(targets);
35877
+ var arr = layers.map(function(o) {
35878
+ return getLayerInfo(o.layer, o.dataset);
35879
+ });
35880
+
35881
+ if (opts.save_to) {
35882
+ var output = [{
35883
+ filename: opts.save_to + (opts.save_to.endsWith('.json') ? '' : '.json'),
35884
+ content: JSON.stringify(arr, null, 2)
35885
+ }];
35886
+ writeFiles(output, opts);
35887
+ }
35888
+ if (opts.to_layer) {
35889
+ return {
35890
+ info: {},
35891
+ layers: [{
35892
+ name: opts.name || 'info',
35893
+ data: new DataTable(arr)
35894
+ }]
35895
+ };
35896
+ }
35897
+ message(formatInfo(arr));
35898
+ };
35899
+
35900
+ cmd.printInfo = cmd.info; // old name
35901
+
35902
+ function getLayerInfo(lyr, dataset) {
35903
+ var n = getFeatureCount(lyr);
35904
+ var o = {
35905
+ layer_name: lyr.name,
35906
+ geometry_type: lyr.geometry_type,
35907
+ feature_count: n,
35908
+ null_shape_count: 0,
35909
+ null_data_count: lyr.data ? countNullRecords(lyr.data.getRecords()) : n
35910
+ };
35911
+ if (lyr.shapes && lyr.shapes.length > 0) {
35912
+ o.null_shape_count = countNullShapes(lyr.shapes);
35913
+ o.bbox = getLayerBounds(lyr, dataset.arcs).toArray();
35914
+ o.proj4 = getProjInfo(dataset);
35915
+ }
35916
+ o.source_file = getLayerSourceFile(lyr, dataset) || null;
35917
+ o.attribute_data = getAttributeTableInfo(lyr);
35918
+ return o;
35919
+ }
35920
+
35921
+ // i: (optional) record index
35922
+ function getAttributeTableInfo(lyr, i) {
35923
+ if (!lyr.data || lyr.data.size() === 0 || lyr.data.getFields().length === 0) {
35924
+ return null;
35925
+ }
35926
+ var fields = applyFieldOrder(lyr.data.getFields(), 'ascending');
35927
+ var valueName = i === undefined ? 'first_value' : 'value';
35928
+ return fields.map(function(fname) {
35929
+ return {
35930
+ field: fname,
35931
+ [valueName]: lyr.data.getReadOnlyRecordAt(i || 0)[fname]
35932
+ };
35933
+ });
35934
+ }
35935
+
35936
+ function formatInfo(arr) {
35937
+ var str = '';
35938
+ arr.forEach(function(info, i) {
35939
+ var title = 'Layer: ' + (info.layer_name || '[unnamed layer]');
35940
+ var tableStr = formatAttributeTableInfo(info.attribute_data);
35941
+ var tableWidth = measureLongestLine(tableStr);
35942
+ var ruleLen = Math.min(Math.max(title.length, tableWidth), MAX_RULE_LEN);
35943
+ str += '\n';
35944
+ str += utils.lpad('', ruleLen, '=') + '\n';
35945
+ str += title + '\n';
35946
+ str += utils.lpad('', ruleLen, '-') + '\n';
35947
+ str += formatLayerInfo(info);
35948
+ str += tableStr;
35949
+ });
35950
+ return str;
35951
+ }
35952
+
35953
+ function formatLayerInfo(data) {
35954
+ var str = '';
35955
+ str += "Type: " + (data.geometry_type || "tabular data") + "\n";
35956
+ str += utils.format("Records: %,d\n",data.feature_count);
35957
+ if (data.null_shape_count > 0) {
35958
+ str += utils.format("Nulls: %'d", data.null_shape_count) + "\n";
35959
+ }
35960
+ if (data.geometry_type && data.feature_count > data.null_shape_count) {
35961
+ str += "Bounds: " + data.bbox.join(',') + "\n";
35962
+ str += "CRS: " + data.proj4 + "\n";
35963
+ }
35964
+ str += "Source: " + (data.source_file || 'n/a') + "\n";
35965
+ return str;
35966
+ }
35967
+
35968
+ function formatAttributeTableInfo(arr) {
35969
+ if (!arr) return "Attribute data: [none]\n";
35970
+ var header = "\nAttribute data\n";
35971
+ var valKey = 'first_value' in arr[0] ? 'first_value' : 'value';
35972
+ var vals = [];
35973
+ var fields = [];
35974
+ arr.forEach(function(o) {
35975
+ fields.push(o.field);
35976
+ vals.push(o[valKey]);
35977
+ });
35978
+ var maxIntegralChars = vals.reduce(function(max, val) {
35979
+ if (utils.isNumber(val)) {
35980
+ max = Math.max(max, countIntegralChars(val));
35981
+ }
35982
+ return max;
35983
+ }, 0);
35984
+ var col1Arr = ['Field'].concat(fields);
35985
+ var col2Arr = vals.reduce(function(memo, val) {
35986
+ memo.push(formatTableValue(val, maxIntegralChars));
35987
+ return memo;
35988
+ }, [valKey == 'first_value' ? 'First value' : 'Value']);
35989
+ var col1Chars = maxChars(col1Arr);
35990
+ var col2Chars = maxChars(col2Arr);
35991
+ var sepStr = (utils.rpad('', col1Chars + 2, '-') + '+' +
35992
+ utils.rpad('', col2Chars + 2, '-')).substr(0, MAX_RULE_LEN);
35993
+ var sepLine = sepStr + '\n';
35994
+ var table = '';
35995
+ col1Arr.forEach(function(col1, i) {
35996
+ var w = stringDisplayWidth(col1);
35997
+ table += ' ' + col1 + utils.rpad('', col1Chars - w, ' ') + ' | ' +
35998
+ col2Arr[i] + '\n';
35999
+ if (i === 0) table += sepLine; // separator after first line
36000
+ });
36001
+ return header + sepLine + table + sepLine;
36002
+ }
36003
+
36004
+ function measureLongestLine(str) {
36005
+ return Math.max.apply(null, str.split('\n').map(function(line) {return stringDisplayWidth(line);}));
36006
+ }
36007
+
36008
+ function stringDisplayWidth(str) {
36009
+ var w = 0;
36010
+ for (var i = 0, n=str.length; i < n; i++) {
36011
+ w += charDisplayWidth(str.charCodeAt(i));
36012
+ }
36013
+ return w;
36014
+ }
36015
+
36016
+ // see https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
36017
+ // this is a simplified version, focusing on double-width CJK chars and ignoring nonprinting etc chars
36018
+ function charDisplayWidth(c) {
36019
+ if (c >= 0x1100 &&
36020
+ (c <= 0x115f || c == 0x2329 || c == 0x232a ||
36021
+ (c >= 0x2e80 && c <= 0xa4cf && c != 0x303f) || /* CJK ... Yi */
36022
+ (c >= 0xac00 && c <= 0xd7a3) || /* Hangul Syllables */
36023
+ (c >= 0xf900 && c <= 0xfaff) || /* CJK Compatibility Ideographs */
36024
+ (c >= 0xfe10 && c <= 0xfe19) || /* Vertical forms */
36025
+ (c >= 0xfe30 && c <= 0xfe6f) || /* CJK Compatibility Forms */
36026
+ (c >= 0xff00 && c <= 0xff60) || /* Fullwidth Forms */
36027
+ (c >= 0xffe0 && c <= 0xffe6) ||
36028
+ (c >= 0x20000 && c <= 0x2fffd) ||
36029
+ (c >= 0x30000 && c <= 0x3fffd))) return 2;
36030
+ return 1;
36031
+ }
36032
+
36033
+ // TODO: consider polygons with zero area or other invalid geometries
36034
+ function countNullShapes(shapes) {
36035
+ var count = 0;
36036
+ for (var i=0; i<shapes.length; i++) {
36037
+ if (!shapes[i] || shapes[i].length === 0) count++;
36038
+ }
36039
+ return count;
36040
+ }
36041
+
36042
+ function countNullRecords(records) {
36043
+ var count = 0;
36044
+ for (var i=0; i<records.length; i++) {
36045
+ if (!records[i]) count++;
36046
+ }
36047
+ return count;
36048
+ }
36049
+
36050
+ function maxChars(arr) {
36051
+ return arr.reduce(function(memo, str) {
36052
+ var w = stringDisplayWidth(str);
36053
+ return w > memo ? w : memo;
36054
+ }, 0);
36055
+ }
36056
+
36057
+ function formatString(str) {
36058
+ var replacements = {
36059
+ '\n': '\\n',
36060
+ '\r': '\\r',
36061
+ '\t': '\\t'
36062
+ };
36063
+ var cleanChar = function(c) {
36064
+ // convert newlines and carriage returns
36065
+ // TODO: better handling of non-printing chars
36066
+ return c in replacements ? replacements[c] : '';
36067
+ };
36068
+ str = str.replace(/[\r\t\n]/g, cleanChar);
36069
+ return "'" + str + "'";
36070
+ }
36071
+
36072
+ function countIntegralChars(val) {
36073
+ return utils.isNumber(val) ? (utils.formatNumber(val) + '.').indexOf('.') : 0;
36074
+ }
36075
+
36076
+ function formatTableValue(val, integralChars) {
36077
+ var str;
36078
+ if (utils.isNumber(val)) {
36079
+ str = utils.lpad("", integralChars - countIntegralChars(val), ' ') +
36080
+ utils.formatNumber(val);
36081
+ } else if (utils.isString(val)) {
36082
+ str = formatString(val);
36083
+ } else if (utils.isDate(val)) {
36084
+ str = utils.formatDateISO(val) + ' (Date)';
36085
+ } else if (utils.isObject(val)) { // if {} or [], display JSON
36086
+ str = JSON.stringify(val);
36087
+ } else {
36088
+ str = String(val);
36089
+ }
36090
+
36091
+ if (typeof str != 'string') {
36092
+ // e.g. JSON.stringify converts functions to undefined
36093
+ str = '[' + (typeof val) + ']';
36094
+ }
36095
+
36096
+ return str;
36097
+ }
36098
+
36099
+ var Info = /*#__PURE__*/Object.freeze({
36100
+ __proto__: null,
36101
+ getLayerInfo: getLayerInfo,
36102
+ getAttributeTableInfo: getAttributeTableInfo,
36103
+ formatAttributeTableInfo: formatAttributeTableInfo,
36104
+ formatTableValue: formatTableValue
36105
+ });
36106
+
36107
+ // import { importGeoJSON } from '../geojson/geojson-import';
36108
+
36109
+
36110
+ function addTargetProxies(targets, ctx) {
36111
+ if (targets && targets.length > 0) {
36112
+ var proxies = expandCommandTargets(targets).reduce(function(memo, target) {
36113
+ var proxy = getTargetProxy(target);
36114
+ memo.push(proxy);
36115
+ // index targets by layer name too
36116
+ if (target.layer.name) {
36117
+ memo[target.layer.name] = proxy;
36118
+ }
36119
+ return memo;
36120
+ }, []);
36121
+ Object.defineProperty(ctx, 'targets', {value: proxies});
36122
+ if (proxies.length == 1) {
36123
+ Object.defineProperty(ctx, 'target', {value: proxies[0]});
36124
+ }
36125
+ }
36126
+ }
36127
+
36128
+ function getTargetProxy(target) {
36129
+ var proxy = getLayerInfo(target.layer, target.dataset); // layer_name, feature_count etc
36130
+ proxy.layer = target.layer;
36131
+ proxy.dataset = target.dataset;
36132
+ addGetters(proxy, {
36133
+ // export as an object, not a string or buffer
36134
+ geojson: getGeoJSON
36135
+ });
36136
+
36137
+ function getGeoJSON() {
36138
+ var features = exportLayerAsGeoJSON(target.layer, target.dataset, {rfc7946: true}, true);
36139
+ return {
36140
+ type: 'FeatureCollection',
36141
+ features: features
36142
+ };
36143
+ }
36144
+
36145
+ return proxy;
36146
+ }
36147
+
35676
36148
  function compileIfCommandExpression(expr, catalog, opts) {
35677
36149
  return compileLayerExpression(expr, catalog, opts);
35678
36150
  }
35679
36151
 
35680
-
35681
36152
  function compileLayerExpression(expr, catalog, opts) {
35682
36153
  var targetId = opts.layer || opts.target || null;
35683
36154
  var targets = catalog.findCommandTargets(targetId);
@@ -35693,6 +36164,10 @@ ${svg}
35693
36164
  } else {
35694
36165
  ctx = getNullLayerProxy(targets);
35695
36166
  }
36167
+
36168
+ // add target/targets proxies, for consistency with the -run command
36169
+ addTargetProxies(targets, ctx);
36170
+
35696
36171
  ctx.global = defs; // TODO: remove duplication with mapshaper.expressions.mjs
35697
36172
  var func = compileExpressionToFunction(expr, opts);
35698
36173
 
@@ -38039,7 +38514,7 @@ ${svg}
38039
38514
  getPointFeatureBounds(shp) : targetDataset.arcs.getMultiShapeBounds(shp);
38040
38515
  bounds = applyRectangleOptions(bounds, crsInfo.crs, opts);
38041
38516
  if (!bounds) return null;
38042
- return convertBboxToGeoJSON(bounds.toArray(), opts);
38517
+ return bboxToPolygon(bounds.toArray(), opts);
38043
38518
  });
38044
38519
  var geojson = {
38045
38520
  type: 'FeatureCollection',
@@ -38089,8 +38564,16 @@ ${svg}
38089
38564
  if (!bounds || !bounds.hasBounds()) {
38090
38565
  stop('Missing rectangle extent');
38091
38566
  }
38092
- var geojson = convertBboxToGeoJSON(bounds.toArray(), opts);
38093
- var dataset = importGeoJSON(geojson, {});
38567
+ var feature = {
38568
+ type: 'Feature',
38569
+ properties: {},
38570
+ geometry: bboxToPolygon(bounds.toArray(), opts)
38571
+ };
38572
+ if (opts.width > 0) {
38573
+ feature.properties.width = opts.width;
38574
+ feature.properties.type = 'frame';
38575
+ }
38576
+ var dataset = importGeoJSON(feature, {});
38094
38577
  dataset.layers[0].name = opts.name || 'rectangle';
38095
38578
  setDatasetCrsInfo(dataset, crsInfo);
38096
38579
  return dataset;
@@ -38140,17 +38623,13 @@ ${svg}
38140
38623
  return bounds;
38141
38624
  }
38142
38625
 
38143
- function convertBboxToGeoJSON(bbox, optsArg) {
38626
+ function bboxToPolygon(bbox, optsArg) {
38144
38627
  var opts = optsArg || {};
38145
- var coords = [[bbox[0], bbox[1]], [bbox[0], bbox[3]], [bbox[2], bbox[3]],
38146
- [bbox[2], bbox[1]], [bbox[0], bbox[1]]];
38628
+ var coords = bboxToCoords(bbox);
38147
38629
  if (opts.interval > 0) {
38148
38630
  coords = densifyPathByInterval(coords, opts.interval);
38149
38631
  }
38150
- return opts.geometry_type == 'polyline' ? {
38151
- type: 'LineString',
38152
- coordinates: coords
38153
- } : {
38632
+ return {
38154
38633
  type: 'Polygon',
38155
38634
  coordinates: [coords]
38156
38635
  };
@@ -38159,7 +38638,7 @@ ${svg}
38159
38638
  var Rectangle = /*#__PURE__*/Object.freeze({
38160
38639
  __proto__: null,
38161
38640
  applyAspectRatio: applyAspectRatio,
38162
- convertBboxToGeoJSON: convertBboxToGeoJSON
38641
+ bboxToPolygon: bboxToPolygon
38163
38642
  });
38164
38643
 
38165
38644
  function getSemiMinorAxis(P) {
@@ -38713,8 +39192,7 @@ ${svg}
38713
39192
  var rotation = getRotationParams(dest);
38714
39193
  if (!bbox) error('Missing expected clip bbox.');
38715
39194
  opts = Object.assign({interval: 0.5}, opts); // make sure edges can curve
38716
- var geojson = convertBboxToGeoJSON(bbox, opts);
38717
- var dataset = importGeoJSON(geojson);
39195
+ var dataset = importGeoJSON(bboxToPolygon(bbox, opts));
38718
39196
  if (rotation) {
38719
39197
  rotateDataset(dataset, {rotation: rotation, invert: true});
38720
39198
  }
@@ -38903,7 +39381,7 @@ ${svg}
38903
39381
  var e = 1e-8;
38904
39382
  var bbox = [lon-e, -91, lon+e, 91];
38905
39383
  // densify (so cut line can curve, e.g. Cupola projection)
38906
- var geojson = convertBboxToGeoJSON(bbox, {interval: 0.5});
39384
+ var geojson = bboxToPolygon(bbox, {interval: 0.5});
38907
39385
  var clip = importGeoJSON(geojson);
38908
39386
  clipLayersInPlace(pathLayers, clip, dataset, 'erase');
38909
39387
  }
@@ -39206,7 +39684,6 @@ ${svg}
39206
39684
 
39207
39685
  function createProjectedGraticule(dest, opts) {
39208
39686
  var src = parseCrsString('wgs84');
39209
- // var outline = getOutlineDataset(src, dest, {inset: 0, geometry_type: 'polyline'});
39210
39687
  var outline = getOutlineDataset(src, dest, {});
39211
39688
  var graticule = importGeoJSON(createGraticule(dest, !!outline, opts));
39212
39689
  projectDataset(graticule, src, dest, {no_clip: false}); // TODO: densify?
@@ -39388,7 +39865,6 @@ ${svg}
39388
39865
  }
39389
39866
 
39390
39867
  function skipCommand(cmdName, job) {
39391
- // allow all control commands to run
39392
39868
  if (jobIsStopped(job)) return true;
39393
39869
  if (isControlFlowCommand(cmdName)) return false;
39394
39870
  return !inActiveBranch(job);
@@ -39481,240 +39957,6 @@ ${svg}
39481
39957
  utils.extend(getStashedVar('defs'), obj);
39482
39958
  };
39483
39959
 
39484
- var MAX_RULE_LEN = 50;
39485
-
39486
- cmd.info = function(targets, opts) {
39487
- var layers = expandCommandTargets(targets);
39488
- var arr = layers.map(function(o) {
39489
- return getLayerInfo(o.layer, o.dataset);
39490
- });
39491
-
39492
- if (opts.save_to) {
39493
- var output = [{
39494
- filename: opts.save_to + (opts.save_to.endsWith('.json') ? '' : '.json'),
39495
- content: JSON.stringify(arr, null, 2)
39496
- }];
39497
- writeFiles(output, opts);
39498
- }
39499
- if (opts.to_layer) {
39500
- return {
39501
- info: {},
39502
- layers: [{
39503
- name: opts.name || 'info',
39504
- data: new DataTable(arr)
39505
- }]
39506
- };
39507
- }
39508
- message(formatInfo(arr));
39509
- };
39510
-
39511
- cmd.printInfo = cmd.info; // old name
39512
-
39513
- function getLayerInfo(lyr, dataset) {
39514
- var n = getFeatureCount(lyr);
39515
- var o = {
39516
- layer_name: lyr.name,
39517
- geometry_type: lyr.geometry_type,
39518
- feature_count: n,
39519
- null_shape_count: 0,
39520
- null_data_count: lyr.data ? countNullRecords(lyr.data.getRecords()) : n
39521
- };
39522
- if (lyr.shapes && lyr.shapes.length > 0) {
39523
- o.null_shape_count = countNullShapes(lyr.shapes);
39524
- o.bbox = getLayerBounds(lyr, dataset.arcs).toArray();
39525
- o.proj4 = getProjInfo(dataset);
39526
- }
39527
- o.source_file = getLayerSourceFile(lyr, dataset) || null;
39528
- o.attribute_data = getAttributeTableInfo(lyr);
39529
- return o;
39530
- }
39531
-
39532
- // i: (optional) record index
39533
- function getAttributeTableInfo(lyr, i) {
39534
- if (!lyr.data || lyr.data.size() === 0 || lyr.data.getFields().length === 0) {
39535
- return null;
39536
- }
39537
- var fields = applyFieldOrder(lyr.data.getFields(), 'ascending');
39538
- var valueName = i === undefined ? 'first_value' : 'value';
39539
- return fields.map(function(fname) {
39540
- return {
39541
- field: fname,
39542
- [valueName]: lyr.data.getReadOnlyRecordAt(i || 0)[fname]
39543
- };
39544
- });
39545
- }
39546
-
39547
- function formatInfo(arr) {
39548
- var str = '';
39549
- arr.forEach(function(info, i) {
39550
- var title = 'Layer: ' + (info.layer_name || '[unnamed layer]');
39551
- var tableStr = formatAttributeTableInfo(info.attribute_data);
39552
- var tableWidth = measureLongestLine(tableStr);
39553
- var ruleLen = Math.min(Math.max(title.length, tableWidth), MAX_RULE_LEN);
39554
- str += '\n';
39555
- str += utils.lpad('', ruleLen, '=') + '\n';
39556
- str += title + '\n';
39557
- str += utils.lpad('', ruleLen, '-') + '\n';
39558
- str += formatLayerInfo(info);
39559
- str += tableStr;
39560
- });
39561
- return str;
39562
- }
39563
-
39564
- function formatLayerInfo(data) {
39565
- var str = '';
39566
- str += "Type: " + (data.geometry_type || "tabular data") + "\n";
39567
- str += utils.format("Records: %,d\n",data.feature_count);
39568
- if (data.null_shape_count > 0) {
39569
- str += utils.format("Nulls: %'d", data.null_shape_count) + "\n";
39570
- }
39571
- if (data.geometry_type && data.feature_count > data.null_shape_count) {
39572
- str += "Bounds: " + data.bbox.join(',') + "\n";
39573
- str += "CRS: " + data.proj4 + "\n";
39574
- }
39575
- str += "Source: " + (data.source_file || 'n/a') + "\n";
39576
- return str;
39577
- }
39578
-
39579
- function formatAttributeTableInfo(arr) {
39580
- if (!arr) return "Attribute data: [none]\n";
39581
- var header = "\nAttribute data\n";
39582
- var valKey = 'first_value' in arr[0] ? 'first_value' : 'value';
39583
- var vals = [];
39584
- var fields = [];
39585
- arr.forEach(function(o) {
39586
- fields.push(o.field);
39587
- vals.push(o[valKey]);
39588
- });
39589
- var maxIntegralChars = vals.reduce(function(max, val) {
39590
- if (utils.isNumber(val)) {
39591
- max = Math.max(max, countIntegralChars(val));
39592
- }
39593
- return max;
39594
- }, 0);
39595
- var col1Arr = ['Field'].concat(fields);
39596
- var col2Arr = vals.reduce(function(memo, val) {
39597
- memo.push(formatTableValue(val, maxIntegralChars));
39598
- return memo;
39599
- }, [valKey == 'first_value' ? 'First value' : 'Value']);
39600
- var col1Chars = maxChars(col1Arr);
39601
- var col2Chars = maxChars(col2Arr);
39602
- var sepStr = (utils.rpad('', col1Chars + 2, '-') + '+' +
39603
- utils.rpad('', col2Chars + 2, '-')).substr(0, MAX_RULE_LEN);
39604
- var sepLine = sepStr + '\n';
39605
- var table = '';
39606
- col1Arr.forEach(function(col1, i) {
39607
- var w = stringDisplayWidth(col1);
39608
- table += ' ' + col1 + utils.rpad('', col1Chars - w, ' ') + ' | ' +
39609
- col2Arr[i] + '\n';
39610
- if (i === 0) table += sepLine; // separator after first line
39611
- });
39612
- return header + sepLine + table + sepLine;
39613
- }
39614
-
39615
- function measureLongestLine(str) {
39616
- return Math.max.apply(null, str.split('\n').map(function(line) {return stringDisplayWidth(line);}));
39617
- }
39618
-
39619
- function stringDisplayWidth(str) {
39620
- var w = 0;
39621
- for (var i = 0, n=str.length; i < n; i++) {
39622
- w += charDisplayWidth(str.charCodeAt(i));
39623
- }
39624
- return w;
39625
- }
39626
-
39627
- // see https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
39628
- // this is a simplified version, focusing on double-width CJK chars and ignoring nonprinting etc chars
39629
- function charDisplayWidth(c) {
39630
- if (c >= 0x1100 &&
39631
- (c <= 0x115f || c == 0x2329 || c == 0x232a ||
39632
- (c >= 0x2e80 && c <= 0xa4cf && c != 0x303f) || /* CJK ... Yi */
39633
- (c >= 0xac00 && c <= 0xd7a3) || /* Hangul Syllables */
39634
- (c >= 0xf900 && c <= 0xfaff) || /* CJK Compatibility Ideographs */
39635
- (c >= 0xfe10 && c <= 0xfe19) || /* Vertical forms */
39636
- (c >= 0xfe30 && c <= 0xfe6f) || /* CJK Compatibility Forms */
39637
- (c >= 0xff00 && c <= 0xff60) || /* Fullwidth Forms */
39638
- (c >= 0xffe0 && c <= 0xffe6) ||
39639
- (c >= 0x20000 && c <= 0x2fffd) ||
39640
- (c >= 0x30000 && c <= 0x3fffd))) return 2;
39641
- return 1;
39642
- }
39643
-
39644
- // TODO: consider polygons with zero area or other invalid geometries
39645
- function countNullShapes(shapes) {
39646
- var count = 0;
39647
- for (var i=0; i<shapes.length; i++) {
39648
- if (!shapes[i] || shapes[i].length === 0) count++;
39649
- }
39650
- return count;
39651
- }
39652
-
39653
- function countNullRecords(records) {
39654
- var count = 0;
39655
- for (var i=0; i<records.length; i++) {
39656
- if (!records[i]) count++;
39657
- }
39658
- return count;
39659
- }
39660
-
39661
- function maxChars(arr) {
39662
- return arr.reduce(function(memo, str) {
39663
- var w = stringDisplayWidth(str);
39664
- return w > memo ? w : memo;
39665
- }, 0);
39666
- }
39667
-
39668
- function formatString(str) {
39669
- var replacements = {
39670
- '\n': '\\n',
39671
- '\r': '\\r',
39672
- '\t': '\\t'
39673
- };
39674
- var cleanChar = function(c) {
39675
- // convert newlines and carriage returns
39676
- // TODO: better handling of non-printing chars
39677
- return c in replacements ? replacements[c] : '';
39678
- };
39679
- str = str.replace(/[\r\t\n]/g, cleanChar);
39680
- return "'" + str + "'";
39681
- }
39682
-
39683
- function countIntegralChars(val) {
39684
- return utils.isNumber(val) ? (utils.formatNumber(val) + '.').indexOf('.') : 0;
39685
- }
39686
-
39687
- function formatTableValue(val, integralChars) {
39688
- var str;
39689
- if (utils.isNumber(val)) {
39690
- str = utils.lpad("", integralChars - countIntegralChars(val), ' ') +
39691
- utils.formatNumber(val);
39692
- } else if (utils.isString(val)) {
39693
- str = formatString(val);
39694
- } else if (utils.isDate(val)) {
39695
- str = utils.formatDateISO(val) + ' (Date)';
39696
- } else if (utils.isObject(val)) { // if {} or [], display JSON
39697
- str = JSON.stringify(val);
39698
- } else {
39699
- str = String(val);
39700
- }
39701
-
39702
- if (typeof str != 'string') {
39703
- // e.g. JSON.stringify converts functions to undefined
39704
- str = '[' + (typeof val) + ']';
39705
- }
39706
-
39707
- return str;
39708
- }
39709
-
39710
- var Info = /*#__PURE__*/Object.freeze({
39711
- __proto__: null,
39712
- getLayerInfo: getLayerInfo,
39713
- getAttributeTableInfo: getAttributeTableInfo,
39714
- formatAttributeTableInfo: formatAttributeTableInfo,
39715
- formatTableValue: formatTableValue
39716
- });
39717
-
39718
39960
  // TODO: make sure that the inlay shapes and data are not shared
39719
39961
  cmd.inlay = function(targetLayers, src, targetDataset, opts) {
39720
39962
  var mergedDataset = mergeLayersForOverlay(targetLayers, targetDataset, src, opts);
@@ -42031,49 +42273,13 @@ ${svg}
42031
42273
  }, {});
42032
42274
  }
42033
42275
 
42034
- // import { importGeoJSON } from '../geojson/geojson-import';
42035
-
42036
- function getTargetProxy(target) {
42037
- var proxy = getLayerInfo(target.layer, target.dataset); // layer_name, feature_count etc
42038
- proxy.layer = target.layer;
42039
- proxy.dataset = target.dataset;
42040
- addGetters(proxy, {
42041
- // export as an object, not a string or buffer
42042
- geojson: getGeoJSON
42043
- });
42044
-
42045
- function getGeoJSON() {
42046
- var features = exportLayerAsGeoJSON(target.layer, target.dataset, {rfc7946: true}, true);
42047
- return {
42048
- type: 'FeatureCollection',
42049
- features: features
42050
- };
42051
- }
42052
-
42053
- return proxy;
42054
- }
42055
-
42056
42276
  // Support for evaluating expressions embedded in curly-brace templates
42057
42277
 
42058
42278
  // Returns: a string (e.g. a command string used by the -run command)
42059
42279
  async function evalTemplateExpression(expression, targets, ctx) {
42060
42280
  ctx = ctx || getBaseContext();
42061
42281
  // TODO: throw an error if target is used when there are multiple targets
42062
- if (targets) {
42063
- var proxies = expandCommandTargets(targets).reduce(function(memo, target) {
42064
- var proxy = getTargetProxy(target);
42065
- memo.push(proxy);
42066
- // index targets by layer name too
42067
- if (target.layer.name) {
42068
- memo[target.layer.name] = proxy;
42069
- }
42070
- return memo;
42071
- }, []);
42072
- Object.defineProperty(ctx, 'targets', {value: proxies});
42073
- if (proxies.length == 1) {
42074
- Object.defineProperty(ctx, 'target', {value: proxies[0]});
42075
- }
42076
- }
42282
+ addTargetProxies(targets, ctx);
42077
42283
  // Add global functions and data to the expression context
42078
42284
  // (e.g. functions imported via the -require command)
42079
42285
  var globals = getStashedVar('defs') || {};
@@ -44564,6 +44770,10 @@ ${svg}
44564
44770
  return done(null);
44565
44771
  }
44566
44772
 
44773
+ if (name == 'comment') {
44774
+ return done(null);
44775
+ }
44776
+
44567
44777
  if (!job) job = new Job();
44568
44778
  job.startCommand(command);
44569
44779
 
@@ -44724,8 +44934,9 @@ ${svg}
44724
44934
  } else if (name == 'filter-slivers') {
44725
44935
  applyCommandToEachLayer(cmd.filterSlivers, targetLayers, targetDataset, opts);
44726
44936
 
44727
- } else if (name == 'frame') {
44728
- cmd.frame(job.catalog, source, opts);
44937
+ // // 'frame' and 'rectangle' have merged
44938
+ // } else if (name == 'frame') {
44939
+ // cmd.frame(job.catalog, source, opts);
44729
44940
 
44730
44941
  } else if (name == 'fuzzy-join') {
44731
44942
  applyCommandToEachLayer(cmd.fuzzyJoin, targetLayers, arcs, source, opts);
@@ -44793,7 +45004,8 @@ ${svg}
44793
45004
  outputLayers = cmd.mosaic(targetLayers, targetDataset, opts);
44794
45005
 
44795
45006
  } else if (name == 'o') {
44796
- outputFiles = await exportTargetLayers(targets, opts);
45007
+ // kludge
45008
+ outputFiles = await exportTargetLayers(job.catalog, targets, opts);
44797
45009
  if (opts.final) {
44798
45010
  // don't propagate data if output is final
44799
45011
  //// catalog = null;
@@ -44826,7 +45038,10 @@ ${svg}
44826
45038
  cmd.proj(targ.dataset, job.catalog, opts);
44827
45039
  });
44828
45040
 
44829
- } else if (name == 'rectangle') {
45041
+ } else if (name == 'rectangle' || name == 'frame') {
45042
+ if (name == 'frame' && !opts.width) {
45043
+ stop('Command requires a width= argument');
45044
+ }
44830
45045
  if (source || opts.bbox || targets.length === 0) {
44831
45046
  job.catalog.addDataset(cmd.rectangle(source, opts));
44832
45047
  } else {
@@ -44992,7 +45207,7 @@ ${svg}
44992
45207
  });
44993
45208
  }
44994
45209
 
44995
- var version = "0.6.63";
45210
+ var version = "0.6.66";
44996
45211
 
44997
45212
  // Parse command line args into commands and run them
44998
45213
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
@@ -45734,6 +45949,7 @@ ${svg}
45734
45949
  Projections,
45735
45950
  ProjectionParams,
45736
45951
  Rectangle,
45952
+ RectangleUtils,
45737
45953
  Rounding,
45738
45954
  RunCommands,
45739
45955
  Scalebar,