mapshaper 0.5.66 → 0.5.70

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/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ v0.5.70
2
+ * Added -join duplication option, which duplicates features in the target layer when many-to-one joins occur.
3
+
4
+ v0.5.69
5
+ * Added undocumented -ignore command.
6
+ * Bug fix
7
+
8
+ v0.5.68
9
+ * Bug fix
10
+
11
+ v0.5.67
12
+ * Bug fix
13
+
1
14
  v0.5.66
2
15
  * Improve interactive selection.
3
16
  * Added undocumented -define command.
package/mapshaper.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
 
3
- var VERSION = "0.5.65";
3
+ var VERSION = "0.5.69";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -1167,6 +1167,10 @@
1167
1167
  throw new UserError(formatLogArgs(arguments));
1168
1168
  };
1169
1169
 
1170
+ var _interrupt = function() {
1171
+ throw new NonFatalError(formatLogArgs(arguments));
1172
+ };
1173
+
1170
1174
  var _message = function() {
1171
1175
  logArgs(arguments);
1172
1176
  };
@@ -1185,10 +1189,14 @@
1185
1189
  }
1186
1190
 
1187
1191
  // Handle an error caused by invalid input or misuse of API
1188
- function stop () {
1192
+ function stop() {
1189
1193
  _stop.apply(null, utils.toArray(arguments));
1190
1194
  }
1191
1195
 
1196
+ function interrupt() {
1197
+ _interrupt.apply(null, utils.toArray(arguments));
1198
+ }
1199
+
1192
1200
  // Print a status message
1193
1201
  function message() {
1194
1202
  _message.apply(null, messageArgs(arguments));
@@ -1228,7 +1236,9 @@
1228
1236
  if (utils.isString(err)) {
1229
1237
  err = new UserError(err);
1230
1238
  }
1231
- if (err.name == 'UserError') {
1239
+ if (err.name == 'NonFatalError') {
1240
+ console.error(messageArgs([err.message]).join(' '));
1241
+ } else if (err.name == 'UserError') {
1232
1242
  msg = err.message;
1233
1243
  if (!/Error/.test(msg)) {
1234
1244
  msg = "Error: " + msg;
@@ -1248,6 +1258,12 @@
1248
1258
  return err;
1249
1259
  }
1250
1260
 
1261
+ function NonFatalError(msg) {
1262
+ var err = new Error(msg);
1263
+ err.name = 'NonFatalError';
1264
+ return err;
1265
+ }
1266
+
1251
1267
  function formatColumns(arr, alignments) {
1252
1268
  var widths = arr.reduce(function(memo, line) {
1253
1269
  return line.map(function(str, i) {
@@ -1308,6 +1324,7 @@
1308
1324
  loggingEnabled: loggingEnabled,
1309
1325
  error: error,
1310
1326
  stop: stop,
1327
+ interrupt: interrupt,
1311
1328
  message: message,
1312
1329
  setLoggingFunctions: setLoggingFunctions,
1313
1330
  print: print,
@@ -1315,6 +1332,7 @@
1315
1332
  debug: debug,
1316
1333
  printError: printError,
1317
1334
  UserError: UserError,
1335
+ NonFatalError: NonFatalError,
1318
1336
  formatColumns: formatColumns,
1319
1337
  formatStringsAsGrid: formatStringsAsGrid,
1320
1338
  formatLogArgs: formatLogArgs,
@@ -14306,7 +14324,7 @@
14306
14324
  joinStr = obj.tag == 'text' || obj.tag == 'tspan' ? '' : '\n';
14307
14325
  svg += '>' + joinStr;
14308
14326
  if (obj.value) {
14309
- svg += obj.value;
14327
+ svg += stringEscape(obj.value);
14310
14328
  }
14311
14329
  if (obj.children) {
14312
14330
  svg += obj.children.map(stringify).join(joinStr);
@@ -14329,8 +14347,13 @@
14329
14347
  "'": '''
14330
14348
  };
14331
14349
  function stringEscape(s) {
14332
- return String(s).replace(rxp, function(s) {
14333
- return map[s];
14350
+ return String(s).replace(rxp, function(match, i) {
14351
+ var entity = map[match];
14352
+ // don't replace & with &
14353
+ if (match == '&' && s.substr(i, entity.length) == entity) {
14354
+ return '&';
14355
+ }
14356
+ return entity;
14334
14357
  });
14335
14358
  }
14336
14359
 
@@ -17140,7 +17163,7 @@ ${svg}
17140
17163
  c = buf[i];
17141
17164
  if (c == DQUOTE) {
17142
17165
  inQuotedText = !inQuotedText;
17143
- } else if (c == CR || c == LF) {
17166
+ } else if ((c == CR || c == LF) && !inQuotedText) {
17144
17167
  if (c == CR && i + 1 < bufLen && buf[i + 1] == LF) {
17145
17168
  // first half of CRLF pair: advance one byte
17146
17169
  i++;
@@ -17234,6 +17257,7 @@ ${svg}
17234
17257
  getRowConverter: getRowConverter,
17235
17258
  parseDelimHeaderSection: parseDelimHeaderSection,
17236
17259
  getDelimFieldFilter: getDelimFieldFilter,
17260
+ readLinesAsString: readLinesAsString,
17237
17261
  parseDelimText: parseDelimText
17238
17262
  });
17239
17263
 
@@ -18415,6 +18439,9 @@ ${svg}
18415
18439
  type: 'strings',
18416
18440
  describe: '[CSV] comma-sep. list of fields to import'
18417
18441
  })
18442
+ // .option('csv-comment', {
18443
+ // describe: '[CSV] comment line character(s)'
18444
+ // })
18418
18445
  .option('decimal-comma', {
18419
18446
  type: 'flag',
18420
18447
  describe: '[CSV] import numbers formatted like 1.000,01 or 1 000,01'
@@ -19164,6 +19191,14 @@ ${svg}
19164
19191
  .option('target', targetOpt)
19165
19192
  .option('no-replace', noReplaceOpt);
19166
19193
 
19194
+ parser.command('ignore')
19195
+ // .describe('stop processing if a condition is met')
19196
+ .option('empty', {
19197
+ describe: 'ignore empty files',
19198
+ type: 'flag'
19199
+ })
19200
+ .option('target', targetOpt);
19201
+
19167
19202
  parser.command('inlay')
19168
19203
  .describe('inscribe a polygon layer inside another polygon layer')
19169
19204
  .option('source', {
@@ -19235,6 +19270,10 @@ ${svg}
19235
19270
  // describe: 'use planar geometry when interpolating by area' // useful for testing
19236
19271
  type: 'flag'
19237
19272
  })
19273
+ .option('duplication', {
19274
+ describe: 'duplicate target features on many-to-one joins',
19275
+ type: 'flag'
19276
+ })
19238
19277
  .option('string-fields', stringFieldsOpt)
19239
19278
  .option('field-types', fieldTypesOpt)
19240
19279
  .option('sum-fields', {
@@ -29801,13 +29840,19 @@ ${svg}
29801
29840
  getJoinFilter: getJoinFilter
29802
29841
  });
29803
29842
 
29843
+ function joinTables(dest, src, join, opts) {
29844
+ return joinTableToLayer({data: dest}, src, join, opts);
29845
+ }
29846
+
29804
29847
  // Join data from @src table to records in @dest table
29805
29848
  // @join function
29806
29849
  // Receives index of record in the dest table
29807
29850
  // Returns array of matching records in src table, or null if no matches
29808
29851
  //
29809
- function joinTables(dest, src, join, opts) {
29810
- var srcRecords = src.getRecords(),
29852
+ function joinTableToLayer(destLyr, src, join, opts) {
29853
+ var dest = destLyr.data,
29854
+ useDuplication = !!opts.duplication,
29855
+ srcRecords = src.getRecords(),
29811
29856
  destRecords = dest.getRecords(),
29812
29857
  prefix = opts.prefix || '',
29813
29858
  unmatchedRecords = [],
@@ -29822,6 +29867,14 @@ ${svg}
29822
29867
  retn = {},
29823
29868
  srcRec, srcId, destRec, joins, count, filter, calc, i, j, n, m;
29824
29869
 
29870
+ // support for duplication
29871
+ var duplicateRecords, destShapes;
29872
+ if (useDuplication) {
29873
+ if (opts.calc) stop('duplication and calc options cannot be used together');
29874
+ duplicateRecords = dest.clone().getRecords();
29875
+ destShapes = destLyr.shapes || [];
29876
+ }
29877
+
29825
29878
  if (opts.where) {
29826
29879
  filter = getJoinFilter(src, opts.where);
29827
29880
  }
@@ -29831,7 +29884,8 @@ ${svg}
29831
29884
  }
29832
29885
 
29833
29886
  // join source records to target records
29834
- for (i=0, n=destRecords.length; i<n; i++) {
29887
+ n = destRecords.length;
29888
+ for (i=0; i<n; i++) {
29835
29889
  destRec = destRecords[i];
29836
29890
  joins = join(i);
29837
29891
  if (joins && filter) {
@@ -29842,7 +29896,12 @@ ${svg}
29842
29896
  for (j=0, count=0, m=joins ? joins.length : 0; j<m; j++) {
29843
29897
  srcId = joins[j];
29844
29898
  srcRec = srcRecords[srcId];
29845
- if (count === 0) {
29899
+ if (count > 0 && useDuplication) {
29900
+ destRec = copyRecord(duplicateRecords[i]);
29901
+ destRecords.push(destRec);
29902
+ destShapes.push(cloneShape(destShapes[i]));
29903
+ }
29904
+ if (count === 0 || useDuplication) {
29846
29905
  if (copyFields.length > 0) {
29847
29906
  // only copying the first match
29848
29907
  joinByCopy(destRec, srcRec, copyFields, prefix);
@@ -29874,7 +29933,7 @@ ${svg}
29874
29933
  }
29875
29934
  }
29876
29935
 
29877
- printJoinMessage(matchCount, destRecords.length,
29936
+ printJoinMessage(matchCount, n,
29878
29937
  countJoins(joinCounts), srcRecords.length, skipCount, collisionCount, collisionFields);
29879
29938
 
29880
29939
  if (opts.unjoined) {
@@ -29902,6 +29961,7 @@ ${svg}
29902
29961
  });
29903
29962
  }
29904
29963
 
29964
+
29905
29965
  function countJoins(counts) {
29906
29966
  var joinCount = 0;
29907
29967
  for (var i=0, n=counts.length; i<n; i++) {
@@ -29964,14 +30024,16 @@ ${svg}
29964
30024
 
29965
30025
  function printJoinMessage(matches, n, joins, m, skipped, collisions, collisionFields) {
29966
30026
  // TODO: add tip for troubleshooting join problems, if join is less than perfect.
30027
+ var unmatched = n - matches;
29967
30028
  if (matches > 0 === false) {
29968
30029
  message("No records could be joined");
29969
30030
  return;
29970
30031
  }
29971
30032
  message(utils.format("Joined data from %'d source record%s to %'d target record%s",
29972
30033
  joins, utils.pluralSuffix(joins), matches, utils.pluralSuffix(matches)));
29973
- if (matches < n) {
29974
- message(utils.format('%d/%d target records received no data', n-matches, n));
30034
+ if (unmatched > 0) {
30035
+ message(utils.format('%d target record%s received no data', unmatched, utils.pluralSuffix(unmatched)));
30036
+ // message(utils.format('%d target records received no data', n-matches));
29975
30037
  }
29976
30038
  if (joins < m) {
29977
30039
  message(utils.format("%d/%d source records could not be joined", m-joins, m));
@@ -30020,6 +30082,7 @@ ${svg}
30020
30082
  var JoinTables = /*#__PURE__*/Object.freeze({
30021
30083
  __proto__: null,
30022
30084
  joinTables: joinTables,
30085
+ joinTableToLayer: joinTableToLayer,
30023
30086
  validateFieldNames: validateFieldNames,
30024
30087
  updateUnmatchedRecord: updateUnmatchedRecord,
30025
30088
  findCollisionFields: findCollisionFields,
@@ -31486,13 +31549,13 @@ ${svg}
31486
31549
  // TODO: option to copy points that can't be joined to a new layer
31487
31550
  var joinFunction = getPolygonToPointsFunction(targetLyr, arcs, pointLyr, opts);
31488
31551
  prepJoinLayers(targetLyr, pointLyr);
31489
- return joinTables(targetLyr.data, pointLyr.data, joinFunction, opts);
31552
+ return joinTableToLayer(targetLyr, pointLyr.data, joinFunction, opts);
31490
31553
  }
31491
31554
 
31492
31555
  function joinPolygonsToPoints(targetLyr, polygonLyr, arcs, opts) {
31493
31556
  var joinFunction = getPointToPolygonsFunction(targetLyr, polygonLyr, arcs, opts);
31494
31557
  prepJoinLayers(targetLyr, polygonLyr);
31495
- return joinTables(targetLyr.data, polygonLyr.data, joinFunction, opts);
31558
+ return joinTableToLayer(targetLyr, polygonLyr.data, joinFunction, opts);
31496
31559
  }
31497
31560
 
31498
31561
 
@@ -33278,6 +33341,12 @@ ${svg}
33278
33341
  };
33279
33342
  }
33280
33343
 
33344
+ cmd.ignore = function(targetLayer, dataset, opts) {
33345
+ if (opts.empty && layerIsEmpty(targetLayer)) {
33346
+ interrupt('Layer is empty, stopping processing');
33347
+ }
33348
+ };
33349
+
33281
33350
  cmd.include = function(opts) {
33282
33351
  var content, obj, context;
33283
33352
  // TODO: handle web context
@@ -33653,9 +33722,10 @@ ${svg}
33653
33722
 
33654
33723
  var joinOpts = utils.extend({}, opts);
33655
33724
  var joinFunction = getPolygonToPolygonFunction(targetLyr, sourceLyr, mosaicIndex, opts);
33656
- var retn = joinTables(targetLyr.data, sourceLyr.data, joinFunction, joinOpts);
33725
+ var retn = joinTableToLayer(targetLyr, sourceLyr.data, joinFunction, joinOpts);
33657
33726
 
33658
33727
  if (opts.interpolate) {
33728
+ if (opts.duplication) stop('duplication and interpolate options cannot be used together');
33659
33729
  interpolateFieldsByArea(targetLyr, sourceLyr, mosaicIndex, opts);
33660
33730
  }
33661
33731
  return retn;
@@ -34570,7 +34640,7 @@ ${svg}
34570
34640
  function joinPointsToPoints(targetLyr, srcLyr, crs, opts) {
34571
34641
  var joinFunction = getPointToPointFunction(targetLyr, srcLyr, crs, opts);
34572
34642
  prepJoinLayers(targetLyr, srcLyr);
34573
- return joinTables(targetLyr.data, srcLyr.data, joinFunction, opts);
34643
+ return joinTableToLayer(targetLyr, srcLyr.data, joinFunction, opts);
34574
34644
  }
34575
34645
 
34576
34646
  function getPointToPointFunction(targetLyr, srcLyr, crs, opts) {
@@ -34620,14 +34690,14 @@ ${svg}
34620
34690
  }
34621
34691
  };
34622
34692
 
34623
- function joinAttributesToFeatures(lyr, srcTable, opts) {
34693
+ function joinAttributesToFeatures(destLyr, srcTable, opts) {
34624
34694
  var keys = opts.keys,
34625
34695
  destKey = keys[0],
34626
34696
  srcKey = keys[1],
34627
- destTable = lyr.data,
34697
+ destTable = destLyr.data,
34628
34698
  joinFunction = getJoinByKey(destTable, destKey, srcTable, srcKey);
34629
34699
  validateFieldNames(keys);
34630
- return joinTables(destTable, srcTable, joinFunction, opts);
34700
+ return joinTableToLayer(destLyr, srcTable, joinFunction, opts);
34631
34701
  }
34632
34702
 
34633
34703
  // Return a function for translating a target id to an array of source ids based on values
@@ -37703,6 +37773,9 @@ ${svg}
37703
37773
  outputLayers = targetDataset.layers; // kludge to allow layer naming below
37704
37774
  }
37705
37775
 
37776
+ } else if (name == 'ignore') {
37777
+ applyCommandToEachLayer(cmd.ignore, targetLayers, targetDataset, opts);
37778
+
37706
37779
  } else if (name == 'include') {
37707
37780
  cmd.include(opts);
37708
37781
 
@@ -38073,6 +38146,7 @@ ${svg}
38073
38146
  cmd.options.output = outputArr;
38074
38147
  }
38075
38148
  });
38149
+
38076
38150
  runParsedCommands(commands, null, callback);
38077
38151
  }
38078
38152
 
@@ -38133,10 +38207,6 @@ ${svg}
38133
38207
  error("Changed in v0.4: runParsedCommands() takes a Catalog object");
38134
38208
  }
38135
38209
 
38136
- if (!utils.isFunction(done)) {
38137
- error("Missing a callback function");
38138
- }
38139
-
38140
38210
  if (!utils.isArray(commands)) {
38141
38211
  error("Expected an array of parsed commands");
38142
38212
  }
@@ -38157,18 +38227,25 @@ ${svg}
38157
38227
  // that it can modify the output dataset in-place instead of making a copy.
38158
38228
  commands[commands.length-1].options.final = true;
38159
38229
  }
38160
- commands = divideImportCommand(commands);
38161
- utils.reduceAsync(commands, catalog, nextCommand, done);
38162
38230
 
38163
- function nextCommand(catalog, cmd, next) {
38164
- setStateVar('current_command', cmd.name); // for log msgs
38165
- setStateVar('verbose', !!cmd.options.verbose);
38166
- setStateVar('debug', !!cmd.options.debug);
38167
- runCommand(cmd, catalog, next);
38231
+ var groups = divideImportCommand(commands);
38232
+ if (groups.length == 1) {
38233
+ // run a simple sequence of commands (input files are not batched)
38234
+ return runParsedCommands2(commands, catalog, done);
38235
+ }
38236
+
38237
+ // run duplicated commands (i.e. batch mode)
38238
+ utils.reduceAsync(groups, catalog, nextGroup, done);
38239
+
38240
+ function nextGroup(catalog, commands, next) {
38241
+ runParsedCommands2(commands, catalog, function(err, catalog) {
38242
+ err = filterError(err);
38243
+ next(err, catalog);
38244
+ });
38168
38245
  }
38169
38246
 
38170
38247
  function done(err, catalog) {
38171
- if (err) printError(err);
38248
+ err = filterError(err);
38172
38249
  cb(err, catalog);
38173
38250
  setStateVar('current_command', null);
38174
38251
  setStateVar('verbose', false);
@@ -38176,11 +38253,31 @@ ${svg}
38176
38253
  }
38177
38254
  }
38178
38255
 
38256
+ function filterError(err) {
38257
+ if (err) printError(err);
38258
+ if (err && err.name == 'NonFatalError') {
38259
+ return null;
38260
+ }
38261
+ return err;
38262
+ }
38263
+
38264
+ function runParsedCommands2(commands, catalog, cb) {
38265
+ utils.reduceAsync(commands, catalog, nextCommand, cb);
38266
+
38267
+ function nextCommand(catalog, cmd, next) {
38268
+ setStateVar('current_command', cmd.name); // for log msgs
38269
+ setStateVar('verbose', !!cmd.options.verbose);
38270
+ setStateVar('debug', !!cmd.options.debug);
38271
+ runCommand(cmd, catalog, next);
38272
+ }
38273
+ }
38274
+
38275
+
38179
38276
  // If an initial import command indicates that several input files should be
38180
38277
  // processed separately, then duplicate the sequence of commands to run
38181
38278
  // once for each input file
38182
38279
  // @commands Array of parsed commands
38183
- // Returns: either original command array or array of duplicated commands.
38280
+ // Returns: Array of one or more sequences of parsed commands
38184
38281
  //
38185
38282
  function divideImportCommand(commands) {
38186
38283
  var firstCmd = commands[0],
@@ -38188,23 +38285,23 @@ ${svg}
38188
38285
 
38189
38286
  if (firstCmd.name != 'i' || opts.stdin || opts.merge_files ||
38190
38287
  opts.combine_files || !opts.files || opts.files.length < 2) {
38191
- return commands;
38288
+ return [commands];
38192
38289
  }
38193
38290
 
38194
- return (opts.files).reduce(function(memo, file) {
38195
- var importCmd = {
38291
+ return opts.files.map(function(file) {
38292
+ var group = [{
38196
38293
  name: 'i',
38197
38294
  options: utils.defaults({
38198
38295
  files:[file],
38199
38296
  replace: true // kludge to replace data catalog
38200
38297
  }, opts)
38201
- };
38202
- memo.push(importCmd);
38203
- memo.push.apply(memo, commands.slice(1));
38204
- return memo;
38205
- }, []);
38298
+ }];
38299
+ group.push.apply(group, commands.slice(1));
38300
+ return group;
38301
+ });
38206
38302
  }
38207
38303
 
38304
+
38208
38305
  function printStartupMessages() {
38209
38306
  // print heap memory message if running with a custom amount
38210
38307
  var rxp = /^--max-old-space-size=([0-9]+)$/;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.5.66",
3
+ "version": "0.5.70",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -42,7 +42,8 @@
42
42
  "dependencies": {
43
43
  "commander": "^5.1.0",
44
44
  "cookies": "^0.8.0",
45
- "d3-scale-chromatic": "^2.0.0",
45
+ "d3-color": "2.0.0",
46
+ "d3-scale-chromatic": "2.0.0",
46
47
  "delaunator": "^5.0.0",
47
48
  "flatbush": "^3.2.1",
48
49
  "geokdbush": "^1.1.0",
package/www/elements.css CHANGED
@@ -144,7 +144,7 @@ div.tip {
144
144
 
145
145
  .dialog-btn {
146
146
  display: inline-block;
147
- margin-bottom: 4px;
147
+ margin-bottom: 1px;
148
148
  margin-top: 1px;
149
149
  font-size: 13px;
150
150
  color: white;
@@ -3976,6 +3976,10 @@
3976
3976
  throw new UserError$1(formatLogArgs(arguments));
3977
3977
  };
3978
3978
 
3979
+ var _interrupt = function() {
3980
+ throw new NonFatalError(formatLogArgs(arguments));
3981
+ };
3982
+
3979
3983
  var _message = function() {
3980
3984
  logArgs(arguments);
3981
3985
  };
@@ -3994,10 +3998,14 @@
3994
3998
  }
3995
3999
 
3996
4000
  // Handle an error caused by invalid input or misuse of API
3997
- function stop$1 () {
4001
+ function stop$1() {
3998
4002
  _stop.apply(null, utils$1.toArray(arguments));
3999
4003
  }
4000
4004
 
4005
+ function interrupt() {
4006
+ _interrupt.apply(null, utils$1.toArray(arguments));
4007
+ }
4008
+
4001
4009
  // Print a status message
4002
4010
  function message$1() {
4003
4011
  _message.apply(null, messageArgs(arguments));
@@ -4037,7 +4045,9 @@
4037
4045
  if (utils$1.isString(err)) {
4038
4046
  err = new UserError$1(err);
4039
4047
  }
4040
- if (err.name == 'UserError') {
4048
+ if (err.name == 'NonFatalError') {
4049
+ console.error(messageArgs([err.message]).join(' '));
4050
+ } else if (err.name == 'UserError') {
4041
4051
  msg = err.message;
4042
4052
  if (!/Error/.test(msg)) {
4043
4053
  msg = "Error: " + msg;
@@ -4057,6 +4067,12 @@
4057
4067
  return err;
4058
4068
  }
4059
4069
 
4070
+ function NonFatalError(msg) {
4071
+ var err = new Error(msg);
4072
+ err.name = 'NonFatalError';
4073
+ return err;
4074
+ }
4075
+
4060
4076
  function formatColumns(arr, alignments) {
4061
4077
  var widths = arr.reduce(function(memo, line) {
4062
4078
  return line.map(function(str, i) {
@@ -9526,7 +9542,6 @@
9526
9542
  return style;
9527
9543
  }
9528
9544
 
9529
-
9530
9545
  // Returns a display style for the overlay layer.
9531
9546
  // The overlay layer renders several kinds of feature, each of which is displayed
9532
9547
  // with a different style.
package/www/mapshaper.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
 
3
- var VERSION = "0.5.65";
3
+ var VERSION = "0.5.69";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -1167,6 +1167,10 @@
1167
1167
  throw new UserError(formatLogArgs(arguments));
1168
1168
  };
1169
1169
 
1170
+ var _interrupt = function() {
1171
+ throw new NonFatalError(formatLogArgs(arguments));
1172
+ };
1173
+
1170
1174
  var _message = function() {
1171
1175
  logArgs(arguments);
1172
1176
  };
@@ -1185,10 +1189,14 @@
1185
1189
  }
1186
1190
 
1187
1191
  // Handle an error caused by invalid input or misuse of API
1188
- function stop () {
1192
+ function stop() {
1189
1193
  _stop.apply(null, utils.toArray(arguments));
1190
1194
  }
1191
1195
 
1196
+ function interrupt() {
1197
+ _interrupt.apply(null, utils.toArray(arguments));
1198
+ }
1199
+
1192
1200
  // Print a status message
1193
1201
  function message() {
1194
1202
  _message.apply(null, messageArgs(arguments));
@@ -1228,7 +1236,9 @@
1228
1236
  if (utils.isString(err)) {
1229
1237
  err = new UserError(err);
1230
1238
  }
1231
- if (err.name == 'UserError') {
1239
+ if (err.name == 'NonFatalError') {
1240
+ console.error(messageArgs([err.message]).join(' '));
1241
+ } else if (err.name == 'UserError') {
1232
1242
  msg = err.message;
1233
1243
  if (!/Error/.test(msg)) {
1234
1244
  msg = "Error: " + msg;
@@ -1248,6 +1258,12 @@
1248
1258
  return err;
1249
1259
  }
1250
1260
 
1261
+ function NonFatalError(msg) {
1262
+ var err = new Error(msg);
1263
+ err.name = 'NonFatalError';
1264
+ return err;
1265
+ }
1266
+
1251
1267
  function formatColumns(arr, alignments) {
1252
1268
  var widths = arr.reduce(function(memo, line) {
1253
1269
  return line.map(function(str, i) {
@@ -1308,6 +1324,7 @@
1308
1324
  loggingEnabled: loggingEnabled,
1309
1325
  error: error,
1310
1326
  stop: stop,
1327
+ interrupt: interrupt,
1311
1328
  message: message,
1312
1329
  setLoggingFunctions: setLoggingFunctions,
1313
1330
  print: print,
@@ -1315,6 +1332,7 @@
1315
1332
  debug: debug,
1316
1333
  printError: printError,
1317
1334
  UserError: UserError,
1335
+ NonFatalError: NonFatalError,
1318
1336
  formatColumns: formatColumns,
1319
1337
  formatStringsAsGrid: formatStringsAsGrid,
1320
1338
  formatLogArgs: formatLogArgs,
@@ -14306,7 +14324,7 @@
14306
14324
  joinStr = obj.tag == 'text' || obj.tag == 'tspan' ? '' : '\n';
14307
14325
  svg += '>' + joinStr;
14308
14326
  if (obj.value) {
14309
- svg += obj.value;
14327
+ svg += stringEscape(obj.value);
14310
14328
  }
14311
14329
  if (obj.children) {
14312
14330
  svg += obj.children.map(stringify).join(joinStr);
@@ -14329,8 +14347,13 @@
14329
14347
  "'": '&apos;'
14330
14348
  };
14331
14349
  function stringEscape(s) {
14332
- return String(s).replace(rxp, function(s) {
14333
- return map[s];
14350
+ return String(s).replace(rxp, function(match, i) {
14351
+ var entity = map[match];
14352
+ // don't replace &amp; with &amp;amp;
14353
+ if (match == '&' && s.substr(i, entity.length) == entity) {
14354
+ return '&';
14355
+ }
14356
+ return entity;
14334
14357
  });
14335
14358
  }
14336
14359
 
@@ -17140,7 +17163,7 @@ ${svg}
17140
17163
  c = buf[i];
17141
17164
  if (c == DQUOTE) {
17142
17165
  inQuotedText = !inQuotedText;
17143
- } else if (c == CR || c == LF) {
17166
+ } else if ((c == CR || c == LF) && !inQuotedText) {
17144
17167
  if (c == CR && i + 1 < bufLen && buf[i + 1] == LF) {
17145
17168
  // first half of CRLF pair: advance one byte
17146
17169
  i++;
@@ -17234,6 +17257,7 @@ ${svg}
17234
17257
  getRowConverter: getRowConverter,
17235
17258
  parseDelimHeaderSection: parseDelimHeaderSection,
17236
17259
  getDelimFieldFilter: getDelimFieldFilter,
17260
+ readLinesAsString: readLinesAsString,
17237
17261
  parseDelimText: parseDelimText
17238
17262
  });
17239
17263
 
@@ -18415,6 +18439,9 @@ ${svg}
18415
18439
  type: 'strings',
18416
18440
  describe: '[CSV] comma-sep. list of fields to import'
18417
18441
  })
18442
+ // .option('csv-comment', {
18443
+ // describe: '[CSV] comment line character(s)'
18444
+ // })
18418
18445
  .option('decimal-comma', {
18419
18446
  type: 'flag',
18420
18447
  describe: '[CSV] import numbers formatted like 1.000,01 or 1 000,01'
@@ -19164,6 +19191,14 @@ ${svg}
19164
19191
  .option('target', targetOpt)
19165
19192
  .option('no-replace', noReplaceOpt);
19166
19193
 
19194
+ parser.command('ignore')
19195
+ // .describe('stop processing if a condition is met')
19196
+ .option('empty', {
19197
+ describe: 'ignore empty files',
19198
+ type: 'flag'
19199
+ })
19200
+ .option('target', targetOpt);
19201
+
19167
19202
  parser.command('inlay')
19168
19203
  .describe('inscribe a polygon layer inside another polygon layer')
19169
19204
  .option('source', {
@@ -19235,6 +19270,10 @@ ${svg}
19235
19270
  // describe: 'use planar geometry when interpolating by area' // useful for testing
19236
19271
  type: 'flag'
19237
19272
  })
19273
+ .option('duplication', {
19274
+ describe: 'duplicate target features on many-to-one joins',
19275
+ type: 'flag'
19276
+ })
19238
19277
  .option('string-fields', stringFieldsOpt)
19239
19278
  .option('field-types', fieldTypesOpt)
19240
19279
  .option('sum-fields', {
@@ -29801,13 +29840,19 @@ ${svg}
29801
29840
  getJoinFilter: getJoinFilter
29802
29841
  });
29803
29842
 
29843
+ function joinTables(dest, src, join, opts) {
29844
+ return joinTableToLayer({data: dest}, src, join, opts);
29845
+ }
29846
+
29804
29847
  // Join data from @src table to records in @dest table
29805
29848
  // @join function
29806
29849
  // Receives index of record in the dest table
29807
29850
  // Returns array of matching records in src table, or null if no matches
29808
29851
  //
29809
- function joinTables(dest, src, join, opts) {
29810
- var srcRecords = src.getRecords(),
29852
+ function joinTableToLayer(destLyr, src, join, opts) {
29853
+ var dest = destLyr.data,
29854
+ useDuplication = !!opts.duplication,
29855
+ srcRecords = src.getRecords(),
29811
29856
  destRecords = dest.getRecords(),
29812
29857
  prefix = opts.prefix || '',
29813
29858
  unmatchedRecords = [],
@@ -29822,6 +29867,14 @@ ${svg}
29822
29867
  retn = {},
29823
29868
  srcRec, srcId, destRec, joins, count, filter, calc, i, j, n, m;
29824
29869
 
29870
+ // support for duplication
29871
+ var duplicateRecords, destShapes;
29872
+ if (useDuplication) {
29873
+ if (opts.calc) stop('duplication and calc options cannot be used together');
29874
+ duplicateRecords = dest.clone().getRecords();
29875
+ destShapes = destLyr.shapes || [];
29876
+ }
29877
+
29825
29878
  if (opts.where) {
29826
29879
  filter = getJoinFilter(src, opts.where);
29827
29880
  }
@@ -29831,7 +29884,8 @@ ${svg}
29831
29884
  }
29832
29885
 
29833
29886
  // join source records to target records
29834
- for (i=0, n=destRecords.length; i<n; i++) {
29887
+ n = destRecords.length;
29888
+ for (i=0; i<n; i++) {
29835
29889
  destRec = destRecords[i];
29836
29890
  joins = join(i);
29837
29891
  if (joins && filter) {
@@ -29842,7 +29896,12 @@ ${svg}
29842
29896
  for (j=0, count=0, m=joins ? joins.length : 0; j<m; j++) {
29843
29897
  srcId = joins[j];
29844
29898
  srcRec = srcRecords[srcId];
29845
- if (count === 0) {
29899
+ if (count > 0 && useDuplication) {
29900
+ destRec = copyRecord(duplicateRecords[i]);
29901
+ destRecords.push(destRec);
29902
+ destShapes.push(cloneShape(destShapes[i]));
29903
+ }
29904
+ if (count === 0 || useDuplication) {
29846
29905
  if (copyFields.length > 0) {
29847
29906
  // only copying the first match
29848
29907
  joinByCopy(destRec, srcRec, copyFields, prefix);
@@ -29874,7 +29933,7 @@ ${svg}
29874
29933
  }
29875
29934
  }
29876
29935
 
29877
- printJoinMessage(matchCount, destRecords.length,
29936
+ printJoinMessage(matchCount, n,
29878
29937
  countJoins(joinCounts), srcRecords.length, skipCount, collisionCount, collisionFields);
29879
29938
 
29880
29939
  if (opts.unjoined) {
@@ -29902,6 +29961,7 @@ ${svg}
29902
29961
  });
29903
29962
  }
29904
29963
 
29964
+
29905
29965
  function countJoins(counts) {
29906
29966
  var joinCount = 0;
29907
29967
  for (var i=0, n=counts.length; i<n; i++) {
@@ -29964,14 +30024,16 @@ ${svg}
29964
30024
 
29965
30025
  function printJoinMessage(matches, n, joins, m, skipped, collisions, collisionFields) {
29966
30026
  // TODO: add tip for troubleshooting join problems, if join is less than perfect.
30027
+ var unmatched = n - matches;
29967
30028
  if (matches > 0 === false) {
29968
30029
  message("No records could be joined");
29969
30030
  return;
29970
30031
  }
29971
30032
  message(utils.format("Joined data from %'d source record%s to %'d target record%s",
29972
30033
  joins, utils.pluralSuffix(joins), matches, utils.pluralSuffix(matches)));
29973
- if (matches < n) {
29974
- message(utils.format('%d/%d target records received no data', n-matches, n));
30034
+ if (unmatched > 0) {
30035
+ message(utils.format('%d target record%s received no data', unmatched, utils.pluralSuffix(unmatched)));
30036
+ // message(utils.format('%d target records received no data', n-matches));
29975
30037
  }
29976
30038
  if (joins < m) {
29977
30039
  message(utils.format("%d/%d source records could not be joined", m-joins, m));
@@ -30020,6 +30082,7 @@ ${svg}
30020
30082
  var JoinTables = /*#__PURE__*/Object.freeze({
30021
30083
  __proto__: null,
30022
30084
  joinTables: joinTables,
30085
+ joinTableToLayer: joinTableToLayer,
30023
30086
  validateFieldNames: validateFieldNames,
30024
30087
  updateUnmatchedRecord: updateUnmatchedRecord,
30025
30088
  findCollisionFields: findCollisionFields,
@@ -31486,13 +31549,13 @@ ${svg}
31486
31549
  // TODO: option to copy points that can't be joined to a new layer
31487
31550
  var joinFunction = getPolygonToPointsFunction(targetLyr, arcs, pointLyr, opts);
31488
31551
  prepJoinLayers(targetLyr, pointLyr);
31489
- return joinTables(targetLyr.data, pointLyr.data, joinFunction, opts);
31552
+ return joinTableToLayer(targetLyr, pointLyr.data, joinFunction, opts);
31490
31553
  }
31491
31554
 
31492
31555
  function joinPolygonsToPoints(targetLyr, polygonLyr, arcs, opts) {
31493
31556
  var joinFunction = getPointToPolygonsFunction(targetLyr, polygonLyr, arcs, opts);
31494
31557
  prepJoinLayers(targetLyr, polygonLyr);
31495
- return joinTables(targetLyr.data, polygonLyr.data, joinFunction, opts);
31558
+ return joinTableToLayer(targetLyr, polygonLyr.data, joinFunction, opts);
31496
31559
  }
31497
31560
 
31498
31561
 
@@ -33278,6 +33341,12 @@ ${svg}
33278
33341
  };
33279
33342
  }
33280
33343
 
33344
+ cmd.ignore = function(targetLayer, dataset, opts) {
33345
+ if (opts.empty && layerIsEmpty(targetLayer)) {
33346
+ interrupt('Layer is empty, stopping processing');
33347
+ }
33348
+ };
33349
+
33281
33350
  cmd.include = function(opts) {
33282
33351
  var content, obj, context;
33283
33352
  // TODO: handle web context
@@ -33653,9 +33722,10 @@ ${svg}
33653
33722
 
33654
33723
  var joinOpts = utils.extend({}, opts);
33655
33724
  var joinFunction = getPolygonToPolygonFunction(targetLyr, sourceLyr, mosaicIndex, opts);
33656
- var retn = joinTables(targetLyr.data, sourceLyr.data, joinFunction, joinOpts);
33725
+ var retn = joinTableToLayer(targetLyr, sourceLyr.data, joinFunction, joinOpts);
33657
33726
 
33658
33727
  if (opts.interpolate) {
33728
+ if (opts.duplication) stop('duplication and interpolate options cannot be used together');
33659
33729
  interpolateFieldsByArea(targetLyr, sourceLyr, mosaicIndex, opts);
33660
33730
  }
33661
33731
  return retn;
@@ -34570,7 +34640,7 @@ ${svg}
34570
34640
  function joinPointsToPoints(targetLyr, srcLyr, crs, opts) {
34571
34641
  var joinFunction = getPointToPointFunction(targetLyr, srcLyr, crs, opts);
34572
34642
  prepJoinLayers(targetLyr, srcLyr);
34573
- return joinTables(targetLyr.data, srcLyr.data, joinFunction, opts);
34643
+ return joinTableToLayer(targetLyr, srcLyr.data, joinFunction, opts);
34574
34644
  }
34575
34645
 
34576
34646
  function getPointToPointFunction(targetLyr, srcLyr, crs, opts) {
@@ -34620,14 +34690,14 @@ ${svg}
34620
34690
  }
34621
34691
  };
34622
34692
 
34623
- function joinAttributesToFeatures(lyr, srcTable, opts) {
34693
+ function joinAttributesToFeatures(destLyr, srcTable, opts) {
34624
34694
  var keys = opts.keys,
34625
34695
  destKey = keys[0],
34626
34696
  srcKey = keys[1],
34627
- destTable = lyr.data,
34697
+ destTable = destLyr.data,
34628
34698
  joinFunction = getJoinByKey(destTable, destKey, srcTable, srcKey);
34629
34699
  validateFieldNames(keys);
34630
- return joinTables(destTable, srcTable, joinFunction, opts);
34700
+ return joinTableToLayer(destLyr, srcTable, joinFunction, opts);
34631
34701
  }
34632
34702
 
34633
34703
  // Return a function for translating a target id to an array of source ids based on values
@@ -37703,6 +37773,9 @@ ${svg}
37703
37773
  outputLayers = targetDataset.layers; // kludge to allow layer naming below
37704
37774
  }
37705
37775
 
37776
+ } else if (name == 'ignore') {
37777
+ applyCommandToEachLayer(cmd.ignore, targetLayers, targetDataset, opts);
37778
+
37706
37779
  } else if (name == 'include') {
37707
37780
  cmd.include(opts);
37708
37781
 
@@ -38073,6 +38146,7 @@ ${svg}
38073
38146
  cmd.options.output = outputArr;
38074
38147
  }
38075
38148
  });
38149
+
38076
38150
  runParsedCommands(commands, null, callback);
38077
38151
  }
38078
38152
 
@@ -38133,10 +38207,6 @@ ${svg}
38133
38207
  error("Changed in v0.4: runParsedCommands() takes a Catalog object");
38134
38208
  }
38135
38209
 
38136
- if (!utils.isFunction(done)) {
38137
- error("Missing a callback function");
38138
- }
38139
-
38140
38210
  if (!utils.isArray(commands)) {
38141
38211
  error("Expected an array of parsed commands");
38142
38212
  }
@@ -38157,18 +38227,25 @@ ${svg}
38157
38227
  // that it can modify the output dataset in-place instead of making a copy.
38158
38228
  commands[commands.length-1].options.final = true;
38159
38229
  }
38160
- commands = divideImportCommand(commands);
38161
- utils.reduceAsync(commands, catalog, nextCommand, done);
38162
38230
 
38163
- function nextCommand(catalog, cmd, next) {
38164
- setStateVar('current_command', cmd.name); // for log msgs
38165
- setStateVar('verbose', !!cmd.options.verbose);
38166
- setStateVar('debug', !!cmd.options.debug);
38167
- runCommand(cmd, catalog, next);
38231
+ var groups = divideImportCommand(commands);
38232
+ if (groups.length == 1) {
38233
+ // run a simple sequence of commands (input files are not batched)
38234
+ return runParsedCommands2(commands, catalog, done);
38235
+ }
38236
+
38237
+ // run duplicated commands (i.e. batch mode)
38238
+ utils.reduceAsync(groups, catalog, nextGroup, done);
38239
+
38240
+ function nextGroup(catalog, commands, next) {
38241
+ runParsedCommands2(commands, catalog, function(err, catalog) {
38242
+ err = filterError(err);
38243
+ next(err, catalog);
38244
+ });
38168
38245
  }
38169
38246
 
38170
38247
  function done(err, catalog) {
38171
- if (err) printError(err);
38248
+ err = filterError(err);
38172
38249
  cb(err, catalog);
38173
38250
  setStateVar('current_command', null);
38174
38251
  setStateVar('verbose', false);
@@ -38176,11 +38253,31 @@ ${svg}
38176
38253
  }
38177
38254
  }
38178
38255
 
38256
+ function filterError(err) {
38257
+ if (err) printError(err);
38258
+ if (err && err.name == 'NonFatalError') {
38259
+ return null;
38260
+ }
38261
+ return err;
38262
+ }
38263
+
38264
+ function runParsedCommands2(commands, catalog, cb) {
38265
+ utils.reduceAsync(commands, catalog, nextCommand, cb);
38266
+
38267
+ function nextCommand(catalog, cmd, next) {
38268
+ setStateVar('current_command', cmd.name); // for log msgs
38269
+ setStateVar('verbose', !!cmd.options.verbose);
38270
+ setStateVar('debug', !!cmd.options.debug);
38271
+ runCommand(cmd, catalog, next);
38272
+ }
38273
+ }
38274
+
38275
+
38179
38276
  // If an initial import command indicates that several input files should be
38180
38277
  // processed separately, then duplicate the sequence of commands to run
38181
38278
  // once for each input file
38182
38279
  // @commands Array of parsed commands
38183
- // Returns: either original command array or array of duplicated commands.
38280
+ // Returns: Array of one or more sequences of parsed commands
38184
38281
  //
38185
38282
  function divideImportCommand(commands) {
38186
38283
  var firstCmd = commands[0],
@@ -38188,23 +38285,23 @@ ${svg}
38188
38285
 
38189
38286
  if (firstCmd.name != 'i' || opts.stdin || opts.merge_files ||
38190
38287
  opts.combine_files || !opts.files || opts.files.length < 2) {
38191
- return commands;
38288
+ return [commands];
38192
38289
  }
38193
38290
 
38194
- return (opts.files).reduce(function(memo, file) {
38195
- var importCmd = {
38291
+ return opts.files.map(function(file) {
38292
+ var group = [{
38196
38293
  name: 'i',
38197
38294
  options: utils.defaults({
38198
38295
  files:[file],
38199
38296
  replace: true // kludge to replace data catalog
38200
38297
  }, opts)
38201
- };
38202
- memo.push(importCmd);
38203
- memo.push.apply(memo, commands.slice(1));
38204
- return memo;
38205
- }, []);
38298
+ }];
38299
+ group.push.apply(group, commands.slice(1));
38300
+ return group;
38301
+ });
38206
38302
  }
38207
38303
 
38304
+
38208
38305
  function printStartupMessages() {
38209
38306
  // print heap memory message if running with a custom amount
38210
38307
  var rxp = /^--max-old-space-size=([0-9]+)$/;
package/www/page.css CHANGED
@@ -456,7 +456,7 @@ body.dragover #import-options-drop-area .drop-area {
456
456
  text-align: left;
457
457
  margin-top: 12px;
458
458
  margin-right: 20px;
459
- padding: 11px 15px 9px 15px;
459
+ padding: 12px 18px 12px 18px;
460
460
  vertical-align: top;
461
461
  display: inline-block;
462
462
  /* border: 1px solid #aaa; */
@@ -904,7 +904,7 @@ img.close-btn:hover,
904
904
  box-sizing: border-box;
905
905
  overflow: hidden;
906
906
  overflow-y: auto;
907
- padding: 4px 12px;
907
+ padding: 4px 12px 3px;
908
908
  line-height: 1.2;
909
909
  }
910
910