mapshaper 0.5.91 → 0.5.94

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/bin/mapshaper-gui CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  var defaultPort = 5555,
4
- program = require('commander')
4
+ commander = require('commander'),
5
+ program = new commander.Command()
5
6
  .name('mapshaper-gui')
6
7
  .usage('[options] [file ...]')
7
8
  .option('-p, --port <port>', 'http port of server on localhost', defaultPort)
@@ -10,10 +11,13 @@ var defaultPort = 5555,
10
11
  .option('-f, --force-save', 'allow overwriting input files with output files')
11
12
  .option('-a, --display-all', 'turn on visibility of all layers')
12
13
  .option('-n, --name <name(s)>', 'rename input layer or layers')
14
+ .option('-c, --commands <string>', 'console commands to run initially')
13
15
  .option('-t, --target <name>', 'name of layer to select initially')
16
+ .addOption(new commander.Option('-b, --blurb <text>',
17
+ 'replace the default blurb on the import screen').hideHelp())
14
18
  .helpOption('-h, --help', 'show this help message')
15
19
  .version(require('../package.json').version)
16
- .parse(),
20
+ .parse(process.argv),
17
21
  opts = program.opts(),
18
22
  http = require("http"),
19
23
  path = require("path"),
@@ -26,7 +30,6 @@ var defaultPort = 5555,
26
30
  dataFiles = expandShapefiles(program.args),
27
31
  probeCount = 0,
28
32
  sessionId = null;
29
-
30
33
  validateFiles(dataFiles);
31
34
 
32
35
  process.on('uncaughtException', function(err) {
@@ -204,12 +207,16 @@ function validateOutputFile(file) {
204
207
  }
205
208
 
206
209
  function getManifestJS(files, opts) {
207
- var o = {files: files};
208
- if (opts.target) o.target = opts.target;
209
- if (opts.directSave) o.allow_saving = true;
210
- if (opts.displayAll) o.display_all = true;
211
- if (opts.quickView) o.quick_view = true;
212
- if (opts.name) o.name = opts.name;
210
+ var o = {
211
+ files: files,
212
+ target: opts.target,
213
+ allow_saving: opts.directSave,
214
+ display_all: opts.displayAll,
215
+ quick_view: opts.quickView,
216
+ name: opts.name,
217
+ commands: opts.commands,
218
+ blurb: opts.blurb
219
+ };
213
220
  return "mapshaper.manifest = " + JSON.stringify(o) + ";\n";
214
221
  }
215
222
 
package/mapshaper.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
 
3
- var VERSION = "0.5.88";
3
+ var VERSION = "0.5.92";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -994,15 +994,23 @@
994
994
  }
995
995
 
996
996
  function copyElements(src, i, dest, j, n, rev) {
997
- if (src === dest && j > i) error ("copy error");
997
+ var same = src == dest || src.buffer && src.buffer == dest.buffer;
998
998
  var inc = 1,
999
- offs = 0;
999
+ offs = 0,
1000
+ k;
1000
1001
  if (rev) {
1002
+ if (same) error('copy error');
1001
1003
  inc = -1;
1002
1004
  offs = n - 1;
1003
1005
  }
1004
- for (var k=0; k<n; k++, offs += inc) {
1005
- dest[k + j] = src[i + offs];
1006
+ if (same && j > i) {
1007
+ for (k=n-1; k>=0; k--) {
1008
+ dest[j + k] = src[i + k];
1009
+ }
1010
+ } else {
1011
+ for (k=0; k<n; k++, offs += inc) {
1012
+ dest[k + j] = src[i + offs];
1013
+ }
1006
1014
  }
1007
1015
  }
1008
1016
 
@@ -1756,6 +1764,69 @@
1756
1764
  return [xmin, ymin, xmax, ymax];
1757
1765
  }
1758
1766
 
1767
+ function deleteVertex(arcs, i) {
1768
+ var data = arcs.getVertexData();
1769
+ var nn = data.nn;
1770
+ var n = data.xx.length;
1771
+ // avoid re-allocating memory
1772
+ var xx2 = new Float64Array(data.xx.buffer, 0, n-1);
1773
+ var yy2 = new Float64Array(data.yy.buffer, 0, n-1);
1774
+ var count = 0;
1775
+ var found = false;
1776
+ for (var j=0; j<nn.length; j++) {
1777
+ count += nn[j];
1778
+ if (count >= i && !found) { // TODO: confirm this
1779
+ nn[j] = nn[j] - 1;
1780
+ found = true;
1781
+ }
1782
+ }
1783
+ utils.copyElements(data.xx, 0, xx2, 0, i);
1784
+ utils.copyElements(data.yy, 0, yy2, 0, i);
1785
+ utils.copyElements(data.xx, i+1, xx2, i, n-i-1);
1786
+ utils.copyElements(data.yy, i+1, yy2, i, n-i-1);
1787
+ arcs.updateVertexData(nn, xx2, yy2, null);
1788
+ }
1789
+
1790
+ function insertVertex(arcs, i, p) {
1791
+ // TODO: add extra bytes to the buffers, to reduce new memory allocation
1792
+ var data = arcs.getVertexData();
1793
+ var nn = data.nn;
1794
+ var n = data.xx.length;
1795
+ var count = 0;
1796
+ var found = false;
1797
+ var xx2, yy2;
1798
+ // avoid re-allocating memory on each insertion
1799
+ if (data.xx.buffer.byteLength >= data.xx.length * 8 + 8) {
1800
+ xx2 = new Float64Array(data.xx.buffer, 0, n+1);
1801
+ yy2 = new Float64Array(data.yy.buffer, 0, n+1);
1802
+ } else {
1803
+ xx2 = new Float64Array(new ArrayBuffer((n + 20) * 8), 0, n+1);
1804
+ yy2 = new Float64Array(new ArrayBuffer((n + 20) * 8), 0, n+1);
1805
+ }
1806
+ for (var j=0; j<nn.length; j++) {
1807
+ count += nn[j];
1808
+ if (count >= i && !found) { // TODO: confirm this
1809
+ nn[j] = nn[j] + 1;
1810
+ found = true;
1811
+ }
1812
+ }
1813
+ utils.copyElements(data.xx, 0, xx2, 0, i);
1814
+ utils.copyElements(data.yy, 0, yy2, 0, i);
1815
+ utils.copyElements(data.xx, i, xx2, i+1, n-i);
1816
+ utils.copyElements(data.yy, i, yy2, i+1, n-i);
1817
+ xx2[i] = p[0];
1818
+ yy2[i] = p[1];
1819
+ arcs.updateVertexData(nn, xx2, yy2, null);
1820
+ }
1821
+
1822
+ var ArcUtils = /*#__PURE__*/Object.freeze({
1823
+ __proto__: null,
1824
+ absArcId: absArcId,
1825
+ calcArcBounds: calcArcBounds,
1826
+ deleteVertex: deleteVertex,
1827
+ insertVertex: insertVertex
1828
+ });
1829
+
1759
1830
  var WGS84 = {
1760
1831
  // https://en.wikipedia.org/wiki/Earth_radius
1761
1832
  SEMIMAJOR_AXIS: 6378137,
@@ -2158,6 +2229,7 @@
2158
2229
  function getPointToPathInfo(px, py, ids, arcs) {
2159
2230
  var iter = arcs.getShapeIter(ids);
2160
2231
  var pPathSq = Infinity;
2232
+ var arcId;
2161
2233
  var ax, ay, bx, by, axmin, aymin, bxmin, bymin, pabSq;
2162
2234
  if (iter.hasNext()) {
2163
2235
  ax = axmin = bxmin = iter.x;
@@ -2169,6 +2241,7 @@
2169
2241
  pabSq = pointSegDistSq2(px, py, ax, ay, bx, by);
2170
2242
  if (pabSq < pPathSq) {
2171
2243
  pPathSq = pabSq;
2244
+ arcId = iter._ids[iter._i]; // kludge
2172
2245
  axmin = ax;
2173
2246
  aymin = ay;
2174
2247
  bxmin = bx;
@@ -2180,7 +2253,8 @@
2180
2253
  if (pPathSq == Infinity) return {distance: Infinity};
2181
2254
  return {
2182
2255
  segment: [[axmin, aymin], [bxmin, bymin]],
2183
- distance: Math.sqrt(pPathSq)
2256
+ distance: Math.sqrt(pPathSq),
2257
+ arcId: arcId
2184
2258
  };
2185
2259
  }
2186
2260
 
@@ -2188,11 +2262,20 @@
2188
2262
  // Return unsigned distance of a point to the nearest point on a polygon or polyline path
2189
2263
  //
2190
2264
  function getPointToShapeDistance(x, y, shp, arcs) {
2191
- var minDist = (shp || []).reduce(function(minDist, ids) {
2192
- var pathDist = getPointToPathDistance(x, y, ids, arcs);
2193
- return Math.min(minDist, pathDist);
2194
- }, Infinity);
2195
- return minDist;
2265
+ var info = getPointToShapeInfo(x, y, shp, arcs);
2266
+ return info ? info.distance : Infinity;
2267
+ }
2268
+
2269
+ function getPointToShapeInfo(x, y, shp, arcs) {
2270
+ return (shp || []).reduce(function(memo, ids) {
2271
+ var pathInfo = getPointToPathInfo(x, y, ids, arcs);
2272
+ if (!memo || pathInfo.distance < memo.distance) return pathInfo;
2273
+ return memo;
2274
+ }, null) || {
2275
+ distance: Infinity,
2276
+ arcId: -1,
2277
+ segment: null
2278
+ };
2196
2279
  }
2197
2280
 
2198
2281
  // @ids array of arc ids
@@ -2277,6 +2360,7 @@
2277
2360
  getPointToPathDistance: getPointToPathDistance,
2278
2361
  getPointToPathInfo: getPointToPathInfo,
2279
2362
  getPointToShapeDistance: getPointToShapeDistance,
2363
+ getPointToShapeInfo: getPointToShapeInfo,
2280
2364
  getAvgPathXY: getAvgPathXY,
2281
2365
  getMaxPath: getMaxPath,
2282
2366
  countVerticesInPath: countVerticesInPath,
@@ -4909,6 +4993,7 @@
4909
4993
  this._n = 0;
4910
4994
  this.x = 0;
4911
4995
  this.y = 0;
4996
+ this.i = -1;
4912
4997
  }
4913
4998
 
4914
4999
  ShapeIter.prototype.hasNext = function() {
@@ -4919,6 +5004,7 @@
4919
5004
  if (arc.hasNext()) {
4920
5005
  this.x = arc.x;
4921
5006
  this.y = arc.y;
5007
+ this.i = arc.i;
4922
5008
  return true;
4923
5009
  }
4924
5010
  this.nextArc();
@@ -5377,6 +5463,10 @@
5377
5463
  };
5378
5464
  };
5379
5465
 
5466
+ this.getVertex2 = function(i) {
5467
+ return [_xx[i], _yy[i]];
5468
+ };
5469
+
5380
5470
  // @nth: index of vertex. ~(idx) starts from the opposite endpoint
5381
5471
  this.indexOfVertex = function(arcId, nth) {
5382
5472
  var absId = arcId < 0 ? ~arcId : arcId,
@@ -5481,6 +5571,8 @@
5481
5571
  }
5482
5572
  };
5483
5573
 
5574
+ this.isFlat = function() { return !_zz; };
5575
+
5484
5576
  this.getRetainedInterval = function() {
5485
5577
  return _zlimit;
5486
5578
  };
@@ -9369,6 +9461,9 @@
9369
9461
  return rec && (rec[name] === val);
9370
9462
  });
9371
9463
  };
9464
+ obj.file_exists = function(name) {
9465
+ return cli.isFile(name);
9466
+ };
9372
9467
  return obj;
9373
9468
  }
9374
9469
 
@@ -23430,7 +23525,16 @@ ${svg}
23430
23525
  // should be in gui-model.js, moved here for testing
23431
23526
  this.getActiveLayer = function() {
23432
23527
  var targ = (this.getDefaultTargets() || [])[0];
23433
- return targ ? {layer: targ.layers[0], dataset: targ.dataset} : null;
23528
+ // var lyr = targ.layers[0];
23529
+ // Reasons to select the last layer of a multi-layer target:
23530
+ // * This layer was imported last
23531
+ // * This layer is displayed on top of other layers
23532
+ // * This layer is at the top of the layers list
23533
+ // * In TopoJSON input, it makes sense to think of the last object/layer
23534
+ // as the topmost one -- it corresponds to the painter's algorithm and
23535
+ // the way that objects are ordered in SVG.
23536
+ var lyr = targ.layers[targ.layers.length - 1];
23537
+ return targ ? {layer: lyr, dataset: targ.dataset} : null;
23434
23538
  };
23435
23539
 
23436
23540
  function layerObject(lyr, dataset) {
@@ -27167,7 +27271,7 @@ ${svg}
27167
27271
  }
27168
27272
 
27169
27273
  function samePoint(a, b) {
27170
- return a[0] === b[0] && a[1] === b[1];
27274
+ return a && b && a[0] === b[0] && a[1] === b[1];
27171
27275
  }
27172
27276
 
27173
27277
  function isClosedPath(arr) {
@@ -27781,11 +27885,15 @@ ${svg}
27781
27885
  }
27782
27886
 
27783
27887
  function getCategoricalColorScheme(name, n) {
27888
+ var colors;
27784
27889
  initSchemes();
27785
- if (index.categorical.includes(name) === false) {
27786
- stop(name, 'is not a categorical color scheme');
27890
+ if (!isColorSchemeName(name)) {
27891
+ stop('Unknown color scheme name:', name);
27892
+ } else if (isCategoricalColorScheme(name)) {
27893
+ colors = ramps[name] || require('d3-scale-chromatic')['scheme' + name];
27894
+ } else {
27895
+ colors = getColorRamp(name, n);
27787
27896
  }
27788
- var colors = ramps[name] || require('d3-scale-chromatic')['scheme' + name];
27789
27897
  if (n > colors.length) {
27790
27898
  stop(name, 'does not contain', n, 'colors');
27791
27899
  }
@@ -27798,6 +27906,11 @@ ${svg}
27798
27906
  index.diverging.includes(name) || index.rainbow.includes(name);
27799
27907
  }
27800
27908
 
27909
+ function isCategoricalColorScheme(name) {
27910
+ initSchemes();
27911
+ return index.categorical.includes(name);
27912
+ }
27913
+
27801
27914
  function getColorRamp(name, n, stops) {
27802
27915
  initSchemes();
27803
27916
  var lib = require('d3-scale-chromatic');
@@ -28287,7 +28400,7 @@ ${svg}
28287
28400
  };
28288
28401
  }
28289
28402
 
28290
- function getSequentialClassifier$1(classValues, nullValue, dataValues, opts) {
28403
+ function getSequentialClassifier$1(classValues, nullValue, dataValues, method, opts) {
28291
28404
  var numValues = classValues.length;
28292
28405
  var numBuckets = opts.continuous ? numValues - 1 : numValues;
28293
28406
 
@@ -28295,7 +28408,6 @@ ${svg}
28295
28408
  // discreetly classed values
28296
28409
  var numBreaks = numBuckets - 1;
28297
28410
  var round = opts.precision ? getRoundingFunction(opts.precision) : null;
28298
- var method = opts.method || 'quantile';
28299
28411
  var breaks, classifier, dataToClass, classToValue;
28300
28412
 
28301
28413
  if (round) {
@@ -28900,34 +29012,35 @@ ${svg}
28900
29012
  var opts = optsArg || {};
28901
29013
  var records = lyr.data && lyr.data.getRecords();
28902
29014
  var nullValue = opts.null_value || null;
28903
- var looksLikeColors = !!opts.colors || !!opts.color_scheme;
29015
+ var valuesAreColors = !!opts.colors || !!opts.color_scheme;
28904
29016
  var colorScheme;
28905
- var classValues, classifyByValue, classifyById;
28906
- var numBuckets, numValues;
29017
+ var values, classifyByValue, classifyById;
29018
+ var numClasses, numValues;
28907
29019
  var dataField, outputField;
29020
+ var method;
28908
29021
 
28909
29022
  // validate explicitly set classes
28910
29023
  if (opts.classes) {
28911
29024
  if (!utils.isInteger(opts.classes) || opts.classes > 1 === false) {
28912
29025
  stop('Invalid number of classes:', opts.classes, '(expected a value greater than 1)');
28913
29026
  }
28914
- numBuckets = opts.classes;
29027
+ numClasses = opts.classes;
28915
29028
  }
28916
29029
 
28917
29030
  // TODO: better validation of breaks values
28918
29031
  if (opts.breaks) {
28919
- numBuckets = opts.breaks.length + 1;
29032
+ numClasses = opts.breaks.length + 1;
28920
29033
  }
28921
29034
 
28922
29035
  if (opts.index_field) {
28923
29036
  dataField = opts.index_field;
28924
- if (numBuckets > 0 === false) {
29037
+ if (numClasses > 0 === false) {
28925
29038
  stop('The index-field= option requires the classes= option to be set');
28926
29039
  }
28927
29040
  // You can't infer the number of classes by looking at index values;
28928
29041
  // this can cause unwanted interpolation if one or more values are
28929
29042
  // not present in the index field
28930
- // numBuckets = validateClassIndexField(records, opts.index_field);
29043
+ // numClasses = validateClassIndexField(records, opts.index_field);
28931
29044
 
28932
29045
  } else if (opts.field) {
28933
29046
  dataField = opts.field;
@@ -28938,7 +29051,19 @@ ${svg}
28938
29051
  opts.categories = getUniqFieldValues(records, dataField);
28939
29052
  }
28940
29053
 
28941
- if (opts.method == 'non-adjacent') {
29054
+ // get classification method
29055
+ //
29056
+ if (opts.method) {
29057
+ method = opts.method;
29058
+ } else if (opts.categories) {
29059
+ method = 'categorical';
29060
+ } else if (opts.index_field) {
29061
+ method = 'indexed';
29062
+ } else {
29063
+ method = 'quantile'; // TODO: validate data field
29064
+ }
29065
+
29066
+ if (method == 'non-adjacent') {
28942
29067
  if (lyr.geometry_type != 'polygon') {
28943
29068
  stop('The non-adjacent option requires a polygon layer');
28944
29069
  }
@@ -28951,8 +29076,8 @@ ${svg}
28951
29076
  requireDataField(lyr.data, dataField);
28952
29077
  }
28953
29078
 
28954
- if (numBuckets) {
28955
- numValues = opts.continuous ? numBuckets + 1 : numBuckets;
29079
+ if (numClasses) {
29080
+ numValues = opts.continuous ? numClasses + 1 : numClasses;
28956
29081
  }
28957
29082
 
28958
29083
  // support both deprecated color-scheme= option and colors=<color-scheme> syntax
@@ -28967,39 +29092,44 @@ ${svg}
28967
29092
  opts.colors.forEach(parseColor); // validate colors -- error if unparsable
28968
29093
  }
28969
29094
 
29095
+ /// get values (usually colors)
29096
+ ///
28970
29097
  if (colorScheme) {
28971
- // using a named color scheme: generate a ramp
29098
+ if (method == 'non-adjacent') {
29099
+ numClasses = numValues = numClasses || 5;
29100
+ values = getCategoricalColorScheme(colorScheme, numValues);
29101
+
29102
+ } else if (method == 'categorical') {
29103
+ values = getCategoricalColorScheme(colorScheme, opts.categories.length);
29104
+ numClasses = numValues = values.length;
28972
29105
 
28973
- if (opts.categories) {
28974
- classValues = getCategoricalColorScheme(colorScheme, opts.categories.length);
28975
- numBuckets = numValues = classValues.length;
28976
29106
  } else {
28977
- if (!numBuckets) {
29107
+ if (!numClasses) {
28978
29108
  // stop('color-scheme= option requires classes= or breaks=');
28979
- numBuckets = 4; // use a default number of classes
28980
- numValues = opts.continuous ? numBuckets + 1 : numBuckets;
29109
+ numClasses = 4; // use a default number of classes
29110
+ numValues = opts.continuous ? numClasses + 1 : numClasses;
28981
29111
  }
28982
- classValues = getColorRamp(colorScheme, numValues, opts.stops);
29112
+ values = getColorRamp(colorScheme, numValues, opts.stops);
28983
29113
  }
28984
29114
 
28985
29115
  } else if (opts.colors || opts.values) {
28986
- classValues = opts.values ? parseValues(opts.values) : opts.colors;
29116
+ values = opts.values ? parseValues(opts.values) : opts.colors;
28987
29117
  if (!numValues) {
28988
- numValues = classValues.length;
29118
+ numValues = values.length;
28989
29119
  }
28990
- if ((classValues.length != numValues || opts.stops) && numValues > 1) {
29120
+ if ((values.length != numValues || opts.stops) && numValues > 1) {
28991
29121
  // TODO: handle numValues == 1
28992
29122
  // TODO: check for non-interpolatable value types (e.g. boolean, text)
28993
- classValues = interpolateValuesToClasses(classValues, numValues, opts.stops);
29123
+ values = interpolateValuesToClasses(values, numValues, opts.stops);
28994
29124
  }
28995
29125
 
28996
29126
  } else if (numValues > 1) {
28997
29127
  // no values were given: assign indexes for each class
28998
- classValues = getIndexValues(numValues);
29128
+ values = getIndexValues(numValues);
28999
29129
  nullValue = -1;
29000
29130
  }
29001
29131
 
29002
- if (looksLikeColors) {
29132
+ if (valuesAreColors) {
29003
29133
  nullValue = nullValue || '#eee';
29004
29134
  }
29005
29135
 
@@ -29008,24 +29138,24 @@ ${svg}
29008
29138
  }
29009
29139
 
29010
29140
  if (opts.invert) {
29011
- classValues = classValues.concat().reverse();
29141
+ values = values.concat().reverse();
29012
29142
  }
29013
29143
 
29014
- if (looksLikeColors) {
29015
- message('Colors:', formatValuesForLogging(classValues));
29144
+ if (valuesAreColors) {
29145
+ message('Colors:', formatValuesForLogging(values));
29016
29146
  }
29017
29147
 
29018
29148
  // get a function to convert input data to class indexes
29019
29149
  //
29020
- if (opts.method == 'non-adjacent') {
29021
- classifyById = getNonAdjacentClassifier(lyr, dataset, classValues);
29150
+ if (method == 'non-adjacent') {
29151
+ classifyById = getNonAdjacentClassifier(lyr, dataset, values);
29022
29152
  } else if (opts.index_field) {
29023
29153
  // data is pre-classified... just read the index from a field
29024
- classifyByValue = getIndexedClassifier(classValues, nullValue, opts);
29025
- } else if (opts.categories) {
29026
- classifyByValue = getCategoricalClassifier(classValues, nullValue, opts);
29154
+ classifyByValue = getIndexedClassifier(values, nullValue, opts);
29155
+ } else if (method == 'categorical') {
29156
+ classifyByValue = getCategoricalClassifier(values, nullValue, opts);
29027
29157
  } else {
29028
- classifyByValue = getSequentialClassifier$1(classValues, nullValue, getFieldValues(records, dataField), opts);
29158
+ classifyByValue = getSequentialClassifier$1(values, nullValue, getFieldValues(records, dataField), method, opts);
29029
29159
  }
29030
29160
 
29031
29161
  if (classifyByValue) {
@@ -29038,7 +29168,7 @@ ${svg}
29038
29168
 
29039
29169
  // get the name of the output field
29040
29170
  //
29041
- if (looksLikeColors) {
29171
+ if (valuesAreColors) {
29042
29172
  outputField = lyr.geometry_type == 'polyline' ? 'stroke' : 'fill';
29043
29173
  } else {
29044
29174
  outputField = 'class';
@@ -34925,16 +35055,7 @@ ${svg}
34925
35055
  return findVertexIds(p2.x, p2.y, arcs);
34926
35056
  }
34927
35057
 
34928
- // Given a location @p (e.g. corresponding to the mouse pointer location),
34929
- // find the midpoint of two vertices on @shp suitable for inserting a new vertex,
34930
- // but only if:
34931
- // 1. point @p is closer to the midpoint than either adjacent vertex
34932
- // 2. the segment containing @p is longer than a minimum distance in pixels.
34933
- //
34934
- function findInsertionPoint(p, shp, arcs, pixelSize) {
34935
- var p2 = findNearestVertex(p[0], p[1], shp, arcs);
34936
35058
 
34937
- }
34938
35059
 
34939
35060
  function snapVerticesToPoint(ids, p, arcs, final) {
34940
35061
  ids.forEach(function(idx) {
@@ -35015,7 +35136,7 @@ ${svg}
35015
35136
  function findNearestVertex(x, y, shp, arcs, spherical) {
35016
35137
  var calcLen = spherical ? geom.greatCircleDistance : geom.distance2D,
35017
35138
  minLen = Infinity,
35018
- minX, minY, dist, iter;
35139
+ minX, minY, vId, dist, iter;
35019
35140
  for (var i=0; i<shp.length; i++) {
35020
35141
  iter = arcs.getShapeIter(shp[i]);
35021
35142
  while (iter.hasNext()) {
@@ -35024,16 +35145,31 @@ ${svg}
35024
35145
  minLen = dist;
35025
35146
  minX = iter.x;
35026
35147
  minY = iter.y;
35148
+ vId = iter.i;
35027
35149
  }
35028
35150
  }
35029
35151
  }
35030
- return minLen < Infinity ? {x: minX, y: minY} : null;
35152
+ return minLen < Infinity ? {x: minX, y: minY, i: vId} : null;
35153
+ }
35154
+
35155
+ // v: vertex in {x, y, i} format
35156
+ function findAdjacentVertex(v, shp, arcs, offs) {
35157
+ var p, i;
35158
+ var arcEnd = offs == 1 && vertexIsArcEnd(v.i, arcs);
35159
+ var arcStart = offs == -1 && vertexIsArcStart(v.i, arcs);
35160
+ if (arcEnd || arcStart) return null;
35161
+ i = v.i + offs;
35162
+ p = getVertexCoords(i, arcs);
35163
+ return {
35164
+ i: i,
35165
+ x: p[0],
35166
+ y: p[1]
35167
+ };
35031
35168
  }
35032
35169
 
35033
35170
  var VertexUtils = /*#__PURE__*/Object.freeze({
35034
35171
  __proto__: null,
35035
35172
  findNearestVertices: findNearestVertices,
35036
- findInsertionPoint: findInsertionPoint,
35037
35173
  snapVerticesToPoint: snapVerticesToPoint,
35038
35174
  snapPointToArcEndpoint: snapPointToArcEndpoint,
35039
35175
  findVertexIds: findVertexIds,
@@ -35041,7 +35177,8 @@ ${svg}
35041
35177
  vertexIsArcEnd: vertexIsArcEnd,
35042
35178
  vertexIsArcStart: vertexIsArcStart,
35043
35179
  setVertexCoords: setVertexCoords,
35044
- findNearestVertex: findNearestVertex
35180
+ findNearestVertex: findNearestVertex,
35181
+ findAdjacentVertex: findAdjacentVertex
35045
35182
  });
35046
35183
 
35047
35184
  // Returns x,y coordinates of the point that is at the midpoint of each polyline feature
@@ -40226,6 +40363,7 @@ ${svg}
40226
40363
  AnchorPoints,
40227
40364
  ArcClassifier,
40228
40365
  ArcDissolve,
40366
+ ArcUtils,
40229
40367
  Bbox2Clipping,
40230
40368
  BinArray$1,
40231
40369
  BufferCommon,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.5.91",
3
+ "version": "0.5.94",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -40,7 +40,7 @@
40
40
  "!.DS_Store"
41
41
  ],
42
42
  "dependencies": {
43
- "commander": "^5.1.0",
43
+ "commander": "7.0.0",
44
44
  "cookies": "^0.8.0",
45
45
  "d3-color": "2.0.0",
46
46
  "d3-scale-chromatic": "2.0.0",
package/www/index.html CHANGED
@@ -201,7 +201,7 @@ a smoother appearance.</div></div></div></div>
201
201
 
202
202
  <div id="splash-screen" class="main-area">
203
203
  <div>
204
- <h3>Mapshaper is an editor for map data</h3>
204
+ <h3 id="splash-screen-blurb">Mapshaper is an editor for map data</h3>
205
205
  </div>
206
206
  <div id="drop-areas" class="drop-area-wrapper main-area">
207
207
 
package/www/manifest.js CHANGED
@@ -1 +1,7 @@
1
- /* replaced by a file manifest by mapshaper-gui server */
1
+ /* This file can contain files and import options; see mapshaper-gui; example:
2
+ mapshaper.manifest = {
3
+ files: [],
4
+ quick_view: true,
5
+ commands: ""
6
+ };
7
+ */