mapshaper 0.5.106 → 0.5.107

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ v0.5.107
2
+ * Bug fixes
3
+ * Added file_exists() to -if/-elif expressions
4
+
1
5
  v0.5.106
2
6
  * More basemap fixes.
3
7
 
package/mapshaper.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
 
3
- var VERSION = "0.5.106";
3
+ var VERSION = "0.5.103";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -1737,6 +1737,16 @@
1737
1737
 
1738
1738
  // Iterate over each [x,y] point in a layer
1739
1739
  // shapes: one layer's "shapes" array
1740
+ function forEachPoint_(shapes, cb) {
1741
+ var i, n, j, m, shp;
1742
+ for (i=0, n=shapes.length; i<n; i++) {
1743
+ shp = shapes[i];
1744
+ for (j=0, m=shp ? shp.length : 0; j<m; j++) {
1745
+ cb(shp[j], i);
1746
+ }
1747
+ }
1748
+ }
1749
+
1740
1750
  function forEachPoint(shapes, cb) {
1741
1751
  var i, n, j, m, shp;
1742
1752
  for (i=0, n=shapes.length; i<n; i++) {
@@ -1753,6 +1763,7 @@
1753
1763
  getPointBounds: getPointBounds$1,
1754
1764
  getPointFeatureBounds: getPointFeatureBounds,
1755
1765
  getPointsInLayer: getPointsInLayer,
1766
+ forEachPoint_: forEachPoint_,
1756
1767
  forEachPoint: forEachPoint
1757
1768
  });
1758
1769
 
@@ -7813,10 +7824,24 @@
7813
7824
  // a, b: two ring data objects (from getPathMetadata);
7814
7825
  function testRingInRing(a, b, arcs) {
7815
7826
  if (b.bounds.contains(a.bounds) === false) return false;
7816
- var p = arcs.getVertex(a.ids[0], 0); // test with first point in the ring
7827
+ // Don't test with first point -- this may return false if a hole intersects
7828
+ // the containing ring at the first vertex.
7829
+ // Instead, use the midpoint of the first segment
7830
+ var p = getFirstMidpoint(a.ids[0], arcs);
7831
+ //// test with first point in the ring
7832
+ // var p = arcs.getVertex(a.ids[0], 0);
7817
7833
  return geom.testPointInRing(p.x, p.y, b.ids, arcs) == 1;
7818
7834
  }
7819
7835
 
7836
+ function getFirstMidpoint(arcId, arcs) {
7837
+ var p1 = arcs.getVertex(arcId, 0);
7838
+ var p2 = arcs.getVertex(arcId, 1);
7839
+ return {
7840
+ x: (p1.x + p2.x) / 2,
7841
+ y: (p1.y + p2.y) / 2
7842
+ };
7843
+ }
7844
+
7820
7845
  // Bundle holes with their containing rings for Topo/GeoJSON polygon export.
7821
7846
  // Assumes outer rings are CW and inner (hole) rings are CCW, unless
7822
7847
  // the reverseWinding flag is set.
@@ -9525,9 +9550,6 @@
9525
9550
  return rec && (rec[name] === val);
9526
9551
  });
9527
9552
  };
9528
- obj.file_exists = function(name) {
9529
- return cli.isFile(name);
9530
- };
9531
9553
  return obj;
9532
9554
  }
9533
9555
 
@@ -9935,6 +9957,11 @@
9935
9957
  ctx.$ = i >= 0 ? getFeatureById(i) : layerOnlyProxy;
9936
9958
  ctx._ = ctx; // provide access to functions when masked by variable names
9937
9959
  ctx.d = rec || null; // expose data properties a la d3 (also exposed as this.properties)
9960
+ if (opts.record_alias) {
9961
+ // this is used to expose source table record as "source" in -join calc=
9962
+ // expresions.
9963
+ ctx[opts.record_alias] = ctx.d;
9964
+ }
9938
9965
  try {
9939
9966
  val = func.call(ctx.$, rec, ctx);
9940
9967
  } catch(e) {
@@ -18954,6 +18981,10 @@ ${svg}
18954
18981
  .option('json-path', {
18955
18982
  old_alias: 'json-subtree',
18956
18983
  describe: '[JSON] path to JSON input data; separator is /'
18984
+ })
18985
+ .option('single-part', {
18986
+ type: 'flag',
18987
+ // describe: '[GeoJSON] split multi-part features into single-part features'
18957
18988
  });
18958
18989
 
18959
18990
  parser.command('o')
@@ -22119,6 +22150,8 @@ ${svg}
22119
22150
  // TODO: improve so geometry_type option skips features instead of creating null geometries
22120
22151
  if (geom && geom.type == 'GeometryCollection') {
22121
22152
  GeoJSON.importComplexFeature(importer, geom, rec, opts);
22153
+ } else if (opts.single_part && isMultiPartGeometry(geom)) {
22154
+ GeoJSON.importMultiAsSingles(importer, geom, rec, opts);
22122
22155
  } else {
22123
22156
  GeoJSON.importSimpleFeature(importer, geom, rec, opts);
22124
22157
  }
@@ -22160,11 +22193,27 @@ ${svg}
22160
22193
  return Object.values(index);
22161
22194
  }
22162
22195
 
22196
+ function isMultiPartGeometry(geom) {
22197
+ return geom && geom.type && geom.type.indexOf('Multi') === 0;
22198
+ }
22199
+
22163
22200
  GeoJSON.importSimpleFeature = function(importer, geom, rec, opts) {
22164
22201
  importer.startShape(rec);
22165
22202
  GeoJSON.importSimpleGeometry(importer, geom, opts);
22166
22203
  };
22167
22204
 
22205
+ // Split a multi-part feature into several single features
22206
+ GeoJSON.importMultiAsSingles = function(importer, geom, rec, opts) {
22207
+ geom.coordinates.forEach(function(coords, i) {
22208
+ var geom2 = {
22209
+ type: geom.type.substr(5),
22210
+ coordinates: coords
22211
+ };
22212
+ var rec2 = i === 0 ? rec : copyRecord(rec);
22213
+ GeoJSON.importSimpleFeature(importer, geom2, rec2, opts);
22214
+ });
22215
+ };
22216
+
22168
22217
  GeoJSON.importSimpleGeometry = function(importer, geom, opts) {
22169
22218
  var type = geom ? geom.type : null;
22170
22219
  if (type === null) {
@@ -34614,19 +34663,33 @@ ${svg}
34614
34663
  return o;
34615
34664
  }
34616
34665
 
34617
- function compileLayerExpression(expr, lyr, dataset, opts) {
34618
- var proxy = getLayerProxy(lyr, dataset.arcs, opts);
34666
+ function compileIfCommandExpression(expr, catalog, target, opts) {
34667
+ var ctx = {};
34668
+
34669
+ if (target && target.layer) {
34670
+ ctx = getLayerProxy(target.layer, target.dataset.arcs, opts);
34671
+ }
34672
+
34673
+ ctx.file_exists = function(name) {
34674
+ return cli.isFile(name);
34675
+ };
34676
+
34677
+ ctx.layer_exists = function(name) {
34678
+ var lyr = catalog.findSingleLayer(name);
34679
+ return !!lyr;
34680
+ };
34681
+
34619
34682
  var exprOpts = Object.assign({returns: true}, opts);
34620
34683
  var func = compileExpressionToFunction(expr, exprOpts);
34621
- var ctx = proxy;
34622
34684
  return function() {
34623
34685
  try {
34624
- return func.call(proxy, {}, ctx);
34686
+ return func.call(ctx, {}, ctx);
34625
34687
  } catch(e) {
34626
34688
  // if (opts.quiet) throw e;
34627
34689
  stop(e.name, "in expression [" + expr + "]:", e.message);
34628
34690
  }
34629
34691
  };
34692
+
34630
34693
  }
34631
34694
 
34632
34695
  function skipCommand(cmdName) {
@@ -34674,7 +34737,7 @@ ${svg}
34674
34737
  function testLayer(catalog, opts) {
34675
34738
  var targ = getTargetLayer(catalog, opts);
34676
34739
  if (opts.expression) {
34677
- return compileLayerExpression(opts.expression, targ.layer, targ.dataset, opts)();
34740
+ return compileIfCommandExpression(opts.expression, catalog, targ, opts)();
34678
34741
  }
34679
34742
  if (opts.empty) {
34680
34743
  return layerIsEmpty(targ.layer);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.5.106",
3
+ "version": "0.5.107",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -6442,10 +6442,13 @@
6442
6442
 
6443
6443
  function CoordinatesDisplay(gui, ext, mouse) {
6444
6444
  var readout = gui.container.findChild('.coordinate-info').hide();
6445
- var enabled = false;
6446
6445
 
6447
- gui.model.on('select', function(e) {
6448
- enabled = !!e.layer.geometry_type; // no display on tabular layers
6446
+ function enabled() {
6447
+ var lyr = gui.map.getActiveLayer();
6448
+ return !!(lyr && lyr.layer.geometry_type);
6449
+ }
6450
+
6451
+ gui.model.on('update', function(e) {
6449
6452
  readout.hide();
6450
6453
  });
6451
6454
 
@@ -6466,7 +6469,7 @@
6466
6469
  mouse.on('leave', clearCoords);
6467
6470
 
6468
6471
  mouse.on('click', function(e) {
6469
- if (!enabled) return;
6472
+ if (!enabled()) return;
6470
6473
  GUI.selectElement(readout.node());
6471
6474
  });
6472
6475
 
@@ -6474,7 +6477,7 @@
6474
6477
  mouse.on('drag', onMouseChange, null, 10); // high priority so editor doesn't block propagation
6475
6478
 
6476
6479
  function onMouseChange(e) {
6477
- if (!enabled) return;
6480
+ if (!enabled()) return;
6478
6481
  if (isOverMap(e)) {
6479
6482
  displayCoords(gui.map.translatePixelCoords(e.x, e.y));
6480
6483
  } else {
@@ -6998,6 +7001,15 @@
6998
7001
  ext.zoomByPct(delta, e.x / ext.width(), e.y / ext.height());
6999
7002
  });
7000
7003
 
7004
+ function fixBounds(bbox) {
7005
+ if (bbox[0] > bbox[2]) {
7006
+ swapElements(bbox, 0, 2);
7007
+ }
7008
+ if (bbox[1] > bbox[3]) {
7009
+ swapElements(bbox, 1, 3);
7010
+ }
7011
+ }
7012
+
7001
7013
  function swapElements(arr, i, j) {
7002
7014
  var tmp = arr[i];
7003
7015
  arr[i] = arr[j];
@@ -7009,14 +7021,10 @@
7009
7021
  var mapBox = [e.x, e.y, dragStartEvt.x, dragStartEvt.y];
7010
7022
  var displayBox = pixToCoords(mapBox);
7011
7023
  var dataBox = getBBoxCoords(gui.map.getActiveLayer(), displayBox);
7012
- if (pageBox[0] > pageBox[2]) {
7013
- swapElements(pageBox, 0, 2);
7014
- swapElements(mapBox, 0, 2);
7015
- }
7016
- if (pageBox[1] > pageBox[3]) {
7017
- swapElements(pageBox, 1, 3);
7018
- swapElements(mapBox, 1, 3);
7019
- }
7024
+ fixBounds(pageBox);
7025
+ fixBounds(mapBox);
7026
+ fixBounds(displayBox);
7027
+ fixBounds(dataBox);
7020
7028
  return {
7021
7029
  map_bbox: mapBox,
7022
7030
  page_bbox: pageBox,
@@ -9372,7 +9380,7 @@
9372
9380
  });
9373
9381
 
9374
9382
  new SimpleButton(popup.findChild('.clip-btn')).on('click', function() {
9375
- runCommand('-clip bbox2=' + bboxData.map_data_bbox.join(','));
9383
+ runCommand('-clip bbox=' + bboxData.map_data_bbox.join(','));
9376
9384
  });
9377
9385
 
9378
9386
  gui.addMode('box_tool', turnOn, turnOff);
package/www/mapshaper.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
 
3
- var VERSION = "0.5.106";
3
+ var VERSION = "0.5.103";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -1737,6 +1737,16 @@
1737
1737
 
1738
1738
  // Iterate over each [x,y] point in a layer
1739
1739
  // shapes: one layer's "shapes" array
1740
+ function forEachPoint_(shapes, cb) {
1741
+ var i, n, j, m, shp;
1742
+ for (i=0, n=shapes.length; i<n; i++) {
1743
+ shp = shapes[i];
1744
+ for (j=0, m=shp ? shp.length : 0; j<m; j++) {
1745
+ cb(shp[j], i);
1746
+ }
1747
+ }
1748
+ }
1749
+
1740
1750
  function forEachPoint(shapes, cb) {
1741
1751
  var i, n, j, m, shp;
1742
1752
  for (i=0, n=shapes.length; i<n; i++) {
@@ -1753,6 +1763,7 @@
1753
1763
  getPointBounds: getPointBounds$1,
1754
1764
  getPointFeatureBounds: getPointFeatureBounds,
1755
1765
  getPointsInLayer: getPointsInLayer,
1766
+ forEachPoint_: forEachPoint_,
1756
1767
  forEachPoint: forEachPoint
1757
1768
  });
1758
1769
 
@@ -7813,10 +7824,24 @@
7813
7824
  // a, b: two ring data objects (from getPathMetadata);
7814
7825
  function testRingInRing(a, b, arcs) {
7815
7826
  if (b.bounds.contains(a.bounds) === false) return false;
7816
- var p = arcs.getVertex(a.ids[0], 0); // test with first point in the ring
7827
+ // Don't test with first point -- this may return false if a hole intersects
7828
+ // the containing ring at the first vertex.
7829
+ // Instead, use the midpoint of the first segment
7830
+ var p = getFirstMidpoint(a.ids[0], arcs);
7831
+ //// test with first point in the ring
7832
+ // var p = arcs.getVertex(a.ids[0], 0);
7817
7833
  return geom.testPointInRing(p.x, p.y, b.ids, arcs) == 1;
7818
7834
  }
7819
7835
 
7836
+ function getFirstMidpoint(arcId, arcs) {
7837
+ var p1 = arcs.getVertex(arcId, 0);
7838
+ var p2 = arcs.getVertex(arcId, 1);
7839
+ return {
7840
+ x: (p1.x + p2.x) / 2,
7841
+ y: (p1.y + p2.y) / 2
7842
+ };
7843
+ }
7844
+
7820
7845
  // Bundle holes with their containing rings for Topo/GeoJSON polygon export.
7821
7846
  // Assumes outer rings are CW and inner (hole) rings are CCW, unless
7822
7847
  // the reverseWinding flag is set.
@@ -9525,9 +9550,6 @@
9525
9550
  return rec && (rec[name] === val);
9526
9551
  });
9527
9552
  };
9528
- obj.file_exists = function(name) {
9529
- return cli.isFile(name);
9530
- };
9531
9553
  return obj;
9532
9554
  }
9533
9555
 
@@ -9935,6 +9957,11 @@
9935
9957
  ctx.$ = i >= 0 ? getFeatureById(i) : layerOnlyProxy;
9936
9958
  ctx._ = ctx; // provide access to functions when masked by variable names
9937
9959
  ctx.d = rec || null; // expose data properties a la d3 (also exposed as this.properties)
9960
+ if (opts.record_alias) {
9961
+ // this is used to expose source table record as "source" in -join calc=
9962
+ // expresions.
9963
+ ctx[opts.record_alias] = ctx.d;
9964
+ }
9938
9965
  try {
9939
9966
  val = func.call(ctx.$, rec, ctx);
9940
9967
  } catch(e) {
@@ -18954,6 +18981,10 @@ ${svg}
18954
18981
  .option('json-path', {
18955
18982
  old_alias: 'json-subtree',
18956
18983
  describe: '[JSON] path to JSON input data; separator is /'
18984
+ })
18985
+ .option('single-part', {
18986
+ type: 'flag',
18987
+ // describe: '[GeoJSON] split multi-part features into single-part features'
18957
18988
  });
18958
18989
 
18959
18990
  parser.command('o')
@@ -22119,6 +22150,8 @@ ${svg}
22119
22150
  // TODO: improve so geometry_type option skips features instead of creating null geometries
22120
22151
  if (geom && geom.type == 'GeometryCollection') {
22121
22152
  GeoJSON.importComplexFeature(importer, geom, rec, opts);
22153
+ } else if (opts.single_part && isMultiPartGeometry(geom)) {
22154
+ GeoJSON.importMultiAsSingles(importer, geom, rec, opts);
22122
22155
  } else {
22123
22156
  GeoJSON.importSimpleFeature(importer, geom, rec, opts);
22124
22157
  }
@@ -22160,11 +22193,27 @@ ${svg}
22160
22193
  return Object.values(index);
22161
22194
  }
22162
22195
 
22196
+ function isMultiPartGeometry(geom) {
22197
+ return geom && geom.type && geom.type.indexOf('Multi') === 0;
22198
+ }
22199
+
22163
22200
  GeoJSON.importSimpleFeature = function(importer, geom, rec, opts) {
22164
22201
  importer.startShape(rec);
22165
22202
  GeoJSON.importSimpleGeometry(importer, geom, opts);
22166
22203
  };
22167
22204
 
22205
+ // Split a multi-part feature into several single features
22206
+ GeoJSON.importMultiAsSingles = function(importer, geom, rec, opts) {
22207
+ geom.coordinates.forEach(function(coords, i) {
22208
+ var geom2 = {
22209
+ type: geom.type.substr(5),
22210
+ coordinates: coords
22211
+ };
22212
+ var rec2 = i === 0 ? rec : copyRecord(rec);
22213
+ GeoJSON.importSimpleFeature(importer, geom2, rec2, opts);
22214
+ });
22215
+ };
22216
+
22168
22217
  GeoJSON.importSimpleGeometry = function(importer, geom, opts) {
22169
22218
  var type = geom ? geom.type : null;
22170
22219
  if (type === null) {
@@ -34614,19 +34663,33 @@ ${svg}
34614
34663
  return o;
34615
34664
  }
34616
34665
 
34617
- function compileLayerExpression(expr, lyr, dataset, opts) {
34618
- var proxy = getLayerProxy(lyr, dataset.arcs, opts);
34666
+ function compileIfCommandExpression(expr, catalog, target, opts) {
34667
+ var ctx = {};
34668
+
34669
+ if (target && target.layer) {
34670
+ ctx = getLayerProxy(target.layer, target.dataset.arcs, opts);
34671
+ }
34672
+
34673
+ ctx.file_exists = function(name) {
34674
+ return cli.isFile(name);
34675
+ };
34676
+
34677
+ ctx.layer_exists = function(name) {
34678
+ var lyr = catalog.findSingleLayer(name);
34679
+ return !!lyr;
34680
+ };
34681
+
34619
34682
  var exprOpts = Object.assign({returns: true}, opts);
34620
34683
  var func = compileExpressionToFunction(expr, exprOpts);
34621
- var ctx = proxy;
34622
34684
  return function() {
34623
34685
  try {
34624
- return func.call(proxy, {}, ctx);
34686
+ return func.call(ctx, {}, ctx);
34625
34687
  } catch(e) {
34626
34688
  // if (opts.quiet) throw e;
34627
34689
  stop(e.name, "in expression [" + expr + "]:", e.message);
34628
34690
  }
34629
34691
  };
34692
+
34630
34693
  }
34631
34694
 
34632
34695
  function skipCommand(cmdName) {
@@ -34674,7 +34737,7 @@ ${svg}
34674
34737
  function testLayer(catalog, opts) {
34675
34738
  var targ = getTargetLayer(catalog, opts);
34676
34739
  if (opts.expression) {
34677
- return compileLayerExpression(opts.expression, targ.layer, targ.dataset, opts)();
34740
+ return compileIfCommandExpression(opts.expression, catalog, targ, opts)();
34678
34741
  }
34679
34742
  if (opts.empty) {
34680
34743
  return layerIsEmpty(targ.layer);
package/www/page.css CHANGED
@@ -210,7 +210,7 @@ body {
210
210
  max-height: 160px;
211
211
  overflow: hidden;
212
212
  overflow-y: auto;
213
- margin-right: -13px;
213
+ margin-right: -3px;
214
214
  }
215
215
 
216
216
  .export-layer-list > div {
@@ -420,8 +420,8 @@ body.dragover #import-options-drop-area .drop-area {
420
420
  background: rgba(255, 255, 255, 0.4);
421
421
  border: 1px solid #999;
422
422
  padding: 0 3px;
423
- height: 17px;
424
- font-size: 12px;
423
+ height: 18px;
424
+ font-size: 13px;
425
425
  }
426
426
 
427
427
  #mshp-not-supported {
@@ -436,10 +436,10 @@ body.dragover #import-options-drop-area .drop-area {
436
436
  overflow-y: auto;
437
437
  }
438
438
 
439
- .dropped-file-list p {
439
+ #import-options .dropped-file-list p {
440
440
  line-height: 1;
441
441
  margin-bottom: 5px;
442
- font-size: 15px;
442
+ font-size: 14px;
443
443
  font-weight: bold;
444
444
  }
445
445
 
@@ -484,7 +484,7 @@ body.dragover #import-options-drop-area .drop-area {
484
484
  }
485
485
 
486
486
  .option-menu {
487
- padding: 0 0 7px 0;
487
+ padding: 0 0 9px 0;
488
488
  }
489
489
 
490
490
  .option-menu input {