mapshaper 0.7.9 → 0.7.11

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
@@ -659,7 +659,7 @@
659
659
  });
660
660
  }
661
661
 
662
- function reorderArray(arr, idxs) {
662
+ function reorderArray$1(arr, idxs) {
663
663
  var len = idxs.length;
664
664
  var arr2 = [];
665
665
  for (var i=0; i<len; i++) {
@@ -1145,7 +1145,7 @@
1145
1145
  parseIntlNumber, parseNumber: parseNumber$1, parseString, pickOne, pluck,
1146
1146
  pluralSuffix, promisify,
1147
1147
  quicksort: quicksort$1, quicksortPartition,
1148
- range, reduceAsync, regexEscape, reorderArray, repeat, repeatString,
1148
+ range, reduceAsync, regexEscape, reorderArray: reorderArray$1, repeat, repeatString,
1149
1149
  replaceArray, rpad, rtrim,
1150
1150
  shuffle, some, sortArrayIndex, sortOn, splitLines, sum: sum$1,
1151
1151
  toArray, toBuffer, trim, trimQuotes,
@@ -3270,7 +3270,7 @@
3270
3270
 
3271
3271
  function findCrossIntersection_fast(ax, ay, bx, by, cx, cy, dx, dy) {
3272
3272
  var den = determinant2D(bx - ax, by - ay, dx - cx, dy - cy);
3273
- var m = orient2D(cx, cy, dx, dy, ax, ay) / den;
3273
+ var m = orient2D$1(cx, cy, dx, dy, ax, ay) / den;
3274
3274
  var p = [ax + m * (bx - ax), ay + m * (by - ay)];
3275
3275
  if (Math.abs(den) < 1e-25) {
3276
3276
  // changed from 1e-18 to 1e-25 (see geom ex1)
@@ -3300,7 +3300,7 @@
3300
3300
  debug('DIV0 error - should have been identified as collinear "touch" intersection.');
3301
3301
  return null;
3302
3302
  }
3303
- var num = orient2D(cx_bi, cy_bi, dx_bi, dy_bi, ax_bi, ay_bi) * k_bi;
3303
+ var num = orient2D$1(cx_bi, cy_bi, dx_bi, dy_bi, ax_bi, ay_bi) * k_bi;
3304
3304
  var m_bi = num / den;
3305
3305
  var x_bi = ax_bi * k_bi + m_bi * (bx_bi - ax_bi);
3306
3306
  var y_bi = ay_bi * k_bi + m_bi * (by_bi - ay_bi);
@@ -3505,7 +3505,7 @@
3505
3505
  // counterclockwise order, a negative value if the points are in clockwise
3506
3506
  // order, and zero if the points are collinear.
3507
3507
  // Source: Jonathan Shewchuk http://www.cs.berkeley.edu/~jrs/meshpapers/robnotes.pdf
3508
- function orient2D(ax, ay, bx, by, cx, cy) {
3508
+ function orient2D$1(ax, ay, bx, by, cx, cy) {
3509
3509
  return determinant2D(ax - cx, ay - cy, bx - cx, by - cy);
3510
3510
  }
3511
3511
 
@@ -3517,7 +3517,7 @@
3517
3517
  var by_bi = BigInt(toScaledStr(by, d));
3518
3518
  var cx_bi = BigInt(toScaledStr(cx, d));
3519
3519
  var cy_bi = BigInt(toScaledStr(cy, d));
3520
- var o2d_bi = orient2D(ax_bi, ay_bi, bx_bi, by_bi, cx_bi, cy_bi);
3520
+ var o2d_bi = orient2D$1(ax_bi, ay_bi, bx_bi, by_bi, cx_bi, cy_bi);
3521
3521
  return fromScaledStr(o2d_bi.toString(), d);
3522
3522
  }
3523
3523
 
@@ -3538,10 +3538,10 @@
3538
3538
  // (Other functions were tried that were more sensitive to floating point errors
3539
3539
  // than this function)
3540
3540
  function segmentHit_fast(ax, ay, bx, by, cx, cy, dx, dy) {
3541
- return orient2D(ax, ay, bx, by, cx, cy) *
3542
- orient2D(ax, ay, bx, by, dx, dy) <= 0 &&
3543
- orient2D(cx, cy, dx, dy, ax, ay) *
3544
- orient2D(cx, cy, dx, dy, bx, by) <= 0;
3541
+ return orient2D$1(ax, ay, bx, by, cx, cy) *
3542
+ orient2D$1(ax, ay, bx, by, dx, dy) <= 0 &&
3543
+ orient2D$1(cx, cy, dx, dy, ax, ay) *
3544
+ orient2D$1(cx, cy, dx, dy, bx, by) <= 0;
3545
3545
  }
3546
3546
 
3547
3547
  // Useful for determining if a segment that intersects another segment is
@@ -3559,7 +3559,7 @@
3559
3559
  dy = by - p3[1],
3560
3560
  cx = p4[0] + dx,
3561
3561
  cy = p4[1] + dy,
3562
- orientation = orient2D(ax, ay, bx, by, cx, cy);
3562
+ orientation = orient2D$1(ax, ay, bx, by, cx, cy);
3563
3563
  if (!orientation) return 0;
3564
3564
  return orientation < 0 ? 1 : -1;
3565
3565
  }
@@ -3568,7 +3568,7 @@
3568
3568
  __proto__: null,
3569
3569
  findClosestPointOnSeg: findClosestPointOnSeg,
3570
3570
  findCrossIntersection_robust: findCrossIntersection_robust,
3571
- orient2D: orient2D,
3571
+ orient2D: orient2D$1,
3572
3572
  orient2D_robust: orient2D_robust,
3573
3573
  segmentHit_fast: segmentHit_fast,
3574
3574
  segmentIntersection: segmentIntersection,
@@ -4362,6 +4362,247 @@
4362
4362
  parseUnknownType: parseUnknownType
4363
4363
  });
4364
4364
 
4365
+ // Lightweight hooks that make in-place mutations observable to GUI undo.
4366
+ // Normal CLI runs leave activeTransaction unset, so capture hooks are no-ops.
4367
+
4368
+ var activeTransaction = null;
4369
+ var nextUndoId = 1;
4370
+ var objectMetadata = new WeakMap();
4371
+
4372
+ function getActiveUndoTransaction() {
4373
+ return activeTransaction;
4374
+ }
4375
+
4376
+ function setActiveUndoTransaction(tx) {
4377
+ activeTransaction = tx || null;
4378
+ }
4379
+
4380
+ function clearActiveUndoTransaction(tx) {
4381
+ if (!tx || activeTransaction == tx) {
4382
+ activeTransaction = null;
4383
+ }
4384
+ }
4385
+
4386
+ function withActiveUndoTransaction(tx, cb) {
4387
+ var prev = activeTransaction;
4388
+ activeTransaction = tx || null;
4389
+ try {
4390
+ return cb();
4391
+ } finally {
4392
+ activeTransaction = prev;
4393
+ }
4394
+ }
4395
+
4396
+ function getUndoMetadata(obj) {
4397
+ var meta = objectMetadata.get(obj);
4398
+ if (!meta) {
4399
+ meta = {
4400
+ id: 'u' + nextUndoId++,
4401
+ revision: 0
4402
+ };
4403
+ objectMetadata.set(obj, meta);
4404
+ }
4405
+ return meta;
4406
+ }
4407
+
4408
+ function getUndoId(obj) {
4409
+ return getUndoMetadata(obj).id;
4410
+ }
4411
+
4412
+ function getUndoRevision(obj) {
4413
+ return getUndoMetadata(obj).revision;
4414
+ }
4415
+
4416
+ function markChanged(obj, detail) {
4417
+ var tx = activeTransaction;
4418
+ var meta = objectMetadata.get(obj);
4419
+ if (!tx && !meta) return 0;
4420
+ if (!meta) meta = getUndoMetadata(obj);
4421
+ meta.revision++;
4422
+ notifyTransaction(tx, 'markChanged', obj, detail);
4423
+ return meta.revision;
4424
+ }
4425
+
4426
+ function noteTableWillChange(table, detail) {
4427
+ notifyTransaction(activeTransaction, 'captureTableBefore', table, detail || {});
4428
+ }
4429
+
4430
+ function noteTableRecordsWillChange(table, ids, detail) {
4431
+ notifyTransaction(activeTransaction, 'captureTableRecordsBefore', table, Object.assign({
4432
+ ids: normalizeIds(ids)
4433
+ }, detail || {}));
4434
+ }
4435
+
4436
+ function noteTableFieldsWillChange(table, fields, detail) {
4437
+ notifyTransaction(activeTransaction, 'captureTableFieldsBefore', table, Object.assign({
4438
+ fields: normalizeStrings(fields)
4439
+ }, detail || {}));
4440
+ }
4441
+
4442
+ function noteTableOrderWillChange(table, ids, detail) {
4443
+ notifyTransaction(activeTransaction, 'captureTableOrderBefore', table, Object.assign({
4444
+ ids: normalizeIds(ids)
4445
+ }, detail || {}));
4446
+ }
4447
+
4448
+ function noteTableSchemaWillChange(table, detail) {
4449
+ notifyTransaction(activeTransaction, 'captureTableSchemaBefore', table, detail || {});
4450
+ }
4451
+
4452
+ function markTableChanged(table, detail) {
4453
+ return markChanged(table, Object.assign({type: 'table'}, detail || {}));
4454
+ }
4455
+
4456
+ function markTableRecordsChanged(table, ids, detail) {
4457
+ return markTableChanged(table, Object.assign({
4458
+ granularity: 'records',
4459
+ ids: normalizeIds(ids)
4460
+ }, detail || {}));
4461
+ }
4462
+
4463
+ function markTableFieldsChanged(table, fields, detail) {
4464
+ return markTableChanged(table, Object.assign({
4465
+ granularity: 'fields',
4466
+ fields: normalizeStrings(fields)
4467
+ }, detail || {}));
4468
+ }
4469
+
4470
+ function markTableSchemaChanged(table, detail) {
4471
+ return markTableChanged(table, Object.assign({
4472
+ granularity: 'schema'
4473
+ }, detail || {}));
4474
+ }
4475
+
4476
+ function markTableOrderChanged(table, ids, detail) {
4477
+ return markTableChanged(table, Object.assign({
4478
+ granularity: 'order',
4479
+ ids: normalizeIds(ids)
4480
+ }, detail || {}));
4481
+ }
4482
+
4483
+ function noteArcsWillChange(arcs, detail) {
4484
+ notifyTransaction(activeTransaction, 'captureArcsBefore', arcs, detail || {});
4485
+ }
4486
+
4487
+ function noteArcsSimplificationWillChange(arcs, detail) {
4488
+ notifyTransaction(activeTransaction, 'captureArcsSimplificationBefore', arcs, detail || {});
4489
+ }
4490
+
4491
+ function markArcsChanged(arcs, detail) {
4492
+ return markChanged(arcs, Object.assign({type: 'arcs'}, detail || {}));
4493
+ }
4494
+
4495
+ function markArcsSimplificationChanged(arcs, detail) {
4496
+ return markArcsChanged(arcs, Object.assign({granularity: 'simplification'}, detail || {}));
4497
+ }
4498
+
4499
+ function noteCatalogWillChange(catalog, detail) {
4500
+ notifyTransaction(activeTransaction, 'captureCatalogBefore', catalog, detail || {});
4501
+ }
4502
+
4503
+ function markCatalogChanged(catalog, detail) {
4504
+ return markChanged(catalog, Object.assign({type: 'catalog'}, detail || {}));
4505
+ }
4506
+
4507
+ function noteDatasetWillChange(dataset, detail) {
4508
+ notifyTransaction(activeTransaction, 'captureDatasetBefore', dataset, detail || {});
4509
+ }
4510
+
4511
+ function noteDatasetInfoWillChange(dataset, detail) {
4512
+ notifyTransaction(activeTransaction, 'captureDatasetInfoBefore', dataset, detail || {});
4513
+ }
4514
+
4515
+ function markDatasetChanged(dataset, detail) {
4516
+ return markChanged(dataset, Object.assign({type: 'dataset'}, detail || {}));
4517
+ }
4518
+
4519
+ function markDatasetInfoChanged(dataset, detail) {
4520
+ return markDatasetChanged(dataset, Object.assign({granularity: 'info'}, detail || {}));
4521
+ }
4522
+
4523
+ function noteLayerWillChange(layer, detail) {
4524
+ notifyTransaction(activeTransaction, 'captureLayerBefore', layer, detail || {});
4525
+ }
4526
+
4527
+ function noteLayerMetadataWillChange(layer, detail) {
4528
+ notifyTransaction(activeTransaction, 'captureLayerMetadataBefore', layer, detail || {});
4529
+ }
4530
+
4531
+ function noteLayerOrderWillChange(layer, ids, detail) {
4532
+ notifyTransaction(activeTransaction, 'captureLayerOrderBefore', layer, Object.assign({
4533
+ ids: normalizeIds(ids)
4534
+ }, detail || {}));
4535
+ }
4536
+
4537
+ function markLayerChanged(layer, detail) {
4538
+ return markChanged(layer, Object.assign({type: 'layer'}, detail || {}));
4539
+ }
4540
+
4541
+ function markLayerMetadataChanged(layer, detail) {
4542
+ return markLayerChanged(layer, Object.assign({granularity: 'metadata'}, detail || {}));
4543
+ }
4544
+
4545
+ function markLayerOrderChanged(layer, ids, detail) {
4546
+ return markLayerChanged(layer, Object.assign({
4547
+ granularity: 'order',
4548
+ ids: normalizeIds(ids)
4549
+ }, detail || {}));
4550
+ }
4551
+
4552
+ function notifyTransaction(tx, method, obj, detail) {
4553
+ if (tx && typeof tx[method] == 'function') {
4554
+ tx[method](obj, detail || {});
4555
+ }
4556
+ }
4557
+
4558
+ function normalizeIds(ids) {
4559
+ if (ids == null) return [];
4560
+ return Array.isArray(ids) ? ids.slice() : [ids];
4561
+ }
4562
+
4563
+ function normalizeStrings(arr) {
4564
+ if (arr == null) return [];
4565
+ return Array.isArray(arr) ? arr.slice() : [arr];
4566
+ }
4567
+
4568
+ var UndoTracking = /*#__PURE__*/Object.freeze({
4569
+ __proto__: null,
4570
+ clearActiveUndoTransaction: clearActiveUndoTransaction,
4571
+ getActiveUndoTransaction: getActiveUndoTransaction,
4572
+ getUndoId: getUndoId,
4573
+ getUndoMetadata: getUndoMetadata,
4574
+ getUndoRevision: getUndoRevision,
4575
+ markArcsChanged: markArcsChanged,
4576
+ markArcsSimplificationChanged: markArcsSimplificationChanged,
4577
+ markCatalogChanged: markCatalogChanged,
4578
+ markChanged: markChanged,
4579
+ markDatasetChanged: markDatasetChanged,
4580
+ markDatasetInfoChanged: markDatasetInfoChanged,
4581
+ markLayerChanged: markLayerChanged,
4582
+ markLayerMetadataChanged: markLayerMetadataChanged,
4583
+ markLayerOrderChanged: markLayerOrderChanged,
4584
+ markTableChanged: markTableChanged,
4585
+ markTableFieldsChanged: markTableFieldsChanged,
4586
+ markTableOrderChanged: markTableOrderChanged,
4587
+ markTableRecordsChanged: markTableRecordsChanged,
4588
+ markTableSchemaChanged: markTableSchemaChanged,
4589
+ noteArcsSimplificationWillChange: noteArcsSimplificationWillChange,
4590
+ noteArcsWillChange: noteArcsWillChange,
4591
+ noteCatalogWillChange: noteCatalogWillChange,
4592
+ noteDatasetInfoWillChange: noteDatasetInfoWillChange,
4593
+ noteDatasetWillChange: noteDatasetWillChange,
4594
+ noteLayerMetadataWillChange: noteLayerMetadataWillChange,
4595
+ noteLayerOrderWillChange: noteLayerOrderWillChange,
4596
+ noteLayerWillChange: noteLayerWillChange,
4597
+ noteTableFieldsWillChange: noteTableFieldsWillChange,
4598
+ noteTableOrderWillChange: noteTableOrderWillChange,
4599
+ noteTableRecordsWillChange: noteTableRecordsWillChange,
4600
+ noteTableSchemaWillChange: noteTableSchemaWillChange,
4601
+ noteTableWillChange: noteTableWillChange,
4602
+ setActiveUndoTransaction: setActiveUndoTransaction,
4603
+ withActiveUndoTransaction: withActiveUndoTransaction
4604
+ });
4605
+
4365
4606
  function DataTable(obj) {
4366
4607
  var records;
4367
4608
  if (utils.isArray(obj)) {
@@ -4390,6 +4631,46 @@
4390
4631
 
4391
4632
  DataTable.prototype = {
4392
4633
 
4634
+ getUndoId: function() {
4635
+ return getUndoId(this);
4636
+ },
4637
+
4638
+ getUndoRevision: function() {
4639
+ return getUndoRevision(this);
4640
+ },
4641
+
4642
+ captureTableBefore: function(detail) {
4643
+ noteTableWillChange(this, detail);
4644
+ },
4645
+
4646
+ captureRecordsBefore: function(ids, detail) {
4647
+ noteTableRecordsWillChange(this, ids, detail);
4648
+ },
4649
+
4650
+ captureFieldsBefore: function(fields, detail) {
4651
+ noteTableFieldsWillChange(this, fields, detail);
4652
+ },
4653
+
4654
+ captureSchemaBefore: function(detail) {
4655
+ noteTableSchemaWillChange(this, detail);
4656
+ },
4657
+
4658
+ markChanged: function(detail) {
4659
+ return markTableChanged(this, detail);
4660
+ },
4661
+
4662
+ markRecordsChanged: function(ids, detail) {
4663
+ return markTableRecordsChanged(this, ids, detail);
4664
+ },
4665
+
4666
+ markFieldsChanged: function(fields, detail) {
4667
+ return markTableFieldsChanged(this, fields, detail);
4668
+ },
4669
+
4670
+ markSchemaChanged: function(detail) {
4671
+ return markTableSchemaChanged(this, detail);
4672
+ },
4673
+
4393
4674
  fieldExists: function(name) {
4394
4675
  return utils.contains(this.getFields(), name);
4395
4676
  },
@@ -4409,9 +4690,11 @@
4409
4690
  // var dataFieldRxp = /^[a-zA-Z_][a-zA-Z_0-9]*$/;
4410
4691
  // if (!dataFieldRxp.test(name)) error("DataTable#addField() invalid field name:", name);
4411
4692
 
4693
+ this.captureSchemaBefore({operation: 'addField', field: name});
4412
4694
  this.getRecords().forEach(function(obj, i) {
4413
4695
  obj[name] = useFunction ? init(obj, i) : init;
4414
4696
  });
4697
+ this.markSchemaChanged({operation: 'addField', field: name});
4415
4698
  },
4416
4699
 
4417
4700
  getRecordAt: function(i) {
@@ -4425,9 +4708,11 @@
4425
4708
  },
4426
4709
 
4427
4710
  deleteField: function(f) {
4711
+ this.captureSchemaBefore({operation: 'deleteField', field: f});
4428
4712
  this.getRecords().forEach(function(o) {
4429
4713
  delete o[f];
4430
4714
  });
4715
+ this.markSchemaChanged({operation: 'deleteField', field: f});
4431
4716
  },
4432
4717
 
4433
4718
  getFields: function() {
@@ -4440,9 +4725,11 @@
4440
4725
 
4441
4726
  update: function(f) {
4442
4727
  var records = this.getRecords();
4728
+ this.captureTableBefore({operation: 'update'});
4443
4729
  for (var i=0, n=records.length; i<n; i++) {
4444
4730
  records[i] = f(records[i], i);
4445
4731
  }
4732
+ this.markChanged({operation: 'update'});
4446
4733
  },
4447
4734
 
4448
4735
  clone: function() {
@@ -4488,22 +4775,42 @@
4488
4775
  // Insert a column of values into a (new or existing) data field
4489
4776
  function insertFieldValues(lyr, fieldName, values) {
4490
4777
  var size = getFeatureCount(lyr) || values.length,
4491
- table = lyr.data = (lyr.data || new DataTable(size)),
4492
- records = table.getRecords(),
4778
+ table = lyr.data,
4779
+ fieldExists,
4780
+ records,
4493
4781
  rec, val;
4494
4782
 
4783
+ if (!table) {
4784
+ noteLayerWillChange(lyr, {operation: 'insertFieldValues', unit: 'data'});
4785
+ table = lyr.data = new DataTable(size);
4786
+ markLayerChanged(lyr, {operation: 'insertFieldValues', unit: 'data'});
4787
+ }
4788
+ fieldExists = table.fieldExists(fieldName);
4789
+ if (fieldExists) {
4790
+ table.captureFieldsBefore([fieldName], {operation: 'insertFieldValues'});
4791
+ } else {
4792
+ table.captureSchemaBefore({operation: 'insertFieldValues', field: fieldName});
4793
+ }
4794
+ records = table.getRecords();
4495
4795
  for (var i=0, n=records.length; i<n; i++) {
4496
4796
  rec = records[i];
4497
4797
  val = values[i];
4498
4798
  if (!rec) rec = records[i] = {};
4499
4799
  rec[fieldName] = val === undefined ? null : val;
4500
4800
  }
4801
+ if (fieldExists) {
4802
+ table.markFieldsChanged([fieldName], {operation: 'insertFieldValues'});
4803
+ } else {
4804
+ table.markSchemaChanged({operation: 'insertFieldValues', field: fieldName});
4805
+ }
4501
4806
  }
4502
4807
 
4503
4808
  function getLayerDataTable(lyr) {
4504
4809
  var data = lyr.data;
4505
4810
  if (!data) {
4811
+ noteLayerWillChange(lyr, {operation: 'getLayerDataTable', unit: 'data'});
4506
4812
  data = lyr.data = new DataTable(lyr.shapes ? lyr.shapes.length : 0);
4813
+ markLayerChanged(lyr, {operation: 'getLayerDataTable', unit: 'data'});
4507
4814
  }
4508
4815
  return data;
4509
4816
  }
@@ -4530,18 +4837,28 @@
4530
4837
  }
4531
4838
 
4532
4839
  function deleteFeatureById(lyr, i) {
4533
- if (lyr.shapes) lyr.shapes.splice(i, 1);
4534
- if (lyr.data) lyr.data.getRecords().splice(i, 1);
4840
+ if (lyr.shapes) {
4841
+ noteLayerWillChange(lyr, {operation: 'deleteFeatureById', unit: 'shapes'});
4842
+ lyr.shapes.splice(i, 1);
4843
+ markLayerChanged(lyr, {operation: 'deleteFeatureById', unit: 'shapes'});
4844
+ }
4845
+ if (lyr.data) {
4846
+ lyr.data.captureTableBefore({operation: 'deleteFeatureById'});
4847
+ lyr.data.getRecords().splice(i, 1);
4848
+ lyr.data.markChanged({operation: 'deleteFeatureById'});
4849
+ }
4535
4850
  }
4536
4851
 
4537
4852
  // TODO: move elsewhere (moved here from mapshaper-point-utils to avoid circular dependency)
4538
4853
  function transformPointsInLayer(lyr, f) {
4539
4854
  if (layerHasPoints(lyr)) {
4855
+ noteLayerWillChange(lyr, {operation: 'transformPointsInLayer', unit: 'shapes'});
4540
4856
  forEachPoint(lyr.shapes, function(p) {
4541
4857
  var p2 = f(p[0], p[1]);
4542
4858
  p[0] = p2[0];
4543
4859
  p[1] = p2[1];
4544
4860
  });
4861
+ markLayerChanged(lyr, {operation: 'transformPointsInLayer', unit: 'shapes'});
4545
4862
  }
4546
4863
  }
4547
4864
 
@@ -4781,7 +5098,9 @@
4781
5098
  }
4782
5099
 
4783
5100
  function initDataTable(lyr) {
5101
+ noteLayerWillChange(lyr, {operation: 'initDataTable', unit: 'data'});
4784
5102
  lyr.data = new DataTable(getFeatureCount(lyr));
5103
+ markLayerChanged(lyr, {operation: 'initDataTable', unit: 'data'});
4785
5104
  }
4786
5105
 
4787
5106
  var LayerUtils = /*#__PURE__*/Object.freeze({
@@ -5918,12 +6237,40 @@
5918
6237
  }
5919
6238
 
5920
6239
  this.updateArcBounds = function(arcId) {
6240
+ this.captureArcsBefore({operation: 'updateArcBounds', arcId: arcId});
5921
6241
  initArcBounds(arcId);
6242
+ this.markArcsChanged({operation: 'updateArcBounds', arcId: arcId});
5922
6243
  };
5923
6244
 
5924
6245
  this.updateVertexData = function(nn, xx, yy, zz) {
6246
+ this.captureArcsBefore({operation: 'updateVertexData'});
5925
6247
  initXYData(nn, xx, yy);
5926
6248
  initZData(zz || null);
6249
+ this.markArcsChanged({operation: 'updateVertexData'});
6250
+ };
6251
+
6252
+ this.getUndoId = function() {
6253
+ return getUndoId(this);
6254
+ };
6255
+
6256
+ this.getUndoRevision = function() {
6257
+ return getUndoRevision(this);
6258
+ };
6259
+
6260
+ this.captureArcsBefore = function(detail) {
6261
+ noteArcsWillChange(this, detail);
6262
+ };
6263
+
6264
+ this.captureArcsSimplificationBefore = function(detail) {
6265
+ noteArcsSimplificationWillChange(this, detail);
6266
+ };
6267
+
6268
+ this.markArcsChanged = function(detail) {
6269
+ return markArcsChanged(this, detail);
6270
+ };
6271
+
6272
+ this.markArcsSimplificationChanged = function(detail) {
6273
+ return markArcsSimplificationChanged(this, detail);
5927
6274
  };
5928
6275
 
5929
6276
  this.getCopy = function() {
@@ -6019,6 +6366,7 @@
6019
6366
 
6020
6367
  this.transformPoints = function(f) {
6021
6368
  var xx = _xx, yy = _yy, arcId = -1, n = 0, p;
6369
+ this.captureArcsBefore({operation: 'transformPoints'});
6022
6370
  for (var i=0, len=xx.length; i<len; i++, n--) {
6023
6371
  while (n === 0) {
6024
6372
  n = _nn[++arcId];
@@ -6030,6 +6378,7 @@
6030
6378
  }
6031
6379
  }
6032
6380
  initBounds();
6381
+ this.markArcsChanged({operation: 'transformPoints'});
6033
6382
  };
6034
6383
 
6035
6384
  // Return an ArcIter object for each path in the dataset
@@ -6087,7 +6436,9 @@
6087
6436
  }
6088
6437
  }
6089
6438
  if (goodArcs < n) {
6439
+ this.captureArcsBefore({operation: 'deleteArcs'});
6090
6440
  condenseArcs(map);
6441
+ this.markArcsChanged({operation: 'deleteArcs'});
6091
6442
  }
6092
6443
  return map;
6093
6444
  };
@@ -6120,6 +6471,7 @@
6120
6471
  arcCount = this.size(),
6121
6472
  zz = _zz,
6122
6473
  arcLen, arcLen2;
6474
+ this.captureArcsBefore({operation: 'dedupCoords'});
6123
6475
  while (arcId < arcCount) {
6124
6476
  arcLen = _nn[arcId];
6125
6477
  arcLen2 = dedupArcCoords(i, i2, arcLen, _xx, _yy, zz);
@@ -6132,6 +6484,7 @@
6132
6484
  initXYData(_nn, _xx.subarray(0, i2), _yy.subarray(0, i2));
6133
6485
  if (zz) initZData(zz.subarray(0, i2));
6134
6486
  }
6487
+ this.markArcsChanged({operation: 'dedupCoords'});
6135
6488
  return i - i2;
6136
6489
  };
6137
6490
 
@@ -6255,6 +6608,7 @@
6255
6608
  this.setThresholds = function(thresholds) {
6256
6609
  var n = this.getPointCount(),
6257
6610
  zz = null;
6611
+ this.captureArcsSimplificationBefore({operation: 'setThresholds'});
6258
6612
  if (!thresholds) ; else if (thresholds.length == n) {
6259
6613
  zz = thresholds;
6260
6614
  } else if (thresholds.length == this.size()) {
@@ -6263,6 +6617,7 @@
6263
6617
  error("Invalid threshold data");
6264
6618
  }
6265
6619
  initZData(zz);
6620
+ this.markArcsSimplificationChanged({operation: 'setThresholds'});
6266
6621
  return this;
6267
6622
  };
6268
6623
 
@@ -6280,6 +6635,7 @@
6280
6635
 
6281
6636
  // bake in current simplification level, if any
6282
6637
  this.flatten = function() {
6638
+ this.captureArcsBefore({operation: 'flatten'});
6283
6639
  if (_zlimit > 0) {
6284
6640
  var data = getFilteredVertexData();
6285
6641
  this.updateVertexData(data.nn, data.xx, data.yy);
@@ -6287,6 +6643,7 @@
6287
6643
  } else {
6288
6644
  _zz = null;
6289
6645
  }
6646
+ this.markArcsChanged({operation: 'flatten'});
6290
6647
  };
6291
6648
 
6292
6649
  this.isFlat = function() { return !_zz; };
@@ -6296,7 +6653,9 @@
6296
6653
  };
6297
6654
 
6298
6655
  this.setRetainedInterval = function(z) {
6656
+ this.captureArcsSimplificationBefore({operation: 'setRetainedInterval'});
6299
6657
  _zlimit = z;
6658
+ this.markArcsSimplificationChanged({operation: 'setRetainedInterval'});
6300
6659
  return this;
6301
6660
  };
6302
6661
 
@@ -6305,12 +6664,14 @@
6305
6664
  };
6306
6665
 
6307
6666
  this.setRetainedPct = function(pct) {
6667
+ this.captureArcsSimplificationBefore({operation: 'setRetainedPct'});
6308
6668
  if (pct >= 1) {
6309
6669
  _zlimit = 0;
6310
6670
  } else {
6311
6671
  _zlimit = this.getThresholdByPct(pct);
6312
6672
  _zlimit = clampIntervalByPct(_zlimit, pct);
6313
6673
  }
6674
+ this.markArcsSimplificationChanged({operation: 'setRetainedPct'});
6314
6675
  return this;
6315
6676
  };
6316
6677
 
@@ -6652,7 +7013,9 @@
6652
7013
  dataset.arcs.updateVertexData(cooked.nn, cooked.xx, cooked.yy);
6653
7014
  dataset.layers.forEach(function(lyr) {
6654
7015
  if (lyr.geometry_type == 'polyline' || lyr.geometry_type == 'polygon') {
7016
+ noteLayerWillChange(lyr, {operation: 'buildTopology', unit: 'shapes'});
6655
7017
  lyr.shapes = replaceArcIds(lyr.shapes, cooked.paths);
7018
+ markLayerChanged(lyr, {operation: 'buildTopology', unit: 'shapes'});
6656
7019
  }
6657
7020
  });
6658
7021
  }
@@ -6959,11 +7322,13 @@
6959
7322
  // Merge arcs from one or more source datasets into target dataset
6960
7323
  // return array of layers from the source dataset (instead of adding them to the target dataset)
6961
7324
  function mergeDatasetsIntoDataset(dataset, datasets) {
7325
+ noteDatasetWillChange(dataset, {operation: 'mergeDatasetsIntoDataset'});
6962
7326
  var merged = mergeDatasets([dataset].concat(datasets));
6963
7327
  var mergedLayers = datasets.reduce(function(memo, dataset) {
6964
7328
  return memo.concat(dataset.layers);
6965
7329
  }, []);
6966
7330
  dataset.arcs = merged.arcs;
7331
+ markDatasetChanged(dataset, {operation: 'mergeDatasetsIntoDataset'});
6967
7332
  return mergedLayers;
6968
7333
  }
6969
7334
 
@@ -6998,7 +7363,9 @@
6998
7363
  }
6999
7364
 
7000
7365
  // remove old datasets after merging, so catalog is not affected if merge throws an error
7001
- targetDatasets.forEach(catalog.removeDataset);
7366
+ targetDatasets.forEach(function(dataset) {
7367
+ catalog.removeDataset(dataset);
7368
+ });
7002
7369
  catalog.addDataset(merged); // sets default target to all layers in merged dataset
7003
7370
  catalog.setDefaultTarget(targetLayers, merged); // reset default target
7004
7371
  return [{
@@ -7029,9 +7396,15 @@
7029
7396
  mergeDatasetInfo$1(merged, dataset);
7030
7397
  dataset.layers.forEach(function(lyr) {
7031
7398
  if (lyr.geometry_type == 'polygon' || lyr.geometry_type == 'polyline') {
7399
+ if (arcCount > 0) {
7400
+ noteLayerWillChange(lyr, {operation: 'mergeDatasets', unit: 'arc-ids'});
7401
+ }
7032
7402
  forEachArcId(lyr.shapes, function(id) {
7033
7403
  return id < 0 ? id - arcCount : id + arcCount;
7034
7404
  });
7405
+ if (arcCount > 0) {
7406
+ markLayerChanged(lyr, {operation: 'mergeDatasets', unit: 'arc-ids'});
7407
+ }
7035
7408
  }
7036
7409
  merged.layers.push(lyr);
7037
7410
  });
@@ -7598,7 +7971,9 @@
7598
7971
  layers = dataset.layers.filter(layerHasPaths);
7599
7972
 
7600
7973
  if (!arcs || !layers.length) {
7974
+ if (dataset.arcs) noteDatasetWillChange(dataset, {operation: 'dissolveArcs', unit: 'arcs'});
7601
7975
  dataset.arcs = null;
7976
+ if (arcs) markDatasetChanged(dataset, {operation: 'dissolveArcs', unit: 'arcs'});
7602
7977
  profileEnd('dissolveArcs.body');
7603
7978
  return;
7604
7979
  }
@@ -7611,13 +7986,17 @@
7611
7986
  // arcStatus: 0 = unvisited, 1 = dropped, 2 = remapped, 3 = remapped + reversed
7612
7987
  profileStart('dissolveArcs.translatePaths');
7613
7988
  layers.forEach(function(lyr) {
7989
+ noteLayerWillChange(lyr, {operation: 'dissolveArcs', unit: 'shapes'});
7614
7990
  lyr.shapes = lyr.shapes.map(function(shape, i) {
7615
- return editShapeParts(shape && shape.concat(), translatePath);
7991
+ return editShapeParts(getDissolveShapeParts(shape, lyr), translatePath);
7616
7992
  });
7993
+ markLayerChanged(lyr, {operation: 'dissolveArcs', unit: 'shapes'});
7617
7994
  });
7618
7995
  profileEnd('dissolveArcs.translatePaths');
7619
7996
  profileStart('dissolveArcs.dissolveArcCollection');
7997
+ noteDatasetWillChange(dataset, {operation: 'dissolveArcs', unit: 'arcs'});
7620
7998
  dataset.arcs = dissolveArcCollection(arcs, newArcs, totalPoints);
7999
+ markDatasetChanged(dataset, {operation: 'dissolveArcs', unit: 'arcs'});
7621
8000
  profileEnd('dissolveArcs.dissolveArcCollection');
7622
8001
  profileEnd('dissolveArcs.body');
7623
8002
 
@@ -7667,6 +8046,42 @@
7667
8046
  }
7668
8047
  }
7669
8048
 
8049
+ function getDissolveShapeParts(shape, lyr) {
8050
+ if (!shape) return null;
8051
+ shape = shape.concat();
8052
+ if (lyr.geometry_type == 'polygon' && shape.length > 1) {
8053
+ shape.sort(comparePolygonRingsByMinArcId);
8054
+ }
8055
+ return shape;
8056
+ }
8057
+
8058
+ function comparePolygonRingsByMinArcId(a, b) {
8059
+ var minA = getPathMinArcId(a),
8060
+ minB = getPathMinArcId(b);
8061
+ if (minA != minB) return minA - minB;
8062
+ return comparePathsByArcId(a, b);
8063
+ }
8064
+
8065
+ function getPathMinArcId(path) {
8066
+ var minId = Infinity,
8067
+ id;
8068
+ for (var i=0, n=path.length; i<n; i++) {
8069
+ id = absArcId(path[i]);
8070
+ if (id < minId) minId = id;
8071
+ }
8072
+ return minId;
8073
+ }
8074
+
8075
+ function comparePathsByArcId(a, b) {
8076
+ var n = Math.min(a.length, b.length),
8077
+ diff;
8078
+ for (var i=0; i<n; i++) {
8079
+ diff = absArcId(a[i]) - absArcId(b[i]);
8080
+ if (diff) return diff;
8081
+ }
8082
+ return a.length - b.length;
8083
+ }
8084
+
7670
8085
  function dissolveArcCollection(arcs, newArcs, newLen) {
7671
8086
  var nn2 = new Uint32Array(newArcs.length),
7672
8087
  xx2 = new Float64Array(newLen),
@@ -7755,10 +8170,12 @@
7755
8170
  function mergeDatasetInfo(dest, src) {
7756
8171
  var srcInfo = src.info || {};
7757
8172
  var destInfo = dest.info || (dest.info = {});
8173
+ noteDatasetInfoWillChange(dest, {operation: 'mergeDatasetInfo'});
7758
8174
  destInfo.input_files = utils.uniq((destInfo.input_files || []).concat(srcInfo.input_files || []));
7759
8175
  destInfo.input_formats = utils.uniq((destInfo.input_formats || []).concat(srcInfo.input_formats || []));
7760
8176
  // merge other info properties (e.g. input_geojson_crs, input_delimiter, prj, crs)
7761
8177
  utils.defaults(destInfo, srcInfo);
8178
+ markDatasetInfoChanged(dest, {operation: 'mergeDatasetInfo'});
7762
8179
  }
7763
8180
 
7764
8181
  function copyDatasetInfo(info) {
@@ -7773,6 +8190,7 @@
7773
8190
 
7774
8191
  function splitApartLayers(dataset, layers) {
7775
8192
  var datasets = [];
8193
+ noteDatasetWillChange(dataset, {operation: 'splitApartLayers', unit: 'layers'});
7776
8194
  dataset.layers = dataset.layers.filter(function(lyr) {
7777
8195
  if (!layers.includes(lyr)) {
7778
8196
  return true;
@@ -7790,6 +8208,7 @@
7790
8208
  dissolveArcs(dataset);
7791
8209
  datasets.push(dataset);
7792
8210
  }
8211
+ markDatasetChanged(dataset, {operation: 'splitApartLayers', unit: 'layers'});
7793
8212
  return datasets;
7794
8213
  }
7795
8214
 
@@ -7852,7 +8271,9 @@
7852
8271
  // (currently cleanupArcs() is run after every command, so be mindful of performance)
7853
8272
  function cleanupArcs(dataset) {
7854
8273
  if (dataset.arcs && !utils.some(dataset.layers, layerHasPaths)) {
8274
+ noteDatasetWillChange(dataset, {operation: 'cleanupArcs', unit: 'arcs'});
7855
8275
  dataset.arcs = null;
8276
+ markDatasetChanged(dataset, {operation: 'cleanupArcs', unit: 'arcs'});
7856
8277
  return true;
7857
8278
  }
7858
8279
  }
@@ -7871,6 +8292,7 @@
7871
8292
  function replaceLayers(dataset, cutLayers, newLayers) {
7872
8293
  // modify a copy in case cutLayers == dataset.layers
7873
8294
  var currLayers = dataset.layers.concat();
8295
+ noteDatasetWillChange(dataset, {operation: 'replaceLayers', unit: 'layers'});
7874
8296
  utils.repeat(Math.max(cutLayers.length, newLayers.length), function(i) {
7875
8297
  var cutLyr = cutLayers[i],
7876
8298
  newLyr = newLayers[i],
@@ -7884,6 +8306,7 @@
7884
8306
  }
7885
8307
  });
7886
8308
  dataset.layers = currLayers;
8309
+ markDatasetChanged(dataset, {operation: 'replaceLayers', unit: 'layers'});
7887
8310
  }
7888
8311
 
7889
8312
  // Replace a layer with a layer from a second dataset
@@ -7921,7 +8344,9 @@
7921
8344
  }
7922
8345
  lyr2.name = opts.name || lyr.name;
7923
8346
  if (!opts.no_replace) {
8347
+ noteLayerWillChange(lyr, {operation: 'mergeOutputLayersIntoDataset'});
7924
8348
  outputLayers[0] = Object.assign(lyr, {data: null, shapes: null}, lyr2);
8349
+ markLayerChanged(lyr, {operation: 'mergeOutputLayersIntoDataset'});
7925
8350
  if (layerHasPaths(lyr)) {
7926
8351
  // Remove unused arcs from replaced layer
7927
8352
  // TODO: consider using clean insead of this
@@ -14808,6 +15233,12 @@
14808
15233
  profileEnd('addIntersectionCuts');
14809
15234
  return new NodeCollection([]);
14810
15235
  }
15236
+ noteArcsWillChange(arcs, {operation: 'addIntersectionCuts'});
15237
+ dataset.layers.forEach(function(lyr) {
15238
+ if (layerHasPaths(lyr)) {
15239
+ noteLayerWillChange(lyr, {operation: 'addIntersectionCuts', unit: 'shapes'});
15240
+ }
15241
+ });
14811
15242
 
14812
15243
  if (opts.snap_interval) {
14813
15244
  snapDist = convertIntervalParam(opts.snap_interval, getDatasetCRS(dataset));
@@ -14845,6 +15276,12 @@
14845
15276
  profileStart('cleanArcReferences');
14846
15277
  nodes = cleanArcReferences(dataset);
14847
15278
  profileEnd('cleanArcReferences');
15279
+ dataset.layers.forEach(function(lyr) {
15280
+ if (layerHasPaths(lyr)) {
15281
+ markLayerChanged(lyr, {operation: 'addIntersectionCuts', unit: 'shapes'});
15282
+ }
15283
+ });
15284
+ markArcsChanged(arcs, {operation: 'addIntersectionCuts'});
14848
15285
  profileEnd('addIntersectionCuts');
14849
15286
  return nodes;
14850
15287
  }
@@ -16097,7 +16534,9 @@
16097
16534
 
16098
16535
  if (!opts.no_arc_dissolve && pathClean && dataset.arcs) {
16099
16536
  profileStart('dissolveArcs');
16537
+ noteArcsWillChange(dataset.arcs, {operation: 'clean-dissolveArcs'});
16100
16538
  dissolveArcs(dataset);
16539
+ markArcsChanged(dataset.arcs, {operation: 'clean-dissolveArcs'});
16101
16540
  profileEnd('dissolveArcs');
16102
16541
  }
16103
16542
  profileEnd('cleanLayers');
@@ -16109,13 +16548,16 @@
16109
16548
  var groups = lyr.shapes.map(function(shp, i) {
16110
16549
  return [i];
16111
16550
  });
16551
+ noteLayerWillChange(lyr, {operation: 'cleanPolygonLayerGeometry', unit: 'shapes'});
16112
16552
  lyr.shapes = dissolvePolygonGroups2(groups, lyr, dataset, opts);
16553
+ markLayerChanged(lyr, {operation: 'cleanPolygonLayerGeometry', unit: 'shapes'});
16113
16554
  }
16114
16555
 
16115
16556
  // Remove duplicate points from multipoint geometries
16116
16557
  // TODO: consider checking for invalid coordinates
16117
16558
  function cleanPointLayerGeometry(lyr, dataset, opts) {
16118
16559
  var index, parts;
16560
+ noteLayerWillChange(lyr, {operation: 'cleanPointLayerGeometry', unit: 'shapes'});
16119
16561
  lyr.shapes = lyr.shapes.map(function(shp, i) {
16120
16562
  if (!shp || shp.length > 0 === false) {
16121
16563
  return null;
@@ -16132,6 +16574,7 @@
16132
16574
  }
16133
16575
  return parts;
16134
16576
  });
16577
+ markLayerChanged(lyr, {operation: 'cleanPointLayerGeometry', unit: 'shapes'});
16135
16578
 
16136
16579
  function onPoint(p) {
16137
16580
  var key = p.join('~');
@@ -16578,7 +17021,8 @@
16578
17021
  var groups = groupPolygonRings(obj.pathData, arcs, opts.invert_y);
16579
17022
  // invert_y is used internally for SVG generation
16580
17023
  // mapshaper's internal winding order is the opposite of RFC 7946
16581
- var reverse = opts.rfc7946 && !opts.invert_y;
17024
+ var rfc7946 = opts.rfc7946 === true;
17025
+ var reverse = rfc7946 !== !!opts.reverse_winding && !opts.invert_y;
16582
17026
  var coords = groups.map(function(paths) {
16583
17027
  return paths.map(function(path) {
16584
17028
  if (reverse) path.points.reverse();
@@ -25965,37 +26409,25 @@ ${svg}
25965
26409
  async function loadZstdLib() {
25966
26410
  var mod;
25967
26411
  if (runningInBrowser()) {
25968
- mod = require$1('zstd-codec');
26412
+ mod = require$1('@bokuweb/zstd-wasm');
25969
26413
  } else {
25970
26414
  if (!zstdPromise) {
25971
- zstdPromise = dynamicImportModule$1('zstd-codec');
26415
+ zstdPromise = dynamicImportModule$1('@bokuweb/zstd-wasm');
25972
26416
  }
25973
26417
  mod = await zstdPromise;
25974
26418
  }
25975
- if (mod && mod.default && !mod.ZstdCodec) {
26419
+ if (mod && mod.default && !mod.compress) {
25976
26420
  mod = mod.default;
25977
26421
  }
25978
- if (!mod || !mod.ZstdCodec || typeof mod.ZstdCodec.run != 'function') {
26422
+ if (!mod || typeof mod.init != 'function' || typeof mod.compress != 'function') {
25979
26423
  stop$1('GeoParquet ZSTD compressor is not loaded');
25980
26424
  }
25981
- return initZstdCodec(mod.ZstdCodec);
26425
+ await mod.init();
26426
+ return initZstdCodec(mod);
25982
26427
  }
25983
26428
 
25984
26429
  function initZstdCodec(codec) {
25985
- return new Promise(function(resolve, reject) {
25986
- try {
25987
- codec.run(function(zstd) {
25988
- var simple = new zstd.Simple();
25989
- resolve({
25990
- compress: function(bytes, level) {
25991
- return simple.compress(bytes, level);
25992
- }
25993
- });
25994
- });
25995
- } catch (e) {
25996
- reject(e);
25997
- }
25998
- });
26430
+ return codec;
25999
26431
  }
26000
26432
 
26001
26433
  function getOutputFormat(dataset, opts) {
@@ -28702,6 +29134,10 @@ ${svg}
28702
29134
  describe: '[GeoJSON] use original GeoJSON spec (not RFC 7946)',
28703
29135
  type: 'flag'
28704
29136
  })
29137
+ .option('reverse-winding', {
29138
+ describe: '[GeoJSON] reverse polygon winding order',
29139
+ type: 'flag'
29140
+ })
28705
29141
  .option('rfc7946', {
28706
29142
  // dummy option so old commands do not break
28707
29143
  type: 'flag'
@@ -29466,20 +29902,30 @@ ${svg}
29466
29902
  .option('no-replace', noReplaceOpt);
29467
29903
 
29468
29904
  parser.command('grid')
29469
- .describe('create a grid of square or hexagonal polygons')
29905
+ .describe('create a grid of square, hexagonal, rhombus or triangle polygons')
29470
29906
  .option('type', {
29471
- describe: 'square, hex or hex2 (default is square)'
29907
+ describe: 'square, square2, hex, hex2, rhombus, rhombus2, triangle or triangle2 (default is square)'
29472
29908
  })
29473
29909
  .option('interval', {
29474
29910
  describe: 'side length (e.g. 500m, 12km)',
29475
29911
  type: 'distance'
29476
29912
  })
29477
29913
  .option('cols', {
29914
+ describe: 'target number of grid columns',
29478
29915
  type: 'integer'
29479
29916
  })
29480
29917
  .option('rows', {
29918
+ describe: 'target number of grid rows',
29919
+ type: 'integer'
29920
+ })
29921
+ .option('cells', {
29922
+ describe: 'target number of grid cells',
29481
29923
  type: 'integer'
29482
29924
  })
29925
+ .option('cell-scale', {
29926
+ describe: 'scale factor for cells, between 0 and 2',
29927
+ type: 'number'
29928
+ })
29483
29929
  // .option('bbox', {
29484
29930
  // type: 'bbox',
29485
29931
  // describe: 'xmin,ymin,xmax,ymax (default is bbox of data)'
@@ -32538,10 +32984,12 @@ ${svg}
32538
32984
  function parseGeoJSON(reader, cb) {
32539
32985
  var src = ByteReader(reader, 0);
32540
32986
  var isObject = seekObjectStart(src);
32987
+ var type;
32541
32988
  if (!isObject) {
32542
32989
  stop$1('File is not GeoJSON');
32543
32990
  }
32544
- var obj = readObject(src, cb);
32991
+ type = readTopLevelType(src);
32992
+ var obj = readObject(src, cb, type);
32545
32993
  if (obj.type == 'FeatureCollection' || obj.type == 'GeometryCollection') {
32546
32994
  return obj;
32547
32995
  }
@@ -32622,6 +33070,17 @@ ${svg}
32622
33070
  return arr;
32623
33071
  }
32624
33072
 
33073
+ function skipArray(src) {
33074
+ var c;
33075
+ eatChar(src, LBRACK);
33076
+ c = scanForSyntaxChar(src, RBRACK);
33077
+ while (c != RBRACK) {
33078
+ src.refresh();
33079
+ skipValue(src);
33080
+ c = scanForAorB(src, COMMA, RBRACK);
33081
+ }
33082
+ }
33083
+
32625
33084
  function readCollectionArray(src, cb) {
32626
33085
  var c;
32627
33086
  eatChar(src, LBRACK);
@@ -32710,6 +33169,18 @@ ${svg}
32710
33169
  return val;
32711
33170
  }
32712
33171
 
33172
+ function skipValue(src) {
33173
+ var c = src.peek();
33174
+ if (isFirstNumChar(c)) readNumber(src);
33175
+ else if (c == LBRACK) skipArray(src);
33176
+ else if (c == DQUOTE) readString(src);
33177
+ else if (c == LBRACE) skipObject(src);
33178
+ else if (c == 110) readNull(src); // "n" -> null
33179
+ else if (c == 116) readTrue(src); // "t" -> true
33180
+ else if (c == 102) readFalse(src); // "f" -> false
33181
+ else unexpectedCharAt(c, src.index());
33182
+ }
33183
+
32713
33184
  function readTrue(src) {
32714
33185
  eatChars(src, 'true');
32715
33186
  return true;
@@ -32736,7 +33207,7 @@ ${svg}
32736
33207
 
32737
33208
  // cb: optional callback for returning GeoJSON features or geometries
32738
33209
  //
32739
- function readObject(src, cb) {
33210
+ function readObject(src, cb, type) {
32740
33211
  var o = {};
32741
33212
  var key, c;
32742
33213
  eatChar(src, LBRACE);
@@ -32747,7 +33218,8 @@ ${svg}
32747
33218
  skipWS(src);
32748
33219
  eatChar(src, 58); // ":"
32749
33220
  skipWS(src);
32750
- if ((key == 'features' || key == 'geometries') &&
33221
+ if ((type == 'FeatureCollection' && key == 'features' ||
33222
+ type == 'GeometryCollection' && key == 'geometries') &&
32751
33223
  src.peek() == LBRACK && cb) {
32752
33224
  readCollectionArray(src, cb);
32753
33225
  o[key] = null;
@@ -32761,6 +33233,43 @@ ${svg}
32761
33233
  return o;
32762
33234
  }
32763
33235
 
33236
+ function skipObject(src) {
33237
+ var c;
33238
+ eatChar(src, LBRACE);
33239
+ c = scanForSyntaxChar(src, RBRACE);
33240
+ while (c != RBRACE) {
33241
+ src.refresh();
33242
+ readKeywordString(src);
33243
+ skipWS(src);
33244
+ eatChar(src, 58); // ":"
33245
+ skipWS(src);
33246
+ skipValue(src);
33247
+ c = scanForAorB(src, COMMA, RBRACE);
33248
+ }
33249
+ }
33250
+
33251
+ function readTopLevelType(src) {
33252
+ var i = src.index();
33253
+ var type, key, c;
33254
+ eatChar(src, LBRACE);
33255
+ c = scanForSyntaxChar(src, RBRACE);
33256
+ while (c != RBRACE) {
33257
+ src.refresh();
33258
+ key = readKeywordString(src);
33259
+ skipWS(src);
33260
+ eatChar(src, 58); // ":"
33261
+ skipWS(src);
33262
+ if (key == 'type' && src.peek() == DQUOTE) {
33263
+ type = readKeywordString(src);
33264
+ break;
33265
+ }
33266
+ skipValue(src);
33267
+ c = scanForAorB(src, COMMA, RBRACE);
33268
+ }
33269
+ src.index(i);
33270
+ return type;
33271
+ }
33272
+
32764
33273
  function growReserve() {
32765
33274
  RESERVE *= 2;
32766
33275
  return RESERVE <= MAX_STRLEN;
@@ -34107,8 +34616,8 @@ ${svg}
34107
34616
  var candidates = getGeoParquetCrsStrings(crs);
34108
34617
  for (var i = 0; i < candidates.length; i++) {
34109
34618
  try {
34110
- parseCrsString$1(candidates[i]);
34111
34619
  await initProjLibrary({crs: candidates[i]});
34620
+ parseCrsString$1(candidates[i]);
34112
34621
  return candidates[i];
34113
34622
  } catch (e) {
34114
34623
  // Keep trying candidates; if none initialize, caller will warn.
@@ -35859,6 +36368,22 @@ ${svg}
35859
36368
  var datasets = [],
35860
36369
  defaultTargets = [];// saved default command targets [{layers:[], dataset}, ...]
35861
36370
 
36371
+ this.getUndoId = function() {
36372
+ return getUndoId(this);
36373
+ };
36374
+
36375
+ this.getUndoRevision = function() {
36376
+ return getUndoRevision(this);
36377
+ };
36378
+
36379
+ this.captureCatalogBefore = function(detail) {
36380
+ noteCatalogWillChange(this, detail);
36381
+ };
36382
+
36383
+ this.markCatalogChanged = function(detail) {
36384
+ return markCatalogChanged(this, detail);
36385
+ };
36386
+
35862
36387
  this.forEachLayer = function(cb) {
35863
36388
  var i = 0;
35864
36389
  datasets.forEach(function(dataset) {
@@ -35870,16 +36395,19 @@ ${svg}
35870
36395
 
35871
36396
  // remove a layer from a dataset
35872
36397
  this.deleteLayer = function(lyr, dataset) {
36398
+ this.captureCatalogBefore({operation: 'deleteLayer'});
35873
36399
  // if deleting first target layer (selected in gui) -- switch to some other layer
35874
36400
  if (this.getActiveLayer().layer == lyr) {
35875
36401
  defaultTargets = [];
35876
36402
  }
35877
36403
 
35878
36404
  // remove layer from its dataset
36405
+ noteDatasetWillChange(dataset, {operation: 'deleteLayer', unit: 'layers'});
35879
36406
  dataset.layers.splice(dataset.layers.indexOf(lyr), 1);
35880
36407
  if (dataset.layers.length === 0) {
35881
- this.removeDataset(dataset);
36408
+ removeDatasetRaw(dataset);
35882
36409
  }
36410
+ markDatasetChanged(dataset, {operation: 'deleteLayer', unit: 'layers'});
35883
36411
 
35884
36412
  // remove layer from defaultTargets
35885
36413
  defaultTargets = defaultTargets.filter(function(targ) {
@@ -35888,6 +36416,7 @@ ${svg}
35888
36416
  targ.layers.splice(i, 1);
35889
36417
  return targ.layers.length > 0;
35890
36418
  });
36419
+ this.markCatalogChanged({operation: 'deleteLayer'});
35891
36420
  };
35892
36421
 
35893
36422
  // @arg: a layer object or a test function
@@ -35916,17 +36445,16 @@ ${svg}
35916
36445
  };
35917
36446
 
35918
36447
  this.clear = function() {
36448
+ this.captureCatalogBefore({operation: 'clear'});
35919
36449
  datasets = [];
35920
36450
  defaultTargets = [];
36451
+ this.markCatalogChanged({operation: 'clear'});
35921
36452
  };
35922
36453
 
35923
36454
  this.removeDataset = function(dataset) {
35924
- defaultTargets = defaultTargets.filter(function(targ) {
35925
- return targ.dataset != dataset;
35926
- });
35927
- datasets = datasets.filter(function(d) {
35928
- return d != dataset;
35929
- });
36455
+ this.captureCatalogBefore({operation: 'removeDataset'});
36456
+ removeDatasetRaw(dataset);
36457
+ this.markCatalogChanged({operation: 'removeDataset'});
35930
36458
  };
35931
36459
 
35932
36460
  this.getDatasets = function() {
@@ -35988,12 +36516,15 @@ ${svg}
35988
36516
 
35989
36517
  // arr: array of target objects {layers:[], dataset:{}}
35990
36518
  this.setDefaultTargets = function(arr) {
36519
+ if (targetsAreSame(defaultTargets, arr)) return;
36520
+ this.captureCatalogBefore({operation: 'setDefaultTargets'});
35991
36521
  arr.forEach(function(target) {
35992
36522
  if (datasets.indexOf(target.dataset) == -1) {
35993
36523
  datasets.push(target.dataset);
35994
36524
  }
35995
36525
  });
35996
36526
  defaultTargets = arr;
36527
+ this.markCatalogChanged({operation: 'setDefaultTargets'});
35997
36528
  };
35998
36529
 
35999
36530
  // should be in gui-model.js, moved here for testing
@@ -36018,6 +36549,24 @@ ${svg}
36018
36549
  };
36019
36550
  }
36020
36551
 
36552
+ function targetsAreSame(a, b) {
36553
+ if (a.length != b.length) return false;
36554
+ for (var i = 0; i < a.length; i++) {
36555
+ if (a[i].dataset != b[i].dataset || !layersAreSame(a[i].layers, b[i].layers)) {
36556
+ return false;
36557
+ }
36558
+ }
36559
+ return true;
36560
+ }
36561
+
36562
+ function layersAreSame(a, b) {
36563
+ if (a.length != b.length) return false;
36564
+ for (var i = 0; i < a.length; i++) {
36565
+ if (a[i] != b[i]) return false;
36566
+ }
36567
+ return true;
36568
+ }
36569
+
36021
36570
  function indexOfLayer(lyr, layers) {
36022
36571
  var idx = -1;
36023
36572
  layers.forEach(function(o, i) {
@@ -36025,6 +36574,15 @@ ${svg}
36025
36574
  });
36026
36575
  return idx;
36027
36576
  }
36577
+
36578
+ function removeDatasetRaw(dataset) {
36579
+ defaultTargets = defaultTargets.filter(function(targ) {
36580
+ return targ.dataset != dataset;
36581
+ });
36582
+ datasets = datasets.filter(function(d) {
36583
+ return d != dataset;
36584
+ });
36585
+ }
36028
36586
  }
36029
36587
 
36030
36588
  function getFormattedLayerList(catalog) {
@@ -36269,6 +36827,7 @@ ${svg}
36269
36827
  var targetShapes = [];
36270
36828
  var otherShapes = [];
36271
36829
  var targetPoints = [];
36830
+ var targetPointLayers = [];
36272
36831
  var targetFlags, otherFlags, transform;
36273
36832
  dataset.layers.filter(layerHasGeometry).forEach(function(lyr) {
36274
36833
  var hits = [],
@@ -36286,6 +36845,7 @@ ${svg}
36286
36845
  }
36287
36846
  if (lyr.geometry_type == 'point') {
36288
36847
  targetPoints = targetPoints.concat(hits);
36848
+ if (hits.length > 0) targetPointLayers.push(lyr);
36289
36849
  } else {
36290
36850
  targetShapes = targetShapes.concat(hits);
36291
36851
  otherShapes = otherShapes.concat(misses);
@@ -36305,7 +36865,9 @@ ${svg}
36305
36865
  if (otherShapes.length > 0) {
36306
36866
  countArcsInShapes(otherShapes, otherFlags);
36307
36867
  applyArrayMask(otherFlags, targetFlags);
36868
+ noteDatasetWillChange(dataset, {operation: 'affine', unit: 'arcs'});
36308
36869
  dataset.arcs = duplicateSelectedArcs(otherShapes, arcs, otherFlags);
36870
+ markDatasetChanged(dataset, {operation: 'affine', unit: 'arcs'});
36309
36871
  }
36310
36872
  dataset.arcs.transformPoints(function(x, y, arcId) {
36311
36873
  if (arcId < targetFlags.length && targetFlags[arcId] > 0) {
@@ -36313,11 +36875,17 @@ ${svg}
36313
36875
  }
36314
36876
  });
36315
36877
  }
36878
+ targetPointLayers.forEach(function(lyr) {
36879
+ noteLayerWillChange(lyr, {operation: 'affine', unit: 'shapes'});
36880
+ });
36316
36881
  forEachPoint(targetPoints, function(p) {
36317
36882
  var p2 = transform(p[0], p[1]);
36318
36883
  p[0] = p2[0];
36319
36884
  p[1] = p2[1];
36320
36885
  });
36886
+ targetPointLayers.forEach(function(lyr) {
36887
+ markLayerChanged(lyr, {operation: 'affine', unit: 'shapes'});
36888
+ });
36321
36889
  };
36322
36890
 
36323
36891
  function getAffineAnchor(dataset, opts) {
@@ -37159,7 +37727,9 @@ ${svg}
37159
37727
  var dataset = getPolygonDataset$2(pointLyr, filter, opts);
37160
37728
  var merged = mergeDatasets([targetDataset, dataset]);
37161
37729
  var lyr = merged.layers.pop();
37730
+ noteDatasetWillChange(targetDataset, {operation: 'alpha-shapes', unit: 'arcs'});
37162
37731
  targetDataset.arcs = merged.arcs;
37732
+ markDatasetChanged(targetDataset, {operation: 'alpha-shapes', unit: 'arcs'});
37163
37733
  setOutputLayerName(lyr, pointLyr, null, opts);
37164
37734
  return lyr;
37165
37735
  };
@@ -41895,9 +42465,11 @@ ${svg}
41895
42465
  // message('Use save-as=<field> to save to a different field');
41896
42466
  }
41897
42467
 
42468
+ lyr.data.captureSchemaBefore({operation: 'classify', field: outputField});
41898
42469
  records.forEach(function(d, i) {
41899
42470
  d[outputField] = classifyByRecordId(i);
41900
42471
  });
42472
+ lyr.data.markSchemaChanged({operation: 'classify', field: outputField});
41901
42473
  };
41902
42474
 
41903
42475
  function formatValuesForLogging(arr) {
@@ -41939,7 +42511,9 @@ ${svg}
41939
42511
  }
41940
42512
  };
41941
42513
 
42514
+ noteLayerWillChange(lyr, {operation: 'filter-slivers', unit: 'shapes'});
41942
42515
  editShapes(lyr.shapes, pathFilter);
42516
+ markLayerChanged(lyr, {operation: 'filter-slivers', unit: 'shapes'});
41943
42517
  message(utils.format("Removed %'d sliver%s using %s", removed, utils.pluralSuffix(removed), filterData.label));
41944
42518
 
41945
42519
  // Remove null shapes (likely removed by clipping/erasing, although possibly already present)
@@ -41974,7 +42548,9 @@ ${svg}
41974
42548
  };
41975
42549
 
41976
42550
  countArcsInShapes(clipLyr.shapes, flags);
42551
+ noteLayerWillChange(lyr, {operation: 'filter-clip-slivers', unit: 'shapes'});
41977
42552
  editShapes(lyr.shapes, pathFilter);
42553
+ markLayerChanged(lyr, {operation: 'filter-clip-slivers', unit: 'shapes'});
41978
42554
  return removed;
41979
42555
  }
41980
42556
 
@@ -42574,7 +43150,9 @@ ${svg}
42574
43150
  clipLyr = mergedDataset.layers[mergedDataset.layers.length-1];
42575
43151
  if (usingPathClip) {
42576
43152
  nodes = addIntersectionCuts(mergedDataset, opts);
43153
+ noteDatasetWillChange(targetDataset, {operation: type, unit: 'arcs'});
42577
43154
  targetDataset.arcs = mergedDataset.arcs;
43155
+ markDatasetChanged(targetDataset, {operation: type, unit: 'arcs'});
42578
43156
  profileStart('clipDissolvePolygonLayer2');
42579
43157
  clipLyr = utils.defaults({data: null}, clipLyr);
42580
43158
  clipLyr = dissolvePolygonLayer2(clipLyr, mergedDataset, {quiet: true, silent: true});
@@ -43054,7 +43632,9 @@ ${svg}
43054
43632
  var exp = `this.geojson = splitFeature(this.geojson)`;
43055
43633
  requirePolylineLayer(lyr);
43056
43634
  defs.splitFeature = getSplitFeatureFunction(crs, opts);
43635
+ noteLayerWillChange(lyr, {operation: 'dashlines', unit: 'shapes'});
43057
43636
  cmd.evaluateEachFeature(lyr, dataset, exp, opts);
43637
+ markLayerChanged(lyr, {operation: 'dashlines', unit: 'shapes'});
43058
43638
  delete defs.splitFeature;
43059
43639
  };
43060
43640
 
@@ -43195,6 +43775,7 @@ ${svg}
43195
43775
  var getNeighbors = getNeighborLookupFunction(lyr, arcs);
43196
43776
  var fillCount, islandCount;
43197
43777
 
43778
+ lyr.data.captureFieldsBefore([field], {operation: 'data-fill'});
43198
43779
  // get function to check if a shape was empty before data-fill
43199
43780
  var initiallyEmpty = (function() {
43200
43781
  var flags = lyr.data.getRecords().map(function(rec) {
@@ -43213,6 +43794,7 @@ ${svg}
43213
43794
  if (opts.contiguous) {
43214
43795
  islandCount = dataFillIslandGroups(field, lyr, arcs, getNeighbors, opts);
43215
43796
  }
43797
+ lyr.data.markFieldsChanged([field], {operation: 'data-fill'});
43216
43798
 
43217
43799
  message('Filled', fillCount, 'empty polygons' + utils.pluralSuffix(fillCount));
43218
43800
  if (islandCount > 0) {
@@ -44732,6 +45314,19 @@ ${svg}
44732
45314
  calc = getJoinCalc(src, opts.calc);
44733
45315
  }
44734
45316
 
45317
+ if (useDuplication) {
45318
+ noteLayerWillChange(destLyr, {operation: 'join', duplication: true});
45319
+ } else if (opts.calc) {
45320
+ dest.captureTableBefore({operation: 'join', calc: true});
45321
+ } else {
45322
+ dest.captureSchemaBefore({
45323
+ operation: 'join',
45324
+ fields: copyFields.concat(sumFields).map(function(field) {
45325
+ return prefix + field;
45326
+ })
45327
+ });
45328
+ }
45329
+
44735
45330
  // join source records to target records
44736
45331
  n = destRecords.length;
44737
45332
  for (i=0; i<n; i++) {
@@ -44825,6 +45420,13 @@ ${svg}
44825
45420
  data: new DataTable(unmatchedRecords)
44826
45421
  };
44827
45422
  }
45423
+ if (useDuplication) {
45424
+ markLayerChanged(destLyr, {operation: 'join', duplication: true});
45425
+ } else if (opts.calc) {
45426
+ dest.markChanged({operation: 'join', calc: true});
45427
+ } else {
45428
+ dest.markSchemaChanged({operation: 'join'});
45429
+ }
44828
45430
  return retn;
44829
45431
  }
44830
45432
 
@@ -45077,12 +45679,17 @@ ${svg}
45077
45679
 
45078
45680
  cmd.divide = function(targetLayers, targetDataset, source, opts) {
45079
45681
  targetLayers.forEach(requirePolylineLayer);
45682
+ targetLayers.forEach(function(lyr) {
45683
+ noteLayerWillChange(lyr, {operation: 'divide', unit: 'shapes-data'});
45684
+ });
45080
45685
  var mergedDataset = mergeLayersForOverlay(targetLayers, targetDataset, source, opts);
45081
45686
  var nodes = addIntersectionCuts(mergedDataset, opts);
45082
45687
  var polygonLyr = mergedDataset.layers.pop();
45083
45688
  requirePolygonLayer(polygonLyr);
45084
45689
  // Assume that topology is now built
45690
+ noteDatasetWillChange(targetDataset, {operation: 'divide', unit: 'arcs'});
45085
45691
  targetDataset.arcs = mergedDataset.arcs;
45692
+ markDatasetChanged(targetDataset, {operation: 'divide', unit: 'arcs'});
45086
45693
  targetLayers.forEach(function(polylineLyr) {
45087
45694
  dividePolylineLayer(polylineLyr, polygonLyr, nodes, opts);
45088
45695
  });
@@ -45111,12 +45718,13 @@ ${svg}
45111
45718
  forEachShapePart(shp, onPart);
45112
45719
  outputLines.forEach(function(shape2, i) {
45113
45720
  shapes2.push(shape2);
45114
- records2.push(i > 0 ? utils.extend({}, rec) : rec); // assume input data is being replaced
45721
+ records2.push(utils.extend({}, rec));
45115
45722
  index2.push(outputMatches[i]);
45116
45723
  });
45117
45724
  });
45118
45725
  polylineLyr.shapes = shapes2;
45119
45726
  polylineLyr.data = new DataTable(records2);
45727
+ markLayerChanged(polylineLyr, {operation: 'divide', unit: 'shapes-data'});
45120
45728
  joinTables(polylineLyr.data, polygonLyr.data, function(i) {
45121
45729
  return index2[i] || [];
45122
45730
  }, opts);
@@ -45891,17 +46499,23 @@ ${svg}
45891
46499
  var allFields = fields && fieldListContainsAll(fields, lyr.data.getFields());
45892
46500
  var deletion = !fields && !opts.geometry && !opts.holes || allFields && opts.geometry;
45893
46501
  if (opts.geometry) {
46502
+ noteLayerWillChange(lyr, {operation: 'drop-geometry'});
45894
46503
  updateArcs |= layerHasPaths(lyr);
45895
46504
  delete lyr.shapes;
45896
46505
  delete lyr.geometry_type;
46506
+ markLayerChanged(lyr, {operation: 'drop-geometry'});
45897
46507
  }
45898
46508
  if (opts.holes && lyr.geometry_type == 'polygon') {
46509
+ noteLayerWillChange(lyr, {operation: 'drop-holes'});
45899
46510
  deleteHoles(lyr, dataset.arcs);
46511
+ markLayerChanged(lyr, {operation: 'drop-holes'});
45900
46512
  }
45901
46513
  if (deletion) {
45902
46514
  catalog.deleteLayer(lyr, dataset);
45903
46515
  } else if (allFields) {
46516
+ noteLayerWillChange(lyr, {operation: 'drop-fields'});
45904
46517
  delete lyr.data;
46518
+ markLayerChanged(lyr, {operation: 'drop-fields'});
45905
46519
  } else if (fields) {
45906
46520
  opts.fields.forEach(lyr.data.deleteField, lyr.data);
45907
46521
  }
@@ -45994,7 +46608,9 @@ ${svg}
45994
46608
 
45995
46609
  function filterByBoundsIntersection(lyr, arcs, opts) {
45996
46610
  var filter = getBoundsIntersectionFilter(opts.bbox, lyr, arcs);
46611
+ noteLayerWillChange(lyr, {operation: 'filter-geom', unit: 'shapes'});
45997
46612
  editShapes(lyr.shapes, filter);
46613
+ markLayerChanged(lyr, {operation: 'filter-geom', unit: 'shapes'});
45998
46614
  }
45999
46615
 
46000
46616
  function getBoundsIntersectionFilter(bbox, lyr, arcs) {
@@ -46136,8 +46752,14 @@ ${svg}
46136
46752
  }
46137
46753
  });
46138
46754
 
46755
+ if (filteredLyr == lyr) {
46756
+ noteLayerWillChange(lyr, {operation: 'filter'});
46757
+ }
46139
46758
  filteredLyr.shapes = filteredShapes;
46140
46759
  filteredLyr.data = filteredRecords ? new DataTable(filteredRecords) : null;
46760
+ if (filteredLyr == lyr) {
46761
+ markLayerChanged(lyr, {operation: 'filter'});
46762
+ }
46141
46763
  if (opts.no_replace) {
46142
46764
  // if adding a layer, don't share objects between source and filtered layer
46143
46765
  filteredLyr = copyLayer(filteredLyr);
@@ -46165,8 +46787,10 @@ ${svg}
46165
46787
  if (records) filteredRecords.push(records[shapeId] || null);
46166
46788
  }
46167
46789
  });
46790
+ noteLayerWillChange(lyr, {operation: 'filterLayerInPlace'});
46168
46791
  lyr.shapes = filteredShapes;
46169
46792
  lyr.data = filteredRecords ? new DataTable(filteredRecords) : null;
46793
+ markLayerChanged(lyr, {operation: 'filterLayerInPlace'});
46170
46794
  }
46171
46795
 
46172
46796
  function getIdFilter(ids) {
@@ -46219,12 +46843,18 @@ ${svg}
46219
46843
  filter = combineFilters(filter, getIdFilter(opts.ids));
46220
46844
  }
46221
46845
  compiled = compileFeatureExpression(exp, lyr, arcs, exprOpts);
46846
+ if (lyr.data) {
46847
+ lyr.data.captureTableBefore({operation: 'each'});
46848
+ }
46222
46849
  // call compiled expression with id of each record
46223
46850
  for (var i=0; i<n; i++) {
46224
46851
  if (!filter || filter(i)) {
46225
46852
  compiled(i);
46226
46853
  }
46227
46854
  }
46855
+ if (lyr.data) {
46856
+ lyr.data.markChanged({operation: 'each'});
46857
+ }
46228
46858
 
46229
46859
  var replacement = exprOpts.geojson_editor ? exprOpts.geojson_editor.done() : null;
46230
46860
  if (replacement) {
@@ -46391,8 +47021,12 @@ ${svg}
46391
47021
  this.done = function() {
46392
47022
  dataset.layers = layers;
46393
47023
  if (arcs.length) {
47024
+ noteDatasetWillChange(dataset, {operation: 'DatasetEditor.done', unit: 'arcs'});
46394
47025
  dataset.arcs = new ArcCollection(arcs);
46395
- buildTopology(dataset);
47026
+ markDatasetChanged(dataset, {operation: 'DatasetEditor.done', unit: 'arcs'});
47027
+ withActiveUndoTransaction(null, function() {
47028
+ buildTopology(dataset);
47029
+ });
46396
47030
  }
46397
47031
  };
46398
47032
 
@@ -46419,7 +47053,9 @@ ${svg}
46419
47053
  }
46420
47054
  return shape2.length > 0 ? shape2 : null;
46421
47055
  });
47056
+ noteLayerWillChange(lyr, {operation: 'DatasetEditor.editLayer', unit: 'shapes'});
46422
47057
  layers.push(Object.assign(lyr, {shapes: shapes}));
47058
+ markLayerChanged(lyr, {operation: 'DatasetEditor.editLayer', unit: 'shapes'});
46423
47059
  };
46424
47060
 
46425
47061
  function extendPathShape(shape, parts) {
@@ -46989,7 +47625,9 @@ ${svg}
46989
47625
  } else {
46990
47626
  filter = getVertexCountTest(opts.min_vertices, arcs);
46991
47627
  }
47628
+ noteLayerWillChange(lyr, {operation: 'filter-islands', unit: 'shapes'});
46992
47629
  removed += filterIslands(lyr, arcs, filter);
47630
+ markLayerChanged(lyr, {operation: 'filter-islands', unit: 'shapes'});
46993
47631
  if (opts.remove_empty) {
46994
47632
  cmd.filterFeatures(lyr, arcs, {remove_empty: true, verbose: false});
46995
47633
  }
@@ -47070,7 +47708,9 @@ ${svg}
47070
47708
  } else {
47071
47709
  filter = getVertexCountTest(opts.min_vertices, arcs);
47072
47710
  }
47711
+ noteLayerWillChange(lyr, {operation: 'filter-islands2', unit: 'shapes'});
47073
47712
  removed += filterIslands2(lyr, arcs, filter);
47713
+ markLayerChanged(lyr, {operation: 'filter-islands2', unit: 'shapes'});
47074
47714
  if (opts.remove_empty) {
47075
47715
  cmd.filterFeatures(lyr, arcs, {remove_empty: true, verbose: false});
47076
47716
  }
@@ -47113,16 +47753,30 @@ ${svg}
47113
47753
  if (opts.invert) {
47114
47754
  map = invertFieldMap(map, table.getFields());
47115
47755
  }
47116
- lyr.data.update(getRecordMapper(map));
47756
+ replaceTableFields(table, map, {operation: 'filter-fields'});
47117
47757
  };
47118
47758
 
47119
47759
  cmd.renameFields = function(lyr, names) {
47120
47760
  var map = mapFieldNames(names);
47121
47761
  requireDataFields(lyr.data, Object.keys(map));
47122
47762
  utils.defaults(map, mapFieldNames(lyr.data.getFields()));
47123
- lyr.data.update(getRecordMapper(map));
47763
+ replaceTableFields(lyr.data, map, {operation: 'rename-fields'});
47124
47764
  };
47125
47765
 
47766
+ function replaceTableFields(table, map, detail) {
47767
+ var fields = table.getFields();
47768
+ var records = table.getRecords();
47769
+ var mapper = getRecordMapper(map);
47770
+ var columnDetail = Object.assign({schema_transform: true}, detail);
47771
+ table.captureSchemaBefore(detail);
47772
+ table.captureFieldsBefore(fields, columnDetail);
47773
+ for (var i=0, n=records.length; i<n; i++) {
47774
+ records[i] = mapper(records[i], i);
47775
+ }
47776
+ table.markFieldsChanged(fields, columnDetail);
47777
+ table.markSchemaChanged(detail);
47778
+ }
47779
+
47126
47780
  function invertFieldMap(map, fields) {
47127
47781
  return fields.reduce(function(memo, name) {
47128
47782
  if (!(name in map)) {
@@ -47577,8 +48231,10 @@ ${svg}
47577
48231
  });
47578
48232
  editor.done();
47579
48233
  if (!opts.debug) {
47580
- buildTopology(dataset);
47581
- cleanProjectedPathLayers(dataset);
48234
+ withActiveUndoTransaction(null, function() {
48235
+ buildTopology(dataset);
48236
+ cleanProjectedPathLayers(dataset);
48237
+ });
47582
48238
  }
47583
48239
  }
47584
48240
 
@@ -47702,7 +48358,9 @@ ${svg}
47702
48358
  });
47703
48359
  }
47704
48360
  var merged = mergeDatasets([dataset, importGeoJSON(geojson, {})]);
48361
+ noteDatasetWillChange(dataset, {operation: 'lines-segments', unit: 'arcs'});
47705
48362
  dataset.arcs = merged.arcs;
48363
+ markDatasetChanged(dataset, {operation: 'lines-segments', unit: 'arcs'});
47706
48364
  // buildTopology(dataset);
47707
48365
  return merged.layers.pop();
47708
48366
  }
@@ -48282,6 +48940,7 @@ ${svg}
48282
48940
  dataset.arcs.flatten(); // bake in any pending simplification
48283
48941
  target.arcs = modifyCopy ? dataset.arcs.getCopy() : dataset.arcs;
48284
48942
  }
48943
+ noteDatasetWillChange(dataset, {operation: 'proj'});
48285
48944
 
48286
48945
  target.layers = dataset.layers.map(function(lyr) {
48287
48946
  if (modifyCopy) {
@@ -48291,16 +48950,45 @@ ${svg}
48291
48950
  return lyr;
48292
48951
  });
48293
48952
 
48294
- projectDataset(target, srcInfo.crs, destInfo.crs, opts || {});
48953
+ withActiveUndoTransaction(null, function() {
48954
+ projectDataset(target, srcInfo.crs, destInfo.crs, opts || {});
48295
48955
 
48296
- // dataset.info.wkt1 = destInfo.wkt1; // may be undefined
48297
- setDatasetCrsInfo(target, destInfo);
48956
+ // dataset.info.wkt1 = destInfo.wkt1; // may be undefined
48957
+ setDatasetCrsInfo(target, destInfo);
48958
+ });
48298
48959
 
48299
48960
  dataset.arcs = target.arcs;
48300
48961
  originals.forEach(function(lyr, i) {
48301
48962
  // replace original layers with modified layers
48302
- utils.extend(lyr, target.layers[i]);
48963
+ if (layerWasChangedByProjection(lyr, target.layers[i])) {
48964
+ noteLayerWillChange(lyr, {operation: 'proj'});
48965
+ utils.extend(lyr, target.layers[i]);
48966
+ markLayerChanged(lyr, {operation: 'proj'});
48967
+ }
48303
48968
  });
48969
+ markDatasetChanged(dataset, {operation: 'proj'});
48970
+ return true;
48971
+ }
48972
+
48973
+ function layerWasChangedByProjection(a, b) {
48974
+ return a.name != b.name ||
48975
+ a.geometry_type != b.geometry_type ||
48976
+ a.data !== b.data ||
48977
+ !arraysAreEqual(a.shapes, b.shapes);
48978
+ }
48979
+
48980
+ function arraysAreEqual(a, b) {
48981
+ var val;
48982
+ if (a === b) return true;
48983
+ if (!a || !b || a.length !== b.length) return false;
48984
+ for (var i = 0; i < a.length; i++) {
48985
+ val = a[i];
48986
+ if (Array.isArray(val)) {
48987
+ if (!arraysAreEqual(val, b[i])) return false;
48988
+ } else if (val !== b[i]) {
48989
+ return false;
48990
+ }
48991
+ }
48304
48992
  return true;
48305
48993
  }
48306
48994
 
@@ -48763,7 +49451,9 @@ ${svg}
48763
49451
  lyr2.name = lyr0.name;
48764
49452
  return lyr2;
48765
49453
  });
49454
+ noteDatasetWillChange(targetDataset, {operation: 'inlay', unit: 'arcs'});
48766
49455
  targetDataset.arcs = mergedDataset.arcs;
49456
+ markDatasetChanged(targetDataset, {operation: 'inlay', unit: 'arcs'});
48767
49457
  return outputLayers;
48768
49458
  };
48769
49459
 
@@ -49196,6 +49886,10 @@ ${svg}
49196
49886
  cmd.createPointLayer = function(srcLyr, dataset, opts) {
49197
49887
  var destLyr = getOutputLayer(srcLyr, opts);
49198
49888
  var arcs = dataset.arcs;
49889
+ var replacingSource = destLyr == srcLyr;
49890
+ if (replacingSource) {
49891
+ noteLayerWillChange(destLyr, {operation: 'createPointLayer', unit: 'geometry'});
49892
+ }
49199
49893
  if (opts.intersections) {
49200
49894
  testIntersections(arcs);
49201
49895
  destLyr = srcLyr;
@@ -49235,6 +49929,9 @@ ${svg}
49235
49929
  if (srcLyr.data) {
49236
49930
  destLyr.data = opts.no_replace ? srcLyr.data.clone() : srcLyr.data;
49237
49931
  }
49932
+ if (replacingSource) {
49933
+ markLayerChanged(destLyr, {operation: 'createPointLayer', unit: 'geometry'});
49934
+ }
49238
49935
  return destLyr;
49239
49936
  };
49240
49937
 
@@ -50217,8 +50914,8 @@ ${svg}
50217
50914
  var v1 = bottom + vi * minorInterval;
50218
50915
  var v2 = v1 + minorInterval;
50219
50916
  var orientation = cwBar ?
50220
- orient2D(u1, v1, u2, v2, u, v) :
50221
- orient2D(u2, v1, u1, v2, u, v);
50917
+ orient2D$1(u1, v1, u2, v2, u, v) :
50918
+ orient2D$1(u2, v1, u1, v2, u, v);
50222
50919
  var colId = orientation > 0 ? ui - 1 : ui;
50223
50920
  var rowId = Math.floor(vi / 2);
50224
50921
  return [colId, rowId];
@@ -50543,16 +51240,28 @@ ${svg}
50543
51240
  var geojson, dataset;
50544
51241
  if (params.type == 'square') {
50545
51242
  geojson = getSquareGridGeoJSON(getSquareGridCoordinates(params));
51243
+ } else if (params.type == 'square2') {
51244
+ geojson = getRotatedSquareGridGeoJSON(params);
50546
51245
  } else if (params.type == 'hex') {
50547
51246
  geojson = getHexGridGeoJSON(getHexGridCoordinates(params));
50548
51247
  } else if (params.type == 'hex2') {
50549
51248
  // use rotated grid
50550
51249
  geojson = getHexGridGeoJSON(getHexGridCoordinates(swapGridParams(params)));
50551
51250
  swapPolygonCoords(geojson);
51251
+ } else if (params.type == 'rhombus') {
51252
+ geojson = getRhombusGridGeoJSON(params, false);
51253
+ } else if (params.type == 'rhombus2') {
51254
+ geojson = getRhombusGridGeoJSON(params, true);
51255
+ } else if (params.type == 'triangle') {
51256
+ geojson = getTriangleGridGeoJSON(params, false);
51257
+ } else if (params.type == 'triangle2') {
51258
+ geojson = getTriangleGridGeoJSON(params, true);
50552
51259
  } else {
50553
51260
  stop$1('Unsupported grid type');
50554
51261
  }
51262
+ scaleGridCells(geojson, params.cellScale);
50555
51263
  alignGridToBounds(geojson, params.bbox);
51264
+ cullGridCells(geojson, params.bbox);
50556
51265
  dataset = importGeoJSON(geojson, {});
50557
51266
  buildTopology(dataset);
50558
51267
  return dataset;
@@ -50578,11 +51287,6 @@ ${svg}
50578
51287
  function getGridParams(layers, dataset, opts) {
50579
51288
  var params = {};
50580
51289
  var crs = dataset ? getDatasetCRS(dataset) : null;
50581
- if (opts.interval) {
50582
- params.interval = convertIntervalParam(opts.interval, crs);
50583
- } else {
50584
- stop$1('Missing required interval option');
50585
- }
50586
51290
  if (opts.bbox) {
50587
51291
  params.bbox = opts.bbox;
50588
51292
  } else if (dataset) {
@@ -50594,9 +51298,143 @@ ${svg}
50594
51298
  params.width = params.bbox[2] - params.bbox[0];
50595
51299
  params.height = params.bbox[3] - params.bbox[1];
50596
51300
  params.type = opts.type || 'square';
51301
+ params.interval = getGridInterval(params, opts, crs);
51302
+ params.cellScale = getCellScale(opts);
50597
51303
  return params;
50598
51304
  }
50599
51305
 
51306
+ function getCellScale(opts) {
51307
+ var scale = opts.cell_scale == null ? 1 : opts.cell_scale;
51308
+ if (scale > 0 && scale < 2) return scale;
51309
+ stop$1('cell-scale= option should be greater than 0 and less than 2');
51310
+ }
51311
+
51312
+ function getGridInterval(params, opts, crs) {
51313
+ var interval = opts.interval ? convertIntervalParam(opts.interval, crs) : 0;
51314
+ var hasSizeOpt = opts.cols != null || opts.rows != null || opts.cells != null;
51315
+ var sizeOpts = 0;
51316
+ if (opts.cols > 0) sizeOpts++;
51317
+ if (opts.rows > 0) sizeOpts++;
51318
+ if (opts.cells > 0) sizeOpts++;
51319
+ if (interval && hasSizeOpt) {
51320
+ stop$1('Use interval= or cols=/rows=/cells=, not both');
51321
+ }
51322
+ if (interval) return interval;
51323
+ validateGridSizeOpt('cols', opts.cols);
51324
+ validateGridSizeOpt('rows', opts.rows);
51325
+ validateGridSizeOpt('cells', opts.cells);
51326
+ if (sizeOpts === 0) {
51327
+ stop$1('Missing required interval, cols, rows or cells option');
51328
+ }
51329
+ return getIntervalFromGridSize(params, opts);
51330
+ }
51331
+
51332
+ function validateGridSizeOpt(name, value) {
51333
+ if (value > 0 || value == null) return;
51334
+ stop$1(name + '= option should be a positive integer');
51335
+ }
51336
+
51337
+ function getIntervalFromGridSize(params, opts) {
51338
+ var factor = getCellAreaFactor(params.type);
51339
+ var intervals = [];
51340
+ if (opts.cols > 0) intervals.push(params.width / opts.cols / Math.sqrt(factor));
51341
+ if (opts.rows > 0) intervals.push(params.height / opts.rows / Math.sqrt(factor));
51342
+ if (opts.cells > 0) intervals.push(Math.sqrt(params.width * params.height / opts.cells / factor));
51343
+ return Math.min.apply(null, intervals);
51344
+ }
51345
+
51346
+ function getCellAreaFactor(type) {
51347
+ if (type == 'square' || type == 'square2') return 1;
51348
+ if (type == 'hex' || type == 'hex2') return 3 * Math.sqrt(3) / 2;
51349
+ if (type == 'rhombus' || type == 'rhombus2') return Math.sqrt(3) / 2;
51350
+ if (type == 'triangle' || type == 'triangle2') return Math.sqrt(3) / 4;
51351
+ stop$1('Unsupported grid type');
51352
+ }
51353
+
51354
+ function scaleGridCells(geojson, scale) {
51355
+ if (scale == 1) return;
51356
+ geojson.geometries.forEach(function(geom) {
51357
+ if (geom.type == 'Polygon') {
51358
+ geom.coordinates[0] = scalePolygonRing(geom.coordinates[0], scale);
51359
+ }
51360
+ });
51361
+ }
51362
+
51363
+ function scalePolygonRing(coords, scale) {
51364
+ var center = getPolygonRingCenter(coords);
51365
+ return coords.map(function(p) {
51366
+ return [
51367
+ center[0] + (p[0] - center[0]) * scale,
51368
+ center[1] + (p[1] - center[1]) * scale
51369
+ ];
51370
+ });
51371
+ }
51372
+
51373
+ function cullGridCells(geojson, bbox) {
51374
+ geojson.geometries = geojson.geometries.filter(function(geom) {
51375
+ return geom.type != 'Polygon' || polygonIntersectsBBox(geom.coordinates[0], bbox);
51376
+ });
51377
+ }
51378
+
51379
+ function polygonIntersectsBBox(coords, bbox) {
51380
+ var rect = [[bbox[0], bbox[1]], [bbox[2], bbox[1]], [bbox[2], bbox[3]], [bbox[0], bbox[3]]];
51381
+ var i, j;
51382
+ for (i=0; i<coords.length - 1; i++) {
51383
+ if (pointInBBox(coords[i], bbox)) return true;
51384
+ }
51385
+ for (i=0; i<rect.length; i++) {
51386
+ if (pointInPolygon(rect[i], coords)) return true;
51387
+ }
51388
+ for (i=0; i<coords.length - 1; i++) {
51389
+ for (j=0; j<rect.length; j++) {
51390
+ if (segmentsIntersect(coords[i], coords[i + 1], rect[j], rect[(j + 1) % rect.length])) {
51391
+ return true;
51392
+ }
51393
+ }
51394
+ }
51395
+ return false;
51396
+ }
51397
+
51398
+ function pointInBBox(p, bbox) {
51399
+ return p[0] >= bbox[0] && p[0] <= bbox[2] && p[1] >= bbox[1] && p[1] <= bbox[3];
51400
+ }
51401
+
51402
+ function pointInPolygon(p, coords) {
51403
+ var isInside = false;
51404
+ var a, b;
51405
+ for (var i=0, n=coords.length - 1, j=n - 1; i<n; j=i++) {
51406
+ a = coords[i];
51407
+ b = coords[j];
51408
+ if (orient2D(a, b, p) === 0 && pointOnSegment(p, a, b)) return true;
51409
+ if (a[1] > p[1] != b[1] > p[1] &&
51410
+ p[0] < (b[0] - a[0]) * (p[1] - a[1]) / (b[1] - a[1]) + a[0]) {
51411
+ isInside = !isInside;
51412
+ }
51413
+ }
51414
+ return isInside;
51415
+ }
51416
+
51417
+ function segmentsIntersect(a, b, c, d) {
51418
+ var ab_c = orient2D(a, b, c);
51419
+ var ab_d = orient2D(a, b, d);
51420
+ var cd_a = orient2D(c, d, a);
51421
+ var cd_b = orient2D(c, d, b);
51422
+ if (ab_c === 0 && pointOnSegment(c, a, b)) return true;
51423
+ if (ab_d === 0 && pointOnSegment(d, a, b)) return true;
51424
+ if (cd_a === 0 && pointOnSegment(a, c, d)) return true;
51425
+ if (cd_b === 0 && pointOnSegment(b, c, d)) return true;
51426
+ return ab_c > 0 != ab_d > 0 && cd_a > 0 != cd_b > 0;
51427
+ }
51428
+
51429
+ function orient2D(a, b, c) {
51430
+ return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
51431
+ }
51432
+
51433
+ function pointOnSegment(p, a, b) {
51434
+ return p[0] >= Math.min(a[0], b[0]) && p[0] <= Math.max(a[0], b[0]) &&
51435
+ p[1] >= Math.min(a[1], b[1]) && p[1] <= Math.max(a[1], b[1]);
51436
+ }
51437
+
50600
51438
  function getPointGridGeoJSON(arr) {
50601
51439
  var geometries = [];
50602
51440
  arr.forEach(function(row) {
@@ -50635,6 +51473,72 @@ ${svg}
50635
51473
  return {type: 'GeometryCollection', geometries: geometries};
50636
51474
  }
50637
51475
 
51476
+ function getRhombusGridGeoJSON(params, rotated) {
51477
+ var geojson;
51478
+ if (rotated) {
51479
+ geojson = getHexGridGeoJSON(getHexGridCoordinates(swapGridParams(params)));
51480
+ swapPolygonCoords(geojson);
51481
+ } else {
51482
+ geojson = getHexGridGeoJSON(getHexGridCoordinates(params));
51483
+ }
51484
+ geojson.geometries = geojson.geometries.reduce(function(memo, geom) {
51485
+ return memo.concat(subdivideHexagon(geom.coordinates[0]));
51486
+ }, []);
51487
+ return geojson;
51488
+ }
51489
+
51490
+ function getTriangleGridGeoJSON(params, rotated) {
51491
+ var geojson;
51492
+ if (rotated) {
51493
+ geojson = getHexGridGeoJSON(getHexGridCoordinates(swapGridParams(params)));
51494
+ swapPolygonCoords(geojson);
51495
+ } else {
51496
+ geojson = getHexGridGeoJSON(getHexGridCoordinates(params));
51497
+ }
51498
+ geojson.geometries = geojson.geometries.reduce(function(memo, geom) {
51499
+ return memo.concat(triangulateHexagon(geom.coordinates[0]));
51500
+ }, []);
51501
+ return geojson;
51502
+ }
51503
+
51504
+ function subdivideHexagon(coords) {
51505
+ var center = getPolygonRingCenter(coords);
51506
+ return [
51507
+ getRhombusCell(coords, center, 0),
51508
+ getRhombusCell(coords, center, 2),
51509
+ getRhombusCell(coords, center, 4)
51510
+ ];
51511
+ }
51512
+
51513
+ function getRhombusCell(coords, center, i) {
51514
+ return {
51515
+ type: 'Polygon',
51516
+ coordinates: [[center, coords[i], coords[i + 1], coords[(i + 2) % 6], center]]
51517
+ };
51518
+ }
51519
+
51520
+ function triangulateHexagon(coords) {
51521
+ var triangles = [];
51522
+ var center = getPolygonRingCenter(coords);
51523
+ for (var i=0; i<6; i++) {
51524
+ triangles.push({
51525
+ type: 'Polygon',
51526
+ coordinates: [[center, coords[i], coords[(i + 1) % 6], center]]
51527
+ });
51528
+ }
51529
+ return triangles;
51530
+ }
51531
+
51532
+ function getPolygonRingCenter(coords) {
51533
+ var x = 0, y = 0;
51534
+ var n = coords.length - 1; // ignore closing point
51535
+ for (var i=0; i<n; i++) {
51536
+ x += coords[i][0];
51537
+ y += coords[i][1];
51538
+ }
51539
+ return [x / n, y / n];
51540
+ }
51541
+
50638
51542
  function getSquareGridGeoJSON(arr) {
50639
51543
  var geometries = [], a, b, c, d;
50640
51544
  for (var row = 0, rows = arr.length - 1; row < rows; row++) {
@@ -50652,6 +51556,35 @@ ${svg}
50652
51556
  return {type: 'GeometryCollection', geometries: geometries};
50653
51557
  }
50654
51558
 
51559
+ function getRotatedSquareGridGeoJSON(params) {
51560
+ var interval = params.interval;
51561
+ var radius = interval * Math.SQRT1_2;
51562
+ var geometries = [];
51563
+ var row = 0;
51564
+ var x, y, x0;
51565
+ for (y = -radius; y <= params.height + radius; y += radius) {
51566
+ x0 = -2 * radius + (row % 2 ? radius : 0);
51567
+ for (x = x0; x <= params.width + 2 * radius; x += 2 * radius) {
51568
+ geometries.push(getRotatedSquareCell(x, y, radius));
51569
+ }
51570
+ row++;
51571
+ }
51572
+ return {type: 'GeometryCollection', geometries: geometries};
51573
+ }
51574
+
51575
+ function getRotatedSquareCell(x, y, radius) {
51576
+ return {
51577
+ type: 'Polygon',
51578
+ coordinates: [[
51579
+ [x, y - radius],
51580
+ [x + radius, y],
51581
+ [x, y + radius],
51582
+ [x - radius, y],
51583
+ [x, y - radius]
51584
+ ]]
51585
+ };
51586
+ }
51587
+
50655
51588
  function getHexGridCoordinates(params) {
50656
51589
  var xInterval = params.interval;
50657
51590
  var yInterval = Math.sqrt(3) * xInterval / 2;
@@ -50847,7 +51780,7 @@ ${svg}
50847
51780
  // Returns a function that receives a cell index and returns indices of points
50848
51781
  // within a given distance of the cell.
50849
51782
  function getGridToPointIndex(points, grid, radius) {
50850
- var Flatbush = require('flatbush');
51783
+ var Flatbush = require$1('flatbush');
50851
51784
  var gridIndex = new IdTestIndex(grid.cells());
50852
51785
  var bboxIndex = new Flatbush(points.length);
50853
51786
  var empty = [];
@@ -51135,6 +52068,7 @@ ${svg}
51135
52068
  function createPolygonLayerFromRings(lyr, dataset) {
51136
52069
  var arcs = dataset.arcs;
51137
52070
  var openCount = 0;
52071
+ noteLayerWillChange(lyr, {operation: 'polygons-from-rings', unit: 'geometry'});
51138
52072
  editShapes(lyr.shapes, function(part) {
51139
52073
  if (geom.pathIsClosed(part, arcs)) {
51140
52074
  return part;
@@ -51147,6 +52081,7 @@ ${svg}
51147
52081
  }
51148
52082
  lyr.geometry_type = 'polygon';
51149
52083
  rewindPolygons(lyr, arcs);
52084
+ markLayerChanged(lyr, {operation: 'polygons-from-rings', unit: 'geometry'});
51150
52085
  return lyr;
51151
52086
  }
51152
52087
 
@@ -51183,7 +52118,9 @@ ${svg}
51183
52118
  var index = mapLayerNames(names);
51184
52119
  catalog.forEachLayer(function(lyr) {
51185
52120
  if (index[lyr.name]) {
52121
+ noteLayerMetadataWillChange(lyr, {operation: 'rename-layers'});
51186
52122
  lyr.name = index[lyr.name];
52123
+ markLayerMetadataChanged(lyr, {operation: 'rename-layers'});
51187
52124
  }
51188
52125
  });
51189
52126
  }
@@ -51199,7 +52136,9 @@ ${svg}
51199
52136
  if (name && nameCount < layers.length && (i >= nameCount - 1)) {
51200
52137
  suffix = (suffix || 0) + 1;
51201
52138
  }
52139
+ noteLayerMetadataWillChange(lyr, {operation: 'rename-layers'});
51202
52140
  lyr.name = name + suffix;
52141
+ markLayerMetadataChanged(lyr, {operation: 'rename-layers'});
51203
52142
  });
51204
52143
  }
51205
52144
 
@@ -52528,6 +53467,8 @@ ${svg}
52528
53467
  var implicitlySimplifiedNames = getImplicitlyTargetedLayerNames(dataset, targetLayers, layerHasPaths);
52529
53468
  if (!arcs || arcs.size() === 0) return; // removed in v0.4.125: stop("Missing path data");
52530
53469
  opts = getStandardSimplifyOpts(dataset, opts); // standardize options
53470
+ noteArcsSimplificationWillChange(arcs, {operation: 'simplify'});
53471
+ noteDatasetInfoWillChange(dataset, {operation: 'simplify'});
52531
53472
  simplifyPaths(arcs, opts);
52532
53473
 
52533
53474
  // calculate and apply simplification interval
@@ -52544,6 +53485,8 @@ ${svg}
52544
53485
  }
52545
53486
 
52546
53487
  finalizeSimplification(dataset, opts);
53488
+ markArcsSimplificationChanged(arcs, {operation: 'simplify'});
53489
+ markDatasetInfoChanged(dataset, {operation: 'simplify'});
52547
53490
  if (implicitlySimplifiedNames.length > 0) {
52548
53491
  message(
52549
53492
  'Also simplified non-target layer' + utils.pluralSuffix(implicitlySimplifiedNames.length) +
@@ -52769,14 +53712,27 @@ ${svg}
52769
53712
  });
52770
53713
 
52771
53714
  var ids = utils.getSortedIds(values, ascending);
53715
+ var undoIds = invertIds$1(ids);
52772
53716
  if (lyr.shapes) {
53717
+ noteLayerOrderWillChange(lyr, undoIds, {operation: 'sort'});
52773
53718
  utils.reorderArray(lyr.shapes, ids);
53719
+ markLayerOrderChanged(lyr, ids, {operation: 'sort'});
52774
53720
  }
52775
53721
  if (lyr.data) {
53722
+ noteTableOrderWillChange(lyr.data, undoIds, {operation: 'sort'});
52776
53723
  utils.reorderArray(lyr.data.getRecords(), ids);
53724
+ markTableOrderChanged(lyr.data, ids, {operation: 'sort'});
52777
53725
  }
52778
53726
  };
52779
53727
 
53728
+ function invertIds$1(ids) {
53729
+ var inverse = [];
53730
+ ids.forEach(function(id, i) {
53731
+ inverse[id] = i;
53732
+ });
53733
+ return inverse;
53734
+ }
53735
+
52780
53736
  cmd.snap = function(target, opts) {
52781
53737
  var interval = 0;
52782
53738
  var snapCount = 0;
@@ -52846,6 +53802,7 @@ ${svg}
52846
53802
  return [lyr0];
52847
53803
  }
52848
53804
 
53805
+ noteLayerWillChange(lyr0, {operation: 'split', unit: 'shared-shapes-data'});
52849
53806
  utils.repeat(n, function(i) {
52850
53807
  var name = namer(i),
52851
53808
  lyr;
@@ -52869,6 +53826,7 @@ ${svg}
52869
53826
  lyr.data.getRecords().push(properties[i]);
52870
53827
  }
52871
53828
  });
53829
+ markLayerChanged(lyr0, {operation: 'split', unit: 'shared-shapes-data'});
52872
53830
 
52873
53831
  return splitLayers;
52874
53832
  };
@@ -52942,7 +53900,7 @@ ${svg}
52942
53900
  };
52943
53901
 
52944
53902
  cmd.svgStyle = function(lyr, dataset, opts) {
52945
- var filterFn;
53903
+ var filterFn, table, fields, hasNewFields;
52946
53904
  if (getFeatureCount(lyr) === 0) {
52947
53905
  return;
52948
53906
  }
@@ -52955,6 +53913,20 @@ ${svg}
52955
53913
  if (opts.clear) {
52956
53914
  lyr.data.getFields().filter(isSupportedSvgStyleProperty).forEach(lyr.data.deleteField, lyr.data);
52957
53915
  }
53916
+ table = getLayerDataTable(lyr);
53917
+ fields = Object.keys(opts).map(function(optName) {
53918
+ return optName.replace('_', '-');
53919
+ }).filter(isSupportedSvgStyleProperty);
53920
+ hasNewFields = fields.some(function(field) {
53921
+ return !table.fieldExists(field);
53922
+ });
53923
+ if (fields.length > 0) {
53924
+ if (hasNewFields) {
53925
+ table.captureSchemaBefore({operation: 'style', fields: fields});
53926
+ } else {
53927
+ table.captureFieldsBefore(fields, {operation: 'style'});
53928
+ }
53929
+ }
52958
53930
  Object.keys(opts).forEach(function(optName) {
52959
53931
  var svgName = optName.replace('_', '-'); // undo cli parser name conversion
52960
53932
  if (!isSupportedSvgStyleProperty(svgName)) {
@@ -52962,7 +53934,7 @@ ${svg}
52962
53934
  }
52963
53935
  var strVal = opts[optName].trim();
52964
53936
  var accessor = getSymbolPropertyAccessor(strVal, svgName, lyr);
52965
- getLayerDataTable(lyr).getRecords().forEach(function(rec, i) {
53937
+ table.getRecords().forEach(function(rec, i) {
52966
53938
  if (filterFn && !filterFn(i)) {
52967
53939
  // make sure field exists if record is excluded by filter
52968
53940
  if (svgName in rec === false) {
@@ -52973,6 +53945,13 @@ ${svg}
52973
53945
  }
52974
53946
  });
52975
53947
  });
53948
+ if (fields.length > 0) {
53949
+ if (hasNewFields) {
53950
+ table.markSchemaChanged({operation: 'style'});
53951
+ } else {
53952
+ table.markFieldsChanged(fields, {operation: 'style'});
53953
+ }
53954
+ }
52976
53955
  };
52977
53956
 
52978
53957
  var roundCoord$1 = getRoundingFunction(0.01);
@@ -53415,7 +54394,19 @@ ${svg}
53415
54394
  requireProjectedDataset(dataset);
53416
54395
  metersPerPx = opts.pixel_scale || getMetersPerPixel(lyr);
53417
54396
  }
53418
- var records = getLayerDataTable(lyr).getRecords();
54397
+ var table = getLayerDataTable(lyr);
54398
+ var symbolFields = getSymbolOutputFields(shapeMode, opts);
54399
+ var hasNewSymbolFields = symbolFields.some(function(field) {
54400
+ return !table.fieldExists(field);
54401
+ });
54402
+ if (symbolFields.length > 0) {
54403
+ if (hasNewSymbolFields) {
54404
+ table.captureSchemaBefore({operation: 'symbols', fields: symbolFields});
54405
+ } else {
54406
+ table.captureFieldsBefore(symbolFields, {operation: 'symbols'});
54407
+ }
54408
+ }
54409
+ var records = table.getRecords();
53419
54410
  var getSymbolData = getSymbolDataAccessor(lyr, opts);
53420
54411
  var geometries = lyr.shapes.map(function(shp, i) {
53421
54412
  if (!shp) return null;
@@ -53475,9 +54466,23 @@ ${svg}
53475
54466
  } else {
53476
54467
  outputLyr = lyr;
53477
54468
  }
54469
+ if (symbolFields.length > 0) {
54470
+ if (hasNewSymbolFields) {
54471
+ table.markSchemaChanged({operation: 'symbols'});
54472
+ } else {
54473
+ table.markFieldsChanged(symbolFields, {operation: 'symbols'});
54474
+ }
54475
+ }
53478
54476
  return [outputLyr];
53479
54477
  };
53480
54478
 
54479
+ function getSymbolOutputFields(shapeMode, opts) {
54480
+ if (!shapeMode) return ['svg-symbol'];
54481
+ return ['fill', 'stroke', 'opacity'].filter(function(field) {
54482
+ return field in opts;
54483
+ });
54484
+ }
54485
+
53481
54486
  function importGeometries(geometries, records) {
53482
54487
  var features = geometries.map(function(geom, i) {
53483
54488
  records[i];
@@ -53675,12 +54680,18 @@ ${svg}
53675
54680
  }
53676
54681
  });
53677
54682
 
54683
+ if (lyr.shapes || records) {
54684
+ noteLayerWillChange(lyr, {operation: 'uniq'});
54685
+ }
53678
54686
  if (lyr.shapes) {
53679
54687
  lyr.shapes = lyr.shapes.filter(filter);
53680
54688
  }
53681
54689
  if (records) {
53682
54690
  lyr.data = new DataTable(records.filter(filter));
53683
54691
  }
54692
+ if (lyr.shapes || records) {
54693
+ markLayerChanged(lyr, {operation: 'uniq'});
54694
+ }
53684
54695
  if (opts.verbose !== false) {
53685
54696
  message(utils.format('Retained %,d of %,d features', getFeatureCount(lyr), n));
53686
54697
  }
@@ -53714,7 +54725,9 @@ ${svg}
53714
54725
  arcThresholds = calculateVariableThresholds(lyr, arcs, getShapeThreshold);
53715
54726
  applyArcThresholds(arcs, arcThresholds);
53716
54727
  arcs.setRetainedInterval(1e20); // set to a huge value
54728
+ noteDatasetInfoWillChange(dataset, {operation: 'variable-simplify'});
53717
54729
  finalizeSimplification(dataset, opts);
54730
+ markDatasetInfoChanged(dataset, {operation: 'variable-simplify'});
53718
54731
  arcs.flatten(); // bake in simplification (different from standard -simplify)
53719
54732
  };
53720
54733
 
@@ -53809,16 +54822,20 @@ ${svg}
53809
54822
  }
53810
54823
 
53811
54824
  if (!lyr.data) {
54825
+ noteLayerWillChange(lyr, {operation: 'split-on-grid', unit: 'data'});
53812
54826
  lyr.data = new DataTable(shapes.length);
54827
+ markLayerChanged(lyr, {operation: 'split-on-grid', unit: 'data'});
53813
54828
  }
53814
54829
  properties = lyr.data.getRecords();
53815
54830
 
54831
+ lyr.data.captureSchemaBefore({operation: 'split-on-grid', field: fieldName});
53816
54832
  lyr.shapes.forEach(function(shp, i) {
53817
54833
  var bounds = type == 'point' ? getPointBounds$1([shp]) : arcs.getMultiShapeBounds(shp);
53818
54834
  var name = bounds.hasBounds() ? classify(bounds) : '';
53819
54835
  var rec = properties[i] = properties[i] || {};
53820
54836
  rec[fieldName] = name;
53821
54837
  });
54838
+ lyr.data.markSchemaChanged({operation: 'split-on-grid', field: fieldName});
53822
54839
 
53823
54840
  if (setId) return lyr; // don't split layer (instead assign cell ids)
53824
54841
 
@@ -54359,7 +55376,9 @@ ${svg}
54359
55376
  error('Command returned invalid output');
54360
55377
  }
54361
55378
 
55379
+ noteDatasetWillChange(targetDataset, {operation: 'appendOutputLayers', command: name});
54362
55380
  targetDataset.layers = targetDataset.layers.concat(outputLayers);
55381
+ markDatasetChanged(targetDataset, {operation: 'appendOutputLayers', command: name});
54363
55382
  } else {
54364
55383
  // TODO: consider replacing old layers as they are generated, for gc
54365
55384
  replaceLayers(targetDataset, targetLayers, outputLayers);
@@ -54412,7 +55431,7 @@ ${svg}
54412
55431
  });
54413
55432
  }
54414
55433
 
54415
- var version = "0.7.9";
55434
+ var version = "0.7.11";
54416
55435
 
54417
55436
  // Parse command line args into commands and run them
54418
55437
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
@@ -54470,8 +55489,10 @@ ${svg}
54470
55489
  var child = require$1('child_process').exec(command, {}, function(err, stdout, stderr) {
54471
55490
  opts.callback(err);
54472
55491
  });
54473
- child.stdout.pipe(process.stdout);
54474
- child.stderr.pipe(process.stderr);
55492
+ if (loggingEnabled()) {
55493
+ child.stdout.pipe(process.stdout);
55494
+ child.stderr.pipe(process.stderr);
55495
+ }
54475
55496
  if (opts.promise) return opts.promise;
54476
55497
  }
54477
55498
 
@@ -55099,6 +56120,744 @@ ${svg}
55099
56120
  return false;
55100
56121
  }
55101
56122
 
56123
+ function UndoTransaction(label) {
56124
+ this.label = label || '';
56125
+ this.units = [];
56126
+ this._captured = {};
56127
+ }
56128
+
56129
+ UndoTransaction.prototype = {
56130
+ run: function(cb) {
56131
+ return withActiveUndoTransaction(this, cb);
56132
+ },
56133
+
56134
+ getCapturedUnits: function() {
56135
+ return this.units.slice();
56136
+ },
56137
+
56138
+ restore: function() {
56139
+ restoreCapturedUnits(this.units);
56140
+ },
56141
+
56142
+ captureCurrentState: function() {
56143
+ return captureCurrentUnits(this.units);
56144
+ },
56145
+
56146
+ captureTableBefore: function(table, detail) {
56147
+ var key = unitKey('table', table);
56148
+ if (this._captured[key]) return;
56149
+ this._captured[key] = true;
56150
+ this.units.push({
56151
+ type: 'table',
56152
+ target: table,
56153
+ id: getUndoId(table),
56154
+ revision: getUndoRevision(table),
56155
+ detail: copyDetail(detail),
56156
+ records: table.getRecords().map(copyRecord)
56157
+ });
56158
+ },
56159
+
56160
+ captureTableRecordsBefore: function(table, detail) {
56161
+ var ids = uniqueNumbers(detail.ids);
56162
+ var captured = [];
56163
+ var records = table.getRecords();
56164
+ ids.forEach(function(id) {
56165
+ var key = unitKey('table-record', table, id);
56166
+ if (this._captured[key]) return;
56167
+ this._captured[key] = true;
56168
+ captured.push({
56169
+ id: id,
56170
+ record: copyRecord(records[id])
56171
+ });
56172
+ }, this);
56173
+ if (captured.length > 0) {
56174
+ this.units.push({
56175
+ type: 'table-records',
56176
+ target: table,
56177
+ id: getUndoId(table),
56178
+ revision: getUndoRevision(table),
56179
+ detail: copyDetail(detail),
56180
+ records: captured
56181
+ });
56182
+ }
56183
+ },
56184
+
56185
+ captureTableFieldsBefore: function(table, detail) {
56186
+ var fields = uniqueStrings(detail.fields);
56187
+ var records = table.getRecords();
56188
+ var columns = [];
56189
+ fields.forEach(function(field) {
56190
+ var key = unitKey('table-field', table, field);
56191
+ if (this._captured[key]) return;
56192
+ this._captured[key] = true;
56193
+ columns.push({
56194
+ field: field,
56195
+ values: records.map(function(rec) {
56196
+ return rec ? rec[field] : undefined;
56197
+ })
56198
+ });
56199
+ }, this);
56200
+ if (columns.length > 0) {
56201
+ this.units.push({
56202
+ type: 'table-fields',
56203
+ target: table,
56204
+ id: getUndoId(table),
56205
+ revision: getUndoRevision(table),
56206
+ detail: copyDetail(detail),
56207
+ columns: columns
56208
+ });
56209
+ }
56210
+ },
56211
+
56212
+ captureTableOrderBefore: function(table, detail) {
56213
+ captureOrderUnit(this, 'table-order', table, detail);
56214
+ },
56215
+
56216
+ captureTableSchemaBefore: function(table, detail) {
56217
+ var key = unitKey('table-schema', table);
56218
+ if (this._captured[key]) return;
56219
+ this._captured[key] = true;
56220
+ this.units.push({
56221
+ type: 'table-schema',
56222
+ target: table,
56223
+ id: getUndoId(table),
56224
+ revision: getUndoRevision(table),
56225
+ detail: copyDetail(detail),
56226
+ fields: table.getFields()
56227
+ });
56228
+ if (detail && (detail.field || detail.fields)) {
56229
+ this.captureTableFieldsBefore(table, {
56230
+ fields: detail.fields || [detail.field],
56231
+ operation: detail.operation
56232
+ });
56233
+ }
56234
+ },
56235
+
56236
+ captureArcsBefore: function(arcs, detail) {
56237
+ var data, key;
56238
+ key = unitKey('arcs', arcs);
56239
+ if (this._captured[key]) return;
56240
+ this._captured[key] = true;
56241
+ data = arcs.getVertexData();
56242
+ this.units.push({
56243
+ type: 'arcs',
56244
+ target: arcs,
56245
+ id: getUndoId(arcs),
56246
+ revision: getUndoRevision(arcs),
56247
+ detail: copyDetail(detail),
56248
+ nn: new Uint32Array(data.nn),
56249
+ xx: new Float64Array(data.xx),
56250
+ yy: new Float64Array(data.yy),
56251
+ zz: data.zz ? new Float64Array(data.zz) : null,
56252
+ zlimit: arcs.getRetainedInterval()
56253
+ });
56254
+ },
56255
+
56256
+ captureArcsSimplificationBefore: function(arcs, detail) {
56257
+ var data, key;
56258
+ key = unitKey('arcs-simplification', arcs);
56259
+ if (this._captured[key]) return;
56260
+ this._captured[key] = true;
56261
+ data = arcs.getVertexData();
56262
+ this.units.push({
56263
+ type: 'arcs-simplification',
56264
+ target: arcs,
56265
+ id: getUndoId(arcs),
56266
+ revision: getUndoRevision(arcs),
56267
+ detail: copyDetail(detail),
56268
+ zz: data.zz ? new Float64Array(data.zz) : null,
56269
+ zlimit: arcs.getRetainedInterval()
56270
+ });
56271
+ },
56272
+
56273
+ captureCatalogBefore: function(catalog, detail) {
56274
+ var key = unitKey('catalog', catalog);
56275
+ if (this._captured[key]) return;
56276
+ this._captured[key] = true;
56277
+ this.units.push({
56278
+ type: 'catalog',
56279
+ target: catalog,
56280
+ id: getUndoId(catalog),
56281
+ revision: getUndoRevision(catalog),
56282
+ detail: copyDetail(detail),
56283
+ datasets: catalog.getDatasets().slice(),
56284
+ targets: catalog.getDefaultTargets().map(function(target) {
56285
+ return {
56286
+ dataset: target.dataset,
56287
+ layers: target.layers.slice()
56288
+ };
56289
+ })
56290
+ });
56291
+ },
56292
+
56293
+ captureDatasetBefore: function(dataset, detail) {
56294
+ var key = unitKey('dataset', dataset, detail && detail.unit || '');
56295
+ if (this._captured[key]) return;
56296
+ this._captured[key] = true;
56297
+ this.units.push({
56298
+ type: 'dataset',
56299
+ target: dataset,
56300
+ id: getUndoId(dataset),
56301
+ revision: getUndoRevision(dataset),
56302
+ detail: copyDetail(detail),
56303
+ layers: dataset.layers ? dataset.layers.slice() : null,
56304
+ arcs: dataset.arcs || null,
56305
+ info: dataset.info ? copyRecord(dataset.info) : null
56306
+ });
56307
+ },
56308
+
56309
+ captureDatasetInfoBefore: function(dataset, detail) {
56310
+ var key = unitKey('dataset-info', dataset);
56311
+ if (this._captured[key]) return;
56312
+ this._captured[key] = true;
56313
+ this.units.push({
56314
+ type: 'dataset-info',
56315
+ target: dataset,
56316
+ id: getUndoId(dataset),
56317
+ revision: getUndoRevision(dataset),
56318
+ detail: copyDetail(detail),
56319
+ info: dataset.info ? copyRecord(dataset.info) : null
56320
+ });
56321
+ },
56322
+
56323
+ captureLayerBefore: function(layer, detail) {
56324
+ var key = unitKey('layer', layer, detail && detail.unit || '');
56325
+ if (this._captured[key]) return;
56326
+ this._captured[key] = true;
56327
+ this.units.push({
56328
+ type: 'layer',
56329
+ target: layer,
56330
+ id: getUndoId(layer),
56331
+ revision: getUndoRevision(layer),
56332
+ detail: copyDetail(detail),
56333
+ name: layer.name,
56334
+ geometry_type: layer.geometry_type,
56335
+ shapes: layer.shapes ? cloneShapes(layer.shapes) : null,
56336
+ data: layer.data || null
56337
+ });
56338
+ },
56339
+
56340
+ captureLayerMetadataBefore: function(layer, detail) {
56341
+ var key = unitKey('layer-metadata', layer, detail && detail.unit || '');
56342
+ if (this._captured[key]) return;
56343
+ this._captured[key] = true;
56344
+ this.units.push({
56345
+ type: 'layer-metadata',
56346
+ target: layer,
56347
+ id: getUndoId(layer),
56348
+ revision: getUndoRevision(layer),
56349
+ detail: copyDetail(detail),
56350
+ name: layer.name,
56351
+ geometry_type: layer.geometry_type
56352
+ });
56353
+ },
56354
+
56355
+ captureLayerOrderBefore: function(layer, detail) {
56356
+ captureOrderUnit(this, 'layer-order', layer, detail);
56357
+ },
56358
+
56359
+ markChanged: function(obj, detail) {
56360
+ this.units.push({
56361
+ type: 'changed',
56362
+ id: getUndoId(obj),
56363
+ revision: getUndoRevision(obj),
56364
+ detail: copyDetail(detail)
56365
+ });
56366
+ }
56367
+ };
56368
+
56369
+ function restoreCapturedUnits(units) {
56370
+ for (var i = units.length - 1; i >= 0; i--) {
56371
+ restoreUnit(units[i]);
56372
+ }
56373
+ }
56374
+
56375
+ function captureCurrentUnits(units) {
56376
+ var captured = [];
56377
+ units.forEach(function(unit) {
56378
+ var current = captureCurrentUnit(unit);
56379
+ if (current) captured.push(current);
56380
+ });
56381
+ return captured;
56382
+ }
56383
+
56384
+ function filterUnchangedRestoreUnits(units) {
56385
+ var arcChanges = getArcsUnitChangeIndex(units);
56386
+ var normalized = units.map(function(unit) {
56387
+ return normalizeUnchangedDatasetArcs(unit, arcChanges);
56388
+ });
56389
+ var protectedArcs = getDatasetRestoreArcsIndex(normalized);
56390
+ var protectedSimplification = getArcsRestoreIndex(normalized);
56391
+ return normalized.filter(function(unit) {
56392
+ return unit.type == 'changed' ||
56393
+ restoreUnitHasChanged(unit, protectedArcs, protectedSimplification);
56394
+ });
56395
+ }
56396
+
56397
+ function normalizeUnchangedDatasetArcs(unit, arcChanges) {
56398
+ var arcsId = unit.arcs && getUndoId(unit.arcs);
56399
+ if (unit.type == 'dataset' &&
56400
+ unit.arcs &&
56401
+ unit.target.arcs &&
56402
+ unit.arcs !== unit.target.arcs &&
56403
+ arcChanges[arcsId] === false &&
56404
+ arcCollectionsAreEqual(unit.arcs, unit.target.arcs)) {
56405
+ return Object.assign({}, unit, {arcs: unit.target.arcs});
56406
+ }
56407
+ return unit;
56408
+ }
56409
+
56410
+ function restoreUnitHasChanged(unit, protectedArcs, protectedSimplification) {
56411
+ if (unit.type == 'arcs') {
56412
+ if (protectedArcs[getUndoId(unit.target)]) return true;
56413
+ return arcsUnitHasChanged(unit);
56414
+ } else if (unit.type == 'arcs-simplification') {
56415
+ if (protectedSimplification[getUndoId(unit.target)]) return true;
56416
+ return arcsSimplificationUnitHasChanged(unit);
56417
+ }
56418
+ return true;
56419
+ }
56420
+
56421
+ function getArcsUnitChangeIndex(units) {
56422
+ var index = {};
56423
+ units.forEach(function(unit) {
56424
+ if (unit.type == 'arcs') {
56425
+ index[getUndoId(unit.target)] = arcsUnitHasChanged(unit);
56426
+ }
56427
+ });
56428
+ return index;
56429
+ }
56430
+
56431
+ function getDatasetRestoreArcsIndex(units) {
56432
+ var index = {};
56433
+ units.forEach(function(unit) {
56434
+ if (unit.type == 'dataset' && unit.arcs && unit.target.arcs !== unit.arcs) {
56435
+ index[getUndoId(unit.arcs)] = true;
56436
+ }
56437
+ });
56438
+ return index;
56439
+ }
56440
+
56441
+ function getArcsRestoreIndex(units) {
56442
+ var index = {};
56443
+ units.forEach(function(unit) {
56444
+ if (unit.type == 'arcs') {
56445
+ index[getUndoId(unit.target)] = true;
56446
+ }
56447
+ });
56448
+ return index;
56449
+ }
56450
+
56451
+ function arcCollectionsAreEqual(a, b) {
56452
+ var dataA, dataB;
56453
+ if (a === b) return true;
56454
+ if (!a || !b) return !a && !b;
56455
+ dataA = a.getVertexData();
56456
+ dataB = b.getVertexData();
56457
+ return a.getRetainedInterval() == b.getRetainedInterval() &&
56458
+ arrayLikeDataIsEqual(dataA.nn, dataB.nn) &&
56459
+ arrayLikeDataIsEqual(dataA.xx, dataB.xx) &&
56460
+ arrayLikeDataIsEqual(dataA.yy, dataB.yy) &&
56461
+ arrayLikeDataIsEqual(dataA.zz, dataB.zz);
56462
+ }
56463
+
56464
+ function arcsUnitHasChanged(unit) {
56465
+ var data = unit.target.getVertexData();
56466
+ return unit.zlimit != unit.target.getRetainedInterval() ||
56467
+ !arrayLikeDataIsEqual(unit.nn, data.nn) ||
56468
+ !arrayLikeDataIsEqual(unit.xx, data.xx) ||
56469
+ !arrayLikeDataIsEqual(unit.yy, data.yy) ||
56470
+ !arrayLikeDataIsEqual(unit.zz, data.zz);
56471
+ }
56472
+
56473
+ function arcsSimplificationUnitHasChanged(unit) {
56474
+ var data = unit.target.getVertexData();
56475
+ return unit.zlimit != unit.target.getRetainedInterval() ||
56476
+ !arrayLikeDataIsEqual(unit.zz, data.zz);
56477
+ }
56478
+
56479
+ function arrayLikeDataIsEqual(a, b) {
56480
+ if (a === b) return true;
56481
+ if (!a || !b) return !a && !b;
56482
+ if (a.length !== b.length) return false;
56483
+ for (var i = 0; i < a.length; i++) {
56484
+ if (a[i] !== b[i]) return false;
56485
+ }
56486
+ return true;
56487
+ }
56488
+
56489
+ function captureCurrentUnit(unit) {
56490
+ if (unit.type == 'changed') return null;
56491
+ if (unit.type == 'table') {
56492
+ return captureCurrentTable(unit);
56493
+ } else if (unit.type == 'table-records') {
56494
+ return captureCurrentTableRecords(unit);
56495
+ } else if (unit.type == 'table-fields') {
56496
+ return captureCurrentTableFields(unit);
56497
+ } else if (unit.type == 'table-order') {
56498
+ return captureCurrentOrder(unit);
56499
+ } else if (unit.type == 'table-schema') {
56500
+ return captureCurrentTableSchema(unit);
56501
+ } else if (unit.type == 'arcs') {
56502
+ return captureCurrentArcs(unit);
56503
+ } else if (unit.type == 'arcs-simplification') {
56504
+ return captureCurrentArcsSimplification(unit);
56505
+ } else if (unit.type == 'catalog') {
56506
+ return captureCurrentCatalog(unit);
56507
+ } else if (unit.type == 'dataset') {
56508
+ return captureCurrentDataset(unit);
56509
+ } else if (unit.type == 'dataset-info') {
56510
+ return captureCurrentDatasetInfo(unit);
56511
+ } else if (unit.type == 'layer') {
56512
+ return captureCurrentLayer(unit);
56513
+ } else if (unit.type == 'layer-metadata') {
56514
+ return captureCurrentLayerMetadata(unit);
56515
+ } else if (unit.type == 'layer-order') {
56516
+ return captureCurrentOrder(unit);
56517
+ }
56518
+ return null;
56519
+ }
56520
+
56521
+ function captureCurrentTable(unit) {
56522
+ return Object.assign({}, unit, {
56523
+ revision: getUndoRevision(unit.target),
56524
+ records: unit.target.getRecords().map(copyRecord)
56525
+ });
56526
+ }
56527
+
56528
+ function captureCurrentTableRecords(unit) {
56529
+ var records = unit.target.getRecords();
56530
+ return Object.assign({}, unit, {
56531
+ revision: getUndoRevision(unit.target),
56532
+ records: unit.records.map(function(item) {
56533
+ return {
56534
+ id: item.id,
56535
+ record: copyRecord(records[item.id])
56536
+ };
56537
+ })
56538
+ });
56539
+ }
56540
+
56541
+ function captureCurrentTableFields(unit) {
56542
+ var records = unit.target.getRecords();
56543
+ var fields = unit.detail && unit.detail.schema_transform ?
56544
+ unit.target.getFields() :
56545
+ unit.columns.map(function(column) { return column.field; });
56546
+ return Object.assign({}, unit, {
56547
+ revision: getUndoRevision(unit.target),
56548
+ columns: fields.map(function(field) {
56549
+ return {
56550
+ field: field,
56551
+ values: records.map(function(rec) {
56552
+ return rec ? rec[field] : undefined;
56553
+ })
56554
+ };
56555
+ })
56556
+ });
56557
+ }
56558
+
56559
+ function captureCurrentOrder(unit) {
56560
+ return Object.assign({}, unit, {
56561
+ revision: getUndoRevision(unit.target),
56562
+ ids: invertIds(unit.ids)
56563
+ });
56564
+ }
56565
+
56566
+ function captureCurrentTableSchema(unit) {
56567
+ return Object.assign({}, unit, {
56568
+ revision: getUndoRevision(unit.target),
56569
+ fields: unit.target.getFields()
56570
+ });
56571
+ }
56572
+
56573
+ function captureCurrentArcs(unit) {
56574
+ var data = unit.target.getVertexData();
56575
+ return Object.assign({}, unit, {
56576
+ revision: getUndoRevision(unit.target),
56577
+ nn: new Uint32Array(data.nn),
56578
+ xx: new Float64Array(data.xx),
56579
+ yy: new Float64Array(data.yy),
56580
+ zz: data.zz ? new Float64Array(data.zz) : null,
56581
+ zlimit: unit.target.getRetainedInterval()
56582
+ });
56583
+ }
56584
+
56585
+ function captureCurrentArcsSimplification(unit) {
56586
+ var data = unit.target.getVertexData();
56587
+ return Object.assign({}, unit, {
56588
+ revision: getUndoRevision(unit.target),
56589
+ zz: data.zz ? new Float64Array(data.zz) : null,
56590
+ zlimit: unit.target.getRetainedInterval()
56591
+ });
56592
+ }
56593
+
56594
+ function captureCurrentCatalog(unit) {
56595
+ return Object.assign({}, unit, {
56596
+ revision: getUndoRevision(unit.target),
56597
+ datasets: unit.target.getDatasets().slice(),
56598
+ targets: unit.target.getDefaultTargets().map(function(target) {
56599
+ return {
56600
+ dataset: target.dataset,
56601
+ layers: target.layers.slice()
56602
+ };
56603
+ })
56604
+ });
56605
+ }
56606
+
56607
+ function captureCurrentDataset(unit) {
56608
+ return Object.assign({}, unit, {
56609
+ revision: getUndoRevision(unit.target),
56610
+ layers: unit.target.layers ? unit.target.layers.slice() : null,
56611
+ arcs: unit.target.arcs || null,
56612
+ info: unit.target.info ? copyRecord(unit.target.info) : null
56613
+ });
56614
+ }
56615
+
56616
+ function captureCurrentDatasetInfo(unit) {
56617
+ return Object.assign({}, unit, {
56618
+ revision: getUndoRevision(unit.target),
56619
+ info: unit.target.info ? copyRecord(unit.target.info) : null
56620
+ });
56621
+ }
56622
+
56623
+ function captureCurrentLayer(unit) {
56624
+ return Object.assign({}, unit, {
56625
+ revision: getUndoRevision(unit.target),
56626
+ name: unit.target.name,
56627
+ geometry_type: unit.target.geometry_type,
56628
+ shapes: unit.target.shapes ? cloneShapes(unit.target.shapes) : null,
56629
+ data: unit.target.data || null
56630
+ });
56631
+ }
56632
+
56633
+ function captureCurrentLayerMetadata(unit) {
56634
+ return Object.assign({}, unit, {
56635
+ revision: getUndoRevision(unit.target),
56636
+ name: unit.target.name,
56637
+ geometry_type: unit.target.geometry_type
56638
+ });
56639
+ }
56640
+
56641
+ function restoreUnit(unit) {
56642
+ if (unit.type == 'changed') return;
56643
+ if (unit.type == 'table') {
56644
+ restoreTable(unit);
56645
+ } else if (unit.type == 'table-records') {
56646
+ restoreTableRecords(unit);
56647
+ } else if (unit.type == 'table-fields') {
56648
+ restoreTableFields(unit);
56649
+ } else if (unit.type == 'table-order') {
56650
+ restoreTableOrder(unit);
56651
+ } else if (unit.type == 'table-schema') {
56652
+ restoreTableSchema(unit);
56653
+ } else if (unit.type == 'arcs') {
56654
+ restoreArcs(unit);
56655
+ } else if (unit.type == 'arcs-simplification') {
56656
+ restoreArcsSimplification(unit);
56657
+ } else if (unit.type == 'catalog') {
56658
+ restoreCatalog(unit);
56659
+ } else if (unit.type == 'dataset') {
56660
+ restoreDataset(unit);
56661
+ } else if (unit.type == 'dataset-info') {
56662
+ restoreDatasetInfo(unit);
56663
+ } else if (unit.type == 'layer') {
56664
+ restoreLayer(unit);
56665
+ } else if (unit.type == 'layer-metadata') {
56666
+ restoreLayerMetadata(unit);
56667
+ } else if (unit.type == 'layer-order') {
56668
+ restoreLayerOrder(unit);
56669
+ }
56670
+ }
56671
+
56672
+ function restoreTable(unit) {
56673
+ var records = unit.target.getRecords();
56674
+ records.splice(0, records.length);
56675
+ unit.records.forEach(function(rec) {
56676
+ records.push(copyRecord(rec));
56677
+ });
56678
+ }
56679
+
56680
+ function restoreTableRecords(unit) {
56681
+ var records = unit.target.getRecords();
56682
+ unit.records.forEach(function(item) {
56683
+ records[item.id] = copyRecord(item.record);
56684
+ });
56685
+ }
56686
+
56687
+ function restoreTableFields(unit) {
56688
+ var records = unit.target.getRecords();
56689
+ unit.columns.forEach(function(column) {
56690
+ column.values.forEach(function(val, i) {
56691
+ if (!records[i]) records[i] = {};
56692
+ records[i][column.field] = val;
56693
+ });
56694
+ });
56695
+ }
56696
+
56697
+ function restoreTableOrder(unit) {
56698
+ reorderArray(unit.target.getRecords(), unit.ids);
56699
+ }
56700
+
56701
+ function restoreTableSchema(unit) {
56702
+ var records = unit.target.getRecords();
56703
+ records.forEach(function(rec, i) {
56704
+ var reordered = {};
56705
+ unit.fields.forEach(function(field) {
56706
+ reordered[field] = rec ? rec[field] : undefined;
56707
+ });
56708
+ records[i] = reordered;
56709
+ });
56710
+ }
56711
+
56712
+ function restoreArcs(unit) {
56713
+ unit.target.updateVertexData(
56714
+ new Uint32Array(unit.nn),
56715
+ new Float64Array(unit.xx),
56716
+ new Float64Array(unit.yy),
56717
+ unit.zz ? new Float64Array(unit.zz) : null
56718
+ );
56719
+ unit.target.setRetainedInterval(unit.zlimit);
56720
+ }
56721
+
56722
+ function restoreArcsSimplification(unit) {
56723
+ unit.target.setThresholds(unit.zz ? new Float64Array(unit.zz) : null);
56724
+ unit.target.setRetainedInterval(unit.zlimit);
56725
+ }
56726
+
56727
+ function restoreCatalog(unit) {
56728
+ var datasets = unit.target.getDatasets();
56729
+ datasets.splice(0, datasets.length);
56730
+ unit.datasets.forEach(function(dataset) {
56731
+ datasets.push(dataset);
56732
+ });
56733
+ unit.target.setDefaultTargets(unit.targets.map(function(target) {
56734
+ return {
56735
+ dataset: target.dataset,
56736
+ layers: target.layers.slice()
56737
+ };
56738
+ }));
56739
+ }
56740
+
56741
+ function restoreDataset(unit) {
56742
+ unit.target.layers = unit.layers ? unit.layers.slice() : null;
56743
+ unit.target.arcs = unit.arcs;
56744
+ unit.target.info = unit.info ? copyRecord(unit.info) : null;
56745
+ }
56746
+
56747
+ function restoreDatasetInfo(unit) {
56748
+ unit.target.info = unit.info ? copyRecord(unit.info) : null;
56749
+ }
56750
+
56751
+ function restoreLayer(unit) {
56752
+ unit.target.name = unit.name;
56753
+ unit.target.geometry_type = unit.geometry_type;
56754
+ unit.target.shapes = unit.shapes ? cloneShapes(unit.shapes) : null;
56755
+ unit.target.data = unit.data;
56756
+ }
56757
+
56758
+ function restoreLayerMetadata(unit) {
56759
+ unit.target.name = unit.name;
56760
+ unit.target.geometry_type = unit.geometry_type;
56761
+ }
56762
+
56763
+ function restoreLayerOrder(unit) {
56764
+ reorderArray(unit.target.shapes, unit.ids);
56765
+ }
56766
+
56767
+ function unitKey(type, obj, extra) {
56768
+ return type + ':' + getUndoId(obj) + (extra == null ? '' : ':' + extra);
56769
+ }
56770
+
56771
+ function captureOrderUnit(tx, type, target, detail) {
56772
+ var unit = findOrderUnit(tx.units, type, target);
56773
+ var ids = uniquePermutation(detail.ids);
56774
+ if (unit) {
56775
+ unit.ids = composeIds(ids, unit.ids);
56776
+ unit.detail = copyDetail(detail);
56777
+ return;
56778
+ }
56779
+ tx.units.push({
56780
+ type: type,
56781
+ target: target,
56782
+ id: getUndoId(target),
56783
+ revision: getUndoRevision(target),
56784
+ detail: copyDetail(detail),
56785
+ ids: ids
56786
+ });
56787
+ }
56788
+
56789
+ function findOrderUnit(units, type, target) {
56790
+ for (var i = units.length - 1; i >= 0; i--) {
56791
+ if (units[i].type == type && units[i].target == target) {
56792
+ return units[i];
56793
+ }
56794
+ }
56795
+ return null;
56796
+ }
56797
+
56798
+ function reorderArray(arr, ids) {
56799
+ var copy = ids.map(function(id) {
56800
+ return arr[id];
56801
+ });
56802
+ arr.splice.apply(arr, [0, arr.length].concat(copy));
56803
+ }
56804
+
56805
+ function composeIds(a, b) {
56806
+ return b.map(function(id) {
56807
+ return a[id];
56808
+ });
56809
+ }
56810
+
56811
+ function invertIds(ids) {
56812
+ var inverse = [];
56813
+ ids.forEach(function(id, i) {
56814
+ inverse[id] = i;
56815
+ });
56816
+ return inverse;
56817
+ }
56818
+
56819
+ function uniquePermutation(ids) {
56820
+ var index = {};
56821
+ ids = ids || [];
56822
+ ids.forEach(function(id) {
56823
+ if (id < 0 || id >= ids.length || index[id]) {
56824
+ throw new Error('Invalid undo order permutation');
56825
+ }
56826
+ index[id] = true;
56827
+ });
56828
+ return ids.slice();
56829
+ }
56830
+
56831
+ function copyDetail(detail) {
56832
+ return Object.assign({}, detail || {});
56833
+ }
56834
+
56835
+ function uniqueNumbers(ids) {
56836
+ var index = {};
56837
+ ids = ids || [];
56838
+ ids.forEach(function(id) {
56839
+ if (id >= 0) index[id] = true;
56840
+ });
56841
+ return Object.keys(index).map(Number);
56842
+ }
56843
+
56844
+ function uniqueStrings(fields) {
56845
+ var index = {};
56846
+ fields = fields || [];
56847
+ fields.forEach(function(field) {
56848
+ index[field] = true;
56849
+ });
56850
+ return Object.keys(index);
56851
+ }
56852
+
56853
+ var UndoTransaction$1 = /*#__PURE__*/Object.freeze({
56854
+ __proto__: null,
56855
+ UndoTransaction: UndoTransaction,
56856
+ captureCurrentUnits: captureCurrentUnits,
56857
+ filterUnchangedRestoreUnits: filterUnchangedRestoreUnits,
56858
+ restoreCapturedUnits: restoreCapturedUnits
56859
+ });
56860
+
55102
56861
  // Attach functions exported by modules to the "internal" object,
55103
56862
  // so they can be run by tests and by the GUI.
55104
56863
  // TODO: rewrite tests to import functions directly from modules,
@@ -55256,6 +57015,8 @@ ${svg}
55256
57015
  TopojsonExport,
55257
57016
  TopojsonImport,
55258
57017
  Topology,
57018
+ UndoTransaction$1,
57019
+ UndoTracking,
55259
57020
  Units,
55260
57021
  SvgHatch,
55261
57022
  SvgEffect,