mapshaper 0.6.97 → 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) {
@@ -25134,12 +25152,10 @@ ${svg}
25134
25152
  // describe: 'multiply min-area by Polsby-Popper compactness (0-1)'
25135
25153
  type: 'flag',
25136
25154
  })
25137
- /*
25138
25155
  .option('remove-empty', {
25139
25156
  type: 'flag',
25140
25157
  describe: 'delete features with null geometry'
25141
25158
  })
25142
- */
25143
25159
  .option('target', targetOpt);
25144
25160
 
25145
25161
  parser.command('graticule')
@@ -34748,6 +34764,11 @@ ${svg}
34748
34764
 
34749
34765
  editShapes(lyr.shapes, pathFilter);
34750
34766
  message(utils.format("Removed %'d sliver%s using %s", removed, utils.pluralSuffix(removed), filterData.label));
34767
+
34768
+ // Remove null shapes (likely removed by clipping/erasing, although possibly already present)
34769
+ if (opts.remove_empty) {
34770
+ cmd.filterFeatures(lyr, dataset.arcs, {remove_empty: true, verbose: false});
34771
+ }
34751
34772
  return removed;
34752
34773
  }
34753
34774
 
@@ -35761,58 +35782,6 @@ ${svg}
35761
35782
 
35762
35783
  cmd.comment = function() {}; // no-op, so -comment doesn't trigger a parsing error
35763
35784
 
35764
- function expressionUsesGeoJSON(exp) {
35765
- return exp.includes('this.geojson');
35766
- }
35767
-
35768
- function getFeatureEditor(lyr, dataset) {
35769
- var api = {};
35770
- // need to copy attribute to avoid circular references if geojson is assigned
35771
- // to a data property.
35772
- var copy = copyLayer(lyr);
35773
- var features = exportLayerAsGeoJSON(copy, dataset, {rfc7946: true}, true);
35774
- var features2 = [];
35775
-
35776
- api.get = function(i) {
35777
- if (i > 0) features[i-1] = null; // garbage-collect old features
35778
- return features[i];
35779
- };
35780
-
35781
- api.set = function(feat, i) {
35782
- var arr;
35783
-
35784
- if (utils.isString(feat)) {
35785
- feat = JSON.parse(feat);
35786
- }
35787
-
35788
- if (!feat) return;
35789
-
35790
- if (feat.type == 'GeometryCollection') {
35791
- arr = feat.geometries.map(geom => GeoJSON.toFeature(geom));
35792
- } else if (feat.type == 'FeatureCollection') {
35793
- arr = feat.features;
35794
- } else {
35795
- feat = GeoJSON.toFeature(feat);
35796
- }
35797
-
35798
- if (arr) {
35799
- features2 = features2.concat(arr);
35800
- } else {
35801
- features2.push(feat);
35802
- }
35803
- };
35804
-
35805
- api.done = function() {
35806
- if (features2.length === 0) return; // read-only expression
35807
- var geojson = {
35808
- type: 'FeatureCollection',
35809
- features: features2
35810
- };
35811
- return importGeoJSON(geojson);
35812
- };
35813
- return api;
35814
- }
35815
-
35816
35785
  cmd.dashlines = function(lyr, dataset, opts) {
35817
35786
  var crs = getDatasetCRS(dataset);
35818
35787
  var defs = getStashedVar('defs');
@@ -37777,6 +37746,76 @@ ${svg}
37777
37746
  }
37778
37747
  };
37779
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
+
37780
37819
  cmd.filterGeom = function(lyr, arcs, opts) {
37781
37820
  if (!layerHasGeometry(lyr)) {
37782
37821
  stop("Layer is missing geometry");
@@ -45687,7 +45726,7 @@ ${svg}
45687
45726
  });
45688
45727
  }
45689
45728
 
45690
- var version = "0.6.97";
45729
+ var version = "0.6.99";
45691
45730
 
45692
45731
  // Parse command line args into commands and run them
45693
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.97",
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]";
@@ -5949,7 +5961,7 @@
5949
5961
  box: 'rectangle tool',
5950
5962
  data: 'edit attributes',
5951
5963
  labels: 'position labels',
5952
- edit_points: 'add/edit points',
5964
+ edit_points: 'add/drag points',
5953
5965
  edit_lines: 'draw/edit polylines',
5954
5966
  edit_polygons: 'draw/edit polygons',
5955
5967
  vertices: 'edit vertices',
@@ -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) {
@@ -25134,12 +25152,10 @@ ${svg}
25134
25152
  // describe: 'multiply min-area by Polsby-Popper compactness (0-1)'
25135
25153
  type: 'flag',
25136
25154
  })
25137
- /*
25138
25155
  .option('remove-empty', {
25139
25156
  type: 'flag',
25140
25157
  describe: 'delete features with null geometry'
25141
25158
  })
25142
- */
25143
25159
  .option('target', targetOpt);
25144
25160
 
25145
25161
  parser.command('graticule')
@@ -34748,6 +34764,11 @@ ${svg}
34748
34764
 
34749
34765
  editShapes(lyr.shapes, pathFilter);
34750
34766
  message(utils.format("Removed %'d sliver%s using %s", removed, utils.pluralSuffix(removed), filterData.label));
34767
+
34768
+ // Remove null shapes (likely removed by clipping/erasing, although possibly already present)
34769
+ if (opts.remove_empty) {
34770
+ cmd.filterFeatures(lyr, dataset.arcs, {remove_empty: true, verbose: false});
34771
+ }
34751
34772
  return removed;
34752
34773
  }
34753
34774
 
@@ -35761,58 +35782,6 @@ ${svg}
35761
35782
 
35762
35783
  cmd.comment = function() {}; // no-op, so -comment doesn't trigger a parsing error
35763
35784
 
35764
- function expressionUsesGeoJSON(exp) {
35765
- return exp.includes('this.geojson');
35766
- }
35767
-
35768
- function getFeatureEditor(lyr, dataset) {
35769
- var api = {};
35770
- // need to copy attribute to avoid circular references if geojson is assigned
35771
- // to a data property.
35772
- var copy = copyLayer(lyr);
35773
- var features = exportLayerAsGeoJSON(copy, dataset, {rfc7946: true}, true);
35774
- var features2 = [];
35775
-
35776
- api.get = function(i) {
35777
- if (i > 0) features[i-1] = null; // garbage-collect old features
35778
- return features[i];
35779
- };
35780
-
35781
- api.set = function(feat, i) {
35782
- var arr;
35783
-
35784
- if (utils.isString(feat)) {
35785
- feat = JSON.parse(feat);
35786
- }
35787
-
35788
- if (!feat) return;
35789
-
35790
- if (feat.type == 'GeometryCollection') {
35791
- arr = feat.geometries.map(geom => GeoJSON.toFeature(geom));
35792
- } else if (feat.type == 'FeatureCollection') {
35793
- arr = feat.features;
35794
- } else {
35795
- feat = GeoJSON.toFeature(feat);
35796
- }
35797
-
35798
- if (arr) {
35799
- features2 = features2.concat(arr);
35800
- } else {
35801
- features2.push(feat);
35802
- }
35803
- };
35804
-
35805
- api.done = function() {
35806
- if (features2.length === 0) return; // read-only expression
35807
- var geojson = {
35808
- type: 'FeatureCollection',
35809
- features: features2
35810
- };
35811
- return importGeoJSON(geojson);
35812
- };
35813
- return api;
35814
- }
35815
-
35816
35785
  cmd.dashlines = function(lyr, dataset, opts) {
35817
35786
  var crs = getDatasetCRS(dataset);
35818
35787
  var defs = getStashedVar('defs');
@@ -37777,6 +37746,76 @@ ${svg}
37777
37746
  }
37778
37747
  };
37779
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
+
37780
37819
  cmd.filterGeom = function(lyr, arcs, opts) {
37781
37820
  if (!layerHasGeometry(lyr)) {
37782
37821
  stop("Layer is missing geometry");
@@ -45687,7 +45726,7 @@ ${svg}
45687
45726
  });
45688
45727
  }
45689
45728
 
45690
- var version = "0.6.97";
45729
+ var version = "0.6.99";
45691
45730
 
45692
45731
  // Parse command line args into commands and run them
45693
45732
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.