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 +47 -3
- package/mapshaper.js +66 -99
- package/package.json +1 -1
- package/www/mapshaper-gui.js +3 -0
- package/www/mapshaper.js +66 -99
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
|
-
|
|
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, "
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 <
|
|
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
|
|
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 ((
|
|
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
|
|
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 =
|
|
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
|
|
34792
|
-
if (buf.length - i >=
|
|
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) <
|
|
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
|
|
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
|
-
|
|
45294
|
-
|
|
45295
|
-
|
|
45296
|
-
|
|
45297
|
-
|
|
45298
|
-
|
|
45299
|
-
|
|
45300
|
-
|
|
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
|
|
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
|
-
|
|
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
package/www/mapshaper-gui.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 <
|
|
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
|
|
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 ((
|
|
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
|
|
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 =
|
|
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
|
|
34792
|
-
if (buf.length - i >=
|
|
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) <
|
|
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
|
|
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
|
-
|
|
45294
|
-
|
|
45295
|
-
|
|
45296
|
-
|
|
45297
|
-
|
|
45298
|
-
|
|
45299
|
-
|
|
45300
|
-
|
|
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
|
|
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
|
-
|
|
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.
|