mapshaper 0.6.65 → 0.6.67

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) {
@@ -4140,7 +4195,7 @@
4140
4195
  [bbox[2], bbox[1]], [bbox[0], bbox[1]]];
4141
4196
  }
4142
4197
 
4143
- var RectangleGeom = /*#__PURE__*/Object.freeze({
4198
+ var RectangleUtils = /*#__PURE__*/Object.freeze({
4144
4199
  __proto__: null,
4145
4200
  pathIsRectangle: pathIsRectangle,
4146
4201
  bboxToCoords: bboxToCoords
@@ -4186,6 +4241,10 @@
4186
4241
  return lyr.geometry_type == 'point' && layerHasNonNullShapes(lyr);
4187
4242
  }
4188
4243
 
4244
+ function layerIsRectangle(lyr, arcs) {
4245
+ return layerOnlyHasRectangles(lyr, arcs) && lyr.shapes.length == 1;
4246
+ }
4247
+
4189
4248
  function layerOnlyHasRectangles(lyr, arcs) {
4190
4249
  if (!layerHasPaths(lyr)) return false;
4191
4250
  if (countMultiPartFeatures(lyr) > 0) return false;
@@ -4438,6 +4497,7 @@
4438
4497
  layerHasGeometry: layerHasGeometry,
4439
4498
  layerHasPaths: layerHasPaths,
4440
4499
  layerHasPoints: layerHasPoints,
4500
+ layerIsRectangle: layerIsRectangle,
4441
4501
  layerOnlyHasRectangles: layerOnlyHasRectangles,
4442
4502
  layerHasNonNullShapes: layerHasNonNullShapes,
4443
4503
  deleteFeatureById: deleteFeatureById,
@@ -13335,6 +13395,19 @@
13335
13395
  miles: 1609.344 // International Statute Mile
13336
13396
  };
13337
13397
 
13398
+ // str: display size in px, pt or in
13399
+ // using: 72pt per inch, 1pt per pixel.
13400
+ function parseSizeParam(p) {
13401
+ var str = String(p),
13402
+ num = parseFloat(str),
13403
+ units = /px|pix/.test(str) && 'px' || /pt|point/.test(str) && 'pt' ||
13404
+ /in/.test(str) && 'in' || !isNaN(+str) && 'px' || null;
13405
+ if (isNaN(num) || !units) {
13406
+ stop('Invalid size:', str);
13407
+ }
13408
+ return units == 'in' && num * 72 || num;
13409
+ }
13410
+
13338
13411
  // Return coeff. for converting a distance measure to dataset coordinates
13339
13412
  // @paramUnits: units code of distance param, or null if units are not specified
13340
13413
  // @crs: Proj.4 CRS object, or null (unknown latlong CRS);
@@ -13478,6 +13551,7 @@
13478
13551
 
13479
13552
  var Units = /*#__PURE__*/Object.freeze({
13480
13553
  __proto__: null,
13554
+ parseSizeParam: parseSizeParam,
13481
13555
  getIntervalConversionFactor: getIntervalConversionFactor,
13482
13556
  parseMeasure: parseMeasure,
13483
13557
  parseMeasure2: parseMeasure2,
@@ -18155,7 +18229,7 @@
18155
18229
  var frameLyr = findFrameLayerInDataset(dataset);
18156
18230
  var frameData;
18157
18231
  if (frameLyr) {
18158
- frameData = Object.assign({}, getFurnitureLayerData(frameLyr));
18232
+ frameData = getFrameLayerData(frameLyr, dataset.arcs);
18159
18233
  } else {
18160
18234
  frameData = calcFrameData(dataset, exportOpts);
18161
18235
  }
@@ -18163,6 +18237,21 @@
18163
18237
  return frameData;
18164
18238
  }
18165
18239
 
18240
+
18241
+ function getFrameLayerData(lyr, arcs) {
18242
+ var bounds = getLayerBounds(lyr, arcs);
18243
+ var d = lyr.data.getReadOnlyRecordAt(0);
18244
+ var w = d.width || 800;
18245
+ var h = w * bounds.height() / bounds.width();
18246
+ return {
18247
+ type: 'frame',
18248
+ width: w,
18249
+ height: h,
18250
+ bbox: bounds.toArray()
18251
+ };
18252
+ }
18253
+
18254
+
18166
18255
  function calcFrameData(dataset, opts) {
18167
18256
  var bounds;
18168
18257
  if (opts.svg_bbox) {
@@ -18180,10 +18269,6 @@
18180
18269
  };
18181
18270
  }
18182
18271
 
18183
- function getFrameLayerData(lyr) {
18184
- return lyr.data && lyr.data.getReadOnlyRecordAt(0);
18185
- }
18186
-
18187
18272
  // Used by mapshaper-frame and mapshaper-pixel-transform. TODO: refactor
18188
18273
  function getFrameSize(bounds, opts) {
18189
18274
  var aspectRatio = bounds.width() / bounds.height();
@@ -18199,30 +18284,34 @@
18199
18284
 
18200
18285
 
18201
18286
  // @lyr dataset layer
18202
- function isFrameLayer(lyr) {
18203
- return getFurnitureLayerType(lyr) == 'frame';
18287
+ function isFrameLayer(lyr, arcs) {
18288
+ return getFurnitureLayerType(lyr) == 'frame' &&
18289
+ layerIsRectangle(lyr, arcs);
18204
18290
  }
18205
18291
 
18206
18292
  function findFrameLayerInDataset(dataset) {
18207
18293
  return utils.find(dataset.layers, function(lyr) {
18208
- return isFrameLayer(lyr);
18294
+ return isFrameLayer(lyr, dataset.arcs);
18209
18295
  });
18210
18296
  }
18211
18297
 
18298
+ // TODO: handle multiple frames in catalog
18212
18299
  function findFrameDataset(catalog) {
18213
- var target = utils.find(catalog.getLayers(), function(o) {
18214
- return isFrameLayer(o.layer);
18215
- });
18216
- return target ? target.dataset : null;
18300
+ var target = findFrame(catalog);
18301
+ return target && target.dataset || null;
18217
18302
  }
18218
18303
 
18219
18304
  function findFrameLayer(catalog) {
18220
- var target = utils.find(catalog.getLayers(), function(o) {
18221
- return isFrameLayer(o.layer);
18222
- });
18305
+ var target = findFrame(catalog);
18223
18306
  return target && target.layer || null;
18224
18307
  }
18225
18308
 
18309
+ function findFrame(catalog) {
18310
+ return utils.find(catalog.getLayers(), function(o) {
18311
+ return isFrameLayer(o.layer, o.dataset.arcs);
18312
+ });
18313
+ }
18314
+
18226
18315
  function getFrameLayerBounds(lyr) {
18227
18316
  return new Bounds(getFurnitureLayerData(lyr).bbox);
18228
18317
  }
@@ -18358,9 +18447,11 @@
18358
18447
  getFrameData: getFrameData,
18359
18448
  getFrameLayerData: getFrameLayerData,
18360
18449
  getFrameSize: getFrameSize,
18450
+ isFrameLayer: isFrameLayer,
18361
18451
  findFrameLayerInDataset: findFrameLayerInDataset,
18362
18452
  findFrameDataset: findFrameDataset,
18363
18453
  findFrameLayer: findFrameLayer,
18454
+ findFrame: findFrame,
18364
18455
  getFrameLayerBounds: getFrameLayerBounds,
18365
18456
  getMapFrameMetersPerPixel: getMapFrameMetersPerPixel,
18366
18457
  calcOutputSizeInPixels: calcOutputSizeInPixels,
@@ -18800,6 +18891,7 @@
18800
18891
  'font-family': null,
18801
18892
  'font-size': null,
18802
18893
  'font-style': null,
18894
+ 'font-stretch': null,
18803
18895
  'font-weight': null,
18804
18896
  'label-text': null, // leaving this null
18805
18897
  'letter-spacing': 'measure',
@@ -18849,7 +18941,7 @@
18849
18941
  polyline: utils.arrayToIndex(commonProperties.concat('stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit')),
18850
18942
  point: utils.arrayToIndex(commonProperties.concat('fill', 'r')),
18851
18943
  label: utils.arrayToIndex(commonProperties.concat(
18852
- 'fill,font-family,font-size,text-anchor,font-weight,font-style,letter-spacing,dominant-baseline'.split(',')))
18944
+ 'fill,font-family,font-size,text-anchor,font-weight,font-style,font-stretch,letter-spacing,dominant-baseline'.split(',')))
18853
18945
  };
18854
18946
 
18855
18947
  // symType: point, polygon, polyline, label
@@ -18859,6 +18951,10 @@
18859
18951
  if (filter && !filter(fields[i])) continue;
18860
18952
  setAttribute(svgObj, fields[i], rec[fields[i]]);
18861
18953
  }
18954
+ // kludge to prevent default black fill on polygons with stroke styles
18955
+ if ((symType == 'polygon' || symType == 'circle') && rec.stroke && !rec.fill) {
18956
+ setAttribute(svgObj, 'fill', 'none');
18957
+ }
18862
18958
  }
18863
18959
 
18864
18960
  function setAttribute(obj, k, v) {
@@ -19633,83 +19729,9 @@
19633
19729
  parseScalebarLabelToKm: parseScalebarLabelToKm
19634
19730
  });
19635
19731
 
19636
- cmd.frame = function(catalog, source, opts) {
19637
- var size, bounds, tmp, dataset;
19638
- if (+opts.width > 0 === false && +opts.pixels > 0 === false) {
19639
- stop("Missing a width or area");
19640
- }
19641
- if (opts.width && opts.height) {
19642
- opts = utils.extend({}, opts);
19643
- // Height is a string containing either a number or a
19644
- // comma-sep. pair of numbers (range); here we convert height to
19645
- // an aspect-ratio parameter for the rectangle() function
19646
- opts.aspect_ratio = getAspectRatioArg(opts.width, opts.height);
19647
- // TODO: currently returns max,min aspect ratio, should return in min,max order
19648
- // (rectangle() function should handle max,min argument correctly now anyway)
19649
- }
19650
- tmp = cmd.rectangle(source, opts);
19651
- bounds = getDatasetBounds(tmp);
19652
- if (probablyDecimalDegreeBounds(bounds)) {
19653
- stop('Frames require projected, not geographical coordinates');
19654
- } else if (!getDatasetCRS(tmp)) {
19655
- message('Warning: missing projection data. Assuming coordinates are meters and k (scale factor) is 1');
19656
- }
19657
- size = getFrameSize(bounds, opts);
19658
- if (size[0] > 0 === false) {
19659
- stop('Missing a valid frame width');
19660
- }
19661
- if (size[1] > 0 === false) {
19662
- stop('Missing a valid frame height');
19663
- }
19664
- dataset = {info: {}, layers:[{
19665
- name: opts.name || 'frame',
19666
- data: new DataTable([{
19667
- width: size[0],
19668
- height: size[1],
19669
- bbox: bounds.toArray(),
19670
- type: 'frame'
19671
- }])
19672
- }]};
19673
- catalog.addDataset(dataset);
19674
- };
19675
-
19676
-
19677
- // Convert width and height args to aspect ratio arg for the rectangle() function
19678
- function getAspectRatioArg(widthArg, heightArg) {
19679
- // heightArg is a string containing either a number or a
19680
- // comma-sep. pair of numbers (range);
19681
- return heightArg.split(',').map(function(opt) {
19682
- var height = Number(opt),
19683
- width = Number(widthArg);
19684
- if (!opt) return '';
19685
- return width / height;
19686
- }).reverse().join(',');
19687
- }
19688
-
19689
- function renderFrame(d) {
19690
- var lineWidth = 1,
19691
- // inset stroke by half of line width
19692
- off = lineWidth / 2,
19693
- obj = importPolygon([[[off, off], [off, d.height - off],
19694
- [d.width - off, d.height - off],
19695
- [d.width - off, off], [off, off]]]);
19696
- utils.extend(obj.properties, {
19697
- fill: 'none',
19698
- stroke: d.stroke || 'black',
19699
- 'stroke-width': d['stroke-width'] || lineWidth
19700
- });
19701
- return [obj];
19702
- }
19703
-
19704
- var Frame = /*#__PURE__*/Object.freeze({
19705
- __proto__: null,
19706
- getAspectRatioArg: getAspectRatioArg,
19707
- renderFrame: renderFrame
19708
- });
19709
-
19710
19732
  var furnitureRenderers = {
19711
- scalebar: renderScalebar,
19712
- frame: renderFrame
19733
+ scalebar: renderScalebar
19734
+ // frame: renderFrame
19713
19735
  };
19714
19736
 
19715
19737
  // @lyr a layer in a dataset
@@ -22057,7 +22079,13 @@ ${svg}
22057
22079
 
22058
22080
  // @targets - non-empty output from Catalog#findCommandTargets()
22059
22081
  //
22060
- async function exportTargetLayers(targets, opts) {
22082
+ async function exportTargetLayers(catalog, targets, opts) {
22083
+ // kludge: get extent of 'fit-extent' layer (if given)
22084
+ if (opts.fit_extent) {
22085
+ var target = catalog.findSingleLayer(opts.fit_extent);
22086
+ var bounds = getLayerBounds(target.layer, target.dataset.arcs);
22087
+ opts = Object.assign({svg_bbox: bounds.toArray()}, opts);
22088
+ }
22061
22089
  // convert target fmt to dataset fmt
22062
22090
  var datasets = targets.map(function(target) {
22063
22091
  return utils.defaults({layers: target.layers}, target.dataset);
@@ -24139,6 +24167,9 @@ ${svg}
24139
24167
  describe: '[SVG] bounding box of SVG map in projected map units',
24140
24168
  type: 'bbox'
24141
24169
  })
24170
+ .option('fit-extent', {
24171
+ describe: '[SVG] layer to use for the map extent'
24172
+ })
24142
24173
  .option('point-symbol', {
24143
24174
  describe: '[SVG] circle or square (default is circle)'
24144
24175
  })
@@ -25083,12 +25114,16 @@ ${svg}
25083
25114
  .validate(validateProjOpts);
25084
25115
 
25085
25116
  parser.command('rectangle')
25086
- .describe('create a rectangle from a bbox or target layer extent')
25117
+ .describe('create a rectangle from a bbox or target layer')
25087
25118
  .option('bbox', {
25088
25119
  describe: 'rectangle coordinates (xmin,ymin,xmax,ymax)',
25089
25120
  type: 'bbox'
25090
25121
  })
25091
25122
  .option('offset', offsetOpt)
25123
+ .option('width', {
25124
+ describe: 'set width of map in pixels, use rectangle as frame'
25125
+ // type: 'number' // use string, to allow units (e.g. in, px, pt)
25126
+ })
25092
25127
  .option('aspect-ratio', aspectRatioOpt)
25093
25128
  .option('source', {
25094
25129
  describe: 'name of layer to enclose'
@@ -25364,7 +25399,10 @@ ${svg}
25364
25399
  .option('font-style', {
25365
25400
  describe: 'CSS font style property of labels (e.g. italic)'
25366
25401
  })
25367
- .option('letter-spacing', {
25402
+ .option('font-stretch', {
25403
+ describe: 'CSS font stretch property of labels (e.g. condensed)'
25404
+ })
25405
+ .option('letter-spacing', {
25368
25406
  describe: 'CSS letter-spacing property of labels'
25369
25407
  })
25370
25408
  .option('line-height', {
@@ -25656,20 +25694,20 @@ ${svg}
25656
25694
  describe: 'frame coordinates (xmin,ymin,xmax,ymax)',
25657
25695
  type: 'bbox'
25658
25696
  })
25659
- .option('offset', offsetOpt)
25697
+ // .option('offset', offsetOpt)
25660
25698
  .option('width', {
25661
- describe: 'pixel width of output (default is 800)'
25662
- })
25663
- .option('height', {
25664
- describe: 'pixel height of output (may be a range)'
25665
- })
25666
- .option('pixels', {
25667
- describe: 'area of output in pixels (alternative to width and height)',
25668
- type: 'number'
25669
- })
25670
- .option('source', {
25671
- describe: 'name of layer to enclose'
25699
+ describe: 'width of output (default is 800px)'
25672
25700
  })
25701
+ // .option('height', {
25702
+ // describe: 'pixel height of output (may be a range)'
25703
+ // })
25704
+ // .option('pixels', {
25705
+ // describe: 'area of output in pixels (alternative to width and height)',
25706
+ // type: 'number'
25707
+ // })
25708
+ // .option('source', {
25709
+ // describe: 'name of layer to enclose'
25710
+ // })
25673
25711
  .option('name', nameOpt);
25674
25712
 
25675
25713
  parser.command('fuzzy-join')
@@ -37668,6 +37706,79 @@ ${svg}
37668
37706
  return parsed[0];
37669
37707
  }
37670
37708
 
37709
+ cmd.frame = function(catalog, source, opts) {
37710
+ var size, bounds, tmp, dataset;
37711
+ if (+opts.width > 0 === false && +opts.pixels > 0 === false) {
37712
+ stop("Missing a width or area");
37713
+ }
37714
+ if (opts.width && opts.height) {
37715
+ opts = utils.extend({}, opts);
37716
+ // Height is a string containing either a number or a
37717
+ // comma-sep. pair of numbers (range); here we convert height to
37718
+ // an aspect-ratio parameter for the rectangle() function
37719
+ opts.aspect_ratio = getAspectRatioArg(opts.width, opts.height);
37720
+ // TODO: currently returns max,min aspect ratio, should return in min,max order
37721
+ // (rectangle() function should handle max,min argument correctly now anyway)
37722
+ }
37723
+ tmp = cmd.rectangle(source, opts);
37724
+ bounds = getDatasetBounds(tmp);
37725
+ if (probablyDecimalDegreeBounds(bounds)) {
37726
+ stop('Frames require projected, not geographical coordinates');
37727
+ } else if (!getDatasetCRS(tmp)) {
37728
+ message('Warning: missing projection data. Assuming coordinates are meters and k (scale factor) is 1');
37729
+ }
37730
+ size = getFrameSize(bounds, opts);
37731
+ if (size[0] > 0 === false) {
37732
+ stop('Missing a valid frame width');
37733
+ }
37734
+ if (size[1] > 0 === false) {
37735
+ stop('Missing a valid frame height');
37736
+ }
37737
+ dataset = {info: {}, layers:[{
37738
+ name: opts.name || 'frame',
37739
+ data: new DataTable([{
37740
+ width: size[0],
37741
+ height: size[1],
37742
+ bbox: bounds.toArray(),
37743
+ type: 'frame'
37744
+ }])
37745
+ }]};
37746
+ catalog.addDataset(dataset);
37747
+ };
37748
+
37749
+
37750
+ // Convert width and height args to aspect ratio arg for the rectangle() function
37751
+ function getAspectRatioArg(widthArg, heightArg) {
37752
+ // heightArg is a string containing either a number or a
37753
+ // comma-sep. pair of numbers (range);
37754
+ return heightArg.split(',').map(function(opt) {
37755
+ var height = Number(opt),
37756
+ width = Number(widthArg);
37757
+ if (!opt) return '';
37758
+ return width / height;
37759
+ }).reverse().join(',');
37760
+ }
37761
+
37762
+ // export function renderFrame(d) {
37763
+ // var lineWidth = 1,
37764
+ // // inset stroke by half of line width
37765
+ // off = lineWidth / 2,
37766
+ // obj = importPolygon([[[off, off], [off, d.height - off],
37767
+ // [d.width - off, d.height - off],
37768
+ // [d.width - off, off], [off, off]]]);
37769
+ // utils.extend(obj.properties, {
37770
+ // fill: 'none',
37771
+ // stroke: d.stroke || 'black',
37772
+ // 'stroke-width': d['stroke-width'] || lineWidth
37773
+ // });
37774
+ // return [obj];
37775
+ // }
37776
+
37777
+ var Frame = /*#__PURE__*/Object.freeze({
37778
+ __proto__: null,
37779
+ getAspectRatioArg: getAspectRatioArg
37780
+ });
37781
+
37671
37782
  cmd.filterIslands = function(lyr, dataset, optsArg) {
37672
37783
  var opts = utils.extend({sliver_control: 0}, optsArg); // no sliver control
37673
37784
  var arcs = dataset.arcs;
@@ -38408,7 +38519,7 @@ ${svg}
38408
38519
  getPointFeatureBounds(shp) : targetDataset.arcs.getMultiShapeBounds(shp);
38409
38520
  bounds = applyRectangleOptions(bounds, crsInfo.crs, opts);
38410
38521
  if (!bounds) return null;
38411
- return convertBboxToGeoJSON(bounds.toArray(), opts);
38522
+ return bboxToPolygon(bounds.toArray(), opts);
38412
38523
  });
38413
38524
  var geojson = {
38414
38525
  type: 'FeatureCollection',
@@ -38434,6 +38545,13 @@ ${svg}
38434
38545
  // Create rectangles around one or more target layers
38435
38546
  //
38436
38547
  cmd.rectangle2 = function(target, opts) {
38548
+ // if target layer is a rectangle and we're applying frame properties,
38549
+ // turn the target into a frame instead of creating a new rectangle
38550
+ if (target.layers.length == 1 && opts.width &&
38551
+ layerIsRectangle(target.layers[0], target.dataset.arcs)) {
38552
+ applyFrameProperties(target.layers[0], opts);
38553
+ return;
38554
+ }
38437
38555
  var datasets = target.layers.map(function(lyr) {
38438
38556
  var dataset = cmd.rectangle({layer: lyr, dataset: target.dataset}, opts);
38439
38557
  setOutputLayerName(dataset.layers[0], lyr, null, opts);
@@ -38458,13 +38576,26 @@ ${svg}
38458
38576
  if (!bounds || !bounds.hasBounds()) {
38459
38577
  stop('Missing rectangle extent');
38460
38578
  }
38461
- var geojson = convertBboxToGeoJSON(bounds.toArray(), opts);
38462
- var dataset = importGeoJSON(geojson, {});
38579
+ var feature = {
38580
+ type: 'Feature',
38581
+ properties: {},
38582
+ geometry: bboxToPolygon(bounds.toArray(), opts)
38583
+ };
38584
+ var dataset = importGeoJSON(feature, {});
38585
+ applyFrameProperties(dataset.layers[0], opts);
38463
38586
  dataset.layers[0].name = opts.name || 'rectangle';
38464
38587
  setDatasetCrsInfo(dataset, crsInfo);
38465
38588
  return dataset;
38466
38589
  };
38467
38590
 
38591
+ function applyFrameProperties(lyr, opts) {
38592
+ if (!opts.width) return;
38593
+ if (!lyr.data) initDataTable(lyr);
38594
+ var d = lyr.data.getRecords()[0] || {};
38595
+ d.width = parseSizeParam(opts.width);
38596
+ d.type = 'frame';
38597
+ }
38598
+
38468
38599
  function applyRectangleOptions(bounds, crs, opts) {
38469
38600
  var isGeoBox = probablyDecimalDegreeBounds(bounds);
38470
38601
  if (opts.offset) {
@@ -38509,16 +38640,13 @@ ${svg}
38509
38640
  return bounds;
38510
38641
  }
38511
38642
 
38512
- function convertBboxToGeoJSON(bbox, optsArg) {
38643
+ function bboxToPolygon(bbox, optsArg) {
38513
38644
  var opts = optsArg || {};
38514
38645
  var coords = bboxToCoords(bbox);
38515
38646
  if (opts.interval > 0) {
38516
38647
  coords = densifyPathByInterval(coords, opts.interval);
38517
38648
  }
38518
- return opts.geometry_type == 'polyline' ? {
38519
- type: 'LineString',
38520
- coordinates: coords
38521
- } : {
38649
+ return {
38522
38650
  type: 'Polygon',
38523
38651
  coordinates: [coords]
38524
38652
  };
@@ -38527,7 +38655,7 @@ ${svg}
38527
38655
  var Rectangle = /*#__PURE__*/Object.freeze({
38528
38656
  __proto__: null,
38529
38657
  applyAspectRatio: applyAspectRatio,
38530
- convertBboxToGeoJSON: convertBboxToGeoJSON
38658
+ bboxToPolygon: bboxToPolygon
38531
38659
  });
38532
38660
 
38533
38661
  function getSemiMinorAxis(P) {
@@ -39081,8 +39209,7 @@ ${svg}
39081
39209
  var rotation = getRotationParams(dest);
39082
39210
  if (!bbox) error('Missing expected clip bbox.');
39083
39211
  opts = Object.assign({interval: 0.5}, opts); // make sure edges can curve
39084
- var geojson = convertBboxToGeoJSON(bbox, opts);
39085
- var dataset = importGeoJSON(geojson);
39212
+ var dataset = importGeoJSON(bboxToPolygon(bbox, opts));
39086
39213
  if (rotation) {
39087
39214
  rotateDataset(dataset, {rotation: rotation, invert: true});
39088
39215
  }
@@ -39271,7 +39398,7 @@ ${svg}
39271
39398
  var e = 1e-8;
39272
39399
  var bbox = [lon-e, -91, lon+e, 91];
39273
39400
  // densify (so cut line can curve, e.g. Cupola projection)
39274
- var geojson = convertBboxToGeoJSON(bbox, {interval: 0.5});
39401
+ var geojson = bboxToPolygon(bbox, {interval: 0.5});
39275
39402
  var clip = importGeoJSON(geojson);
39276
39403
  clipLayersInPlace(pathLayers, clip, dataset, 'erase');
39277
39404
  }
@@ -39574,7 +39701,6 @@ ${svg}
39574
39701
 
39575
39702
  function createProjectedGraticule(dest, opts) {
39576
39703
  var src = parseCrsString('wgs84');
39577
- // var outline = getOutlineDataset(src, dest, {inset: 0, geometry_type: 'polyline'});
39578
39704
  var outline = getOutlineDataset(src, dest, {});
39579
39705
  var graticule = importGeoJSON(createGraticule(dest, !!outline, opts));
39580
39706
  projectDataset(graticule, src, dest, {no_clip: false}); // TODO: densify?
@@ -44825,8 +44951,9 @@ ${svg}
44825
44951
  } else if (name == 'filter-slivers') {
44826
44952
  applyCommandToEachLayer(cmd.filterSlivers, targetLayers, targetDataset, opts);
44827
44953
 
44828
- } else if (name == 'frame') {
44829
- cmd.frame(job.catalog, source, opts);
44954
+ // // 'frame' and 'rectangle' have merged
44955
+ // } else if (name == 'frame') {
44956
+ // cmd.frame(job.catalog, source, opts);
44830
44957
 
44831
44958
  } else if (name == 'fuzzy-join') {
44832
44959
  applyCommandToEachLayer(cmd.fuzzyJoin, targetLayers, arcs, source, opts);
@@ -44894,7 +45021,8 @@ ${svg}
44894
45021
  outputLayers = cmd.mosaic(targetLayers, targetDataset, opts);
44895
45022
 
44896
45023
  } else if (name == 'o') {
44897
- outputFiles = await exportTargetLayers(targets, opts);
45024
+ // kludge
45025
+ outputFiles = await exportTargetLayers(job.catalog, targets, opts);
44898
45026
  if (opts.final) {
44899
45027
  // don't propagate data if output is final
44900
45028
  //// catalog = null;
@@ -44927,7 +45055,10 @@ ${svg}
44927
45055
  cmd.proj(targ.dataset, job.catalog, opts);
44928
45056
  });
44929
45057
 
44930
- } else if (name == 'rectangle') {
45058
+ } else if (name == 'rectangle' || name == 'frame') {
45059
+ if (name == 'frame' && !opts.width) {
45060
+ stop('Command requires a width= argument');
45061
+ }
44931
45062
  if (source || opts.bbox || targets.length === 0) {
44932
45063
  job.catalog.addDataset(cmd.rectangle(source, opts));
44933
45064
  } else {
@@ -45093,7 +45224,7 @@ ${svg}
45093
45224
  });
45094
45225
  }
45095
45226
 
45096
- var version = "0.6.65";
45227
+ var version = "0.6.67";
45097
45228
 
45098
45229
  // Parse command line args into commands and run them
45099
45230
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
@@ -45835,7 +45966,7 @@ ${svg}
45835
45966
  Projections,
45836
45967
  ProjectionParams,
45837
45968
  Rectangle,
45838
- RectangleGeom,
45969
+ RectangleUtils,
45839
45970
  Rounding,
45840
45971
  RunCommands,
45841
45972
  Scalebar,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.6.65",
3
+ "version": "0.6.67",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
package/www/index.html CHANGED
@@ -104,8 +104,8 @@
104
104
  <h4>Source files</h4>
105
105
  <div class="file-list"></div>
106
106
  <div>
107
- <div id="add-file-btn" class="dialog-btn btn">Add a file</div>
108
- <!-- <div id="add-empty-btn" class="dialog-btn btn">Empty layer</div> -->
107
+ <div id="add-file-btn" class="dialog-btn btn">Add files</div>
108
+ <div id="add-empty-btn" class="dialog-btn btn">Add empty layer</div>
109
109
  </div>
110
110
  </div>
111
111
  </div>
@@ -134,7 +134,8 @@
134
134
  <div class="filter-btn btn dialog-btn">Keep</div>
135
135
  <div class="duplicate-btn btn dialog-btn">Duplicate</div>
136
136
  <div class="split-btn btn dialog-btn">Split</div>
137
- <div class="coords-btn btn dialog-btn">Coords</div>
137
+ <div class="coords-btn btn dialog-btn toggle-btn">Coords</div>
138
+ <div class="data-btn btn dialog-btn toggle-btn">Edit data</div>
138
139
  <div class="cancel-btn btn dialog-btn">Clear</div>
139
140
  </div>
140
141
  <div class="box-coords selectable"></div>