mapshaper 0.6.55 → 0.6.56

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.55";
3
+ var VERSION = "0.6.56";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -1403,6 +1403,14 @@
1403
1403
  else console.error(msg);
1404
1404
  }
1405
1405
 
1406
+ function truncateString(str, maxLen) {
1407
+ maxLen = maxLen || 80;
1408
+ if (str.length > maxLen) {
1409
+ str = str.substring(0, maxLen - 3).trimEnd() + '...';
1410
+ }
1411
+ return str;
1412
+ }
1413
+
1406
1414
  var Logging = /*#__PURE__*/Object.freeze({
1407
1415
  __proto__: null,
1408
1416
  getLoggingSetter: getLoggingSetter,
@@ -1424,7 +1432,8 @@
1424
1432
  formatColumns: formatColumns,
1425
1433
  formatStringsAsGrid: formatStringsAsGrid,
1426
1434
  formatLogArgs: formatLogArgs,
1427
- logArgs: logArgs
1435
+ logArgs: logArgs,
1436
+ truncateString: truncateString
1428
1437
  });
1429
1438
 
1430
1439
  function Transform() {
@@ -14209,6 +14218,87 @@
14209
14218
  };
14210
14219
  }
14211
14220
 
14221
+ // Return array of variables on the left side of assignment operations
14222
+ // @hasDot (bool) Return property assignments via dot notation
14223
+ function getAssignedVars(exp, hasDot) {
14224
+ var rxp = /[a-z_$][.a-z0-9_$]*(?= *=[^>=])/ig; // ignore arrow functions and comparisons
14225
+ var matches = exp.match(rxp) || [];
14226
+ var f = function(s) {
14227
+ var i = s.indexOf('.');
14228
+ return hasDot ? i > -1 : i == -1;
14229
+ };
14230
+ var vars = utils.uniq(matches.filter(f));
14231
+ return vars;
14232
+ }
14233
+
14234
+ // Return array of objects with properties assigned via dot notation
14235
+ // e.g. 'd.value = 45' -> ['d']
14236
+ // export function getAssignmentObjects(exp) {
14237
+ // var matches = getAssignedVars(exp, true),
14238
+ // names = [];
14239
+ // matches.forEach(function(s) {
14240
+ // var match = /^([^.]+)\.[^.]+$/.exec(s);
14241
+ // var name = match ? match[1] : null;
14242
+ // if (name && name != 'this') {
14243
+ // names.push(name);
14244
+ // }
14245
+ // });
14246
+ // return utils.uniq(names);
14247
+ // }
14248
+
14249
+ function getExpressionFunction(exp, opts) {
14250
+ var func = compileExpressionToFunction(exp, opts);
14251
+ return function(rec, ctx) {
14252
+ var val;
14253
+ try {
14254
+ val = func.call(ctx.$, rec, ctx);
14255
+ } catch(e) {
14256
+ stop(e.name, "in expression [" + exp + "]:", e.message);
14257
+ }
14258
+ return val;
14259
+ };
14260
+ }
14261
+
14262
+ function compileExpressionToFunction(exp, opts) {
14263
+ // $$ added to avoid duplication with data field variables (an error condition)
14264
+ var functionBody, func;
14265
+ if (opts.returns) {
14266
+ // functionBody = 'return ' + functionBody;
14267
+ functionBody = 'var $$retn = ' + exp + '; return $$retn;';
14268
+ } else {
14269
+ functionBody = exp;
14270
+ }
14271
+ functionBody = 'with($$env){with($$record){ ' + functionBody + '}}';
14272
+ try {
14273
+ func = new Function('$$record,$$env', functionBody);
14274
+ } catch(e) {
14275
+ // if (opts.quiet) throw e;
14276
+ stop(e.name, 'in expression [' + exp + ']');
14277
+ }
14278
+ return func;
14279
+ }
14280
+
14281
+ function getBaseContext(ctx) {
14282
+ ctx = ctx || {};
14283
+ // Mask global properties (is this effective/worth doing?)
14284
+ ctx.globalThis = void 0; // some globals are not iterable
14285
+ (function() {
14286
+ for (var key in this) {
14287
+ ctx[key] = void 0;
14288
+ }
14289
+ }());
14290
+ ctx.console = console;
14291
+ return ctx;
14292
+ }
14293
+
14294
+ var Expressions = /*#__PURE__*/Object.freeze({
14295
+ __proto__: null,
14296
+ getAssignedVars: getAssignedVars,
14297
+ getExpressionFunction: getExpressionFunction,
14298
+ compileExpressionToFunction: compileExpressionToFunction,
14299
+ getBaseContext: getBaseContext
14300
+ });
14301
+
14212
14302
  // Compiled expression returns a value
14213
14303
  function compileValueExpression(exp, lyr, arcs, opts) {
14214
14304
  opts = opts || {};
@@ -14216,7 +14306,6 @@
14216
14306
  return compileFeatureExpression(exp, lyr, arcs, opts);
14217
14307
  }
14218
14308
 
14219
-
14220
14309
  function compileFeaturePairFilterExpression(exp, lyr, arcs) {
14221
14310
  var func = compileFeaturePairExpression(exp, lyr, arcs);
14222
14311
  return function(idA, idB) {
@@ -14232,13 +14321,16 @@
14232
14321
  var exp = cleanExpression(rawExp);
14233
14322
  // don't add layer data to the context
14234
14323
  // (fields are not added to the pair expression context)
14235
- var ctx = getExpressionContext({});
14324
+ var ctx = getFeatureExpressionContext({});
14236
14325
  var getA = getProxyFactory(lyr, arcs);
14237
14326
  var getB = getProxyFactory(lyr, arcs);
14238
14327
  var vars = getAssignedVars(exp);
14239
14328
  var functionBody = "with($$env){with($$record){return " + exp + "}}";
14240
14329
  var func;
14241
14330
 
14331
+ // protect global object from assigned values
14332
+ nullifyUnsetProperties(vars, ctx);
14333
+
14242
14334
  try {
14243
14335
  func = new Function("$$record,$$env", functionBody);
14244
14336
  } catch(e) {
@@ -14246,9 +14338,6 @@
14246
14338
  stop(e.name, "in expression [" + exp + "]");
14247
14339
  }
14248
14340
 
14249
- // protect global object from assigned values
14250
- nullifyUnsetProperties(vars, ctx);
14251
-
14252
14341
  function getProxyFactory(lyr, arcs) {
14253
14342
  var records = lyr.data ? lyr.data.getRecords() : [];
14254
14343
  var getFeatureById = initFeatureProxy(lyr, arcs);
@@ -14284,12 +14373,12 @@
14284
14373
  };
14285
14374
  }
14286
14375
 
14287
- function compileFeatureExpression(rawExp, lyr, arcs, opts_) {
14288
- var opts = utils.extend({}, opts_),
14376
+ function compileFeatureExpression(rawExp, lyr, arcs, optsArg) {
14377
+ var opts = optsArg || {},
14378
+ ctx = opts.context || {},
14289
14379
  exp = cleanExpression(rawExp || ''),
14290
14380
  mutable = !opts.no_assign, // block assignment expressions
14291
- vars = getAssignedVars(exp),
14292
- func, records;
14381
+ vars = getAssignedVars(exp);
14293
14382
 
14294
14383
  if (mutable && vars.length > 0 && !lyr.data) {
14295
14384
  initDataTable(lyr);
@@ -14297,110 +14386,45 @@
14297
14386
 
14298
14387
  if (!mutable) {
14299
14388
  // protect global object from assigned values
14300
- opts.context = opts.context || {};
14301
- nullifyUnsetProperties(vars, opts.context);
14389
+ nullifyUnsetProperties(vars, ctx);
14302
14390
  }
14303
14391
 
14304
- records = lyr.data ? lyr.data.getRecords() : [];
14305
- func = getExpressionFunction(exp, lyr, arcs, opts);
14392
+ var records = lyr.data ? lyr.data.getRecords() : [];
14393
+ var getFeatureById = initFeatureProxy(lyr, arcs, opts);
14394
+ var layerOnlyProxy = addLayerGetters({}, lyr, arcs);
14395
+ var func = getExpressionFunction(exp, opts);
14396
+ ctx = getFeatureExpressionContext(lyr, ctx, opts);
14306
14397
 
14307
- // @destRec (optional) substitute for records[recId] (used by -calc)
14398
+ // recId: index of a data record in the records array.
14399
+ // destRec: (optional argument, used by -calc) an object used to capture assignments
14400
+ // By default, assignments are captured by records[recId]
14401
+ //
14308
14402
  return function(recId, destRec) {
14309
- var record;
14403
+ var rec;
14310
14404
  if (destRec) {
14311
- record = destRec;
14405
+ rec = destRec;
14312
14406
  } else {
14313
- record = records[recId] || (records[recId] = {});
14314
- }
14407
+ rec = records[recId] || (records[recId] = {});
14408
+ }
14409
+ // Assigning feature/layer proxy to '$' ... ctx.$ is also exposed as 'this'
14410
+ // in the expression context.
14411
+ ctx.$ = recId >= 0 ? getFeatureById(recId) : layerOnlyProxy;
14412
+ // "_" is used as an alias for the expression context, so functions can still
14413
+ // be used when masked by variables of the same name.
14414
+ ctx._ = ctx;
14415
+ // Expose data properties using "d", like d3 does. (data propertries are
14416
+ // also available as "this.properties")
14417
+ ctx.d = rec || null;
14315
14418
 
14316
- // initialize new fields to null so assignments work
14317
14419
  if (mutable) {
14318
- nullifyUnsetProperties(vars, record);
14319
- }
14320
- return func(record, recId);
14321
- };
14322
- }
14323
-
14324
- // Return array of variables on the left side of assignment operations
14325
- // @hasDot (bool) Return property assignments via dot notation
14326
- function getAssignedVars(exp, hasDot) {
14327
- var rxp = /[a-z_$][.a-z0-9_$]*(?= *=[^>=])/ig; // ignore arrow functions and comparisons
14328
- var matches = exp.match(rxp) || [];
14329
- var f = function(s) {
14330
- var i = s.indexOf('.');
14331
- return hasDot ? i > -1 : i == -1;
14332
- };
14333
- var vars = utils.uniq(matches.filter(f));
14334
- return vars;
14335
- }
14336
-
14337
- // Return array of objects with properties assigned via dot notation
14338
- // e.g. 'd.value = 45' -> ['d']
14339
- function getAssignmentObjects(exp) {
14340
- var matches = getAssignedVars(exp, true),
14341
- names = [];
14342
- matches.forEach(function(s) {
14343
- var match = /^([^.]+)\.[^.]+$/.exec(s);
14344
- var name = match ? match[1] : null;
14345
- if (name && name != 'this') {
14346
- names.push(name);
14347
- }
14348
- });
14349
- return utils.uniq(names);
14350
- }
14351
-
14352
- function compileExpressionToFunction(exp, opts) {
14353
- // $$ added to avoid duplication with data field variables (an error condition)
14354
- var functionBody, func;
14355
- if (opts.returns) {
14356
- // functionBody = 'return ' + functionBody;
14357
- functionBody = 'var $$retn = ' + exp + '; return $$retn;';
14358
- } else {
14359
- functionBody = exp;
14360
- }
14361
- functionBody = 'with($$env){with($$record){ ' + functionBody + '}}';
14362
- try {
14363
- func = new Function('$$record,$$env', functionBody);
14364
- } catch(e) {
14365
- // if (opts.quiet) throw e;
14366
- stop(e.name, 'in expression [' + exp + ']');
14367
- }
14368
- return func;
14369
- }
14370
-
14371
- function getExpressionFunction(exp, lyr, arcs, opts) {
14372
- var getFeatureById = initFeatureProxy(lyr, arcs, opts);
14373
- var layerOnlyProxy = addLayerGetters({}, lyr, arcs);
14374
- var ctx = getExpressionContext(lyr, opts.context, opts);
14375
- var func = compileExpressionToFunction(exp, opts);
14376
- return function(rec, i) {
14377
- var val;
14378
- // Assigning feature/layer proxy to '$' -- maybe this should be removed,
14379
- // since it is also exposed as "this".
14380
- // (kludge) i is undefined in calc expressions ... we still
14381
- // may need layer data (but not single-feature data)
14382
- ctx.$ = i >= 0 ? getFeatureById(i) : layerOnlyProxy;
14383
- ctx._ = ctx; // provide access to functions when masked by variable names
14384
- ctx.d = rec || null; // expose data properties a la d3 (also exposed as this.properties)
14385
- try {
14386
- val = func.call(ctx.$, rec, ctx);
14387
- } catch(e) {
14388
- // if (opts.quiet) throw e;
14389
- stop(e.name, "in expression [" + exp + "]:", e.message);
14420
+ // initialize assigned variables to rec.null so rec can capture them
14421
+ nullifyUnsetProperties(vars, rec);
14390
14422
  }
14391
- return val;
14423
+ return func(rec, ctx);
14392
14424
  };
14393
14425
  }
14394
14426
 
14395
- function nullifyUnsetProperties(vars, obj) {
14396
- for (var i=0; i<vars.length; i++) {
14397
- if (vars[i] in obj === false) {
14398
- obj[vars[i]] = null;
14399
- }
14400
- }
14401
- }
14402
-
14403
- function getExpressionContext(lyr, mixins, opts) {
14427
+ function getFeatureExpressionContext(lyr, mixins, opts) {
14404
14428
  var defs = getStashedVar('defs');
14405
14429
  var env = getBaseContext();
14406
14430
  var ctx = {};
@@ -14446,29 +14470,20 @@
14446
14470
  }, ctx);
14447
14471
  }
14448
14472
 
14449
- function getBaseContext(ctx) {
14450
- ctx = ctx || {};
14451
- // Mask global properties (is this effective/worth doing?)
14452
- ctx.globalThis = void 0; // some globals are not iterable
14453
- (function() {
14454
- for (var key in this) {
14455
- ctx[key] = void 0;
14473
+ function nullifyUnsetProperties(vars, obj) {
14474
+ for (var i=0; i<vars.length; i++) {
14475
+ if (vars[i] in obj === false) {
14476
+ obj[vars[i]] = null;
14456
14477
  }
14457
- }());
14458
- ctx.console = console;
14459
- return ctx;
14478
+ }
14460
14479
  }
14461
14480
 
14462
- var Expressions = /*#__PURE__*/Object.freeze({
14481
+ var FeatureExpressions = /*#__PURE__*/Object.freeze({
14463
14482
  __proto__: null,
14464
14483
  compileValueExpression: compileValueExpression,
14465
14484
  compileFeaturePairFilterExpression: compileFeaturePairFilterExpression,
14466
14485
  compileFeaturePairExpression: compileFeaturePairExpression,
14467
- compileFeatureExpression: compileFeatureExpression,
14468
- getAssignedVars: getAssignedVars,
14469
- getAssignmentObjects: getAssignmentObjects,
14470
- compileExpressionToFunction: compileExpressionToFunction,
14471
- getBaseContext: getBaseContext
14486
+ compileFeatureExpression: compileFeatureExpression
14472
14487
  });
14473
14488
 
14474
14489
  function getMode(values) {
@@ -25221,11 +25236,12 @@ ${svg}
25221
25236
  describe: 'use field values to calculate data island weights'
25222
25237
  });
25223
25238
 
25224
- parser.command('external')
25225
- .option('module', {
25226
- DEFAULT: true,
25227
- describe: 'name of Node module containing the command'
25228
- });
25239
+ // replaced by -require
25240
+ // parser.command('external')
25241
+ // .option('module', {
25242
+ // DEFAULT: true,
25243
+ // describe: 'name of Node module containing the command'
25244
+ // });
25229
25245
 
25230
25246
  parser.command('filter-points')
25231
25247
  // .describe('remove points that are not part of a group')
@@ -25311,17 +25327,17 @@ ${svg}
25311
25327
  .option('no-replace', noReplaceOpt);
25312
25328
 
25313
25329
  parser.command('require')
25314
- .describe('require a Node module for use in -each expressions')
25330
+ .describe('require a Node module or ES module to use in JS expressions')
25315
25331
  .option('module', {
25316
25332
  DEFAULT: true,
25317
- describe: 'name of Node module or path to module file'
25333
+ describe: 'name of installed module or path to module file'
25318
25334
  })
25319
25335
  .option('alias', {
25320
25336
  describe: 'Set the module name to an alias'
25321
- })
25322
- .option('init', {
25323
- describe: 'JS expression to run after the module loads'
25324
25337
  });
25338
+ // .option('init', {
25339
+ // describe: 'JS expression to run after the module loads'
25340
+ // });
25325
25341
 
25326
25342
  parser.command('rotate')
25327
25343
  // .describe('apply d3-style 3-axis rotation to a lat-long dataset')
@@ -35318,7 +35334,6 @@ ${svg}
35318
35334
  if (targets.length === 0 && targetId) {
35319
35335
  stop('Layer not found:', targetId);
35320
35336
  }
35321
- // var vars = getAssignedVars(exp);
35322
35337
  var defs = getStashedVar('defs') || {};
35323
35338
 
35324
35339
  var ctx;
@@ -41983,8 +41998,8 @@ ${svg}
41983
41998
  return true;
41984
41999
  }
41985
42000
 
41986
- cmd.require = async function(targets, opts) {
41987
- var defs = getStashedVar('defs');
42001
+ cmd.require = async function(opts) {
42002
+ var globals = getStashedVar('defs');
41988
42003
  var moduleFile, moduleName, mod;
41989
42004
  if (!opts.module) {
41990
42005
  stop("Missing module name or path to module");
@@ -42000,25 +42015,33 @@ ${svg}
42000
42015
  moduleFile = require$1('path').join(process.cwd(), moduleFile);
42001
42016
  }
42002
42017
  try {
42003
- mod = require$1(moduleFile || moduleName);
42018
+ // import CJS and ES modules
42019
+ mod = await import(moduleFile || moduleName);
42020
+ if (mod.default) {
42021
+ mod = mod.default;
42022
+ }
42004
42023
  if (typeof mod == 'function') {
42005
- // -require now includes the functionality of the old -external command
42024
+ // assuming that functions are mapshpaper command generators...
42025
+ // this MUST be changed asap.
42006
42026
  var retn = mod(api);
42007
42027
  if (retn && isValidExternalCommand(retn)) {
42008
42028
  cmd.registerCommand(retn.name, retn);
42009
42029
  }
42010
42030
  }
42011
42031
  } catch(e) {
42012
- stop('Unable to load external module:', e.message, getErrorDetail(e));
42032
+ if (!mod) {
42033
+ stop('Unable to load external module:', e.message, getErrorDetail(e));
42034
+ }
42013
42035
  }
42014
42036
  if (moduleName || opts.alias) {
42015
- defs[opts.alias || moduleName] = mod;
42037
+ globals[opts.alias || moduleName] = mod;
42016
42038
  } else {
42017
- Object.assign(defs, mod);
42018
- }
42019
- if (opts.init) {
42020
- await evalTemplateExpression(opts.init, targets);
42039
+ Object.assign(globals, mod);
42021
42040
  }
42041
+ // instead of an init expression, you could use -run <expression>
42042
+ // if (opts.init) {
42043
+ // await evalTemplateExpression(opts.init, targets);
42044
+ // }
42022
42045
  };
42023
42046
 
42024
42047
  // Parse an array or a string of command line tokens into an array of
@@ -42123,7 +42146,8 @@ ${svg}
42123
42146
  stop('Expected a string containing mapshaper commands; received:', tmp);
42124
42147
  }
42125
42148
  if (tmp) {
42126
- message(`command: [${tmp}]`);
42149
+ // truncate message (command might include a large GeoJSON string in an -i command)
42150
+ message(`command: [${truncateString(tmp, 150)}]`);
42127
42151
  commands = parseCommands(tmp);
42128
42152
 
42129
42153
  // TODO: remove duplication with mapshaper-run-commands.mjs
@@ -44473,9 +44497,9 @@ ${svg}
44473
44497
  } else if (name == 'explode') {
44474
44498
  outputLayers = applyCommandToEachLayer(cmd.explodeFeatures, targetLayers, arcs, opts);
44475
44499
 
44476
- } else if (name == 'external') {
44477
- // -require now incorporates -external
44478
- cmd.require(targets, opts);
44500
+ // -require now incorporates functionality of -external
44501
+ // } else if (name == 'external') {
44502
+ // cmd.require(targets, opts);
44479
44503
 
44480
44504
  } else if (name == 'filter') {
44481
44505
  outputLayers = applyCommandToEachLayer(cmd.filterFeatures, targetLayers, arcs, opts);
@@ -44617,7 +44641,7 @@ ${svg}
44617
44641
  cmd.renameLayers(targetLayers, opts.names, job.catalog);
44618
44642
 
44619
44643
  } else if (name == 'require') {
44620
- cmd.require(targets, opts);
44644
+ await cmd.require(opts);
44621
44645
 
44622
44646
  } else if (name == 'rotate') {
44623
44647
  targets.forEach(function(targ) {
@@ -45445,6 +45469,7 @@ ${svg}
45445
45469
  Explode,
45446
45470
  Export,
45447
45471
  Expressions,
45472
+ FeatureExpressions,
45448
45473
  FileExport,
45449
45474
  FileImport,
45450
45475
  FilenameUtils,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.6.55",
3
+ "version": "0.6.56",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
package/www/index.html CHANGED
@@ -309,7 +309,7 @@ encoding=big5</span>. Click to see all options.</div></div></div></div>
309
309
  <div class="coordinate-info colored-text selectable"></div>
310
310
  <div class="intersection-display">
311
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">
312
+ <img class="close-btn" draggable="false" src="images/close.png">
313
313
  <div class="repair-btn text-btn colored-text">Repair</div>
314
314
 
315
315
  </div>
@@ -5374,6 +5374,14 @@
5374
5374
  else console.error(msg);
5375
5375
  }
5376
5376
 
5377
+ function truncateString(str, maxLen) {
5378
+ maxLen = maxLen || 80;
5379
+ if (str.length > maxLen) {
5380
+ str = str.substring(0, maxLen - 3).trimEnd() + '...';
5381
+ }
5382
+ return str;
5383
+ }
5384
+
5377
5385
  var uniqCount = 0;
5378
5386
  function getUniqueName(prefix) {
5379
5387
  return (prefix || "__id_") + (++uniqCount);
package/www/mapshaper.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
 
3
- var VERSION = "0.6.55";
3
+ var VERSION = "0.6.56";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -1403,6 +1403,14 @@
1403
1403
  else console.error(msg);
1404
1404
  }
1405
1405
 
1406
+ function truncateString(str, maxLen) {
1407
+ maxLen = maxLen || 80;
1408
+ if (str.length > maxLen) {
1409
+ str = str.substring(0, maxLen - 3).trimEnd() + '...';
1410
+ }
1411
+ return str;
1412
+ }
1413
+
1406
1414
  var Logging = /*#__PURE__*/Object.freeze({
1407
1415
  __proto__: null,
1408
1416
  getLoggingSetter: getLoggingSetter,
@@ -1424,7 +1432,8 @@
1424
1432
  formatColumns: formatColumns,
1425
1433
  formatStringsAsGrid: formatStringsAsGrid,
1426
1434
  formatLogArgs: formatLogArgs,
1427
- logArgs: logArgs
1435
+ logArgs: logArgs,
1436
+ truncateString: truncateString
1428
1437
  });
1429
1438
 
1430
1439
  function Transform() {
@@ -14209,6 +14218,87 @@
14209
14218
  };
14210
14219
  }
14211
14220
 
14221
+ // Return array of variables on the left side of assignment operations
14222
+ // @hasDot (bool) Return property assignments via dot notation
14223
+ function getAssignedVars(exp, hasDot) {
14224
+ var rxp = /[a-z_$][.a-z0-9_$]*(?= *=[^>=])/ig; // ignore arrow functions and comparisons
14225
+ var matches = exp.match(rxp) || [];
14226
+ var f = function(s) {
14227
+ var i = s.indexOf('.');
14228
+ return hasDot ? i > -1 : i == -1;
14229
+ };
14230
+ var vars = utils.uniq(matches.filter(f));
14231
+ return vars;
14232
+ }
14233
+
14234
+ // Return array of objects with properties assigned via dot notation
14235
+ // e.g. 'd.value = 45' -> ['d']
14236
+ // export function getAssignmentObjects(exp) {
14237
+ // var matches = getAssignedVars(exp, true),
14238
+ // names = [];
14239
+ // matches.forEach(function(s) {
14240
+ // var match = /^([^.]+)\.[^.]+$/.exec(s);
14241
+ // var name = match ? match[1] : null;
14242
+ // if (name && name != 'this') {
14243
+ // names.push(name);
14244
+ // }
14245
+ // });
14246
+ // return utils.uniq(names);
14247
+ // }
14248
+
14249
+ function getExpressionFunction(exp, opts) {
14250
+ var func = compileExpressionToFunction(exp, opts);
14251
+ return function(rec, ctx) {
14252
+ var val;
14253
+ try {
14254
+ val = func.call(ctx.$, rec, ctx);
14255
+ } catch(e) {
14256
+ stop(e.name, "in expression [" + exp + "]:", e.message);
14257
+ }
14258
+ return val;
14259
+ };
14260
+ }
14261
+
14262
+ function compileExpressionToFunction(exp, opts) {
14263
+ // $$ added to avoid duplication with data field variables (an error condition)
14264
+ var functionBody, func;
14265
+ if (opts.returns) {
14266
+ // functionBody = 'return ' + functionBody;
14267
+ functionBody = 'var $$retn = ' + exp + '; return $$retn;';
14268
+ } else {
14269
+ functionBody = exp;
14270
+ }
14271
+ functionBody = 'with($$env){with($$record){ ' + functionBody + '}}';
14272
+ try {
14273
+ func = new Function('$$record,$$env', functionBody);
14274
+ } catch(e) {
14275
+ // if (opts.quiet) throw e;
14276
+ stop(e.name, 'in expression [' + exp + ']');
14277
+ }
14278
+ return func;
14279
+ }
14280
+
14281
+ function getBaseContext(ctx) {
14282
+ ctx = ctx || {};
14283
+ // Mask global properties (is this effective/worth doing?)
14284
+ ctx.globalThis = void 0; // some globals are not iterable
14285
+ (function() {
14286
+ for (var key in this) {
14287
+ ctx[key] = void 0;
14288
+ }
14289
+ }());
14290
+ ctx.console = console;
14291
+ return ctx;
14292
+ }
14293
+
14294
+ var Expressions = /*#__PURE__*/Object.freeze({
14295
+ __proto__: null,
14296
+ getAssignedVars: getAssignedVars,
14297
+ getExpressionFunction: getExpressionFunction,
14298
+ compileExpressionToFunction: compileExpressionToFunction,
14299
+ getBaseContext: getBaseContext
14300
+ });
14301
+
14212
14302
  // Compiled expression returns a value
14213
14303
  function compileValueExpression(exp, lyr, arcs, opts) {
14214
14304
  opts = opts || {};
@@ -14216,7 +14306,6 @@
14216
14306
  return compileFeatureExpression(exp, lyr, arcs, opts);
14217
14307
  }
14218
14308
 
14219
-
14220
14309
  function compileFeaturePairFilterExpression(exp, lyr, arcs) {
14221
14310
  var func = compileFeaturePairExpression(exp, lyr, arcs);
14222
14311
  return function(idA, idB) {
@@ -14232,13 +14321,16 @@
14232
14321
  var exp = cleanExpression(rawExp);
14233
14322
  // don't add layer data to the context
14234
14323
  // (fields are not added to the pair expression context)
14235
- var ctx = getExpressionContext({});
14324
+ var ctx = getFeatureExpressionContext({});
14236
14325
  var getA = getProxyFactory(lyr, arcs);
14237
14326
  var getB = getProxyFactory(lyr, arcs);
14238
14327
  var vars = getAssignedVars(exp);
14239
14328
  var functionBody = "with($$env){with($$record){return " + exp + "}}";
14240
14329
  var func;
14241
14330
 
14331
+ // protect global object from assigned values
14332
+ nullifyUnsetProperties(vars, ctx);
14333
+
14242
14334
  try {
14243
14335
  func = new Function("$$record,$$env", functionBody);
14244
14336
  } catch(e) {
@@ -14246,9 +14338,6 @@
14246
14338
  stop(e.name, "in expression [" + exp + "]");
14247
14339
  }
14248
14340
 
14249
- // protect global object from assigned values
14250
- nullifyUnsetProperties(vars, ctx);
14251
-
14252
14341
  function getProxyFactory(lyr, arcs) {
14253
14342
  var records = lyr.data ? lyr.data.getRecords() : [];
14254
14343
  var getFeatureById = initFeatureProxy(lyr, arcs);
@@ -14284,12 +14373,12 @@
14284
14373
  };
14285
14374
  }
14286
14375
 
14287
- function compileFeatureExpression(rawExp, lyr, arcs, opts_) {
14288
- var opts = utils.extend({}, opts_),
14376
+ function compileFeatureExpression(rawExp, lyr, arcs, optsArg) {
14377
+ var opts = optsArg || {},
14378
+ ctx = opts.context || {},
14289
14379
  exp = cleanExpression(rawExp || ''),
14290
14380
  mutable = !opts.no_assign, // block assignment expressions
14291
- vars = getAssignedVars(exp),
14292
- func, records;
14381
+ vars = getAssignedVars(exp);
14293
14382
 
14294
14383
  if (mutable && vars.length > 0 && !lyr.data) {
14295
14384
  initDataTable(lyr);
@@ -14297,110 +14386,45 @@
14297
14386
 
14298
14387
  if (!mutable) {
14299
14388
  // protect global object from assigned values
14300
- opts.context = opts.context || {};
14301
- nullifyUnsetProperties(vars, opts.context);
14389
+ nullifyUnsetProperties(vars, ctx);
14302
14390
  }
14303
14391
 
14304
- records = lyr.data ? lyr.data.getRecords() : [];
14305
- func = getExpressionFunction(exp, lyr, arcs, opts);
14392
+ var records = lyr.data ? lyr.data.getRecords() : [];
14393
+ var getFeatureById = initFeatureProxy(lyr, arcs, opts);
14394
+ var layerOnlyProxy = addLayerGetters({}, lyr, arcs);
14395
+ var func = getExpressionFunction(exp, opts);
14396
+ ctx = getFeatureExpressionContext(lyr, ctx, opts);
14306
14397
 
14307
- // @destRec (optional) substitute for records[recId] (used by -calc)
14398
+ // recId: index of a data record in the records array.
14399
+ // destRec: (optional argument, used by -calc) an object used to capture assignments
14400
+ // By default, assignments are captured by records[recId]
14401
+ //
14308
14402
  return function(recId, destRec) {
14309
- var record;
14403
+ var rec;
14310
14404
  if (destRec) {
14311
- record = destRec;
14405
+ rec = destRec;
14312
14406
  } else {
14313
- record = records[recId] || (records[recId] = {});
14314
- }
14407
+ rec = records[recId] || (records[recId] = {});
14408
+ }
14409
+ // Assigning feature/layer proxy to '$' ... ctx.$ is also exposed as 'this'
14410
+ // in the expression context.
14411
+ ctx.$ = recId >= 0 ? getFeatureById(recId) : layerOnlyProxy;
14412
+ // "_" is used as an alias for the expression context, so functions can still
14413
+ // be used when masked by variables of the same name.
14414
+ ctx._ = ctx;
14415
+ // Expose data properties using "d", like d3 does. (data propertries are
14416
+ // also available as "this.properties")
14417
+ ctx.d = rec || null;
14315
14418
 
14316
- // initialize new fields to null so assignments work
14317
14419
  if (mutable) {
14318
- nullifyUnsetProperties(vars, record);
14319
- }
14320
- return func(record, recId);
14321
- };
14322
- }
14323
-
14324
- // Return array of variables on the left side of assignment operations
14325
- // @hasDot (bool) Return property assignments via dot notation
14326
- function getAssignedVars(exp, hasDot) {
14327
- var rxp = /[a-z_$][.a-z0-9_$]*(?= *=[^>=])/ig; // ignore arrow functions and comparisons
14328
- var matches = exp.match(rxp) || [];
14329
- var f = function(s) {
14330
- var i = s.indexOf('.');
14331
- return hasDot ? i > -1 : i == -1;
14332
- };
14333
- var vars = utils.uniq(matches.filter(f));
14334
- return vars;
14335
- }
14336
-
14337
- // Return array of objects with properties assigned via dot notation
14338
- // e.g. 'd.value = 45' -> ['d']
14339
- function getAssignmentObjects(exp) {
14340
- var matches = getAssignedVars(exp, true),
14341
- names = [];
14342
- matches.forEach(function(s) {
14343
- var match = /^([^.]+)\.[^.]+$/.exec(s);
14344
- var name = match ? match[1] : null;
14345
- if (name && name != 'this') {
14346
- names.push(name);
14347
- }
14348
- });
14349
- return utils.uniq(names);
14350
- }
14351
-
14352
- function compileExpressionToFunction(exp, opts) {
14353
- // $$ added to avoid duplication with data field variables (an error condition)
14354
- var functionBody, func;
14355
- if (opts.returns) {
14356
- // functionBody = 'return ' + functionBody;
14357
- functionBody = 'var $$retn = ' + exp + '; return $$retn;';
14358
- } else {
14359
- functionBody = exp;
14360
- }
14361
- functionBody = 'with($$env){with($$record){ ' + functionBody + '}}';
14362
- try {
14363
- func = new Function('$$record,$$env', functionBody);
14364
- } catch(e) {
14365
- // if (opts.quiet) throw e;
14366
- stop(e.name, 'in expression [' + exp + ']');
14367
- }
14368
- return func;
14369
- }
14370
-
14371
- function getExpressionFunction(exp, lyr, arcs, opts) {
14372
- var getFeatureById = initFeatureProxy(lyr, arcs, opts);
14373
- var layerOnlyProxy = addLayerGetters({}, lyr, arcs);
14374
- var ctx = getExpressionContext(lyr, opts.context, opts);
14375
- var func = compileExpressionToFunction(exp, opts);
14376
- return function(rec, i) {
14377
- var val;
14378
- // Assigning feature/layer proxy to '$' -- maybe this should be removed,
14379
- // since it is also exposed as "this".
14380
- // (kludge) i is undefined in calc expressions ... we still
14381
- // may need layer data (but not single-feature data)
14382
- ctx.$ = i >= 0 ? getFeatureById(i) : layerOnlyProxy;
14383
- ctx._ = ctx; // provide access to functions when masked by variable names
14384
- ctx.d = rec || null; // expose data properties a la d3 (also exposed as this.properties)
14385
- try {
14386
- val = func.call(ctx.$, rec, ctx);
14387
- } catch(e) {
14388
- // if (opts.quiet) throw e;
14389
- stop(e.name, "in expression [" + exp + "]:", e.message);
14420
+ // initialize assigned variables to rec.null so rec can capture them
14421
+ nullifyUnsetProperties(vars, rec);
14390
14422
  }
14391
- return val;
14423
+ return func(rec, ctx);
14392
14424
  };
14393
14425
  }
14394
14426
 
14395
- function nullifyUnsetProperties(vars, obj) {
14396
- for (var i=0; i<vars.length; i++) {
14397
- if (vars[i] in obj === false) {
14398
- obj[vars[i]] = null;
14399
- }
14400
- }
14401
- }
14402
-
14403
- function getExpressionContext(lyr, mixins, opts) {
14427
+ function getFeatureExpressionContext(lyr, mixins, opts) {
14404
14428
  var defs = getStashedVar('defs');
14405
14429
  var env = getBaseContext();
14406
14430
  var ctx = {};
@@ -14446,29 +14470,20 @@
14446
14470
  }, ctx);
14447
14471
  }
14448
14472
 
14449
- function getBaseContext(ctx) {
14450
- ctx = ctx || {};
14451
- // Mask global properties (is this effective/worth doing?)
14452
- ctx.globalThis = void 0; // some globals are not iterable
14453
- (function() {
14454
- for (var key in this) {
14455
- ctx[key] = void 0;
14473
+ function nullifyUnsetProperties(vars, obj) {
14474
+ for (var i=0; i<vars.length; i++) {
14475
+ if (vars[i] in obj === false) {
14476
+ obj[vars[i]] = null;
14456
14477
  }
14457
- }());
14458
- ctx.console = console;
14459
- return ctx;
14478
+ }
14460
14479
  }
14461
14480
 
14462
- var Expressions = /*#__PURE__*/Object.freeze({
14481
+ var FeatureExpressions = /*#__PURE__*/Object.freeze({
14463
14482
  __proto__: null,
14464
14483
  compileValueExpression: compileValueExpression,
14465
14484
  compileFeaturePairFilterExpression: compileFeaturePairFilterExpression,
14466
14485
  compileFeaturePairExpression: compileFeaturePairExpression,
14467
- compileFeatureExpression: compileFeatureExpression,
14468
- getAssignedVars: getAssignedVars,
14469
- getAssignmentObjects: getAssignmentObjects,
14470
- compileExpressionToFunction: compileExpressionToFunction,
14471
- getBaseContext: getBaseContext
14486
+ compileFeatureExpression: compileFeatureExpression
14472
14487
  });
14473
14488
 
14474
14489
  function getMode(values) {
@@ -25221,11 +25236,12 @@ ${svg}
25221
25236
  describe: 'use field values to calculate data island weights'
25222
25237
  });
25223
25238
 
25224
- parser.command('external')
25225
- .option('module', {
25226
- DEFAULT: true,
25227
- describe: 'name of Node module containing the command'
25228
- });
25239
+ // replaced by -require
25240
+ // parser.command('external')
25241
+ // .option('module', {
25242
+ // DEFAULT: true,
25243
+ // describe: 'name of Node module containing the command'
25244
+ // });
25229
25245
 
25230
25246
  parser.command('filter-points')
25231
25247
  // .describe('remove points that are not part of a group')
@@ -25311,17 +25327,17 @@ ${svg}
25311
25327
  .option('no-replace', noReplaceOpt);
25312
25328
 
25313
25329
  parser.command('require')
25314
- .describe('require a Node module for use in -each expressions')
25330
+ .describe('require a Node module or ES module to use in JS expressions')
25315
25331
  .option('module', {
25316
25332
  DEFAULT: true,
25317
- describe: 'name of Node module or path to module file'
25333
+ describe: 'name of installed module or path to module file'
25318
25334
  })
25319
25335
  .option('alias', {
25320
25336
  describe: 'Set the module name to an alias'
25321
- })
25322
- .option('init', {
25323
- describe: 'JS expression to run after the module loads'
25324
25337
  });
25338
+ // .option('init', {
25339
+ // describe: 'JS expression to run after the module loads'
25340
+ // });
25325
25341
 
25326
25342
  parser.command('rotate')
25327
25343
  // .describe('apply d3-style 3-axis rotation to a lat-long dataset')
@@ -35318,7 +35334,6 @@ ${svg}
35318
35334
  if (targets.length === 0 && targetId) {
35319
35335
  stop('Layer not found:', targetId);
35320
35336
  }
35321
- // var vars = getAssignedVars(exp);
35322
35337
  var defs = getStashedVar('defs') || {};
35323
35338
 
35324
35339
  var ctx;
@@ -41983,8 +41998,8 @@ ${svg}
41983
41998
  return true;
41984
41999
  }
41985
42000
 
41986
- cmd.require = async function(targets, opts) {
41987
- var defs = getStashedVar('defs');
42001
+ cmd.require = async function(opts) {
42002
+ var globals = getStashedVar('defs');
41988
42003
  var moduleFile, moduleName, mod;
41989
42004
  if (!opts.module) {
41990
42005
  stop("Missing module name or path to module");
@@ -42000,25 +42015,33 @@ ${svg}
42000
42015
  moduleFile = require$1('path').join(process.cwd(), moduleFile);
42001
42016
  }
42002
42017
  try {
42003
- mod = require$1(moduleFile || moduleName);
42018
+ // import CJS and ES modules
42019
+ mod = await import(moduleFile || moduleName);
42020
+ if (mod.default) {
42021
+ mod = mod.default;
42022
+ }
42004
42023
  if (typeof mod == 'function') {
42005
- // -require now includes the functionality of the old -external command
42024
+ // assuming that functions are mapshpaper command generators...
42025
+ // this MUST be changed asap.
42006
42026
  var retn = mod(api);
42007
42027
  if (retn && isValidExternalCommand(retn)) {
42008
42028
  cmd.registerCommand(retn.name, retn);
42009
42029
  }
42010
42030
  }
42011
42031
  } catch(e) {
42012
- stop('Unable to load external module:', e.message, getErrorDetail(e));
42032
+ if (!mod) {
42033
+ stop('Unable to load external module:', e.message, getErrorDetail(e));
42034
+ }
42013
42035
  }
42014
42036
  if (moduleName || opts.alias) {
42015
- defs[opts.alias || moduleName] = mod;
42037
+ globals[opts.alias || moduleName] = mod;
42016
42038
  } else {
42017
- Object.assign(defs, mod);
42018
- }
42019
- if (opts.init) {
42020
- await evalTemplateExpression(opts.init, targets);
42039
+ Object.assign(globals, mod);
42021
42040
  }
42041
+ // instead of an init expression, you could use -run <expression>
42042
+ // if (opts.init) {
42043
+ // await evalTemplateExpression(opts.init, targets);
42044
+ // }
42022
42045
  };
42023
42046
 
42024
42047
  // Parse an array or a string of command line tokens into an array of
@@ -42123,7 +42146,8 @@ ${svg}
42123
42146
  stop('Expected a string containing mapshaper commands; received:', tmp);
42124
42147
  }
42125
42148
  if (tmp) {
42126
- message(`command: [${tmp}]`);
42149
+ // truncate message (command might include a large GeoJSON string in an -i command)
42150
+ message(`command: [${truncateString(tmp, 150)}]`);
42127
42151
  commands = parseCommands(tmp);
42128
42152
 
42129
42153
  // TODO: remove duplication with mapshaper-run-commands.mjs
@@ -44473,9 +44497,9 @@ ${svg}
44473
44497
  } else if (name == 'explode') {
44474
44498
  outputLayers = applyCommandToEachLayer(cmd.explodeFeatures, targetLayers, arcs, opts);
44475
44499
 
44476
- } else if (name == 'external') {
44477
- // -require now incorporates -external
44478
- cmd.require(targets, opts);
44500
+ // -require now incorporates functionality of -external
44501
+ // } else if (name == 'external') {
44502
+ // cmd.require(targets, opts);
44479
44503
 
44480
44504
  } else if (name == 'filter') {
44481
44505
  outputLayers = applyCommandToEachLayer(cmd.filterFeatures, targetLayers, arcs, opts);
@@ -44617,7 +44641,7 @@ ${svg}
44617
44641
  cmd.renameLayers(targetLayers, opts.names, job.catalog);
44618
44642
 
44619
44643
  } else if (name == 'require') {
44620
- cmd.require(targets, opts);
44644
+ await cmd.require(opts);
44621
44645
 
44622
44646
  } else if (name == 'rotate') {
44623
44647
  targets.forEach(function(targ) {
@@ -45445,6 +45469,7 @@ ${svg}
45445
45469
  Explode,
45446
45470
  Export,
45447
45471
  Expressions,
45472
+ FeatureExpressions,
45448
45473
  FileExport,
45449
45474
  FileImport,
45450
45475
  FilenameUtils,