mapshaper 0.6.65 → 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) {
@@ -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,18 @@
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(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
+
13338
13410
  // Return coeff. for converting a distance measure to dataset coordinates
13339
13411
  // @paramUnits: units code of distance param, or null if units are not specified
13340
13412
  // @crs: Proj.4 CRS object, or null (unknown latlong CRS);
@@ -13478,6 +13550,7 @@
13478
13550
 
13479
13551
  var Units = /*#__PURE__*/Object.freeze({
13480
13552
  __proto__: null,
13553
+ parseSizeParam: parseSizeParam,
13481
13554
  getIntervalConversionFactor: getIntervalConversionFactor,
13482
13555
  parseMeasure: parseMeasure,
13483
13556
  parseMeasure2: parseMeasure2,
@@ -18155,7 +18228,7 @@
18155
18228
  var frameLyr = findFrameLayerInDataset(dataset);
18156
18229
  var frameData;
18157
18230
  if (frameLyr) {
18158
- frameData = Object.assign({}, getFurnitureLayerData(frameLyr));
18231
+ frameData = getFrameLayerData(frameLyr, dataset);
18159
18232
  } else {
18160
18233
  frameData = calcFrameData(dataset, exportOpts);
18161
18234
  }
@@ -18163,6 +18236,25 @@
18163
18236
  return frameData;
18164
18237
  }
18165
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
+
18166
18258
  function calcFrameData(dataset, opts) {
18167
18259
  var bounds;
18168
18260
  if (opts.svg_bbox) {
@@ -18180,10 +18272,6 @@
18180
18272
  };
18181
18273
  }
18182
18274
 
18183
- function getFrameLayerData(lyr) {
18184
- return lyr.data && lyr.data.getReadOnlyRecordAt(0);
18185
- }
18186
-
18187
18275
  // Used by mapshaper-frame and mapshaper-pixel-transform. TODO: refactor
18188
18276
  function getFrameSize(bounds, opts) {
18189
18277
  var aspectRatio = bounds.width() / bounds.height();
@@ -18199,26 +18287,27 @@
18199
18287
 
18200
18288
 
18201
18289
  // @lyr dataset layer
18202
- function isFrameLayer(lyr) {
18203
- return getFurnitureLayerType(lyr) == 'frame';
18290
+ function isFrameLayer(lyr, dataset) {
18291
+ return getFurnitureLayerType(lyr) == 'frame' &&
18292
+ layerIsRectangle(lyr, dataset.arcs);
18204
18293
  }
18205
18294
 
18206
18295
  function findFrameLayerInDataset(dataset) {
18207
18296
  return utils.find(dataset.layers, function(lyr) {
18208
- return isFrameLayer(lyr);
18297
+ return isFrameLayer(lyr, dataset);
18209
18298
  });
18210
18299
  }
18211
18300
 
18212
18301
  function findFrameDataset(catalog) {
18213
18302
  var target = utils.find(catalog.getLayers(), function(o) {
18214
- return isFrameLayer(o.layer);
18303
+ return isFrameLayer(o.layer, o.dataset);
18215
18304
  });
18216
18305
  return target ? target.dataset : null;
18217
18306
  }
18218
18307
 
18219
18308
  function findFrameLayer(catalog) {
18220
18309
  var target = utils.find(catalog.getLayers(), function(o) {
18221
- return isFrameLayer(o.layer);
18310
+ return isFrameLayer(o.layer, o.dataset);
18222
18311
  });
18223
18312
  return target && target.layer || null;
18224
18313
  }
@@ -18356,7 +18445,6 @@
18356
18445
  var FrameData = /*#__PURE__*/Object.freeze({
18357
18446
  __proto__: null,
18358
18447
  getFrameData: getFrameData,
18359
- getFrameLayerData: getFrameLayerData,
18360
18448
  getFrameSize: getFrameSize,
18361
18449
  findFrameLayerInDataset: findFrameLayerInDataset,
18362
18450
  findFrameDataset: findFrameDataset,
@@ -18859,6 +18947,10 @@
18859
18947
  if (filter && !filter(fields[i])) continue;
18860
18948
  setAttribute(svgObj, fields[i], rec[fields[i]]);
18861
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
+ }
18862
18954
  }
18863
18955
 
18864
18956
  function setAttribute(obj, k, v) {
@@ -19686,6 +19778,7 @@
19686
19778
  }).reverse().join(',');
19687
19779
  }
19688
19780
 
19781
+
19689
19782
  function renderFrame(d) {
19690
19783
  var lineWidth = 1,
19691
19784
  // inset stroke by half of line width
@@ -22057,7 +22150,13 @@ ${svg}
22057
22150
 
22058
22151
  // @targets - non-empty output from Catalog#findCommandTargets()
22059
22152
  //
22060
- 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
+ }
22061
22160
  // convert target fmt to dataset fmt
22062
22161
  var datasets = targets.map(function(target) {
22063
22162
  return utils.defaults({layers: target.layers}, target.dataset);
@@ -24139,6 +24238,9 @@ ${svg}
24139
24238
  describe: '[SVG] bounding box of SVG map in projected map units',
24140
24239
  type: 'bbox'
24141
24240
  })
24241
+ .option('fit-extent', {
24242
+ describe: '[SVG] layer to use for the map extent'
24243
+ })
24142
24244
  .option('point-symbol', {
24143
24245
  describe: '[SVG] circle or square (default is circle)'
24144
24246
  })
@@ -25089,6 +25191,10 @@ ${svg}
25089
25191
  type: 'bbox'
25090
25192
  })
25091
25193
  .option('offset', offsetOpt)
25194
+ .option('width', {
25195
+ // describe: 'set viewport width in pixels',
25196
+ type: 'number'
25197
+ })
25092
25198
  .option('aspect-ratio', aspectRatioOpt)
25093
25199
  .option('source', {
25094
25200
  describe: 'name of layer to enclose'
@@ -38408,7 +38514,7 @@ ${svg}
38408
38514
  getPointFeatureBounds(shp) : targetDataset.arcs.getMultiShapeBounds(shp);
38409
38515
  bounds = applyRectangleOptions(bounds, crsInfo.crs, opts);
38410
38516
  if (!bounds) return null;
38411
- return convertBboxToGeoJSON(bounds.toArray(), opts);
38517
+ return bboxToPolygon(bounds.toArray(), opts);
38412
38518
  });
38413
38519
  var geojson = {
38414
38520
  type: 'FeatureCollection',
@@ -38458,8 +38564,16 @@ ${svg}
38458
38564
  if (!bounds || !bounds.hasBounds()) {
38459
38565
  stop('Missing rectangle extent');
38460
38566
  }
38461
- var geojson = convertBboxToGeoJSON(bounds.toArray(), opts);
38462
- 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, {});
38463
38577
  dataset.layers[0].name = opts.name || 'rectangle';
38464
38578
  setDatasetCrsInfo(dataset, crsInfo);
38465
38579
  return dataset;
@@ -38509,16 +38623,13 @@ ${svg}
38509
38623
  return bounds;
38510
38624
  }
38511
38625
 
38512
- function convertBboxToGeoJSON(bbox, optsArg) {
38626
+ function bboxToPolygon(bbox, optsArg) {
38513
38627
  var opts = optsArg || {};
38514
38628
  var coords = bboxToCoords(bbox);
38515
38629
  if (opts.interval > 0) {
38516
38630
  coords = densifyPathByInterval(coords, opts.interval);
38517
38631
  }
38518
- return opts.geometry_type == 'polyline' ? {
38519
- type: 'LineString',
38520
- coordinates: coords
38521
- } : {
38632
+ return {
38522
38633
  type: 'Polygon',
38523
38634
  coordinates: [coords]
38524
38635
  };
@@ -38527,7 +38638,7 @@ ${svg}
38527
38638
  var Rectangle = /*#__PURE__*/Object.freeze({
38528
38639
  __proto__: null,
38529
38640
  applyAspectRatio: applyAspectRatio,
38530
- convertBboxToGeoJSON: convertBboxToGeoJSON
38641
+ bboxToPolygon: bboxToPolygon
38531
38642
  });
38532
38643
 
38533
38644
  function getSemiMinorAxis(P) {
@@ -39081,8 +39192,7 @@ ${svg}
39081
39192
  var rotation = getRotationParams(dest);
39082
39193
  if (!bbox) error('Missing expected clip bbox.');
39083
39194
  opts = Object.assign({interval: 0.5}, opts); // make sure edges can curve
39084
- var geojson = convertBboxToGeoJSON(bbox, opts);
39085
- var dataset = importGeoJSON(geojson);
39195
+ var dataset = importGeoJSON(bboxToPolygon(bbox, opts));
39086
39196
  if (rotation) {
39087
39197
  rotateDataset(dataset, {rotation: rotation, invert: true});
39088
39198
  }
@@ -39271,7 +39381,7 @@ ${svg}
39271
39381
  var e = 1e-8;
39272
39382
  var bbox = [lon-e, -91, lon+e, 91];
39273
39383
  // densify (so cut line can curve, e.g. Cupola projection)
39274
- var geojson = convertBboxToGeoJSON(bbox, {interval: 0.5});
39384
+ var geojson = bboxToPolygon(bbox, {interval: 0.5});
39275
39385
  var clip = importGeoJSON(geojson);
39276
39386
  clipLayersInPlace(pathLayers, clip, dataset, 'erase');
39277
39387
  }
@@ -39574,7 +39684,6 @@ ${svg}
39574
39684
 
39575
39685
  function createProjectedGraticule(dest, opts) {
39576
39686
  var src = parseCrsString('wgs84');
39577
- // var outline = getOutlineDataset(src, dest, {inset: 0, geometry_type: 'polyline'});
39578
39687
  var outline = getOutlineDataset(src, dest, {});
39579
39688
  var graticule = importGeoJSON(createGraticule(dest, !!outline, opts));
39580
39689
  projectDataset(graticule, src, dest, {no_clip: false}); // TODO: densify?
@@ -44825,8 +44934,9 @@ ${svg}
44825
44934
  } else if (name == 'filter-slivers') {
44826
44935
  applyCommandToEachLayer(cmd.filterSlivers, targetLayers, targetDataset, opts);
44827
44936
 
44828
- } else if (name == 'frame') {
44829
- cmd.frame(job.catalog, source, opts);
44937
+ // // 'frame' and 'rectangle' have merged
44938
+ // } else if (name == 'frame') {
44939
+ // cmd.frame(job.catalog, source, opts);
44830
44940
 
44831
44941
  } else if (name == 'fuzzy-join') {
44832
44942
  applyCommandToEachLayer(cmd.fuzzyJoin, targetLayers, arcs, source, opts);
@@ -44894,7 +45004,8 @@ ${svg}
44894
45004
  outputLayers = cmd.mosaic(targetLayers, targetDataset, opts);
44895
45005
 
44896
45006
  } else if (name == 'o') {
44897
- outputFiles = await exportTargetLayers(targets, opts);
45007
+ // kludge
45008
+ outputFiles = await exportTargetLayers(job.catalog, targets, opts);
44898
45009
  if (opts.final) {
44899
45010
  // don't propagate data if output is final
44900
45011
  //// catalog = null;
@@ -44927,7 +45038,10 @@ ${svg}
44927
45038
  cmd.proj(targ.dataset, job.catalog, opts);
44928
45039
  });
44929
45040
 
44930
- } 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
+ }
44931
45045
  if (source || opts.bbox || targets.length === 0) {
44932
45046
  job.catalog.addDataset(cmd.rectangle(source, opts));
44933
45047
  } else {
@@ -45093,7 +45207,7 @@ ${svg}
45093
45207
  });
45094
45208
  }
45095
45209
 
45096
- var version = "0.6.65";
45210
+ var version = "0.6.66";
45097
45211
 
45098
45212
  // Parse command line args into commands and run them
45099
45213
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
@@ -45835,7 +45949,7 @@ ${svg}
45835
45949
  Projections,
45836
45950
  ProjectionParams,
45837
45951
  Rectangle,
45838
- RectangleGeom,
45952
+ RectangleUtils,
45839
45953
  Rounding,
45840
45954
  RunCommands,
45841
45955
  Scalebar,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.6.65",
3
+ "version": "0.6.66",
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>