mapshaper 0.6.53 → 0.6.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/mapshaper.js +185 -92
- package/package.json +1 -1
- package/www/index.html +6 -20
- package/www/mapshaper-gui.js +176 -132
- package/www/mapshaper.js +185 -92
- package/www/page.css +15 -6
package/mapshaper.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
|
|
3
|
-
var VERSION = "0.6.
|
|
3
|
+
var VERSION = "0.6.55";
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
var utils = /*#__PURE__*/Object.freeze({
|
|
@@ -11075,7 +11075,7 @@
|
|
|
11075
11075
|
}
|
|
11076
11076
|
|
|
11077
11077
|
function isGzipFile(file) {
|
|
11078
|
-
return /\.gz
|
|
11078
|
+
return /\.gz$/i.test(file);
|
|
11079
11079
|
}
|
|
11080
11080
|
|
|
11081
11081
|
function isSupportedOutputFormat(fmt) {
|
|
@@ -25338,7 +25338,7 @@ ${svg}
|
|
|
25338
25338
|
.describe('create commands on-the-fly and run them')
|
|
25339
25339
|
.option('expression', {
|
|
25340
25340
|
DEFAULT: true,
|
|
25341
|
-
describe: 'JS expression to generate command(s)'
|
|
25341
|
+
describe: 'JS expression or template to generate command(s)'
|
|
25342
25342
|
})
|
|
25343
25343
|
// deprecated
|
|
25344
25344
|
.option('commands', {alias_to: 'expression'})
|
|
@@ -41842,6 +41842,185 @@ ${svg}
|
|
|
41842
41842
|
}, {});
|
|
41843
41843
|
}
|
|
41844
41844
|
|
|
41845
|
+
// import { importGeoJSON } from '../geojson/geojson-import';
|
|
41846
|
+
|
|
41847
|
+
function getTargetProxy(target) {
|
|
41848
|
+
var lyr = target.layers[0];
|
|
41849
|
+
var data = getLayerInfo(lyr, target.dataset); // layer_name, feature_count etc
|
|
41850
|
+
data.layer = lyr;
|
|
41851
|
+
data.dataset = target.dataset;
|
|
41852
|
+
addGetters(data, {
|
|
41853
|
+
// export as an object, not a string or buffer
|
|
41854
|
+
geojson: getGeoJSON
|
|
41855
|
+
});
|
|
41856
|
+
|
|
41857
|
+
function getGeoJSON() {
|
|
41858
|
+
var features = exportLayerAsGeoJSON(lyr, target.dataset, {rfc7946: true}, true);
|
|
41859
|
+
return {
|
|
41860
|
+
type: 'FeatureCollection',
|
|
41861
|
+
features: features
|
|
41862
|
+
};
|
|
41863
|
+
}
|
|
41864
|
+
|
|
41865
|
+
return data;
|
|
41866
|
+
}
|
|
41867
|
+
|
|
41868
|
+
// Support for evaluating expressions embedded in curly-brace templates
|
|
41869
|
+
|
|
41870
|
+
// Returns: a string (e.g. a command string used by the -run command)
|
|
41871
|
+
async function evalTemplateExpression(expression, targets, ctx) {
|
|
41872
|
+
ctx = ctx || getBaseContext();
|
|
41873
|
+
// TODO: throw an error if target is used when there are multiple targets
|
|
41874
|
+
if (targets && targets.length == 1) {
|
|
41875
|
+
Object.defineProperty(ctx, 'target', {value: getTargetProxy(targets[0])});
|
|
41876
|
+
}
|
|
41877
|
+
// Add global functions and data to the expression context
|
|
41878
|
+
// (e.g. functions imported via the -require command)
|
|
41879
|
+
var globals = getStashedVar('defs') || {};
|
|
41880
|
+
ctx.global = globals;
|
|
41881
|
+
utils.extend(ctx, ctx.global);
|
|
41882
|
+
|
|
41883
|
+
var output = await compileTemplate(expression, ctx);
|
|
41884
|
+
if (hasFunctionCall(output, ctx)) {
|
|
41885
|
+
// also evaluate function calls that are not enclosed in curly braces
|
|
41886
|
+
// (convenience syntax)
|
|
41887
|
+
output = await evalExpression(output, ctx);
|
|
41888
|
+
}
|
|
41889
|
+
return output;
|
|
41890
|
+
}
|
|
41891
|
+
|
|
41892
|
+
async function compileTemplate(template, ctx) {
|
|
41893
|
+
var subExpressions = parseTemplate(template);
|
|
41894
|
+
var promises = subExpressions.map(expr => evalExpression(expr, ctx));
|
|
41895
|
+
var replacements = await Promise.all(promises);
|
|
41896
|
+
return applyReplacements(template, replacements);
|
|
41897
|
+
}
|
|
41898
|
+
|
|
41899
|
+
async function evalExpression(expression, ctx) {
|
|
41900
|
+
var output;
|
|
41901
|
+
try {
|
|
41902
|
+
output = Function('ctx', 'with(ctx) {return (' + expression + ');}').call({}, ctx);
|
|
41903
|
+
} catch(e) {
|
|
41904
|
+
stop(e.name, 'in JS source:', e.message);
|
|
41905
|
+
}
|
|
41906
|
+
return output;
|
|
41907
|
+
}
|
|
41908
|
+
|
|
41909
|
+
// Returns array of 0 or more embedded curly-brace expressions
|
|
41910
|
+
function parseTemplate(str) {
|
|
41911
|
+
var arr = [];
|
|
41912
|
+
parseTemplateParts(str).forEach(function(s, i) {
|
|
41913
|
+
if (i % 2 == 1) {
|
|
41914
|
+
arr.push(s.substring(1, s.length-1)); // remove braces
|
|
41915
|
+
}
|
|
41916
|
+
});
|
|
41917
|
+
return arr;
|
|
41918
|
+
}
|
|
41919
|
+
|
|
41920
|
+
// template: template string
|
|
41921
|
+
// replacements: array of strings or values that can be coerced to strings
|
|
41922
|
+
function applyReplacements(template, replacements) {
|
|
41923
|
+
var parts = parseTemplateParts(template);
|
|
41924
|
+
return parts.reduce(function(memo, s, i) {
|
|
41925
|
+
return i % 2 == 1 ? memo + (replacements.shift() || '') : memo + s;
|
|
41926
|
+
}, '');
|
|
41927
|
+
}
|
|
41928
|
+
|
|
41929
|
+
// Divides a string into substrings; even-index strings contain literal strings,
|
|
41930
|
+
// Odd-indexed strings contain curly-brace-delimited template expressions.
|
|
41931
|
+
// JSON objects are treated as literal strings; other top-level curly braces are
|
|
41932
|
+
// assumed to be embedded expressions.
|
|
41933
|
+
// For example: parseTemplateParts('{"hello"}, world!') => ['', '{"hello"}', ', world!']
|
|
41934
|
+
//
|
|
41935
|
+
function parseTemplateParts(str) {
|
|
41936
|
+
// TODO: consider adding \ escapes
|
|
41937
|
+
var depth=0;
|
|
41938
|
+
var parts = [];
|
|
41939
|
+
var part = '';
|
|
41940
|
+
var c;
|
|
41941
|
+
|
|
41942
|
+
for (var i=0, n=str.length; i<n; i++) {
|
|
41943
|
+
c = str.charAt(i);
|
|
41944
|
+
if (c == '{') {
|
|
41945
|
+
if (depth == 0) {
|
|
41946
|
+
parts.push(part);
|
|
41947
|
+
part = '';
|
|
41948
|
+
}
|
|
41949
|
+
depth++;
|
|
41950
|
+
}
|
|
41951
|
+
part += c;
|
|
41952
|
+
if (c == '}') {
|
|
41953
|
+
depth--;
|
|
41954
|
+
if (depth < 0) {
|
|
41955
|
+
// unexpected... throw an error?
|
|
41956
|
+
depth++;
|
|
41957
|
+
} else if (depth == 0 && isValidJSON(part)) {
|
|
41958
|
+
// embedded JSON objects are not template parts -- undo
|
|
41959
|
+
part = parts.pop() + part;
|
|
41960
|
+
} else if (depth == 0) {
|
|
41961
|
+
parts.push(part);
|
|
41962
|
+
part = '';
|
|
41963
|
+
}
|
|
41964
|
+
}
|
|
41965
|
+
}
|
|
41966
|
+
parts.push(part);
|
|
41967
|
+
return parts;
|
|
41968
|
+
}
|
|
41969
|
+
|
|
41970
|
+
// Tests if an expression string contains a call to an indexed function
|
|
41971
|
+
// defs: Object containing functions indexed by function name
|
|
41972
|
+
function hasFunctionCall(str, defs) {
|
|
41973
|
+
var rxp = /([$_a-z][$_a-z0-9]*)\(/ig;
|
|
41974
|
+
return Array.from(str.matchAll(rxp)).some(match => match[1] in defs);
|
|
41975
|
+
}
|
|
41976
|
+
|
|
41977
|
+
function isValidJSON(str) {
|
|
41978
|
+
try {
|
|
41979
|
+
JSON.parse(str);
|
|
41980
|
+
} catch(e) {
|
|
41981
|
+
return false;
|
|
41982
|
+
}
|
|
41983
|
+
return true;
|
|
41984
|
+
}
|
|
41985
|
+
|
|
41986
|
+
cmd.require = async function(targets, opts) {
|
|
41987
|
+
var defs = getStashedVar('defs');
|
|
41988
|
+
var moduleFile, moduleName, mod;
|
|
41989
|
+
if (!opts.module) {
|
|
41990
|
+
stop("Missing module name or path to module");
|
|
41991
|
+
}
|
|
41992
|
+
if (cli.isFile(opts.module)) {
|
|
41993
|
+
moduleFile = opts.module;
|
|
41994
|
+
} else if (cli.isFile(opts.module + '.js')) {
|
|
41995
|
+
moduleFile = opts.module + '.js';
|
|
41996
|
+
} else {
|
|
41997
|
+
moduleName = opts.module;
|
|
41998
|
+
}
|
|
41999
|
+
if (moduleFile && !require$1('path').isAbsolute(moduleFile)) {
|
|
42000
|
+
moduleFile = require$1('path').join(process.cwd(), moduleFile);
|
|
42001
|
+
}
|
|
42002
|
+
try {
|
|
42003
|
+
mod = require$1(moduleFile || moduleName);
|
|
42004
|
+
if (typeof mod == 'function') {
|
|
42005
|
+
// -require now includes the functionality of the old -external command
|
|
42006
|
+
var retn = mod(api);
|
|
42007
|
+
if (retn && isValidExternalCommand(retn)) {
|
|
42008
|
+
cmd.registerCommand(retn.name, retn);
|
|
42009
|
+
}
|
|
42010
|
+
}
|
|
42011
|
+
} catch(e) {
|
|
42012
|
+
stop('Unable to load external module:', e.message, getErrorDetail(e));
|
|
42013
|
+
}
|
|
42014
|
+
if (moduleName || opts.alias) {
|
|
42015
|
+
defs[opts.alias || moduleName] = mod;
|
|
42016
|
+
} else {
|
|
42017
|
+
Object.assign(defs, mod);
|
|
42018
|
+
}
|
|
42019
|
+
if (opts.init) {
|
|
42020
|
+
await evalTemplateExpression(opts.init, targets);
|
|
42021
|
+
}
|
|
42022
|
+
};
|
|
42023
|
+
|
|
41845
42024
|
// Parse an array or a string of command line tokens into an array of
|
|
41846
42025
|
// command objects.
|
|
41847
42026
|
function parseCommands(tokens) {
|
|
@@ -41894,29 +42073,6 @@ ${svg}
|
|
|
41894
42073
|
parseConsoleCommands: parseConsoleCommands
|
|
41895
42074
|
});
|
|
41896
42075
|
|
|
41897
|
-
// import { importGeoJSON } from '../geojson/geojson-import';
|
|
41898
|
-
|
|
41899
|
-
function getTargetProxy(target) {
|
|
41900
|
-
var lyr = target.layers[0];
|
|
41901
|
-
var data = getLayerInfo(lyr, target.dataset); // layer_name, feature_count etc
|
|
41902
|
-
data.layer = lyr;
|
|
41903
|
-
data.dataset = target.dataset;
|
|
41904
|
-
addGetters(data, {
|
|
41905
|
-
// export as an object, not a string or buffer
|
|
41906
|
-
geojson: getGeoJSON
|
|
41907
|
-
});
|
|
41908
|
-
|
|
41909
|
-
function getGeoJSON() {
|
|
41910
|
-
var features = exportLayerAsGeoJSON(lyr, target.dataset, {rfc7946: true}, true);
|
|
41911
|
-
return {
|
|
41912
|
-
type: 'FeatureCollection',
|
|
41913
|
-
features: features
|
|
41914
|
-
};
|
|
41915
|
-
}
|
|
41916
|
-
|
|
41917
|
-
return data;
|
|
41918
|
-
}
|
|
41919
|
-
|
|
41920
42076
|
function getIOProxy(job) {
|
|
41921
42077
|
var obj = {
|
|
41922
42078
|
_cache: {}
|
|
@@ -41955,20 +42111,14 @@ ${svg}
|
|
|
41955
42111
|
// }
|
|
41956
42112
|
|
|
41957
42113
|
cmd.run = async function(job, targets, opts) {
|
|
41958
|
-
var tmp, commands;
|
|
42114
|
+
var tmp, commands, ctx;
|
|
41959
42115
|
if (!opts.expression) {
|
|
41960
42116
|
stop("Missing expression parameter");
|
|
41961
42117
|
}
|
|
41962
|
-
|
|
42118
|
+
ctx = getBaseContext();
|
|
41963
42119
|
// io proxy adds ability to add datasets dynamically in a required function
|
|
41964
|
-
var ctx = getBaseContext();
|
|
41965
42120
|
ctx.io = getIOProxy();
|
|
41966
|
-
tmp =
|
|
41967
|
-
|
|
41968
|
-
// Support async functions as expressions
|
|
41969
|
-
if (utils.isPromise(tmp)) {
|
|
41970
|
-
tmp = await tmp;
|
|
41971
|
-
}
|
|
42121
|
+
tmp = await evalTemplateExpression(opts.expression, targets, ctx);
|
|
41972
42122
|
if (tmp && !utils.isString(tmp)) {
|
|
41973
42123
|
stop('Expected a string containing mapshaper commands; received:', tmp);
|
|
41974
42124
|
}
|
|
@@ -41987,63 +42137,6 @@ ${svg}
|
|
|
41987
42137
|
}
|
|
41988
42138
|
};
|
|
41989
42139
|
|
|
41990
|
-
// This could return a Promise or a value or nothing
|
|
41991
|
-
function runGlobalExpression(expression, targets, ctx) {
|
|
41992
|
-
ctx = ctx || getBaseContext();
|
|
41993
|
-
var output;
|
|
41994
|
-
// TODO: throw an informative error if target is used when there are multiple targets
|
|
41995
|
-
if (targets && targets.length == 1) {
|
|
41996
|
-
Object.defineProperty(ctx, 'target', {value: getTargetProxy(targets[0])});
|
|
41997
|
-
}
|
|
41998
|
-
// Add defined functions and data to the expression context
|
|
41999
|
-
// (Such as functions imported via the -require command)
|
|
42000
|
-
utils.extend(ctx, getStashedVar('defs'));
|
|
42001
|
-
try {
|
|
42002
|
-
output = Function('ctx', 'with(ctx) {return (' + expression + ');}').call({}, ctx);
|
|
42003
|
-
} catch(e) {
|
|
42004
|
-
stop(e.name, 'in JS source:', e.message);
|
|
42005
|
-
}
|
|
42006
|
-
return output;
|
|
42007
|
-
}
|
|
42008
|
-
|
|
42009
|
-
cmd.require = function(targets, opts) {
|
|
42010
|
-
var defs = getStashedVar('defs');
|
|
42011
|
-
var moduleFile, moduleName, mod;
|
|
42012
|
-
if (!opts.module) {
|
|
42013
|
-
stop("Missing module name or path to module");
|
|
42014
|
-
}
|
|
42015
|
-
if (cli.isFile(opts.module)) {
|
|
42016
|
-
moduleFile = opts.module;
|
|
42017
|
-
} else if (cli.isFile(opts.module + '.js')) {
|
|
42018
|
-
moduleFile = opts.module + '.js';
|
|
42019
|
-
} else {
|
|
42020
|
-
moduleName = opts.module;
|
|
42021
|
-
}
|
|
42022
|
-
if (moduleFile && !require$1('path').isAbsolute(moduleFile)) {
|
|
42023
|
-
moduleFile = require$1('path').join(process.cwd(), moduleFile);
|
|
42024
|
-
}
|
|
42025
|
-
try {
|
|
42026
|
-
mod = require$1(moduleFile || moduleName);
|
|
42027
|
-
if (typeof mod == 'function') {
|
|
42028
|
-
// -require now includes the functionality of the old -external command
|
|
42029
|
-
var retn = mod(api);
|
|
42030
|
-
if (retn && isValidExternalCommand(retn)) {
|
|
42031
|
-
cmd.registerCommand(retn.name, retn);
|
|
42032
|
-
}
|
|
42033
|
-
}
|
|
42034
|
-
} catch(e) {
|
|
42035
|
-
stop('Unable to load external module:', e.message, getErrorDetail(e));
|
|
42036
|
-
}
|
|
42037
|
-
if (moduleName || opts.alias) {
|
|
42038
|
-
defs[opts.alias || moduleName] = mod;
|
|
42039
|
-
} else {
|
|
42040
|
-
Object.assign(defs, mod);
|
|
42041
|
-
}
|
|
42042
|
-
if (opts.init) {
|
|
42043
|
-
runGlobalExpression(opts.init, targets);
|
|
42044
|
-
}
|
|
42045
|
-
};
|
|
42046
|
-
|
|
42047
42140
|
cmd.shape = function(targetDataset, opts) {
|
|
42048
42141
|
var geojson, dataset;
|
|
42049
42142
|
if (opts.coordinates) {
|
package/package.json
CHANGED
package/www/index.html
CHANGED
|
@@ -276,27 +276,11 @@ a smoother appearance.</div></div></div></div>
|
|
|
276
276
|
|
|
277
277
|
<div class="option-menu">
|
|
278
278
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
<div><label for="repair-intersections-opt"><input type="checkbox" checked class="checkbox" id="repair-intersections-opt"/>detect line intersections</label>
|
|
283
|
-
<div class="tip-button">?<div class="tip-anchor">
|
|
284
|
-
|
|
285
|
-
<div class="tip">Detect line intersections, including
|
|
286
|
-
self-intersections, to help identify
|
|
287
|
-
topological errors in a dataset.</div></div></div></div>
|
|
288
|
-
|
|
289
|
-
<!-- <div><label for="snap-points-opt"><input type="checkbox" class="checkbox" id="snap-points-opt" />snap vertices</label>
|
|
290
|
-
<div class="tip-button">?<div class="tip-anchor">
|
|
291
|
-
<div class="tip">Fix topology errors by snapping
|
|
292
|
-
together points with nearly identical
|
|
293
|
-
coordinates. This option does not
|
|
294
|
-
apply to TopoJSON files.</div></div></div></div> -->
|
|
295
|
-
<div style="height:5px"></div>
|
|
296
|
-
|
|
279
|
+
<div id="path-import-options">
|
|
280
|
+
<h4>Options</h4>
|
|
297
281
|
</div>
|
|
298
282
|
|
|
299
|
-
<div><input type="text" class="text-input advanced-options" placeholder="import options" />
|
|
283
|
+
<div><input type="text" class="text-input advanced-options" placeholder="command line import options" />
|
|
300
284
|
<a href="https://github.com/mbloch/mapshaper/wiki/Command-Reference#-i-input" target="_mapshaper_import_docs">
|
|
301
285
|
<div class="tip-button">?<div class="tip-anchor">
|
|
302
286
|
<div class="tip">Enter options from the command line
|
|
@@ -324,8 +308,10 @@ encoding=big5</span>. Click to see all options.</div></div></div></div>
|
|
|
324
308
|
<div class="mshp-main-map main-area map-area">
|
|
325
309
|
<div class="coordinate-info colored-text selectable"></div>
|
|
326
310
|
<div class="intersection-display">
|
|
327
|
-
<
|
|
311
|
+
<span class="intersection-check text-btn colored-text">Check line intersections</span><span class="intersection-count">0 line intersections</span>
|
|
312
|
+
<img class="close-btn" src="images/close.png">
|
|
328
313
|
<div class="repair-btn text-btn colored-text">Repair</div>
|
|
314
|
+
|
|
329
315
|
</div>
|
|
330
316
|
<div class="map-layers"></div>
|
|
331
317
|
<div class="basemap-container"><div class="basemap"></div></div>
|
package/www/mapshaper-gui.js
CHANGED
|
@@ -654,8 +654,9 @@
|
|
|
654
654
|
},
|
|
655
655
|
|
|
656
656
|
show: function(css) {
|
|
657
|
+
var tag = this.el && this.el.tagName;
|
|
657
658
|
if (!this.visible()) {
|
|
658
|
-
this.css('display:block
|
|
659
|
+
this.css('display', tag == 'SPAN' ? 'inline-block' : 'block');
|
|
659
660
|
this._hidden = false;
|
|
660
661
|
}
|
|
661
662
|
return this;
|
|
@@ -2101,8 +2102,6 @@
|
|
|
2101
2102
|
} else {
|
|
2102
2103
|
var freeform = El('#import-options .advanced-options').node().value;
|
|
2103
2104
|
importOpts = GUI.parseFreeformOptions(freeform, 'i');
|
|
2104
|
-
importOpts.no_repair = !El("#repair-intersections-opt").node().checked;
|
|
2105
|
-
// importOpts.snap = !!El("#snap-points-opt").node().checked;
|
|
2106
2105
|
}
|
|
2107
2106
|
return importOpts;
|
|
2108
2107
|
}
|
|
@@ -3495,117 +3494,214 @@
|
|
|
3495
3494
|
|
|
3496
3495
|
}
|
|
3497
3496
|
|
|
3497
|
+
// Test if map should be re-framed to show updated layer
|
|
3498
|
+
function mapNeedsReset(newBounds, prevBounds, viewportBounds, flags) {
|
|
3499
|
+
var viewportPct = getIntersectionPct(newBounds, viewportBounds);
|
|
3500
|
+
var contentPct = getIntersectionPct(viewportBounds, newBounds);
|
|
3501
|
+
var boundsChanged = !prevBounds.equals(newBounds);
|
|
3502
|
+
var inView = newBounds.intersects(viewportBounds);
|
|
3503
|
+
var areaChg = newBounds.area() / prevBounds.area();
|
|
3504
|
+
var chgThreshold = flags.proj ? 1e3 : 1e8;
|
|
3505
|
+
// don't reset if layer extent hasn't changed
|
|
3506
|
+
if (!boundsChanged) return false;
|
|
3507
|
+
// reset if layer is out-of-view
|
|
3508
|
+
if (!inView) return true;
|
|
3509
|
+
// reset if content is mostly offscreen
|
|
3510
|
+
if (viewportPct < 0.3 && contentPct < 0.9) return true;
|
|
3511
|
+
// reset if content bounds have changed a lot (e.g. after projection)
|
|
3512
|
+
if (areaChg > chgThreshold || areaChg < 1/chgThreshold) return true;
|
|
3513
|
+
return false;
|
|
3514
|
+
}
|
|
3515
|
+
|
|
3516
|
+
// Test if an update may have affected the visible shape of arcs
|
|
3517
|
+
// @flags Flags from update event
|
|
3518
|
+
function arcsMayHaveChanged(flags) {
|
|
3519
|
+
return flags.simplify_method || flags.simplify || flags.proj ||
|
|
3520
|
+
flags.arc_count || flags.repair || flags.clip || flags.erase ||
|
|
3521
|
+
flags.slice || flags.affine || flags.rectangle || flags.buffer ||
|
|
3522
|
+
flags.union || flags.mosaic || flags.snap || flags.clean || flags.drop || false;
|
|
3523
|
+
}
|
|
3524
|
+
|
|
3525
|
+
// check for operations that may change the number of self intersections in the
|
|
3526
|
+
// target layer.
|
|
3527
|
+
function intersectionsMayHaveChanged(flags) {
|
|
3528
|
+
return arcsMayHaveChanged(flags) || flags.select || flags['merge-layers'] ||
|
|
3529
|
+
flags.filter || flags.dissolve || flags.dissolve2;
|
|
3530
|
+
}
|
|
3531
|
+
|
|
3532
|
+
// Test if an update allows hover popup to stay open
|
|
3533
|
+
function popupCanStayOpen(flags) {
|
|
3534
|
+
// keeping popup open after -drop geometry causes problems...
|
|
3535
|
+
// // if (arcsMayHaveChanged(flags)) return false;
|
|
3536
|
+
if (arcsMayHaveChanged(flags)) return false;
|
|
3537
|
+
if (flags.points || flags.proj) return false;
|
|
3538
|
+
if (!flags.same_table) return false;
|
|
3539
|
+
return true;
|
|
3540
|
+
}
|
|
3541
|
+
|
|
3542
|
+
// Returns proportion of bb2 occupied by bb1
|
|
3543
|
+
function getIntersectionPct(bb1, bb2) {
|
|
3544
|
+
return getBoundsIntersection(bb1, bb2).area() / bb2.area() || 0;
|
|
3545
|
+
}
|
|
3546
|
+
|
|
3547
|
+
function getBoundsIntersection(a, b) {
|
|
3548
|
+
var c = new Bounds();
|
|
3549
|
+
if (a.intersects(b)) {
|
|
3550
|
+
c.setBounds(Math.max(a.xmin, b.xmin), Math.max(a.ymin, b.ymin),
|
|
3551
|
+
Math.min(a.xmax, b.xmax), Math.min(a.ymax, b.ymax));
|
|
3552
|
+
}
|
|
3553
|
+
return c;
|
|
3554
|
+
}
|
|
3555
|
+
|
|
3498
3556
|
function RepairControl(gui) {
|
|
3499
3557
|
var map = gui.map,
|
|
3500
3558
|
model = gui.model,
|
|
3501
3559
|
el = gui.container.findChild(".intersection-display"),
|
|
3502
3560
|
readout = el.findChild(".intersection-count"),
|
|
3561
|
+
checkBtn = el.findChild(".intersection-check"),
|
|
3503
3562
|
repairBtn = el.findChild(".repair-btn"),
|
|
3504
|
-
//
|
|
3505
|
-
//
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
_currXX;
|
|
3563
|
+
_simplifiedXX, // saved simplified intersections, for repair
|
|
3564
|
+
_unsimplifiedXX, // saved unsimplified intersection data, for performance
|
|
3565
|
+
_disabled = false,
|
|
3566
|
+
_on = false;
|
|
3509
3567
|
|
|
3510
|
-
|
|
3511
|
-
gui.on('simplify_drag_end', updateAsync);
|
|
3568
|
+
el.findChild('.close-btn').on('click', dismissForever);
|
|
3512
3569
|
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
flags.arc_count || flags.snap || flags.affine || flags.points || flags['merge-layers'];
|
|
3517
|
-
if (intersectionsMayHaveChanged) {
|
|
3518
|
-
// delete any cached intersection data, to trigger re-calculation
|
|
3519
|
-
e.dataset._intersections = null;
|
|
3520
|
-
updateAsync();
|
|
3521
|
-
} else if (flags.select) {
|
|
3522
|
-
// new active layer, but no editing commands were run -- use cached intersections (if available)
|
|
3523
|
-
updateAsync();
|
|
3570
|
+
gui.on('simplify_drag_start', function() {
|
|
3571
|
+
if (intersectionsAreOn()) {
|
|
3572
|
+
hide();
|
|
3524
3573
|
}
|
|
3525
3574
|
});
|
|
3526
3575
|
|
|
3576
|
+
gui.on('simplify_drag_end', function() {
|
|
3577
|
+
updateSync('simplify_drag_end');
|
|
3578
|
+
});
|
|
3579
|
+
|
|
3580
|
+
checkBtn.on('click', function() {
|
|
3581
|
+
checkBtn.hide();
|
|
3582
|
+
_on = true;
|
|
3583
|
+
updateSync();
|
|
3584
|
+
});
|
|
3585
|
+
|
|
3527
3586
|
repairBtn.on('click', function() {
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3587
|
+
var e = model.getActiveLayer();
|
|
3588
|
+
if (!_simplifiedXX || !e.dataset.arcs) return;
|
|
3589
|
+
_simplifiedXX = internal.repairIntersections(e.dataset.arcs, _simplifiedXX);
|
|
3590
|
+
showIntersections(_simplifiedXX, e.layer, e.dataset.arcs);
|
|
3591
|
+
repairBtn.hide();
|
|
3531
3592
|
model.updated({repair: true});
|
|
3532
3593
|
gui.session.simplificationRepair();
|
|
3533
3594
|
});
|
|
3534
3595
|
|
|
3535
|
-
function
|
|
3536
|
-
|
|
3537
|
-
|
|
3596
|
+
model.on('update', function(e) {
|
|
3597
|
+
if (!intersectionsAreOn()) {
|
|
3598
|
+
reset(); // need this?
|
|
3599
|
+
return;
|
|
3600
|
+
}
|
|
3601
|
+
var needRefresh = e.flags.simplify_method || e.flags.simplify ||
|
|
3602
|
+
e.flags.repair || e.flags.clean;
|
|
3603
|
+
if (needRefresh) {
|
|
3604
|
+
updateAsync();
|
|
3605
|
+
} else if (e.flags.simplify_amount) {
|
|
3606
|
+
// slider is being dragged - hide readout and dots, retain data
|
|
3607
|
+
hide();
|
|
3608
|
+
} else if (intersectionsMayHaveChanged(e.flags)) {
|
|
3609
|
+
// intersections may have changed -- reset the display
|
|
3610
|
+
reset();
|
|
3611
|
+
} else {
|
|
3612
|
+
// keep displaying the current intersections
|
|
3613
|
+
}
|
|
3614
|
+
});
|
|
3615
|
+
|
|
3616
|
+
function intersectionsAreOn() {
|
|
3617
|
+
return _on && !_disabled;
|
|
3538
3618
|
}
|
|
3539
3619
|
|
|
3540
|
-
function
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3620
|
+
function turnOff() {
|
|
3621
|
+
hide();
|
|
3622
|
+
_on = false;
|
|
3623
|
+
_simplifiedXX = null;
|
|
3624
|
+
_unsimplifiedXX = null;
|
|
3544
3625
|
}
|
|
3545
3626
|
|
|
3546
|
-
|
|
3547
|
-
|
|
3627
|
+
function reset() {
|
|
3628
|
+
turnOff();
|
|
3629
|
+
if (_disabled) {
|
|
3630
|
+
return;
|
|
3631
|
+
}
|
|
3632
|
+
var e = model.getActiveLayer();
|
|
3633
|
+
if (internal.layerHasPaths(e.layer)) {
|
|
3634
|
+
el.show();
|
|
3635
|
+
checkBtn.show();
|
|
3636
|
+
readout.hide();
|
|
3637
|
+
repairBtn.hide();
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
|
|
3641
|
+
function dismissForever() {
|
|
3642
|
+
_disabled = true;
|
|
3643
|
+
turnOff();
|
|
3644
|
+
}
|
|
3645
|
+
|
|
3646
|
+
function hide() {
|
|
3647
|
+
map.setIntersectionLayer(null);
|
|
3648
|
+
el.hide();
|
|
3649
|
+
}
|
|
3650
|
+
|
|
3651
|
+
// Update intersection display, after a short delay so map can redraw after previous
|
|
3652
|
+
// operation (e.g. simplification change)
|
|
3548
3653
|
function updateAsync() {
|
|
3549
|
-
reset();
|
|
3550
3654
|
setTimeout(updateSync, 10);
|
|
3551
3655
|
}
|
|
3552
3656
|
|
|
3553
|
-
function updateSync() {
|
|
3657
|
+
function updateSync(action) {
|
|
3658
|
+
if (!intersectionsAreOn()) return;
|
|
3554
3659
|
var e = model.getActiveLayer();
|
|
3555
|
-
var
|
|
3556
|
-
var
|
|
3557
|
-
var XX, showBtn;
|
|
3558
|
-
var opts = {
|
|
3660
|
+
var arcs = e.dataset && e.dataset.arcs;
|
|
3661
|
+
var intersectionOpts = {
|
|
3559
3662
|
unique: true,
|
|
3560
3663
|
tolerance: 0
|
|
3561
3664
|
};
|
|
3562
|
-
if (!arcs || !internal.layerHasPaths(e.layer)
|
|
3665
|
+
if (!arcs || !internal.layerHasPaths(e.layer)) {
|
|
3666
|
+
return;
|
|
3667
|
+
}
|
|
3563
3668
|
if (arcs.getRetainedInterval() > 0) {
|
|
3564
|
-
//
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
if (
|
|
3570
|
-
//
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
}
|
|
3574
|
-
showBtn = false;
|
|
3669
|
+
// simplification
|
|
3670
|
+
_simplifiedXX = internal.findSegmentIntersections(arcs, intersectionOpts);
|
|
3671
|
+
} else {
|
|
3672
|
+
// no simplification
|
|
3673
|
+
_simplifiedXX = null; // clear any old simplified XX
|
|
3674
|
+
if (_unsimplifiedXX && action == 'simplify_drag_end') {
|
|
3675
|
+
// re-use previously generated intersection data (optimization)
|
|
3676
|
+
} else {
|
|
3677
|
+
_unsimplifiedXX = internal.findSegmentIntersections(arcs, intersectionOpts);
|
|
3678
|
+
}
|
|
3575
3679
|
}
|
|
3576
|
-
|
|
3577
|
-
_currLayer = e.layer;
|
|
3578
|
-
_currArcs = arcs;
|
|
3579
|
-
_currXX = XX;
|
|
3580
|
-
showIntersections();
|
|
3581
|
-
repairBtn.classed('disabled', !showBtn);
|
|
3680
|
+
showIntersections(_simplifiedXX || _unsimplifiedXX, e.layer, arcs);
|
|
3582
3681
|
}
|
|
3583
3682
|
|
|
3584
|
-
function
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
hide();
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
function showIntersections() {
|
|
3599
|
-
var n = _currXX.length, pointLyr;
|
|
3600
|
-
if (n > 0) {
|
|
3601
|
-
// console.log("first intersection:", internal.getIntersectionDebugData(XX[0], arcs));
|
|
3602
|
-
pointLyr = internal.getIntersectionLayer(_currXX, _currLayer, _currArcs);
|
|
3683
|
+
function showIntersections(xx, lyr, arcs) {
|
|
3684
|
+
var pointLyr, count = 0, html;
|
|
3685
|
+
el.show();
|
|
3686
|
+
readout.show();
|
|
3687
|
+
checkBtn.hide();
|
|
3688
|
+
if (xx.length > 0) {
|
|
3689
|
+
pointLyr = internal.getIntersectionLayer(xx, lyr, arcs);
|
|
3690
|
+
count = internal.countPointsInLayer(pointLyr);
|
|
3691
|
+
}
|
|
3692
|
+
if (count == 0) {
|
|
3693
|
+
map.setIntersectionLayer(null);
|
|
3694
|
+
html = '<span class="icon black"></span>No self-intersections';
|
|
3695
|
+
} else {
|
|
3603
3696
|
map.setIntersectionLayer(pointLyr, {layers:[pointLyr]});
|
|
3604
|
-
|
|
3605
|
-
|
|
3697
|
+
html = utils$1.format('<span class="icon"></span>%s line intersection%s', count, utils$1.pluralSuffix(count));
|
|
3698
|
+
}
|
|
3699
|
+
readout.html(html);
|
|
3700
|
+
|
|
3701
|
+
if (_simplifiedXX && count > 0) {
|
|
3702
|
+
repairBtn.show();
|
|
3606
3703
|
} else {
|
|
3607
|
-
|
|
3608
|
-
readout.html('');
|
|
3704
|
+
repairBtn.hide();
|
|
3609
3705
|
}
|
|
3610
3706
|
}
|
|
3611
3707
|
}
|
|
@@ -8472,39 +8568,6 @@
|
|
|
8472
8568
|
return _self;
|
|
8473
8569
|
}
|
|
8474
8570
|
|
|
8475
|
-
// Test if map should be re-framed to show updated layer
|
|
8476
|
-
function mapNeedsReset(newBounds, prevBounds, viewportBounds, flags) {
|
|
8477
|
-
var viewportPct = getIntersectionPct(newBounds, viewportBounds);
|
|
8478
|
-
var contentPct = getIntersectionPct(viewportBounds, newBounds);
|
|
8479
|
-
var boundsChanged = !prevBounds.equals(newBounds);
|
|
8480
|
-
var inView = newBounds.intersects(viewportBounds);
|
|
8481
|
-
var areaChg = newBounds.area() / prevBounds.area();
|
|
8482
|
-
var chgThreshold = flags.proj ? 1e3 : 1e8;
|
|
8483
|
-
// don't reset if layer extent hasn't changed
|
|
8484
|
-
if (!boundsChanged) return false;
|
|
8485
|
-
// reset if layer is out-of-view
|
|
8486
|
-
if (!inView) return true;
|
|
8487
|
-
// reset if content is mostly offscreen
|
|
8488
|
-
if (viewportPct < 0.3 && contentPct < 0.9) return true;
|
|
8489
|
-
// reset if content bounds have changed a lot (e.g. after projection)
|
|
8490
|
-
if (areaChg > chgThreshold || areaChg < 1/chgThreshold) return true;
|
|
8491
|
-
return false;
|
|
8492
|
-
}
|
|
8493
|
-
|
|
8494
|
-
// Returns proportion of bb2 occupied by bb1
|
|
8495
|
-
function getIntersectionPct(bb1, bb2) {
|
|
8496
|
-
return getBoundsIntersection(bb1, bb2).area() / bb2.area() || 0;
|
|
8497
|
-
}
|
|
8498
|
-
|
|
8499
|
-
function getBoundsIntersection(a, b) {
|
|
8500
|
-
var c = new Bounds();
|
|
8501
|
-
if (a.intersects(b)) {
|
|
8502
|
-
c.setBounds(Math.max(a.xmin, b.xmin), Math.max(a.ymin, b.ymin),
|
|
8503
|
-
Math.min(a.xmax, b.xmax), Math.min(a.ymax, b.ymax));
|
|
8504
|
-
}
|
|
8505
|
-
return c;
|
|
8506
|
-
}
|
|
8507
|
-
|
|
8508
8571
|
function isMultilineLabel(textNode) {
|
|
8509
8572
|
return textNode.childNodes.length > 1;
|
|
8510
8573
|
}
|
|
@@ -11297,25 +11360,6 @@
|
|
|
11297
11360
|
};
|
|
11298
11361
|
}
|
|
11299
11362
|
|
|
11300
|
-
// Test if an update may have affected the visible shape of arcs
|
|
11301
|
-
// @flags Flags from update event
|
|
11302
|
-
function arcsMayHaveChanged(flags) {
|
|
11303
|
-
return flags.simplify_method || flags.simplify || flags.proj ||
|
|
11304
|
-
flags.arc_count || flags.repair || flags.clip || flags.erase ||
|
|
11305
|
-
flags.slice || flags.affine || flags.rectangle || flags.buffer ||
|
|
11306
|
-
flags.union || flags.mosaic || flags.snap || flags.clean || flags.drop || false;
|
|
11307
|
-
}
|
|
11308
|
-
|
|
11309
|
-
// Test if an update allows hover popup to stay open
|
|
11310
|
-
function popupCanStayOpen(flags) {
|
|
11311
|
-
// keeping popup open after -drop geometry causes problems...
|
|
11312
|
-
// // if (arcsMayHaveChanged(flags)) return false;
|
|
11313
|
-
if (arcsMayHaveChanged(flags)) return false;
|
|
11314
|
-
if (flags.points || flags.proj) return false;
|
|
11315
|
-
if (!flags.same_table) return false;
|
|
11316
|
-
return true;
|
|
11317
|
-
}
|
|
11318
|
-
|
|
11319
11363
|
|
|
11320
11364
|
// Update map frame after user navigates the map in frame edit mode
|
|
11321
11365
|
function updateFrameExtent() {
|
package/www/mapshaper.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
|
|
3
|
-
var VERSION = "0.6.
|
|
3
|
+
var VERSION = "0.6.55";
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
var utils = /*#__PURE__*/Object.freeze({
|
|
@@ -11075,7 +11075,7 @@
|
|
|
11075
11075
|
}
|
|
11076
11076
|
|
|
11077
11077
|
function isGzipFile(file) {
|
|
11078
|
-
return /\.gz
|
|
11078
|
+
return /\.gz$/i.test(file);
|
|
11079
11079
|
}
|
|
11080
11080
|
|
|
11081
11081
|
function isSupportedOutputFormat(fmt) {
|
|
@@ -25338,7 +25338,7 @@ ${svg}
|
|
|
25338
25338
|
.describe('create commands on-the-fly and run them')
|
|
25339
25339
|
.option('expression', {
|
|
25340
25340
|
DEFAULT: true,
|
|
25341
|
-
describe: 'JS expression to generate command(s)'
|
|
25341
|
+
describe: 'JS expression or template to generate command(s)'
|
|
25342
25342
|
})
|
|
25343
25343
|
// deprecated
|
|
25344
25344
|
.option('commands', {alias_to: 'expression'})
|
|
@@ -41842,6 +41842,185 @@ ${svg}
|
|
|
41842
41842
|
}, {});
|
|
41843
41843
|
}
|
|
41844
41844
|
|
|
41845
|
+
// import { importGeoJSON } from '../geojson/geojson-import';
|
|
41846
|
+
|
|
41847
|
+
function getTargetProxy(target) {
|
|
41848
|
+
var lyr = target.layers[0];
|
|
41849
|
+
var data = getLayerInfo(lyr, target.dataset); // layer_name, feature_count etc
|
|
41850
|
+
data.layer = lyr;
|
|
41851
|
+
data.dataset = target.dataset;
|
|
41852
|
+
addGetters(data, {
|
|
41853
|
+
// export as an object, not a string or buffer
|
|
41854
|
+
geojson: getGeoJSON
|
|
41855
|
+
});
|
|
41856
|
+
|
|
41857
|
+
function getGeoJSON() {
|
|
41858
|
+
var features = exportLayerAsGeoJSON(lyr, target.dataset, {rfc7946: true}, true);
|
|
41859
|
+
return {
|
|
41860
|
+
type: 'FeatureCollection',
|
|
41861
|
+
features: features
|
|
41862
|
+
};
|
|
41863
|
+
}
|
|
41864
|
+
|
|
41865
|
+
return data;
|
|
41866
|
+
}
|
|
41867
|
+
|
|
41868
|
+
// Support for evaluating expressions embedded in curly-brace templates
|
|
41869
|
+
|
|
41870
|
+
// Returns: a string (e.g. a command string used by the -run command)
|
|
41871
|
+
async function evalTemplateExpression(expression, targets, ctx) {
|
|
41872
|
+
ctx = ctx || getBaseContext();
|
|
41873
|
+
// TODO: throw an error if target is used when there are multiple targets
|
|
41874
|
+
if (targets && targets.length == 1) {
|
|
41875
|
+
Object.defineProperty(ctx, 'target', {value: getTargetProxy(targets[0])});
|
|
41876
|
+
}
|
|
41877
|
+
// Add global functions and data to the expression context
|
|
41878
|
+
// (e.g. functions imported via the -require command)
|
|
41879
|
+
var globals = getStashedVar('defs') || {};
|
|
41880
|
+
ctx.global = globals;
|
|
41881
|
+
utils.extend(ctx, ctx.global);
|
|
41882
|
+
|
|
41883
|
+
var output = await compileTemplate(expression, ctx);
|
|
41884
|
+
if (hasFunctionCall(output, ctx)) {
|
|
41885
|
+
// also evaluate function calls that are not enclosed in curly braces
|
|
41886
|
+
// (convenience syntax)
|
|
41887
|
+
output = await evalExpression(output, ctx);
|
|
41888
|
+
}
|
|
41889
|
+
return output;
|
|
41890
|
+
}
|
|
41891
|
+
|
|
41892
|
+
async function compileTemplate(template, ctx) {
|
|
41893
|
+
var subExpressions = parseTemplate(template);
|
|
41894
|
+
var promises = subExpressions.map(expr => evalExpression(expr, ctx));
|
|
41895
|
+
var replacements = await Promise.all(promises);
|
|
41896
|
+
return applyReplacements(template, replacements);
|
|
41897
|
+
}
|
|
41898
|
+
|
|
41899
|
+
async function evalExpression(expression, ctx) {
|
|
41900
|
+
var output;
|
|
41901
|
+
try {
|
|
41902
|
+
output = Function('ctx', 'with(ctx) {return (' + expression + ');}').call({}, ctx);
|
|
41903
|
+
} catch(e) {
|
|
41904
|
+
stop(e.name, 'in JS source:', e.message);
|
|
41905
|
+
}
|
|
41906
|
+
return output;
|
|
41907
|
+
}
|
|
41908
|
+
|
|
41909
|
+
// Returns array of 0 or more embedded curly-brace expressions
|
|
41910
|
+
function parseTemplate(str) {
|
|
41911
|
+
var arr = [];
|
|
41912
|
+
parseTemplateParts(str).forEach(function(s, i) {
|
|
41913
|
+
if (i % 2 == 1) {
|
|
41914
|
+
arr.push(s.substring(1, s.length-1)); // remove braces
|
|
41915
|
+
}
|
|
41916
|
+
});
|
|
41917
|
+
return arr;
|
|
41918
|
+
}
|
|
41919
|
+
|
|
41920
|
+
// template: template string
|
|
41921
|
+
// replacements: array of strings or values that can be coerced to strings
|
|
41922
|
+
function applyReplacements(template, replacements) {
|
|
41923
|
+
var parts = parseTemplateParts(template);
|
|
41924
|
+
return parts.reduce(function(memo, s, i) {
|
|
41925
|
+
return i % 2 == 1 ? memo + (replacements.shift() || '') : memo + s;
|
|
41926
|
+
}, '');
|
|
41927
|
+
}
|
|
41928
|
+
|
|
41929
|
+
// Divides a string into substrings; even-index strings contain literal strings,
|
|
41930
|
+
// Odd-indexed strings contain curly-brace-delimited template expressions.
|
|
41931
|
+
// JSON objects are treated as literal strings; other top-level curly braces are
|
|
41932
|
+
// assumed to be embedded expressions.
|
|
41933
|
+
// For example: parseTemplateParts('{"hello"}, world!') => ['', '{"hello"}', ', world!']
|
|
41934
|
+
//
|
|
41935
|
+
function parseTemplateParts(str) {
|
|
41936
|
+
// TODO: consider adding \ escapes
|
|
41937
|
+
var depth=0;
|
|
41938
|
+
var parts = [];
|
|
41939
|
+
var part = '';
|
|
41940
|
+
var c;
|
|
41941
|
+
|
|
41942
|
+
for (var i=0, n=str.length; i<n; i++) {
|
|
41943
|
+
c = str.charAt(i);
|
|
41944
|
+
if (c == '{') {
|
|
41945
|
+
if (depth == 0) {
|
|
41946
|
+
parts.push(part);
|
|
41947
|
+
part = '';
|
|
41948
|
+
}
|
|
41949
|
+
depth++;
|
|
41950
|
+
}
|
|
41951
|
+
part += c;
|
|
41952
|
+
if (c == '}') {
|
|
41953
|
+
depth--;
|
|
41954
|
+
if (depth < 0) {
|
|
41955
|
+
// unexpected... throw an error?
|
|
41956
|
+
depth++;
|
|
41957
|
+
} else if (depth == 0 && isValidJSON(part)) {
|
|
41958
|
+
// embedded JSON objects are not template parts -- undo
|
|
41959
|
+
part = parts.pop() + part;
|
|
41960
|
+
} else if (depth == 0) {
|
|
41961
|
+
parts.push(part);
|
|
41962
|
+
part = '';
|
|
41963
|
+
}
|
|
41964
|
+
}
|
|
41965
|
+
}
|
|
41966
|
+
parts.push(part);
|
|
41967
|
+
return parts;
|
|
41968
|
+
}
|
|
41969
|
+
|
|
41970
|
+
// Tests if an expression string contains a call to an indexed function
|
|
41971
|
+
// defs: Object containing functions indexed by function name
|
|
41972
|
+
function hasFunctionCall(str, defs) {
|
|
41973
|
+
var rxp = /([$_a-z][$_a-z0-9]*)\(/ig;
|
|
41974
|
+
return Array.from(str.matchAll(rxp)).some(match => match[1] in defs);
|
|
41975
|
+
}
|
|
41976
|
+
|
|
41977
|
+
function isValidJSON(str) {
|
|
41978
|
+
try {
|
|
41979
|
+
JSON.parse(str);
|
|
41980
|
+
} catch(e) {
|
|
41981
|
+
return false;
|
|
41982
|
+
}
|
|
41983
|
+
return true;
|
|
41984
|
+
}
|
|
41985
|
+
|
|
41986
|
+
cmd.require = async function(targets, opts) {
|
|
41987
|
+
var defs = getStashedVar('defs');
|
|
41988
|
+
var moduleFile, moduleName, mod;
|
|
41989
|
+
if (!opts.module) {
|
|
41990
|
+
stop("Missing module name or path to module");
|
|
41991
|
+
}
|
|
41992
|
+
if (cli.isFile(opts.module)) {
|
|
41993
|
+
moduleFile = opts.module;
|
|
41994
|
+
} else if (cli.isFile(opts.module + '.js')) {
|
|
41995
|
+
moduleFile = opts.module + '.js';
|
|
41996
|
+
} else {
|
|
41997
|
+
moduleName = opts.module;
|
|
41998
|
+
}
|
|
41999
|
+
if (moduleFile && !require$1('path').isAbsolute(moduleFile)) {
|
|
42000
|
+
moduleFile = require$1('path').join(process.cwd(), moduleFile);
|
|
42001
|
+
}
|
|
42002
|
+
try {
|
|
42003
|
+
mod = require$1(moduleFile || moduleName);
|
|
42004
|
+
if (typeof mod == 'function') {
|
|
42005
|
+
// -require now includes the functionality of the old -external command
|
|
42006
|
+
var retn = mod(api);
|
|
42007
|
+
if (retn && isValidExternalCommand(retn)) {
|
|
42008
|
+
cmd.registerCommand(retn.name, retn);
|
|
42009
|
+
}
|
|
42010
|
+
}
|
|
42011
|
+
} catch(e) {
|
|
42012
|
+
stop('Unable to load external module:', e.message, getErrorDetail(e));
|
|
42013
|
+
}
|
|
42014
|
+
if (moduleName || opts.alias) {
|
|
42015
|
+
defs[opts.alias || moduleName] = mod;
|
|
42016
|
+
} else {
|
|
42017
|
+
Object.assign(defs, mod);
|
|
42018
|
+
}
|
|
42019
|
+
if (opts.init) {
|
|
42020
|
+
await evalTemplateExpression(opts.init, targets);
|
|
42021
|
+
}
|
|
42022
|
+
};
|
|
42023
|
+
|
|
41845
42024
|
// Parse an array or a string of command line tokens into an array of
|
|
41846
42025
|
// command objects.
|
|
41847
42026
|
function parseCommands(tokens) {
|
|
@@ -41894,29 +42073,6 @@ ${svg}
|
|
|
41894
42073
|
parseConsoleCommands: parseConsoleCommands
|
|
41895
42074
|
});
|
|
41896
42075
|
|
|
41897
|
-
// import { importGeoJSON } from '../geojson/geojson-import';
|
|
41898
|
-
|
|
41899
|
-
function getTargetProxy(target) {
|
|
41900
|
-
var lyr = target.layers[0];
|
|
41901
|
-
var data = getLayerInfo(lyr, target.dataset); // layer_name, feature_count etc
|
|
41902
|
-
data.layer = lyr;
|
|
41903
|
-
data.dataset = target.dataset;
|
|
41904
|
-
addGetters(data, {
|
|
41905
|
-
// export as an object, not a string or buffer
|
|
41906
|
-
geojson: getGeoJSON
|
|
41907
|
-
});
|
|
41908
|
-
|
|
41909
|
-
function getGeoJSON() {
|
|
41910
|
-
var features = exportLayerAsGeoJSON(lyr, target.dataset, {rfc7946: true}, true);
|
|
41911
|
-
return {
|
|
41912
|
-
type: 'FeatureCollection',
|
|
41913
|
-
features: features
|
|
41914
|
-
};
|
|
41915
|
-
}
|
|
41916
|
-
|
|
41917
|
-
return data;
|
|
41918
|
-
}
|
|
41919
|
-
|
|
41920
42076
|
function getIOProxy(job) {
|
|
41921
42077
|
var obj = {
|
|
41922
42078
|
_cache: {}
|
|
@@ -41955,20 +42111,14 @@ ${svg}
|
|
|
41955
42111
|
// }
|
|
41956
42112
|
|
|
41957
42113
|
cmd.run = async function(job, targets, opts) {
|
|
41958
|
-
var tmp, commands;
|
|
42114
|
+
var tmp, commands, ctx;
|
|
41959
42115
|
if (!opts.expression) {
|
|
41960
42116
|
stop("Missing expression parameter");
|
|
41961
42117
|
}
|
|
41962
|
-
|
|
42118
|
+
ctx = getBaseContext();
|
|
41963
42119
|
// io proxy adds ability to add datasets dynamically in a required function
|
|
41964
|
-
var ctx = getBaseContext();
|
|
41965
42120
|
ctx.io = getIOProxy();
|
|
41966
|
-
tmp =
|
|
41967
|
-
|
|
41968
|
-
// Support async functions as expressions
|
|
41969
|
-
if (utils.isPromise(tmp)) {
|
|
41970
|
-
tmp = await tmp;
|
|
41971
|
-
}
|
|
42121
|
+
tmp = await evalTemplateExpression(opts.expression, targets, ctx);
|
|
41972
42122
|
if (tmp && !utils.isString(tmp)) {
|
|
41973
42123
|
stop('Expected a string containing mapshaper commands; received:', tmp);
|
|
41974
42124
|
}
|
|
@@ -41987,63 +42137,6 @@ ${svg}
|
|
|
41987
42137
|
}
|
|
41988
42138
|
};
|
|
41989
42139
|
|
|
41990
|
-
// This could return a Promise or a value or nothing
|
|
41991
|
-
function runGlobalExpression(expression, targets, ctx) {
|
|
41992
|
-
ctx = ctx || getBaseContext();
|
|
41993
|
-
var output;
|
|
41994
|
-
// TODO: throw an informative error if target is used when there are multiple targets
|
|
41995
|
-
if (targets && targets.length == 1) {
|
|
41996
|
-
Object.defineProperty(ctx, 'target', {value: getTargetProxy(targets[0])});
|
|
41997
|
-
}
|
|
41998
|
-
// Add defined functions and data to the expression context
|
|
41999
|
-
// (Such as functions imported via the -require command)
|
|
42000
|
-
utils.extend(ctx, getStashedVar('defs'));
|
|
42001
|
-
try {
|
|
42002
|
-
output = Function('ctx', 'with(ctx) {return (' + expression + ');}').call({}, ctx);
|
|
42003
|
-
} catch(e) {
|
|
42004
|
-
stop(e.name, 'in JS source:', e.message);
|
|
42005
|
-
}
|
|
42006
|
-
return output;
|
|
42007
|
-
}
|
|
42008
|
-
|
|
42009
|
-
cmd.require = function(targets, opts) {
|
|
42010
|
-
var defs = getStashedVar('defs');
|
|
42011
|
-
var moduleFile, moduleName, mod;
|
|
42012
|
-
if (!opts.module) {
|
|
42013
|
-
stop("Missing module name or path to module");
|
|
42014
|
-
}
|
|
42015
|
-
if (cli.isFile(opts.module)) {
|
|
42016
|
-
moduleFile = opts.module;
|
|
42017
|
-
} else if (cli.isFile(opts.module + '.js')) {
|
|
42018
|
-
moduleFile = opts.module + '.js';
|
|
42019
|
-
} else {
|
|
42020
|
-
moduleName = opts.module;
|
|
42021
|
-
}
|
|
42022
|
-
if (moduleFile && !require$1('path').isAbsolute(moduleFile)) {
|
|
42023
|
-
moduleFile = require$1('path').join(process.cwd(), moduleFile);
|
|
42024
|
-
}
|
|
42025
|
-
try {
|
|
42026
|
-
mod = require$1(moduleFile || moduleName);
|
|
42027
|
-
if (typeof mod == 'function') {
|
|
42028
|
-
// -require now includes the functionality of the old -external command
|
|
42029
|
-
var retn = mod(api);
|
|
42030
|
-
if (retn && isValidExternalCommand(retn)) {
|
|
42031
|
-
cmd.registerCommand(retn.name, retn);
|
|
42032
|
-
}
|
|
42033
|
-
}
|
|
42034
|
-
} catch(e) {
|
|
42035
|
-
stop('Unable to load external module:', e.message, getErrorDetail(e));
|
|
42036
|
-
}
|
|
42037
|
-
if (moduleName || opts.alias) {
|
|
42038
|
-
defs[opts.alias || moduleName] = mod;
|
|
42039
|
-
} else {
|
|
42040
|
-
Object.assign(defs, mod);
|
|
42041
|
-
}
|
|
42042
|
-
if (opts.init) {
|
|
42043
|
-
runGlobalExpression(opts.init, targets);
|
|
42044
|
-
}
|
|
42045
|
-
};
|
|
42046
|
-
|
|
42047
42140
|
cmd.shape = function(targetDataset, opts) {
|
|
42048
42141
|
var geojson, dataset;
|
|
42049
42142
|
if (opts.coordinates) {
|
package/www/page.css
CHANGED
|
@@ -468,6 +468,11 @@ body.dragover #import-options-drop-area .drop-area {
|
|
|
468
468
|
margin: 0 0 5px 0;
|
|
469
469
|
}
|
|
470
470
|
|
|
471
|
+
::placeholder {
|
|
472
|
+
color: #aaa;
|
|
473
|
+
opacity: 1;
|
|
474
|
+
}
|
|
475
|
+
|
|
471
476
|
|
|
472
477
|
#mshp-not-supported {
|
|
473
478
|
display: none;
|
|
@@ -954,8 +959,8 @@ img.close-btn:hover,
|
|
|
954
959
|
left: 13px;
|
|
955
960
|
}
|
|
956
961
|
|
|
957
|
-
.intersection-
|
|
958
|
-
|
|
962
|
+
.intersection-check {
|
|
963
|
+
display: none;
|
|
959
964
|
}
|
|
960
965
|
|
|
961
966
|
.intersection-count .icon {
|
|
@@ -966,18 +971,22 @@ img.close-btn:hover,
|
|
|
966
971
|
margin: 0 5px 2px 0;
|
|
967
972
|
}
|
|
968
973
|
|
|
969
|
-
.intersection-count .
|
|
974
|
+
.intersection-count .icon.black {
|
|
975
|
+
background-color: black;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
.intersection-display .close-btn {
|
|
970
979
|
width: 16px;
|
|
971
980
|
height: 16px;
|
|
972
981
|
cursor: pointer;
|
|
973
982
|
position: relative;
|
|
974
983
|
top: 4px;
|
|
975
|
-
margin-left:
|
|
984
|
+
margin-left: 1px;
|
|
976
985
|
}
|
|
977
986
|
|
|
978
|
-
|
|
987
|
+
/*.intersection-display .text-btn.disabled {
|
|
979
988
|
visibility: hidden;
|
|
980
|
-
}
|
|
989
|
+
}*/
|
|
981
990
|
|
|
982
991
|
/* --- Popup -------------------- */
|
|
983
992
|
|