mapshaper 0.6.98 → 0.6.99

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
@@ -14931,6 +14931,24 @@
14931
14931
  return opts.geojson_editor.get(_id);
14932
14932
  }
14933
14933
  });
14934
+
14935
+ Object.defineProperty(ctx, 'feature', {
14936
+ set: function(o) {
14937
+ opts.geojson_editor.set(o, _id);
14938
+ },
14939
+ get: function() {
14940
+ return opts.geojson_editor.get(_id);
14941
+ }
14942
+ });
14943
+
14944
+ Object.defineProperty(ctx, 'geometry', {
14945
+ set: function(o) {
14946
+ opts.geojson_editor.setGeometry(o, _id);
14947
+ },
14948
+ get: function() {
14949
+ return opts.geojson_editor.getGeometry(_id);
14950
+ }
14951
+ });
14934
14952
  }
14935
14953
 
14936
14954
  if (_records) {
@@ -35764,58 +35782,6 @@ ${svg}
35764
35782
 
35765
35783
  cmd.comment = function() {}; // no-op, so -comment doesn't trigger a parsing error
35766
35784
 
35767
- function expressionUsesGeoJSON(exp) {
35768
- return exp.includes('this.geojson');
35769
- }
35770
-
35771
- function getFeatureEditor(lyr, dataset) {
35772
- var api = {};
35773
- // need to copy attribute to avoid circular references if geojson is assigned
35774
- // to a data property.
35775
- var copy = copyLayer(lyr);
35776
- var features = exportLayerAsGeoJSON(copy, dataset, {rfc7946: true}, true);
35777
- var features2 = [];
35778
-
35779
- api.get = function(i) {
35780
- if (i > 0) features[i-1] = null; // garbage-collect old features
35781
- return features[i];
35782
- };
35783
-
35784
- api.set = function(feat, i) {
35785
- var arr;
35786
-
35787
- if (utils.isString(feat)) {
35788
- feat = JSON.parse(feat);
35789
- }
35790
-
35791
- if (!feat) return;
35792
-
35793
- if (feat.type == 'GeometryCollection') {
35794
- arr = feat.geometries.map(geom => GeoJSON.toFeature(geom));
35795
- } else if (feat.type == 'FeatureCollection') {
35796
- arr = feat.features;
35797
- } else {
35798
- feat = GeoJSON.toFeature(feat);
35799
- }
35800
-
35801
- if (arr) {
35802
- features2 = features2.concat(arr);
35803
- } else {
35804
- features2.push(feat);
35805
- }
35806
- };
35807
-
35808
- api.done = function() {
35809
- if (features2.length === 0) return; // read-only expression
35810
- var geojson = {
35811
- type: 'FeatureCollection',
35812
- features: features2
35813
- };
35814
- return importGeoJSON(geojson);
35815
- };
35816
- return api;
35817
- }
35818
-
35819
35785
  cmd.dashlines = function(lyr, dataset, opts) {
35820
35786
  var crs = getDatasetCRS(dataset);
35821
35787
  var defs = getStashedVar('defs');
@@ -37780,6 +37746,76 @@ ${svg}
37780
37746
  }
37781
37747
  };
37782
37748
 
37749
+ function expressionUsesGeoJSON(exp) {
37750
+ return exp.includes('this.geojson') || exp.includes('this.geometry') || exp.includes('this.feature');
37751
+ }
37752
+
37753
+ function getFeatureEditor(lyr, dataset) {
37754
+ var api = {};
37755
+ // need to copy attribute to avoid circular references if geojson is assigned
37756
+ // to a data property.
37757
+ var copy = copyLayer(lyr);
37758
+ var features = exportLayerAsGeoJSON(copy, dataset, {rfc7946: true}, true);
37759
+ var features2 = [];
37760
+
37761
+ api.get = function(i) {
37762
+ if (i > 0) features[i-1] = null; // garbage-collect old features
37763
+ return features[i];
37764
+ };
37765
+
37766
+ api.getGeometry = function(i) {
37767
+ if (i > 0) features[i-1] = null; // garbage-collect old features
37768
+ return features[i] ? features[i].geometry : null;
37769
+ };
37770
+
37771
+ api.setGeometry = function(geom, i) {
37772
+ if (utils.isString(geom)) {
37773
+ geom = JSON.parse(geom);
37774
+ }
37775
+ // TODO: validate? validate geometry in feature setter?
37776
+ var feat = {
37777
+ type: 'Feature',
37778
+ properties: features[i] ? features[i].properties : null,
37779
+ geometry: geom || null
37780
+ };
37781
+ api.set(feat, i);
37782
+ };
37783
+
37784
+ api.set = function(feat, i) {
37785
+ var arr;
37786
+
37787
+ if (utils.isString(feat)) {
37788
+ feat = JSON.parse(feat);
37789
+ }
37790
+
37791
+ if (!feat) return;
37792
+
37793
+ if (feat.type == 'GeometryCollection') {
37794
+ arr = feat.geometries.map(geom => GeoJSON.toFeature(geom));
37795
+ } else if (feat.type == 'FeatureCollection') {
37796
+ arr = feat.features;
37797
+ } else {
37798
+ feat = GeoJSON.toFeature(feat);
37799
+ }
37800
+
37801
+ if (arr) {
37802
+ features2 = features2.concat(arr);
37803
+ } else {
37804
+ features2.push(feat);
37805
+ }
37806
+ };
37807
+
37808
+ api.done = function() {
37809
+ if (features2.length === 0) return; // read-only expression
37810
+ var geojson = {
37811
+ type: 'FeatureCollection',
37812
+ features: features2
37813
+ };
37814
+ return importGeoJSON(geojson);
37815
+ };
37816
+ return api;
37817
+ }
37818
+
37783
37819
  cmd.filterGeom = function(lyr, arcs, opts) {
37784
37820
  if (!layerHasGeometry(lyr)) {
37785
37821
  stop("Layer is missing geometry");
@@ -45690,7 +45726,7 @@ ${svg}
45690
45726
  });
45691
45727
  }
45692
45728
 
45693
- var version = "0.6.98";
45729
+ var version = "0.6.99";
45694
45730
 
45695
45731
  // Parse command line args into commands and run them
45696
45732
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.6.98",
3
+ "version": "0.6.99",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -1089,6 +1089,103 @@
1089
1089
  return _el;
1090
1090
  }
1091
1091
 
1092
+ function filterLayerByIds(lyr, ids) {
1093
+ var shapes;
1094
+ if (lyr.shapes) {
1095
+ shapes = ids.map(function(id) {
1096
+ return lyr.shapes[id];
1097
+ });
1098
+ return utils$1.defaults({shapes: shapes, data: null}, lyr);
1099
+ }
1100
+ return lyr;
1101
+ }
1102
+
1103
+ function formatLayerNameForDisplay(name) {
1104
+ return name || '[unnamed]';
1105
+ }
1106
+
1107
+ function cleanLayerName(raw) {
1108
+ return raw.replace(/[\n\t/\\]/g, '')
1109
+ .replace(/^[.\s]+/, '').replace(/[.\s]+$/, '');
1110
+ }
1111
+
1112
+ function updateLayerStackOrder(layers) {
1113
+ // 1. assign ascending ids to unassigned layers above the range of other layers
1114
+ layers.forEach(function(o, i) {
1115
+ if (!o.layer.menu_order) o.layer.menu_order = 1e6 + i;
1116
+ });
1117
+ // 2. sort in ascending order
1118
+ layers.sort(function(a, b) {
1119
+ return a.layer.menu_order - b.layer.menu_order;
1120
+ });
1121
+ // 3. assign consecutve ids
1122
+ layers.forEach(function(o, i) {
1123
+ o.layer.menu_order = i + 1;
1124
+ });
1125
+ return layers;
1126
+ }
1127
+
1128
+ function sortLayersForMenuDisplay(layers) {
1129
+ layers = updateLayerStackOrder(layers);
1130
+ return layers.reverse();
1131
+ }
1132
+
1133
+ function setLayerPinning(lyr, pinned) {
1134
+ lyr.pinned = !!pinned;
1135
+ }
1136
+
1137
+
1138
+ function adjustPointSymbolSizes(layers, overlayLyr, ext) {
1139
+ var bbox = ext.getBounds().scale(1.5).toArray();
1140
+ var testInBounds = function(p) {
1141
+ return p[0] > bbox[0] && p[0] < bbox[2] && p[1] > bbox[1] && p[1] < bbox[3];
1142
+ };
1143
+ var topTier = 50000;
1144
+ var count = 0;
1145
+ layers = layers.filter(function(lyr) {
1146
+ return lyr.geometry_type == 'point' && lyr.gui.style.dotSize > 0;
1147
+ });
1148
+ layers.forEach(function(lyr) {
1149
+ // short-circuit point counting above top threshold
1150
+ count += countPoints(lyr.gui.displayLayer.shapes, topTier, testInBounds);
1151
+ });
1152
+ count = Math.min(topTier, count) || 1;
1153
+ var k = Math.pow(6 - utils$1.clamp(Math.log10(count), 1, 5), 1.3);
1154
+
1155
+ // zoom adjustments
1156
+ var mapScale = ext.scale();
1157
+ if (mapScale < 0.5) {
1158
+ k *= Math.pow(mapScale + 0.5, 0.35);
1159
+ } else if (mapScale > 1) {
1160
+ // scale faster at first
1161
+ k *= Math.pow(Math.min(mapScale, 4), 0.15);
1162
+ k *= Math.pow(mapScale, 0.05);
1163
+ }
1164
+
1165
+ // scale down when map is small
1166
+ var smallSide = Math.min(ext.width(), ext.height());
1167
+ k *= utils$1.clamp(smallSide / 500, 0.5, 1);
1168
+
1169
+ layers.forEach(function(lyr) {
1170
+ lyr.gui.style.dotScale = k;
1171
+ });
1172
+ if (overlayLyr && overlayLyr.geometry_type == 'point' && overlayLyr.gui.style.dotSize > 0) {
1173
+ overlayLyr.gui.style.dotScale = k;
1174
+ }
1175
+ }
1176
+
1177
+ function countPoints(shapes, max, filter) {
1178
+ var count = 0;
1179
+ var i, j, n, m, shp;
1180
+ for (i=0, n=shapes.length; i<n && count<max; i++) {
1181
+ shp = shapes[i];
1182
+ for (j=0, m=(shp ? shp.length : 0); j<m; j++) {
1183
+ count += filter(shp[j]) ? 1 : 0;
1184
+ }
1185
+ }
1186
+ return count;
1187
+ }
1188
+
1092
1189
  async function showPrompt(msg, title) {
1093
1190
  var popup = showPopupAlert(msg, title);
1094
1191
  return new Promise(function(resolve) {
@@ -1971,7 +2068,12 @@
1971
2068
  model.setDefaultTarget([target.layers[0]], target.dataset);
1972
2069
  }
1973
2070
  }
1974
- model.updated({select: true});
2071
+ if (opts.display_all && importTotal === 0) {
2072
+ model.getLayers().forEach(function(o) {
2073
+ setLayerPinning(o.layer, true);
2074
+ });
2075
+ }
2076
+ model.updated({select: true}); // trigger redraw
1975
2077
  }
1976
2078
 
1977
2079
  function clearQueuedFiles() {
@@ -3206,99 +3308,6 @@
3206
3308
  }
3207
3309
  }
3208
3310
 
3209
- function filterLayerByIds(lyr, ids) {
3210
- var shapes;
3211
- if (lyr.shapes) {
3212
- shapes = ids.map(function(id) {
3213
- return lyr.shapes[id];
3214
- });
3215
- return utils$1.defaults({shapes: shapes, data: null}, lyr);
3216
- }
3217
- return lyr;
3218
- }
3219
-
3220
- function formatLayerNameForDisplay(name) {
3221
- return name || '[unnamed]';
3222
- }
3223
-
3224
- function cleanLayerName(raw) {
3225
- return raw.replace(/[\n\t/\\]/g, '')
3226
- .replace(/^[.\s]+/, '').replace(/[.\s]+$/, '');
3227
- }
3228
-
3229
- function updateLayerStackOrder(layers) {
3230
- // 1. assign ascending ids to unassigned layers above the range of other layers
3231
- layers.forEach(function(o, i) {
3232
- if (!o.layer.menu_order) o.layer.menu_order = 1e6 + i;
3233
- });
3234
- // 2. sort in ascending order
3235
- layers.sort(function(a, b) {
3236
- return a.layer.menu_order - b.layer.menu_order;
3237
- });
3238
- // 3. assign consecutve ids
3239
- layers.forEach(function(o, i) {
3240
- o.layer.menu_order = i + 1;
3241
- });
3242
- return layers;
3243
- }
3244
-
3245
- function sortLayersForMenuDisplay(layers) {
3246
- layers = updateLayerStackOrder(layers);
3247
- return layers.reverse();
3248
- }
3249
-
3250
-
3251
- function adjustPointSymbolSizes(layers, overlayLyr, ext) {
3252
- var bbox = ext.getBounds().scale(1.5).toArray();
3253
- var testInBounds = function(p) {
3254
- return p[0] > bbox[0] && p[0] < bbox[2] && p[1] > bbox[1] && p[1] < bbox[3];
3255
- };
3256
- var topTier = 50000;
3257
- var count = 0;
3258
- layers = layers.filter(function(lyr) {
3259
- return lyr.geometry_type == 'point' && lyr.gui.style.dotSize > 0;
3260
- });
3261
- layers.forEach(function(lyr) {
3262
- // short-circuit point counting above top threshold
3263
- count += countPoints(lyr.gui.displayLayer.shapes, topTier, testInBounds);
3264
- });
3265
- count = Math.min(topTier, count) || 1;
3266
- var k = Math.pow(6 - utils$1.clamp(Math.log10(count), 1, 5), 1.3);
3267
-
3268
- // zoom adjustments
3269
- var mapScale = ext.scale();
3270
- if (mapScale < 0.5) {
3271
- k *= Math.pow(mapScale + 0.5, 0.35);
3272
- } else if (mapScale > 1) {
3273
- // scale faster at first
3274
- k *= Math.pow(Math.min(mapScale, 4), 0.15);
3275
- k *= Math.pow(mapScale, 0.05);
3276
- }
3277
-
3278
- // scale down when map is small
3279
- var smallSide = Math.min(ext.width(), ext.height());
3280
- k *= utils$1.clamp(smallSide / 500, 0.5, 1);
3281
-
3282
- layers.forEach(function(lyr) {
3283
- lyr.gui.style.dotScale = k;
3284
- });
3285
- if (overlayLyr && overlayLyr.geometry_type == 'point' && overlayLyr.gui.style.dotSize > 0) {
3286
- overlayLyr.gui.style.dotScale = k;
3287
- }
3288
- }
3289
-
3290
- function countPoints(shapes, max, filter) {
3291
- var count = 0;
3292
- var i, j, n, m, shp;
3293
- for (i=0, n=shapes.length; i<n && count<max; i++) {
3294
- shp = shapes[i];
3295
- for (j=0, m=(shp ? shp.length : 0); j<m; j++) {
3296
- count += filter(shp[j]) ? 1 : 0;
3297
- }
3298
- }
3299
- return count;
3300
- }
3301
-
3302
3311
  // lyr: a map layer with gui property
3303
3312
  // displayCRS: CRS to use for display, or null (which clears any current display CRS)
3304
3313
  function projectLayerForDisplay(lyr, displayCRS) {
@@ -5029,7 +5038,7 @@
5029
5038
  pinAll.on('click', function() {
5030
5039
  var allOn = testAllLayersPinned();
5031
5040
  model.getLayers().forEach(function(target) {
5032
- map.setLayerPinning(target, !allOn);
5041
+ setLayerPinning(target.layer, !allOn);
5033
5042
  });
5034
5043
  El.findAll('.pinnable', el.node()).forEach(function(item) {
5035
5044
  El(item).classed('pinned', !allOn);
@@ -5200,7 +5209,7 @@
5200
5209
 
5201
5210
  html = '<!-- ' + lyr.menu_id + '--><div class="' + classes + '">';
5202
5211
  html += rowHTML('name', '<span class="layer-name colored-text dot-underline">' + formatLayerNameForDisplay(lyr.name) + '</span>', 'row1');
5203
- html += rowHTML('contents', describeLyr(lyr));
5212
+ html += rowHTML('contents', describeLyr(lyr, dataset));
5204
5213
  html += '<img class="close-btn" draggable="false" src="images/close.png">';
5205
5214
  if (opts.pinnable) {
5206
5215
  html += '<img class="eye-btn black-eye" draggable="false" src="images/eye.png">';
@@ -5265,7 +5274,7 @@
5265
5274
  e.stopPropagation();
5266
5275
  if (map.isVisibleLayer(target.layer)) {
5267
5276
  // TODO: check for double map refresh after model.deleteLayer() below
5268
- map.setLayerPinning(target, false);
5277
+ setLayerPinning(target.layer, false);
5269
5278
  }
5270
5279
  model.deleteLayer(target.layer, target.dataset);
5271
5280
  });
@@ -5289,7 +5298,7 @@
5289
5298
  }
5290
5299
  lyr.hidden = hidden;
5291
5300
  lyr.unpinned = unpinned;
5292
- map.setLayerPinning(target, pinned);
5301
+ setLayerPinning(lyr, pinned);
5293
5302
  entry.classed('pinned', pinned);
5294
5303
  entry.classed('invisible', hidden);
5295
5304
  updatePinAllButton();
@@ -5330,15 +5339,18 @@
5330
5339
  });
5331
5340
  }
5332
5341
 
5333
- function describeLyr(lyr) {
5342
+ function describeLyr(lyr, dataset) {
5334
5343
  var n = internal.getFeatureCount(lyr),
5344
+ isFrame = internal.isFrameLayer(lyr, dataset.arcs),
5335
5345
  str, type;
5336
5346
  if (lyr.data && !lyr.shapes) {
5337
5347
  type = 'data record';
5338
5348
  } else if (lyr.geometry_type) {
5339
5349
  type = lyr.geometry_type + ' feature';
5340
5350
  }
5341
- if (type) {
5351
+ if (isFrame) {
5352
+ str = 'map frame';
5353
+ } else if (type) {
5342
5354
  str = utils$1.format('%,d %s%s', n, type, utils$1.pluralSuffix(n));
5343
5355
  } else {
5344
5356
  str = "[empty]";
@@ -12477,10 +12489,6 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12477
12489
  drawLayers();
12478
12490
  };
12479
12491
 
12480
- this.setLayerPinning = function(target, pinned) {
12481
- target.layer.pinned = !!pinned;
12482
- };
12483
-
12484
12492
  this.pixelCoordsToLngLatCoords = function(x, y) {
12485
12493
  var crsFrom = this.getDisplayCRS();
12486
12494
  if (!crsFrom) return null; // e.g. table view
@@ -12904,11 +12912,13 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12904
12912
  updateLayerStyles(contentLayers);
12905
12913
  updateLayerStackOrder(model.getLayers());// update menu_order property of all layers
12906
12914
  }
12907
- adjustPointSymbolSizes(contentLayers, _overlayLyr, _ext);
12908
12915
  sortMapLayers(contentLayers);
12909
12916
  if (_intersectionLyr) {
12910
12917
  contentLayers = contentLayers.concat(_intersectionLyr);
12911
12918
  }
12919
+ // moved this below intersection layer addition, so intersection dots get scaled
12920
+ adjustPointSymbolSizes(contentLayers, _overlayLyr, _ext);
12921
+
12912
12922
  // RENDERING
12913
12923
  // draw main content layers
12914
12924
  _renderer.drawMainLayers(contentLayers, action);
@@ -13558,14 +13568,9 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13558
13568
  dataLoaded = true;
13559
13569
  gui.buttons.show();
13560
13570
  gui.basemap.show();
13561
- El('#mode-buttons').show();
13562
- El('#splash-buttons').hide();
13571
+ El('#mode-buttons').show(); // show Simplify, Console, Export, etc.
13572
+ El('#splash-buttons').hide(); // hide Wiki, Github buttons
13563
13573
  El('body').addClass('map-view');
13564
- if (importOpts.display_all) {
13565
- gui.model.getLayers().forEach(function(o) {
13566
- gui.map.setLayerPinning(o, true);
13567
- });
13568
- }
13569
13574
  gui.console.runInitialCommands(getInitialConsoleCommands());
13570
13575
  });
13571
13576
  };
package/www/mapshaper.js CHANGED
@@ -14931,6 +14931,24 @@
14931
14931
  return opts.geojson_editor.get(_id);
14932
14932
  }
14933
14933
  });
14934
+
14935
+ Object.defineProperty(ctx, 'feature', {
14936
+ set: function(o) {
14937
+ opts.geojson_editor.set(o, _id);
14938
+ },
14939
+ get: function() {
14940
+ return opts.geojson_editor.get(_id);
14941
+ }
14942
+ });
14943
+
14944
+ Object.defineProperty(ctx, 'geometry', {
14945
+ set: function(o) {
14946
+ opts.geojson_editor.setGeometry(o, _id);
14947
+ },
14948
+ get: function() {
14949
+ return opts.geojson_editor.getGeometry(_id);
14950
+ }
14951
+ });
14934
14952
  }
14935
14953
 
14936
14954
  if (_records) {
@@ -35764,58 +35782,6 @@ ${svg}
35764
35782
 
35765
35783
  cmd.comment = function() {}; // no-op, so -comment doesn't trigger a parsing error
35766
35784
 
35767
- function expressionUsesGeoJSON(exp) {
35768
- return exp.includes('this.geojson');
35769
- }
35770
-
35771
- function getFeatureEditor(lyr, dataset) {
35772
- var api = {};
35773
- // need to copy attribute to avoid circular references if geojson is assigned
35774
- // to a data property.
35775
- var copy = copyLayer(lyr);
35776
- var features = exportLayerAsGeoJSON(copy, dataset, {rfc7946: true}, true);
35777
- var features2 = [];
35778
-
35779
- api.get = function(i) {
35780
- if (i > 0) features[i-1] = null; // garbage-collect old features
35781
- return features[i];
35782
- };
35783
-
35784
- api.set = function(feat, i) {
35785
- var arr;
35786
-
35787
- if (utils.isString(feat)) {
35788
- feat = JSON.parse(feat);
35789
- }
35790
-
35791
- if (!feat) return;
35792
-
35793
- if (feat.type == 'GeometryCollection') {
35794
- arr = feat.geometries.map(geom => GeoJSON.toFeature(geom));
35795
- } else if (feat.type == 'FeatureCollection') {
35796
- arr = feat.features;
35797
- } else {
35798
- feat = GeoJSON.toFeature(feat);
35799
- }
35800
-
35801
- if (arr) {
35802
- features2 = features2.concat(arr);
35803
- } else {
35804
- features2.push(feat);
35805
- }
35806
- };
35807
-
35808
- api.done = function() {
35809
- if (features2.length === 0) return; // read-only expression
35810
- var geojson = {
35811
- type: 'FeatureCollection',
35812
- features: features2
35813
- };
35814
- return importGeoJSON(geojson);
35815
- };
35816
- return api;
35817
- }
35818
-
35819
35785
  cmd.dashlines = function(lyr, dataset, opts) {
35820
35786
  var crs = getDatasetCRS(dataset);
35821
35787
  var defs = getStashedVar('defs');
@@ -37780,6 +37746,76 @@ ${svg}
37780
37746
  }
37781
37747
  };
37782
37748
 
37749
+ function expressionUsesGeoJSON(exp) {
37750
+ return exp.includes('this.geojson') || exp.includes('this.geometry') || exp.includes('this.feature');
37751
+ }
37752
+
37753
+ function getFeatureEditor(lyr, dataset) {
37754
+ var api = {};
37755
+ // need to copy attribute to avoid circular references if geojson is assigned
37756
+ // to a data property.
37757
+ var copy = copyLayer(lyr);
37758
+ var features = exportLayerAsGeoJSON(copy, dataset, {rfc7946: true}, true);
37759
+ var features2 = [];
37760
+
37761
+ api.get = function(i) {
37762
+ if (i > 0) features[i-1] = null; // garbage-collect old features
37763
+ return features[i];
37764
+ };
37765
+
37766
+ api.getGeometry = function(i) {
37767
+ if (i > 0) features[i-1] = null; // garbage-collect old features
37768
+ return features[i] ? features[i].geometry : null;
37769
+ };
37770
+
37771
+ api.setGeometry = function(geom, i) {
37772
+ if (utils.isString(geom)) {
37773
+ geom = JSON.parse(geom);
37774
+ }
37775
+ // TODO: validate? validate geometry in feature setter?
37776
+ var feat = {
37777
+ type: 'Feature',
37778
+ properties: features[i] ? features[i].properties : null,
37779
+ geometry: geom || null
37780
+ };
37781
+ api.set(feat, i);
37782
+ };
37783
+
37784
+ api.set = function(feat, i) {
37785
+ var arr;
37786
+
37787
+ if (utils.isString(feat)) {
37788
+ feat = JSON.parse(feat);
37789
+ }
37790
+
37791
+ if (!feat) return;
37792
+
37793
+ if (feat.type == 'GeometryCollection') {
37794
+ arr = feat.geometries.map(geom => GeoJSON.toFeature(geom));
37795
+ } else if (feat.type == 'FeatureCollection') {
37796
+ arr = feat.features;
37797
+ } else {
37798
+ feat = GeoJSON.toFeature(feat);
37799
+ }
37800
+
37801
+ if (arr) {
37802
+ features2 = features2.concat(arr);
37803
+ } else {
37804
+ features2.push(feat);
37805
+ }
37806
+ };
37807
+
37808
+ api.done = function() {
37809
+ if (features2.length === 0) return; // read-only expression
37810
+ var geojson = {
37811
+ type: 'FeatureCollection',
37812
+ features: features2
37813
+ };
37814
+ return importGeoJSON(geojson);
37815
+ };
37816
+ return api;
37817
+ }
37818
+
37783
37819
  cmd.filterGeom = function(lyr, arcs, opts) {
37784
37820
  if (!layerHasGeometry(lyr)) {
37785
37821
  stop("Layer is missing geometry");
@@ -45690,7 +45726,7 @@ ${svg}
45690
45726
  });
45691
45727
  }
45692
45728
 
45693
- var version = "0.6.98";
45729
+ var version = "0.6.99";
45694
45730
 
45695
45731
  // Parse command line args into commands and run them
45696
45732
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.