mapshaper 0.7.20 → 0.7.21
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 +40 -18
- package/package.json +1 -1
- package/www/mapshaper-gui.js +3 -0
- package/www/mapshaper.js +40 -18
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);
|
|
@@ -45286,23 +45291,23 @@ ${svg}
|
|
|
45286
45291
|
profileEnd('clipLayers');
|
|
45287
45292
|
return result;
|
|
45288
45293
|
}
|
|
45294
|
+
if (!usingPathClip) {
|
|
45295
|
+
result = clipLayersByClipDataset(targetLayers, clipDataset, type, opts);
|
|
45296
|
+
profileEnd('clipLayers');
|
|
45297
|
+
return result;
|
|
45298
|
+
}
|
|
45289
45299
|
profileStart('mergeLayersForOverlay');
|
|
45290
45300
|
mergedDataset = mergeLayersForOverlay2(targetLayers, targetDataset, clipDataset);
|
|
45291
45301
|
profileEnd('mergeLayersForOverlay');
|
|
45292
45302
|
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
|
-
}
|
|
45303
|
+
nodes = addIntersectionCuts(mergedDataset, opts);
|
|
45304
|
+
noteDatasetWillChange(targetDataset, {operation: type, unit: 'arcs'});
|
|
45305
|
+
targetDataset.arcs = mergedDataset.arcs;
|
|
45306
|
+
markDatasetChanged(targetDataset, {operation: type, unit: 'arcs'});
|
|
45307
|
+
profileStart('clipDissolvePolygonLayer2');
|
|
45308
|
+
clipLyr = utils.defaults({data: null}, clipLyr);
|
|
45309
|
+
clipLyr = dissolvePolygonLayer2(clipLyr, mergedDataset, {quiet: true, silent: true});
|
|
45310
|
+
profileEnd('clipDissolvePolygonLayer2');
|
|
45306
45311
|
|
|
45307
45312
|
profileStart('clipLayersByLayer');
|
|
45308
45313
|
result = clipLayersByLayer(targetLayers, clipLyr, nodes, type, opts);
|
|
@@ -45311,6 +45316,16 @@ ${svg}
|
|
|
45311
45316
|
return result;
|
|
45312
45317
|
}
|
|
45313
45318
|
|
|
45319
|
+
function clipLayersByClipDataset(targetLayers, clipDataset, type, opts) {
|
|
45320
|
+
var clipLyr = clipDataset.layers[0];
|
|
45321
|
+
var nodes = new NodeCollection(clipDataset.arcs);
|
|
45322
|
+
var result;
|
|
45323
|
+
profileStart('clipLayersByLayer');
|
|
45324
|
+
result = clipLayersByLayer(targetLayers, clipLyr, nodes, type, opts);
|
|
45325
|
+
profileEnd('clipLayersByLayer');
|
|
45326
|
+
return result;
|
|
45327
|
+
}
|
|
45328
|
+
|
|
45314
45329
|
function clipRasterLayers(targetLayers, clipSrc, targetDataset, type, opts) {
|
|
45315
45330
|
var clipDataset, clipBounds, bbox;
|
|
45316
45331
|
if (type != 'clip') {
|
|
@@ -58299,8 +58314,7 @@ ${svg}
|
|
|
58299
58314
|
// TODO: consider replacing old layers as they are generated, for gc
|
|
58300
58315
|
replaceLayers(targetDataset, targetLayers, outputLayers);
|
|
58301
58316
|
// some operations leave unreferenced arcs that should be cleaned up
|
|
58302
|
-
if ((name
|
|
58303
|
-
name == 'rectangles' || name == 'filter' && opts.cleanup) && !opts.no_cleanup) {
|
|
58317
|
+
if (commandNeedsArcDissolve(name, targetLayers, opts)) {
|
|
58304
58318
|
dissolveArcs(targetDataset);
|
|
58305
58319
|
}
|
|
58306
58320
|
}
|
|
@@ -58347,7 +58361,15 @@ ${svg}
|
|
|
58347
58361
|
});
|
|
58348
58362
|
}
|
|
58349
58363
|
|
|
58350
|
-
|
|
58364
|
+
function commandNeedsArcDissolve(name, targetLayers, opts) {
|
|
58365
|
+
if (opts.no_cleanup) return false;
|
|
58366
|
+
if (name == 'clip' || name == 'erase') {
|
|
58367
|
+
return utils.some(targetLayers || [], layerHasPaths);
|
|
58368
|
+
}
|
|
58369
|
+
return name == 'rectangle' || name == 'rectangles' || name == 'filter' && opts.cleanup;
|
|
58370
|
+
}
|
|
58371
|
+
|
|
58372
|
+
var version = "0.7.21";
|
|
58351
58373
|
|
|
58352
58374
|
// Parse command line args into commands and run them
|
|
58353
58375
|
// 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);
|
|
@@ -45286,23 +45291,23 @@ ${svg}
|
|
|
45286
45291
|
profileEnd('clipLayers');
|
|
45287
45292
|
return result;
|
|
45288
45293
|
}
|
|
45294
|
+
if (!usingPathClip) {
|
|
45295
|
+
result = clipLayersByClipDataset(targetLayers, clipDataset, type, opts);
|
|
45296
|
+
profileEnd('clipLayers');
|
|
45297
|
+
return result;
|
|
45298
|
+
}
|
|
45289
45299
|
profileStart('mergeLayersForOverlay');
|
|
45290
45300
|
mergedDataset = mergeLayersForOverlay2(targetLayers, targetDataset, clipDataset);
|
|
45291
45301
|
profileEnd('mergeLayersForOverlay');
|
|
45292
45302
|
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
|
-
}
|
|
45303
|
+
nodes = addIntersectionCuts(mergedDataset, opts);
|
|
45304
|
+
noteDatasetWillChange(targetDataset, {operation: type, unit: 'arcs'});
|
|
45305
|
+
targetDataset.arcs = mergedDataset.arcs;
|
|
45306
|
+
markDatasetChanged(targetDataset, {operation: type, unit: 'arcs'});
|
|
45307
|
+
profileStart('clipDissolvePolygonLayer2');
|
|
45308
|
+
clipLyr = utils.defaults({data: null}, clipLyr);
|
|
45309
|
+
clipLyr = dissolvePolygonLayer2(clipLyr, mergedDataset, {quiet: true, silent: true});
|
|
45310
|
+
profileEnd('clipDissolvePolygonLayer2');
|
|
45306
45311
|
|
|
45307
45312
|
profileStart('clipLayersByLayer');
|
|
45308
45313
|
result = clipLayersByLayer(targetLayers, clipLyr, nodes, type, opts);
|
|
@@ -45311,6 +45316,16 @@ ${svg}
|
|
|
45311
45316
|
return result;
|
|
45312
45317
|
}
|
|
45313
45318
|
|
|
45319
|
+
function clipLayersByClipDataset(targetLayers, clipDataset, type, opts) {
|
|
45320
|
+
var clipLyr = clipDataset.layers[0];
|
|
45321
|
+
var nodes = new NodeCollection(clipDataset.arcs);
|
|
45322
|
+
var result;
|
|
45323
|
+
profileStart('clipLayersByLayer');
|
|
45324
|
+
result = clipLayersByLayer(targetLayers, clipLyr, nodes, type, opts);
|
|
45325
|
+
profileEnd('clipLayersByLayer');
|
|
45326
|
+
return result;
|
|
45327
|
+
}
|
|
45328
|
+
|
|
45314
45329
|
function clipRasterLayers(targetLayers, clipSrc, targetDataset, type, opts) {
|
|
45315
45330
|
var clipDataset, clipBounds, bbox;
|
|
45316
45331
|
if (type != 'clip') {
|
|
@@ -58299,8 +58314,7 @@ ${svg}
|
|
|
58299
58314
|
// TODO: consider replacing old layers as they are generated, for gc
|
|
58300
58315
|
replaceLayers(targetDataset, targetLayers, outputLayers);
|
|
58301
58316
|
// some operations leave unreferenced arcs that should be cleaned up
|
|
58302
|
-
if ((name
|
|
58303
|
-
name == 'rectangles' || name == 'filter' && opts.cleanup) && !opts.no_cleanup) {
|
|
58317
|
+
if (commandNeedsArcDissolve(name, targetLayers, opts)) {
|
|
58304
58318
|
dissolveArcs(targetDataset);
|
|
58305
58319
|
}
|
|
58306
58320
|
}
|
|
@@ -58347,7 +58361,15 @@ ${svg}
|
|
|
58347
58361
|
});
|
|
58348
58362
|
}
|
|
58349
58363
|
|
|
58350
|
-
|
|
58364
|
+
function commandNeedsArcDissolve(name, targetLayers, opts) {
|
|
58365
|
+
if (opts.no_cleanup) return false;
|
|
58366
|
+
if (name == 'clip' || name == 'erase') {
|
|
58367
|
+
return utils.some(targetLayers || [], layerHasPaths);
|
|
58368
|
+
}
|
|
58369
|
+
return name == 'rectangle' || name == 'rectangles' || name == 'filter' && opts.cleanup;
|
|
58370
|
+
}
|
|
58371
|
+
|
|
58372
|
+
var version = "0.7.21";
|
|
58351
58373
|
|
|
58352
58374
|
// Parse command line args into commands and run them
|
|
58353
58375
|
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.
|