mapshaper 0.6.52 → 0.6.54

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
 
3
- var VERSION = "0.6.52";
3
+ var VERSION = "0.6.54";
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/i.test(file);
11078
+ return /\.gz$/i.test(file);
11079
11079
  }
11080
11080
 
11081
11081
  function isSupportedOutputFormat(fmt) {
@@ -23703,14 +23703,14 @@ ${svg}
23703
23703
  .option('geojson-type', {
23704
23704
  describe: '[GeoJSON] FeatureCollection, GeometryCollection or Feature'
23705
23705
  })
23706
- .option('ndjson', {
23707
- describe: '[GeoJSON/JSON] output newline-delimited features or records',
23708
- type: 'flag'
23709
- })
23710
23706
  .option('hoist', {
23711
23707
  describe: '[GeoJSON] move properties to the root level of each Feature',
23712
23708
  type: 'strings'
23713
23709
  })
23710
+ .option('ndjson', {
23711
+ describe: '[GeoJSON/JSON] output newline-delimited features or records',
23712
+ type: 'flag'
23713
+ })
23714
23714
  .option('width', {
23715
23715
  describe: '[SVG/TopoJSON] pixel width of output (SVG default is 800)',
23716
23716
  type: 'number'
@@ -23865,10 +23865,6 @@ ${svg}
23865
23865
  describe: 'a pair of values (0-100) for limiting a color ramp',
23866
23866
  type: 'numbers'
23867
23867
  })
23868
- .option('range', {
23869
- // describe: 'a pair of numbers defining the effective data range',
23870
- type: 'numbers'
23871
- })
23872
23868
  .option('null-value', {
23873
23869
  describe: 'value (or color) to use for invalid or missing data'
23874
23870
  })
@@ -23895,6 +23891,11 @@ ${svg}
23895
23891
  describe: 'user-defined sequential class breaks',
23896
23892
  type: 'numbers'
23897
23893
  })
23894
+ .option('outer-breaks', {
23895
+ describe: 'min,max breakpoints, to limit the effect of outliers',
23896
+ old_alias: 'range',
23897
+ type: 'numbers'
23898
+ })
23898
23899
  .option('classes', {
23899
23900
  describe: 'number of classes (can be inferred from other options)',
23900
23901
  type: 'integer'
@@ -23904,7 +23905,7 @@ ${svg}
23904
23905
  type: 'flag'
23905
23906
  })
23906
23907
  .option('continuous', {
23907
- describe: 'output continuous interpolated values (experimental)',
23908
+ describe: 'output interpolated values, for unclassed colors',
23908
23909
  type: 'flag'
23909
23910
  })
23910
23911
  .option('index-field', {
@@ -25337,7 +25338,7 @@ ${svg}
25337
25338
  .describe('create commands on-the-fly and run them')
25338
25339
  .option('expression', {
25339
25340
  DEFAULT: true,
25340
- describe: 'JS expression to generate command(s)'
25341
+ describe: 'JS expression or template to generate command(s)'
25341
25342
  })
25342
25343
  // deprecated
25343
25344
  .option('commands', {alias_to: 'expression'})
@@ -32134,23 +32135,22 @@ ${svg}
32134
32135
  }
32135
32136
 
32136
32137
  var ascending = getAscendingNumbers(dataValues);
32137
- if (opts.range) {
32138
- ascending = applyDataRange(ascending, opts.range);
32138
+ if (opts.outer_breaks) {
32139
+ ascending = applyDataRange(ascending, opts.outer_breaks);
32139
32140
  }
32140
32141
  var nullCount = dataValues.length - ascending.length;
32141
32142
  var minVal = ascending[0];
32142
32143
  var maxVal = ascending[ascending.length - 1];
32143
32144
 
32144
- // kludge
32145
- var clamp = opts.range ? function(val) {
32146
- if (val < opts.range[0]) val = opts.range[0];
32147
- if (val > opts.range[1]) val = opts.range[1];
32145
+ var clamp = opts.outer_breaks ? function(val) {
32146
+ if (val < opts.outer_breaks[0]) val = opts.outer_breaks[0];
32147
+ if (val > opts.outer_breaks[1]) val = opts.outer_breaks[1];
32148
32148
  return val;
32149
32149
  } : null;
32150
32150
 
32151
- if (opts.range) {
32152
- minVal = opts.range[0];
32153
- maxVal = opts.range[1];
32151
+ if (opts.outer_breaks) {
32152
+ minVal = opts.outer_breaks[0];
32153
+ maxVal = opts.outer_breaks[1];
32154
32154
  }
32155
32155
 
32156
32156
  if (numBreaks === 0) {
@@ -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 = runGlobalExpression(opts.expression, targets, ctx);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.6.52",
3
+ "version": "0.6.54",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
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
- <div id="path-import-options">
280
- <h4>Options</h4>
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,6 +308,7 @@ 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">
311
+ <div class="intersection-check text-btn colored-text">Check line intersections</div>
327
312
  <div class="intersection-count">0 line intersections</div>
328
313
  <div class="repair-btn text-btn colored-text">Repair</div>
329
314
  </div>
@@ -2101,8 +2101,6 @@
2101
2101
  } else {
2102
2102
  var freeform = El('#import-options .advanced-options').node().value;
2103
2103
  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
2104
  }
2107
2105
  return importOpts;
2108
2106
  }
@@ -3495,117 +3493,208 @@
3495
3493
 
3496
3494
  }
3497
3495
 
3496
+ // Test if map should be re-framed to show updated layer
3497
+ function mapNeedsReset(newBounds, prevBounds, viewportBounds, flags) {
3498
+ var viewportPct = getIntersectionPct(newBounds, viewportBounds);
3499
+ var contentPct = getIntersectionPct(viewportBounds, newBounds);
3500
+ var boundsChanged = !prevBounds.equals(newBounds);
3501
+ var inView = newBounds.intersects(viewportBounds);
3502
+ var areaChg = newBounds.area() / prevBounds.area();
3503
+ var chgThreshold = flags.proj ? 1e3 : 1e8;
3504
+ // don't reset if layer extent hasn't changed
3505
+ if (!boundsChanged) return false;
3506
+ // reset if layer is out-of-view
3507
+ if (!inView) return true;
3508
+ // reset if content is mostly offscreen
3509
+ if (viewportPct < 0.3 && contentPct < 0.9) return true;
3510
+ // reset if content bounds have changed a lot (e.g. after projection)
3511
+ if (areaChg > chgThreshold || areaChg < 1/chgThreshold) return true;
3512
+ return false;
3513
+ }
3514
+
3515
+ // Test if an update may have affected the visible shape of arcs
3516
+ // @flags Flags from update event
3517
+ function arcsMayHaveChanged(flags) {
3518
+ return flags.simplify_method || flags.simplify || flags.proj ||
3519
+ flags.arc_count || flags.repair || flags.clip || flags.erase ||
3520
+ flags.slice || flags.affine || flags.rectangle || flags.buffer ||
3521
+ flags.union || flags.mosaic || flags.snap || flags.clean || flags.drop || false;
3522
+ }
3523
+
3524
+ // check for operations that may change the number of self intersections in the
3525
+ // target layer.
3526
+ function intersectionsMayHaveChanged(flags) {
3527
+ return arcsMayHaveChanged(flags) || flags.select || flags['merge-layers'] ||
3528
+ flags.filter || flags.dissolve || flags.dissolve2;
3529
+ }
3530
+
3531
+ // Test if an update allows hover popup to stay open
3532
+ function popupCanStayOpen(flags) {
3533
+ // keeping popup open after -drop geometry causes problems...
3534
+ // // if (arcsMayHaveChanged(flags)) return false;
3535
+ if (arcsMayHaveChanged(flags)) return false;
3536
+ if (flags.points || flags.proj) return false;
3537
+ if (!flags.same_table) return false;
3538
+ return true;
3539
+ }
3540
+
3541
+ // Returns proportion of bb2 occupied by bb1
3542
+ function getIntersectionPct(bb1, bb2) {
3543
+ return getBoundsIntersection(bb1, bb2).area() / bb2.area() || 0;
3544
+ }
3545
+
3546
+ function getBoundsIntersection(a, b) {
3547
+ var c = new Bounds();
3548
+ if (a.intersects(b)) {
3549
+ c.setBounds(Math.max(a.xmin, b.xmin), Math.max(a.ymin, b.ymin),
3550
+ Math.min(a.xmax, b.xmax), Math.min(a.ymax, b.ymax));
3551
+ }
3552
+ return c;
3553
+ }
3554
+
3498
3555
  function RepairControl(gui) {
3499
3556
  var map = gui.map,
3500
3557
  model = gui.model,
3501
3558
  el = gui.container.findChild(".intersection-display"),
3502
3559
  readout = el.findChild(".intersection-count"),
3560
+ checkBtn = el.findChild(".intersection-check"),
3503
3561
  repairBtn = el.findChild(".repair-btn"),
3504
- // keeping a reference to current arcs and intersections, so intersections
3505
- // don't need to be recalculated when 'repair' button is pressed.
3506
- _currArcs,
3507
- _currLayer,
3508
- _currXX;
3562
+ _simplifiedXX, // saved simplified intersections, for repair
3563
+ _unsimplifiedXX, // saved unsimplified intersection data, for performance
3564
+ _disabled = false;
3509
3565
 
3510
- gui.on('simplify_drag_start', hide);
3511
- gui.on('simplify_drag_end', updateAsync);
3512
-
3513
- model.on('update', function(e) {
3514
- var flags = e.flags;
3515
- var intersectionsMayHaveChanged = flags.simplify || flags.proj ||
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();
3566
+ gui.on('simplify_drag_start', function() {
3567
+ if (intersectionsAreOn()) {
3568
+ hide();
3524
3569
  }
3525
3570
  });
3526
3571
 
3572
+ gui.on('simplify_drag_end', function() {
3573
+ updateAsync();
3574
+ });
3575
+
3576
+ checkBtn.on('click', function() {
3577
+ checkBtn.hide();
3578
+ refreshSync();
3579
+ });
3580
+
3527
3581
  repairBtn.on('click', function() {
3528
- _currXX = internal.repairIntersections(_currArcs, _currXX);
3529
- showIntersections();
3530
- repairBtn.addClass('disabled');
3582
+ var e = model.getActiveLayer();
3583
+ if (!_simplifiedXX || !e.dataset.arcs) return;
3584
+ var xx = _simplifiedXX = internal.repairIntersections(e.dataset.arcs, _simplifiedXX);
3585
+ showIntersections(xx, e.layer, e.dataset.arcs);
3586
+ repairBtn.hide();
3531
3587
  model.updated({repair: true});
3532
3588
  gui.session.simplificationRepair();
3533
3589
  });
3534
3590
 
3535
- function hide() {
3536
- el.hide();
3537
- map.setIntersectionLayer(null);
3591
+ model.on('update', function(e) {
3592
+ if (!intersectionsAreOn()) {
3593
+ reset(); // need this?
3594
+ return;
3595
+ }
3596
+ var needRefresh = e.flags.simplify_method || e.flags.simplify || e.flags.repair;
3597
+ if (needRefresh) {
3598
+ updateAsync();
3599
+ } else if (e.flags.simplify_amount) {
3600
+ // slider is being dragged - hide readout and dots, retain data
3601
+ hide();
3602
+ } else if (intersectionsMayHaveChanged(e.flags)) {
3603
+ // intersections may have changed -- reset the display
3604
+ reset();
3605
+ } else {
3606
+ // keep displaying the current intersections
3607
+ }
3608
+ });
3609
+
3610
+ function intersectionsAreOn() {
3611
+ return !!(_simplifiedXX || _unsimplifiedXX);
3538
3612
  }
3539
3613
 
3540
- function enabledForDataset(dataset) {
3541
- var info = dataset.info || {};
3542
- var opts = info.import_options || {};
3543
- return !opts.no_repair && !info.no_intersections;
3614
+ function clearSavedData() {
3615
+ _simplifiedXX = null;
3616
+ _unsimplifiedXX = null;
3544
3617
  }
3545
3618
 
3546
- // Delay intersection calculation, so map can redraw after previous
3547
- // operation (e.g. layer load, simplification change)
3619
+ function reset() {
3620
+ clearSavedData();
3621
+ hide();
3622
+ if (_disabled) {
3623
+ return;
3624
+ }
3625
+ var e = model.getActiveLayer();
3626
+ if (internal.layerHasPaths(e.layer)) {
3627
+ el.show();
3628
+ checkBtn.show();
3629
+ readout.hide();
3630
+ repairBtn.hide();
3631
+ }
3632
+ }
3633
+
3634
+ function dismissForever() {
3635
+ _disabled = true;
3636
+ clearSavedData();
3637
+ hide();
3638
+ }
3639
+
3640
+ function hide() {
3641
+ map.setIntersectionLayer(null);
3642
+ el.hide();
3643
+ }
3644
+
3645
+ // Update intersection display, after a short delay so map can redraw after previous
3646
+ // operation (e.g. simplification change)
3548
3647
  function updateAsync() {
3549
- reset();
3550
- setTimeout(updateSync, 10);
3648
+ if (intersectionsAreOn()) {
3649
+ setTimeout(refreshSync, 10);
3650
+ }
3551
3651
  }
3552
3652
 
3553
- function updateSync() {
3653
+ function refreshSync() {
3554
3654
  var e = model.getActiveLayer();
3555
- var dataset = e.dataset;
3556
- var arcs = dataset && dataset.arcs;
3557
- var XX, showBtn;
3558
- var opts = {
3655
+ var arcs = e.dataset && e.dataset.arcs;
3656
+ var intersectionOpts = {
3559
3657
  unique: true,
3560
3658
  tolerance: 0
3561
3659
  };
3562
- if (!arcs || !internal.layerHasPaths(e.layer) || !enabledForDataset(dataset)) return;
3660
+ if (!arcs || !internal.layerHasPaths(e.layer)) {
3661
+ return;
3662
+ }
3563
3663
  if (arcs.getRetainedInterval() > 0) {
3564
- // TODO: cache these intersections
3565
- XX = internal.findSegmentIntersections(arcs, opts);
3566
- showBtn = XX.length > 0;
3567
- } else { // no simplification
3568
- XX = dataset._intersections;
3569
- if (!XX) {
3570
- // cache intersections at 0 simplification, to avoid recalculating
3664
+ _simplifiedXX = internal.findSegmentIntersections(arcs, intersectionOpts);
3665
+ } else {
3666
+ // no simplification
3667
+ _simplifiedXX = null; // clear old simplified XX
3668
+ if (!_unsimplifiedXX) {
3669
+ // save intersections at 0 simplification, to avoid recalculating
3571
3670
  // every time the simplification slider is set to 100% or the layer is selected at 100%
3572
- XX = dataset._intersections = internal.findSegmentIntersections(arcs, opts);
3671
+ _unsimplifiedXX = internal.findSegmentIntersections(arcs, intersectionOpts);
3573
3672
  }
3574
- showBtn = false;
3575
3673
  }
3576
- el.show();
3577
- _currLayer = e.layer;
3578
- _currArcs = arcs;
3579
- _currXX = XX;
3580
- showIntersections();
3581
- repairBtn.classed('disabled', !showBtn);
3674
+ showIntersections(_simplifiedXX || _unsimplifiedXX, e.layer, arcs);
3582
3675
  }
3583
3676
 
3584
- function reset() {
3585
- _currArcs = null;
3586
- _currXX = null;
3587
- _currLayer = null;
3588
- hide();
3589
- }
3590
-
3591
- function dismiss() {
3592
- var dataset = model.getActiveLayer().dataset;
3593
- dataset._intersections = null;
3594
- dataset.info.no_intersections = true;
3595
- reset();
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);
3677
+ function showIntersections(xx, lyr, arcs) {
3678
+ var pointLyr, count = 0;
3679
+ el.show();
3680
+ readout.show();
3681
+ checkBtn.hide();
3682
+ if (xx.length > 0) {
3683
+ pointLyr = internal.getIntersectionLayer(xx, lyr, arcs);
3684
+ count = internal.countPointsInLayer(pointLyr);
3685
+ }
3686
+ if (count == 0) {
3687
+ map.setIntersectionLayer(null);
3688
+ readout.html('<span class="icon black"></span>No self-intersections');
3689
+ } else {
3603
3690
  map.setIntersectionLayer(pointLyr, {layers:[pointLyr]});
3604
- readout.html(utils$1.format('<span class="icon"></span>%s line intersection%s <img class="close-btn" src="images/close.png">', n, utils$1.pluralSuffix(n)));
3605
- readout.findChild('.close-btn').on('click', dismiss);
3691
+ readout.html(utils$1.format('<span class="icon"></span>%s line intersection%s <img class="close-btn" src="images/close.png">', count, utils$1.pluralSuffix(count)));
3692
+ readout.findChild('.close-btn').on('click', dismissForever);
3693
+ }
3694
+ if (_simplifiedXX && count > 0) {
3695
+ repairBtn.show();
3606
3696
  } else {
3607
- map.setIntersectionLayer(null);
3608
- readout.html('');
3697
+ repairBtn.hide();
3609
3698
  }
3610
3699
  }
3611
3700
  }
@@ -8472,39 +8561,6 @@
8472
8561
  return _self;
8473
8562
  }
8474
8563
 
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
8564
  function isMultilineLabel(textNode) {
8509
8565
  return textNode.childNodes.length > 1;
8510
8566
  }
@@ -11297,25 +11353,6 @@
11297
11353
  };
11298
11354
  }
11299
11355
 
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
11356
 
11320
11357
  // Update map frame after user navigates the map in frame edit mode
11321
11358
  function updateFrameExtent() {
package/www/mapshaper.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
 
3
- var VERSION = "0.6.52";
3
+ var VERSION = "0.6.54";
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/i.test(file);
11078
+ return /\.gz$/i.test(file);
11079
11079
  }
11080
11080
 
11081
11081
  function isSupportedOutputFormat(fmt) {
@@ -23703,14 +23703,14 @@ ${svg}
23703
23703
  .option('geojson-type', {
23704
23704
  describe: '[GeoJSON] FeatureCollection, GeometryCollection or Feature'
23705
23705
  })
23706
- .option('ndjson', {
23707
- describe: '[GeoJSON/JSON] output newline-delimited features or records',
23708
- type: 'flag'
23709
- })
23710
23706
  .option('hoist', {
23711
23707
  describe: '[GeoJSON] move properties to the root level of each Feature',
23712
23708
  type: 'strings'
23713
23709
  })
23710
+ .option('ndjson', {
23711
+ describe: '[GeoJSON/JSON] output newline-delimited features or records',
23712
+ type: 'flag'
23713
+ })
23714
23714
  .option('width', {
23715
23715
  describe: '[SVG/TopoJSON] pixel width of output (SVG default is 800)',
23716
23716
  type: 'number'
@@ -23865,10 +23865,6 @@ ${svg}
23865
23865
  describe: 'a pair of values (0-100) for limiting a color ramp',
23866
23866
  type: 'numbers'
23867
23867
  })
23868
- .option('range', {
23869
- // describe: 'a pair of numbers defining the effective data range',
23870
- type: 'numbers'
23871
- })
23872
23868
  .option('null-value', {
23873
23869
  describe: 'value (or color) to use for invalid or missing data'
23874
23870
  })
@@ -23895,6 +23891,11 @@ ${svg}
23895
23891
  describe: 'user-defined sequential class breaks',
23896
23892
  type: 'numbers'
23897
23893
  })
23894
+ .option('outer-breaks', {
23895
+ describe: 'min,max breakpoints, to limit the effect of outliers',
23896
+ old_alias: 'range',
23897
+ type: 'numbers'
23898
+ })
23898
23899
  .option('classes', {
23899
23900
  describe: 'number of classes (can be inferred from other options)',
23900
23901
  type: 'integer'
@@ -23904,7 +23905,7 @@ ${svg}
23904
23905
  type: 'flag'
23905
23906
  })
23906
23907
  .option('continuous', {
23907
- describe: 'output continuous interpolated values (experimental)',
23908
+ describe: 'output interpolated values, for unclassed colors',
23908
23909
  type: 'flag'
23909
23910
  })
23910
23911
  .option('index-field', {
@@ -25337,7 +25338,7 @@ ${svg}
25337
25338
  .describe('create commands on-the-fly and run them')
25338
25339
  .option('expression', {
25339
25340
  DEFAULT: true,
25340
- describe: 'JS expression to generate command(s)'
25341
+ describe: 'JS expression or template to generate command(s)'
25341
25342
  })
25342
25343
  // deprecated
25343
25344
  .option('commands', {alias_to: 'expression'})
@@ -32134,23 +32135,22 @@ ${svg}
32134
32135
  }
32135
32136
 
32136
32137
  var ascending = getAscendingNumbers(dataValues);
32137
- if (opts.range) {
32138
- ascending = applyDataRange(ascending, opts.range);
32138
+ if (opts.outer_breaks) {
32139
+ ascending = applyDataRange(ascending, opts.outer_breaks);
32139
32140
  }
32140
32141
  var nullCount = dataValues.length - ascending.length;
32141
32142
  var minVal = ascending[0];
32142
32143
  var maxVal = ascending[ascending.length - 1];
32143
32144
 
32144
- // kludge
32145
- var clamp = opts.range ? function(val) {
32146
- if (val < opts.range[0]) val = opts.range[0];
32147
- if (val > opts.range[1]) val = opts.range[1];
32145
+ var clamp = opts.outer_breaks ? function(val) {
32146
+ if (val < opts.outer_breaks[0]) val = opts.outer_breaks[0];
32147
+ if (val > opts.outer_breaks[1]) val = opts.outer_breaks[1];
32148
32148
  return val;
32149
32149
  } : null;
32150
32150
 
32151
- if (opts.range) {
32152
- minVal = opts.range[0];
32153
- maxVal = opts.range[1];
32151
+ if (opts.outer_breaks) {
32152
+ minVal = opts.outer_breaks[0];
32153
+ maxVal = opts.outer_breaks[1];
32154
32154
  }
32155
32155
 
32156
32156
  if (numBreaks === 0) {
@@ -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 = runGlobalExpression(opts.expression, targets, ctx);
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;
@@ -958,6 +963,10 @@ img.close-btn:hover,
958
963
  vertical-align: middle;
959
964
  }
960
965
 
966
+ .intersection-check {
967
+ display: none;
968
+ }
969
+
961
970
  .intersection-count .icon {
962
971
  background-color: #F24400;
963
972
  display: inline-block;
@@ -966,6 +975,10 @@ img.close-btn:hover,
966
975
  margin: 0 5px 2px 0;
967
976
  }
968
977
 
978
+ .intersection-count .icon.black {
979
+ background-color: black;
980
+ }
981
+
969
982
  .intersection-count .close-btn {
970
983
  width: 16px;
971
984
  height: 16px;
@@ -975,9 +988,9 @@ img.close-btn:hover,
975
988
  margin-left: 2px;
976
989
  }
977
990
 
978
- .intersection-display .text-btn.disabled {
991
+ /*.intersection-display .text-btn.disabled {
979
992
  visibility: hidden;
980
- }
993
+ }*/
981
994
 
982
995
  /* --- Popup -------------------- */
983
996