mapshaper 0.7.20 → 0.7.22

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
@@ -14,6 +14,7 @@ var defaultPort = 5555,
14
14
  .option('-n, --name <name(s)>', 'rename input layer or layers')
15
15
  .option('-c, --commands <string>', 'console commands to run initially')
16
16
  .option('-t, --target <name>', 'name of layer to select initially')
17
+ .option('-w, --watch', 'watch input files and reload the browser on change')
17
18
  .addOption(new commander.Option('-b, --blurb <text>',
18
19
  'replace the default blurb on the import screen').hideHelp())
19
20
  .argument('[file...]', 'data files to load')
@@ -30,9 +31,26 @@ var defaultPort = 5555,
30
31
  port = parseInt(opts.port, 10) || defaultPort,
31
32
  dataFiles = expandShapefiles(program.args),
32
33
  probeCount = 0,
33
- sessionId = null;
34
+ sessionId = null,
35
+ sseClients = [],
36
+ reloadTimer = null;
34
37
  validateFiles(dataFiles);
35
38
 
39
+ if (opts.watch && dataFiles.length > 0) {
40
+ dataFiles.forEach(function(file) {
41
+ if (isUrl(file)) return;
42
+ fs.watch(file, function() {
43
+ // debounce: editors often write files in multiple steps
44
+ clearTimeout(reloadTimer);
45
+ reloadTimer = setTimeout(function() {
46
+ sseClients.forEach(function(res) {
47
+ try { res.write('event: reload\ndata: changed\n\n'); } catch(e) {}
48
+ });
49
+ }, 150);
50
+ });
51
+ });
52
+ }
53
+
36
54
  process.on('uncaughtException', function(err) {
37
55
  // added 'code' for Node.js v16
38
56
  if (err.errno === 'EADDRINUSE' || err.code === 'EADDRINUSE') {
@@ -99,6 +117,18 @@ function startServer(port) {
99
117
  serveFile(getDataFilePath(uri), response);
100
118
  } else if (uri.indexOf('/save') === 0) {
101
119
  saveContent(request, response);
120
+ } else if (uri == '/sse' && opts.watch) {
121
+ // Server-Sent Events endpoint for file-watch reload notifications
122
+ response.writeHead(200, {
123
+ 'Content-Type': 'text/event-stream',
124
+ 'Cache-Control': 'no-cache',
125
+ 'Connection': 'keep-alive'
126
+ });
127
+ response.write('data: connected\n\n');
128
+ sseClients.push(response);
129
+ request.on('close', function() {
130
+ sseClients = sseClients.filter(function(c) { return c !== response; });
131
+ });
102
132
  } else {
103
133
  // serve a file from the web root. Translate directory paths (any URI
104
134
  // ending in '/') to their index.html so that links like '/docs/' work
@@ -106,7 +136,21 @@ function startServer(port) {
106
136
  if (uri.endsWith('/')) {
107
137
  uri += 'index.html';
108
138
  }
109
- serveFile(getAssetFilePath(uri), response);
139
+ if (opts.watch && uri == '/index.html') {
140
+ // inject SSE reload script into index.html
141
+ var htmlPath = getAssetFilePath('/index.html');
142
+ fs.readFile(htmlPath, 'utf8', function(err, content) {
143
+ if (err) return serve404(response);
144
+ var script = '<script>(function(){' +
145
+ 'var es=new EventSource("/sse");' +
146
+ 'es.addEventListener("reload",function(){location.reload();});' +
147
+ '})();</script>';
148
+ content = content.replace('</body>', script + '\n</body>');
149
+ serveContent(content, response, 'text/html');
150
+ });
151
+ } else {
152
+ serveFile(getAssetFilePath(uri), response);
153
+ }
110
154
  }
111
155
  }).listen(port, 'localhost', function() {
112
156
  if (process.env.MAPSHAPER_GUI_NO_OPEN) return;
@@ -165,7 +209,7 @@ function serveContent(content, response, mimeType) {
165
209
  }
166
210
  response.setHeader('Cache-Control', 'no-cache');
167
211
  response.writeHead(200);
168
- response.write(content, "binary");
212
+ response.write(content, Buffer.isBuffer(content) ? undefined : "utf8");
169
213
  response.end();
170
214
  }
171
215
 
package/mapshaper.js CHANGED
@@ -7417,7 +7417,7 @@
7417
7417
  i = fw ? arcId : ~arcId,
7418
7418
  iter = _zz && _zlimit ? _filteredArcIter : _arcIter;
7419
7419
  if (i >= _nn.length) {
7420
- error("#getArcId() out-of-range arc id:", arcId);
7420
+ error("#getArcId() out-of-range arc id:", arcId, `(max id: ${_nn.length - 1})`);
7421
7421
  }
7422
7422
  return iter.init(_ii[i], _nn[i], fw, _zlimit);
7423
7423
  };
@@ -17446,9 +17446,14 @@
17446
17446
  var layers = layersArg.filter(getFeatureCount); // ignore empty layers
17447
17447
  var merged = {};
17448
17448
  opts = opts || {};
17449
+ if (!layers.length && layersArg.length > 1) {
17450
+ layers = layersArg; // merge all-empty layer sets
17451
+ }
17449
17452
  if (!layers.length) return null;
17450
17453
  if (layers.length == 1) {
17451
- message('Use the target= option to specify multiple layers for merging');
17454
+ if (layersArg.length == 1) {
17455
+ message('Use the target= option to specify multiple layers for merging');
17456
+ }
17452
17457
  return layers.concat();
17453
17458
  }
17454
17459
  merged.data = mergeDataFromLayers(layers, opts);
@@ -34262,7 +34267,8 @@ ${svg}
34262
34267
 
34263
34268
  var EOF; // undefined is used as an EOF marker
34264
34269
 
34265
- var RESERVE = 4096; // RESERVE is the number of bytes to keep in read buffer
34270
+ // Default number of bytes to keep in the read buffer.
34271
+ var RESERVE_DEFAULT = 4096;
34266
34272
  var BUFLEN = 1e7; // buffer chunk size
34267
34273
  var MAX_STRLEN = 5e6; // max byte len of a value string (object keys are shorter)
34268
34274
 
@@ -34295,12 +34301,10 @@ ${svg}
34295
34301
  function parseGeoJSON(reader, cb) {
34296
34302
  var src = ByteReader(reader, 0);
34297
34303
  var isObject = seekObjectStart(src);
34298
- var type;
34299
34304
  if (!isObject) {
34300
34305
  stop$1('File is not GeoJSON');
34301
34306
  }
34302
- type = readTopLevelType(src);
34303
- var obj = readObject(src, cb, type);
34307
+ var obj = readObject(src, cb);
34304
34308
  if (obj.type == 'FeatureCollection' || obj.type == 'GeometryCollection') {
34305
34309
  return obj;
34306
34310
  }
@@ -34349,8 +34353,9 @@ ${svg}
34349
34353
 
34350
34354
  function seekObjectStart(src) {
34351
34355
  var c = src.getChar();
34356
+ var reserve = src.getReserve();
34352
34357
  var i = 0;
34353
- while (c != EOF && i < RESERVE) {
34358
+ while (c != EOF && i < reserve) {
34354
34359
  i++;
34355
34360
  if (c == LBRACE) {
34356
34361
  src.back();
@@ -34381,17 +34386,6 @@ ${svg}
34381
34386
  return arr;
34382
34387
  }
34383
34388
 
34384
- function skipArray(src) {
34385
- var c;
34386
- eatChar(src, LBRACK);
34387
- c = scanForSyntaxChar(src, RBRACK);
34388
- while (c != RBRACK) {
34389
- src.refresh();
34390
- skipValue(src);
34391
- c = scanForAorB(src, COMMA, RBRACK);
34392
- }
34393
- }
34394
-
34395
34389
  function readCollectionArray(src, cb) {
34396
34390
  var c;
34397
34391
  eatChar(src, LBRACK);
@@ -34480,18 +34474,6 @@ ${svg}
34480
34474
  return val;
34481
34475
  }
34482
34476
 
34483
- function skipValue(src) {
34484
- var c = src.peek();
34485
- if (isFirstNumChar(c)) readNumber(src);
34486
- else if (c == LBRACK) skipArray(src);
34487
- else if (c == DQUOTE) readString(src);
34488
- else if (c == LBRACE) skipObject(src);
34489
- else if (c == 110) readNull(src); // "n" -> null
34490
- else if (c == 116) readTrue(src); // "t" -> true
34491
- else if (c == 102) readFalse(src); // "f" -> false
34492
- else unexpectedCharAt(c, src.index());
34493
- }
34494
-
34495
34477
  function readTrue(src) {
34496
34478
  eatChars(src, 'true');
34497
34479
  return true;
@@ -34518,7 +34500,7 @@ ${svg}
34518
34500
 
34519
34501
  // cb: optional callback for returning GeoJSON features or geometries
34520
34502
  //
34521
- function readObject(src, cb, type) {
34503
+ function readObject(src, cb) {
34522
34504
  var o = {};
34523
34505
  var key, c;
34524
34506
  eatChar(src, LBRACE);
@@ -34529,8 +34511,7 @@ ${svg}
34529
34511
  skipWS(src);
34530
34512
  eatChar(src, 58); // ":"
34531
34513
  skipWS(src);
34532
- if ((type == 'FeatureCollection' && key == 'features' ||
34533
- type == 'GeometryCollection' && key == 'geometries') &&
34514
+ if ((key == 'features' || key == 'geometries') &&
34534
34515
  src.peek() == LBRACK && cb) {
34535
34516
  readCollectionArray(src, cb);
34536
34517
  o[key] = null;
@@ -34544,53 +34525,11 @@ ${svg}
34544
34525
  return o;
34545
34526
  }
34546
34527
 
34547
- function skipObject(src) {
34548
- var c;
34549
- eatChar(src, LBRACE);
34550
- c = scanForSyntaxChar(src, RBRACE);
34551
- while (c != RBRACE) {
34552
- src.refresh();
34553
- readKeywordString(src);
34554
- skipWS(src);
34555
- eatChar(src, 58); // ":"
34556
- skipWS(src);
34557
- skipValue(src);
34558
- c = scanForAorB(src, COMMA, RBRACE);
34559
- }
34560
- }
34561
-
34562
- function readTopLevelType(src) {
34563
- var i = src.index();
34564
- var type, key, c;
34565
- eatChar(src, LBRACE);
34566
- c = scanForSyntaxChar(src, RBRACE);
34567
- while (c != RBRACE) {
34568
- src.refresh();
34569
- key = readKeywordString(src);
34570
- skipWS(src);
34571
- eatChar(src, 58); // ":"
34572
- skipWS(src);
34573
- if (key == 'type' && src.peek() == DQUOTE) {
34574
- type = readKeywordString(src);
34575
- break;
34576
- }
34577
- skipValue(src);
34578
- c = scanForAorB(src, COMMA, RBRACE);
34579
- }
34580
- src.index(i);
34581
- return type;
34582
- }
34583
-
34584
- function growReserve() {
34585
- RESERVE *= 2;
34586
- return RESERVE <= MAX_STRLEN;
34587
- }
34588
-
34589
34528
  // Uses caching to speed up parsing of repeated strings.
34590
34529
  // The caching scheme used here can give a 20% overall speed improvement
34591
34530
  // when parsing files consisting mostly of attribute data (e.g. typical Point features)
34592
34531
  function readKeywordString(src) {
34593
- var MAXLEN = 2000; // must be less than RESERVE
34532
+ var MAXLEN = 2000; // must be less than RESERVE_DEFAULT
34594
34533
  var i = src.index();
34595
34534
  var cache = src.cache;
34596
34535
  var escapeNext = false;
@@ -34648,7 +34587,7 @@ ${svg}
34648
34587
  // Fallback for reading long strings, escaped strings, non-ascii strings, etc.
34649
34588
  function readString_slow(src) {
34650
34589
  src.refresh();
34651
- var LIMIT = RESERVE - 2;
34590
+ var LIMIT = src.getReserve() - 2;
34652
34591
  var i = src.index();
34653
34592
  var n = 0;
34654
34593
  var escapeNext = false;
@@ -34660,7 +34599,7 @@ ${svg}
34660
34599
  if (n > LIMIT) {
34661
34600
  // we've exceeded the number of reserved bytes
34662
34601
  // expand the limit and try reading this string again
34663
- if (c == EOF || !growReserve()) {
34602
+ if (c == EOF || !src.growReserve()) {
34664
34603
  stringOverflow(i, c);
34665
34604
  }
34666
34605
  src.index(i);
@@ -34778,9 +34717,10 @@ ${svg}
34778
34717
  function ByteReader(reader, start) {
34779
34718
  var fileLen = reader.size();
34780
34719
  var bufOffs = start;
34720
+ var reserve = RESERVE_DEFAULT; // per-instance reserve; may grow (see growReserve)
34781
34721
  var buf = reader.readSync(bufOffs, BUFLEN);
34782
34722
  var i = 0;
34783
- var obj = { peek, getChar, advance, back, toString, index, refresh };
34723
+ var obj = { peek, getChar, advance, back, toString, index, refresh, getReserve, growReserve };
34784
34724
  obj.cache = []; // kludgy place to put the key cache
34785
34725
  refresh();
34786
34726
  return obj;
@@ -34788,14 +34728,14 @@ ${svg}
34788
34728
  // This function should be called to make sure that the buffer has enough
34789
34729
  // bytes remaining to read any reasonable JSON content.
34790
34730
  function refresh() {
34791
- // if RESERVE bytes are still available in the buffer, no update is required
34792
- if (buf.length - i >= RESERVE) return;
34731
+ // if reserve bytes are still available in the buffer, no update is required
34732
+ if (buf.length - i >= reserve) return;
34793
34733
 
34794
34734
  // CHANGE: now using undefined as an EOF marker, so a bounds check is unneeded
34795
34735
  // // if we're close to the end of the file, start checking for overflow
34796
34736
  // // (we don't do this all the time because the bounds check on every read
34797
34737
  // // causes a significant slowdown, as much as 20%)
34798
- // if (fileLen - (bufOffs + i) < RESERVE) {
34738
+ // if (fileLen - (bufOffs + i) < reserve) {
34799
34739
  // obj.peek = safePeek;
34800
34740
  // obj.getChar = safeGetChar;
34801
34741
  // }
@@ -34803,11 +34743,18 @@ ${svg}
34803
34743
  // if buffer reaches the end of the file, no update is required
34804
34744
  if (bufOffs + buf.length >= fileLen) return;
34805
34745
 
34806
- // fewer than RESERVE bytes are unread in buffer -- update the buffer
34746
+ // fewer than reserve bytes are unread in buffer -- update the buffer
34807
34747
  bufOffs += i;
34808
34748
  i = 0;
34809
34749
  buf = reader.readSync(bufOffs, BUFLEN);
34810
34750
  }
34751
+ function getReserve() {
34752
+ return reserve;
34753
+ }
34754
+ function growReserve() {
34755
+ reserve *= 2;
34756
+ return reserve <= MAX_STRLEN;
34757
+ }
34811
34758
  function peek() {
34812
34759
  return buf[i];
34813
34760
  }
@@ -34822,6 +34769,9 @@ ${svg}
34822
34769
  }
34823
34770
  function index(idx) {
34824
34771
  if (idx >= 0 === false) return i + bufOffs;
34772
+ if (idx < bufOffs || idx > bufOffs + buf.length) {
34773
+ error('JSON parser seek out of buffer range');
34774
+ }
34825
34775
  i = idx - bufOffs;
34826
34776
  }
34827
34777
  function toString(idx, n) {
@@ -45286,23 +45236,23 @@ ${svg}
45286
45236
  profileEnd('clipLayers');
45287
45237
  return result;
45288
45238
  }
45239
+ if (!usingPathClip) {
45240
+ result = clipLayersByClipDataset(targetLayers, clipDataset, type, opts);
45241
+ profileEnd('clipLayers');
45242
+ return result;
45243
+ }
45289
45244
  profileStart('mergeLayersForOverlay');
45290
45245
  mergedDataset = mergeLayersForOverlay2(targetLayers, targetDataset, clipDataset);
45291
45246
  profileEnd('mergeLayersForOverlay');
45292
45247
  clipLyr = mergedDataset.layers[mergedDataset.layers.length-1];
45293
- if (usingPathClip) {
45294
- nodes = addIntersectionCuts(mergedDataset, opts);
45295
- noteDatasetWillChange(targetDataset, {operation: type, unit: 'arcs'});
45296
- targetDataset.arcs = mergedDataset.arcs;
45297
- markDatasetChanged(targetDataset, {operation: type, unit: 'arcs'});
45298
- profileStart('clipDissolvePolygonLayer2');
45299
- clipLyr = utils.defaults({data: null}, clipLyr);
45300
- clipLyr = dissolvePolygonLayer2(clipLyr, mergedDataset, {quiet: true, silent: true});
45301
- profileEnd('clipDissolvePolygonLayer2');
45302
-
45303
- } else {
45304
- nodes = new NodeCollection(mergedDataset.arcs);
45305
- }
45248
+ nodes = addIntersectionCuts(mergedDataset, opts);
45249
+ noteDatasetWillChange(targetDataset, {operation: type, unit: 'arcs'});
45250
+ targetDataset.arcs = mergedDataset.arcs;
45251
+ markDatasetChanged(targetDataset, {operation: type, unit: 'arcs'});
45252
+ profileStart('clipDissolvePolygonLayer2');
45253
+ clipLyr = utils.defaults({data: null}, clipLyr);
45254
+ clipLyr = dissolvePolygonLayer2(clipLyr, mergedDataset, {quiet: true, silent: true});
45255
+ profileEnd('clipDissolvePolygonLayer2');
45306
45256
 
45307
45257
  profileStart('clipLayersByLayer');
45308
45258
  result = clipLayersByLayer(targetLayers, clipLyr, nodes, type, opts);
@@ -45311,6 +45261,16 @@ ${svg}
45311
45261
  return result;
45312
45262
  }
45313
45263
 
45264
+ function clipLayersByClipDataset(targetLayers, clipDataset, type, opts) {
45265
+ var clipLyr = clipDataset.layers[0];
45266
+ var nodes = new NodeCollection(clipDataset.arcs);
45267
+ var result;
45268
+ profileStart('clipLayersByLayer');
45269
+ result = clipLayersByLayer(targetLayers, clipLyr, nodes, type, opts);
45270
+ profileEnd('clipLayersByLayer');
45271
+ return result;
45272
+ }
45273
+
45314
45274
  function clipRasterLayers(targetLayers, clipSrc, targetDataset, type, opts) {
45315
45275
  var clipDataset, clipBounds, bbox;
45316
45276
  if (type != 'clip') {
@@ -58299,8 +58259,7 @@ ${svg}
58299
58259
  // TODO: consider replacing old layers as they are generated, for gc
58300
58260
  replaceLayers(targetDataset, targetLayers, outputLayers);
58301
58261
  // some operations leave unreferenced arcs that should be cleaned up
58302
- if ((name == 'clip' || name == 'erase' || name == 'rectangle' ||
58303
- name == 'rectangles' || name == 'filter' && opts.cleanup) && !opts.no_cleanup) {
58262
+ if (commandNeedsArcDissolve(name, targetLayers, opts)) {
58304
58263
  dissolveArcs(targetDataset);
58305
58264
  }
58306
58265
  }
@@ -58347,7 +58306,15 @@ ${svg}
58347
58306
  });
58348
58307
  }
58349
58308
 
58350
- var version = "0.7.20";
58309
+ function commandNeedsArcDissolve(name, targetLayers, opts) {
58310
+ if (opts.no_cleanup) return false;
58311
+ if (name == 'clip' || name == 'erase') {
58312
+ return utils.some(targetLayers || [], layerHasPaths);
58313
+ }
58314
+ return name == 'rectangle' || name == 'rectangles' || name == 'filter' && opts.cleanup;
58315
+ }
58316
+
58317
+ var version = "0.7.22";
58351
58318
 
58352
58319
  // Parse command line args into commands and run them
58353
58320
  // 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.7.20",
3
+ "version": "0.7.22",
4
4
  "description": "A tool for editing geospatial data for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -17024,6 +17024,9 @@
17024
17024
  }
17025
17025
 
17026
17026
  function selectStyleFeature(id, e) {
17027
+ if (selectionIds.includes(id)) {
17028
+ return utils$1.difference(selectionIds, [id]);
17029
+ }
17027
17030
  return eventUsesAdditiveSelection(e) ?
17028
17031
  toggleId(id, selectionIds) :
17029
17032
  [id];
package/www/mapshaper.js CHANGED
@@ -7417,7 +7417,7 @@
7417
7417
  i = fw ? arcId : ~arcId,
7418
7418
  iter = _zz && _zlimit ? _filteredArcIter : _arcIter;
7419
7419
  if (i >= _nn.length) {
7420
- error("#getArcId() out-of-range arc id:", arcId);
7420
+ error("#getArcId() out-of-range arc id:", arcId, `(max id: ${_nn.length - 1})`);
7421
7421
  }
7422
7422
  return iter.init(_ii[i], _nn[i], fw, _zlimit);
7423
7423
  };
@@ -17446,9 +17446,14 @@
17446
17446
  var layers = layersArg.filter(getFeatureCount); // ignore empty layers
17447
17447
  var merged = {};
17448
17448
  opts = opts || {};
17449
+ if (!layers.length && layersArg.length > 1) {
17450
+ layers = layersArg; // merge all-empty layer sets
17451
+ }
17449
17452
  if (!layers.length) return null;
17450
17453
  if (layers.length == 1) {
17451
- message('Use the target= option to specify multiple layers for merging');
17454
+ if (layersArg.length == 1) {
17455
+ message('Use the target= option to specify multiple layers for merging');
17456
+ }
17452
17457
  return layers.concat();
17453
17458
  }
17454
17459
  merged.data = mergeDataFromLayers(layers, opts);
@@ -34262,7 +34267,8 @@ ${svg}
34262
34267
 
34263
34268
  var EOF; // undefined is used as an EOF marker
34264
34269
 
34265
- var RESERVE = 4096; // RESERVE is the number of bytes to keep in read buffer
34270
+ // Default number of bytes to keep in the read buffer.
34271
+ var RESERVE_DEFAULT = 4096;
34266
34272
  var BUFLEN = 1e7; // buffer chunk size
34267
34273
  var MAX_STRLEN = 5e6; // max byte len of a value string (object keys are shorter)
34268
34274
 
@@ -34295,12 +34301,10 @@ ${svg}
34295
34301
  function parseGeoJSON(reader, cb) {
34296
34302
  var src = ByteReader(reader, 0);
34297
34303
  var isObject = seekObjectStart(src);
34298
- var type;
34299
34304
  if (!isObject) {
34300
34305
  stop$1('File is not GeoJSON');
34301
34306
  }
34302
- type = readTopLevelType(src);
34303
- var obj = readObject(src, cb, type);
34307
+ var obj = readObject(src, cb);
34304
34308
  if (obj.type == 'FeatureCollection' || obj.type == 'GeometryCollection') {
34305
34309
  return obj;
34306
34310
  }
@@ -34349,8 +34353,9 @@ ${svg}
34349
34353
 
34350
34354
  function seekObjectStart(src) {
34351
34355
  var c = src.getChar();
34356
+ var reserve = src.getReserve();
34352
34357
  var i = 0;
34353
- while (c != EOF && i < RESERVE) {
34358
+ while (c != EOF && i < reserve) {
34354
34359
  i++;
34355
34360
  if (c == LBRACE) {
34356
34361
  src.back();
@@ -34381,17 +34386,6 @@ ${svg}
34381
34386
  return arr;
34382
34387
  }
34383
34388
 
34384
- function skipArray(src) {
34385
- var c;
34386
- eatChar(src, LBRACK);
34387
- c = scanForSyntaxChar(src, RBRACK);
34388
- while (c != RBRACK) {
34389
- src.refresh();
34390
- skipValue(src);
34391
- c = scanForAorB(src, COMMA, RBRACK);
34392
- }
34393
- }
34394
-
34395
34389
  function readCollectionArray(src, cb) {
34396
34390
  var c;
34397
34391
  eatChar(src, LBRACK);
@@ -34480,18 +34474,6 @@ ${svg}
34480
34474
  return val;
34481
34475
  }
34482
34476
 
34483
- function skipValue(src) {
34484
- var c = src.peek();
34485
- if (isFirstNumChar(c)) readNumber(src);
34486
- else if (c == LBRACK) skipArray(src);
34487
- else if (c == DQUOTE) readString(src);
34488
- else if (c == LBRACE) skipObject(src);
34489
- else if (c == 110) readNull(src); // "n" -> null
34490
- else if (c == 116) readTrue(src); // "t" -> true
34491
- else if (c == 102) readFalse(src); // "f" -> false
34492
- else unexpectedCharAt(c, src.index());
34493
- }
34494
-
34495
34477
  function readTrue(src) {
34496
34478
  eatChars(src, 'true');
34497
34479
  return true;
@@ -34518,7 +34500,7 @@ ${svg}
34518
34500
 
34519
34501
  // cb: optional callback for returning GeoJSON features or geometries
34520
34502
  //
34521
- function readObject(src, cb, type) {
34503
+ function readObject(src, cb) {
34522
34504
  var o = {};
34523
34505
  var key, c;
34524
34506
  eatChar(src, LBRACE);
@@ -34529,8 +34511,7 @@ ${svg}
34529
34511
  skipWS(src);
34530
34512
  eatChar(src, 58); // ":"
34531
34513
  skipWS(src);
34532
- if ((type == 'FeatureCollection' && key == 'features' ||
34533
- type == 'GeometryCollection' && key == 'geometries') &&
34514
+ if ((key == 'features' || key == 'geometries') &&
34534
34515
  src.peek() == LBRACK && cb) {
34535
34516
  readCollectionArray(src, cb);
34536
34517
  o[key] = null;
@@ -34544,53 +34525,11 @@ ${svg}
34544
34525
  return o;
34545
34526
  }
34546
34527
 
34547
- function skipObject(src) {
34548
- var c;
34549
- eatChar(src, LBRACE);
34550
- c = scanForSyntaxChar(src, RBRACE);
34551
- while (c != RBRACE) {
34552
- src.refresh();
34553
- readKeywordString(src);
34554
- skipWS(src);
34555
- eatChar(src, 58); // ":"
34556
- skipWS(src);
34557
- skipValue(src);
34558
- c = scanForAorB(src, COMMA, RBRACE);
34559
- }
34560
- }
34561
-
34562
- function readTopLevelType(src) {
34563
- var i = src.index();
34564
- var type, key, c;
34565
- eatChar(src, LBRACE);
34566
- c = scanForSyntaxChar(src, RBRACE);
34567
- while (c != RBRACE) {
34568
- src.refresh();
34569
- key = readKeywordString(src);
34570
- skipWS(src);
34571
- eatChar(src, 58); // ":"
34572
- skipWS(src);
34573
- if (key == 'type' && src.peek() == DQUOTE) {
34574
- type = readKeywordString(src);
34575
- break;
34576
- }
34577
- skipValue(src);
34578
- c = scanForAorB(src, COMMA, RBRACE);
34579
- }
34580
- src.index(i);
34581
- return type;
34582
- }
34583
-
34584
- function growReserve() {
34585
- RESERVE *= 2;
34586
- return RESERVE <= MAX_STRLEN;
34587
- }
34588
-
34589
34528
  // Uses caching to speed up parsing of repeated strings.
34590
34529
  // The caching scheme used here can give a 20% overall speed improvement
34591
34530
  // when parsing files consisting mostly of attribute data (e.g. typical Point features)
34592
34531
  function readKeywordString(src) {
34593
- var MAXLEN = 2000; // must be less than RESERVE
34532
+ var MAXLEN = 2000; // must be less than RESERVE_DEFAULT
34594
34533
  var i = src.index();
34595
34534
  var cache = src.cache;
34596
34535
  var escapeNext = false;
@@ -34648,7 +34587,7 @@ ${svg}
34648
34587
  // Fallback for reading long strings, escaped strings, non-ascii strings, etc.
34649
34588
  function readString_slow(src) {
34650
34589
  src.refresh();
34651
- var LIMIT = RESERVE - 2;
34590
+ var LIMIT = src.getReserve() - 2;
34652
34591
  var i = src.index();
34653
34592
  var n = 0;
34654
34593
  var escapeNext = false;
@@ -34660,7 +34599,7 @@ ${svg}
34660
34599
  if (n > LIMIT) {
34661
34600
  // we've exceeded the number of reserved bytes
34662
34601
  // expand the limit and try reading this string again
34663
- if (c == EOF || !growReserve()) {
34602
+ if (c == EOF || !src.growReserve()) {
34664
34603
  stringOverflow(i, c);
34665
34604
  }
34666
34605
  src.index(i);
@@ -34778,9 +34717,10 @@ ${svg}
34778
34717
  function ByteReader(reader, start) {
34779
34718
  var fileLen = reader.size();
34780
34719
  var bufOffs = start;
34720
+ var reserve = RESERVE_DEFAULT; // per-instance reserve; may grow (see growReserve)
34781
34721
  var buf = reader.readSync(bufOffs, BUFLEN);
34782
34722
  var i = 0;
34783
- var obj = { peek, getChar, advance, back, toString, index, refresh };
34723
+ var obj = { peek, getChar, advance, back, toString, index, refresh, getReserve, growReserve };
34784
34724
  obj.cache = []; // kludgy place to put the key cache
34785
34725
  refresh();
34786
34726
  return obj;
@@ -34788,14 +34728,14 @@ ${svg}
34788
34728
  // This function should be called to make sure that the buffer has enough
34789
34729
  // bytes remaining to read any reasonable JSON content.
34790
34730
  function refresh() {
34791
- // if RESERVE bytes are still available in the buffer, no update is required
34792
- if (buf.length - i >= RESERVE) return;
34731
+ // if reserve bytes are still available in the buffer, no update is required
34732
+ if (buf.length - i >= reserve) return;
34793
34733
 
34794
34734
  // CHANGE: now using undefined as an EOF marker, so a bounds check is unneeded
34795
34735
  // // if we're close to the end of the file, start checking for overflow
34796
34736
  // // (we don't do this all the time because the bounds check on every read
34797
34737
  // // causes a significant slowdown, as much as 20%)
34798
- // if (fileLen - (bufOffs + i) < RESERVE) {
34738
+ // if (fileLen - (bufOffs + i) < reserve) {
34799
34739
  // obj.peek = safePeek;
34800
34740
  // obj.getChar = safeGetChar;
34801
34741
  // }
@@ -34803,11 +34743,18 @@ ${svg}
34803
34743
  // if buffer reaches the end of the file, no update is required
34804
34744
  if (bufOffs + buf.length >= fileLen) return;
34805
34745
 
34806
- // fewer than RESERVE bytes are unread in buffer -- update the buffer
34746
+ // fewer than reserve bytes are unread in buffer -- update the buffer
34807
34747
  bufOffs += i;
34808
34748
  i = 0;
34809
34749
  buf = reader.readSync(bufOffs, BUFLEN);
34810
34750
  }
34751
+ function getReserve() {
34752
+ return reserve;
34753
+ }
34754
+ function growReserve() {
34755
+ reserve *= 2;
34756
+ return reserve <= MAX_STRLEN;
34757
+ }
34811
34758
  function peek() {
34812
34759
  return buf[i];
34813
34760
  }
@@ -34822,6 +34769,9 @@ ${svg}
34822
34769
  }
34823
34770
  function index(idx) {
34824
34771
  if (idx >= 0 === false) return i + bufOffs;
34772
+ if (idx < bufOffs || idx > bufOffs + buf.length) {
34773
+ error('JSON parser seek out of buffer range');
34774
+ }
34825
34775
  i = idx - bufOffs;
34826
34776
  }
34827
34777
  function toString(idx, n) {
@@ -45286,23 +45236,23 @@ ${svg}
45286
45236
  profileEnd('clipLayers');
45287
45237
  return result;
45288
45238
  }
45239
+ if (!usingPathClip) {
45240
+ result = clipLayersByClipDataset(targetLayers, clipDataset, type, opts);
45241
+ profileEnd('clipLayers');
45242
+ return result;
45243
+ }
45289
45244
  profileStart('mergeLayersForOverlay');
45290
45245
  mergedDataset = mergeLayersForOverlay2(targetLayers, targetDataset, clipDataset);
45291
45246
  profileEnd('mergeLayersForOverlay');
45292
45247
  clipLyr = mergedDataset.layers[mergedDataset.layers.length-1];
45293
- if (usingPathClip) {
45294
- nodes = addIntersectionCuts(mergedDataset, opts);
45295
- noteDatasetWillChange(targetDataset, {operation: type, unit: 'arcs'});
45296
- targetDataset.arcs = mergedDataset.arcs;
45297
- markDatasetChanged(targetDataset, {operation: type, unit: 'arcs'});
45298
- profileStart('clipDissolvePolygonLayer2');
45299
- clipLyr = utils.defaults({data: null}, clipLyr);
45300
- clipLyr = dissolvePolygonLayer2(clipLyr, mergedDataset, {quiet: true, silent: true});
45301
- profileEnd('clipDissolvePolygonLayer2');
45302
-
45303
- } else {
45304
- nodes = new NodeCollection(mergedDataset.arcs);
45305
- }
45248
+ nodes = addIntersectionCuts(mergedDataset, opts);
45249
+ noteDatasetWillChange(targetDataset, {operation: type, unit: 'arcs'});
45250
+ targetDataset.arcs = mergedDataset.arcs;
45251
+ markDatasetChanged(targetDataset, {operation: type, unit: 'arcs'});
45252
+ profileStart('clipDissolvePolygonLayer2');
45253
+ clipLyr = utils.defaults({data: null}, clipLyr);
45254
+ clipLyr = dissolvePolygonLayer2(clipLyr, mergedDataset, {quiet: true, silent: true});
45255
+ profileEnd('clipDissolvePolygonLayer2');
45306
45256
 
45307
45257
  profileStart('clipLayersByLayer');
45308
45258
  result = clipLayersByLayer(targetLayers, clipLyr, nodes, type, opts);
@@ -45311,6 +45261,16 @@ ${svg}
45311
45261
  return result;
45312
45262
  }
45313
45263
 
45264
+ function clipLayersByClipDataset(targetLayers, clipDataset, type, opts) {
45265
+ var clipLyr = clipDataset.layers[0];
45266
+ var nodes = new NodeCollection(clipDataset.arcs);
45267
+ var result;
45268
+ profileStart('clipLayersByLayer');
45269
+ result = clipLayersByLayer(targetLayers, clipLyr, nodes, type, opts);
45270
+ profileEnd('clipLayersByLayer');
45271
+ return result;
45272
+ }
45273
+
45314
45274
  function clipRasterLayers(targetLayers, clipSrc, targetDataset, type, opts) {
45315
45275
  var clipDataset, clipBounds, bbox;
45316
45276
  if (type != 'clip') {
@@ -58299,8 +58259,7 @@ ${svg}
58299
58259
  // TODO: consider replacing old layers as they are generated, for gc
58300
58260
  replaceLayers(targetDataset, targetLayers, outputLayers);
58301
58261
  // some operations leave unreferenced arcs that should be cleaned up
58302
- if ((name == 'clip' || name == 'erase' || name == 'rectangle' ||
58303
- name == 'rectangles' || name == 'filter' && opts.cleanup) && !opts.no_cleanup) {
58262
+ if (commandNeedsArcDissolve(name, targetLayers, opts)) {
58304
58263
  dissolveArcs(targetDataset);
58305
58264
  }
58306
58265
  }
@@ -58347,7 +58306,15 @@ ${svg}
58347
58306
  });
58348
58307
  }
58349
58308
 
58350
- var version = "0.7.20";
58309
+ function commandNeedsArcDissolve(name, targetLayers, opts) {
58310
+ if (opts.no_cleanup) return false;
58311
+ if (name == 'clip' || name == 'erase') {
58312
+ return utils.some(targetLayers || [], layerHasPaths);
58313
+ }
58314
+ return name == 'rectangle' || name == 'rectangles' || name == 'filter' && opts.cleanup;
58315
+ }
58316
+
58317
+ var version = "0.7.22";
58351
58318
 
58352
58319
  // Parse command line args into commands and run them
58353
58320
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.