mapshaper 0.7.8 → 0.7.10

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/www/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('~');
@@ -25683,52 +26126,71 @@ ${svg}
25683
26126
  }
25684
26127
 
25685
26128
  var writerPromise = null;
26129
+ var zstdPromise = null;
25686
26130
  var dynamicImportModule$1 = Function('id', 'return import(id)');
25687
26131
 
25688
26132
  async function exportGeoParquet(dataset, opts, filenameOverride) {
25689
26133
  var writer = await loadGeoParquetWriter();
26134
+ var compression = await getGeoParquetCompression(opts);
25690
26135
  var extension = opts.extension || 'parquet';
26136
+ var files = [];
25691
26137
  if (opts.file) {
25692
26138
  extension = getFileExtension(opts.file) || extension;
25693
26139
  }
25694
- return dataset.layers.map(function(lyr) {
25695
- if (!lyr.geometry_type) {
25696
- stop$1('GeoParquet export requires a geometry layer');
25697
- }
26140
+ dataset.layers.forEach(function(lyr) {
25698
26141
  var features = exportLayerAsGeoJSON(lyr, dataset, opts, true, null);
25699
- var output = buildGeoParquetColumns(features);
25700
- var geoMetadata = buildGeoMetadata(features, dataset);
25701
- var content = writer.parquetWriteBuffer({
26142
+ var hasGeometry = features.some(function(feat) {
26143
+ return !!feat.geometry;
26144
+ });
26145
+ var output = buildGeoParquetColumns(features, hasGeometry);
26146
+ var writeOptions = {
25702
26147
  columnData: output.columnData,
25703
- kvMetadata: [{
26148
+ codec: compression.codec,
26149
+ compressors: compression.compressors,
26150
+ pageSize: compression.pageSize
26151
+ };
26152
+ if (hasGeometry) {
26153
+ writeOptions.kvMetadata = [{
25704
26154
  key: 'geo',
25705
- value: JSON.stringify(geoMetadata)
25706
- }]
25707
- });
25708
- return {
26155
+ value: JSON.stringify(buildGeoMetadata(features, dataset))
26156
+ }];
26157
+ } else {
26158
+ warn('GeoParquet export: layer has no geometry; writing attribute data only.');
26159
+ }
26160
+ var content = writer.parquetWriteBuffer(writeOptions);
26161
+ files.push({
25709
26162
  filename: filenameOverride || (lyr.name + '.' + extension),
25710
26163
  content: content
25711
- };
26164
+ });
25712
26165
  });
26166
+ return files;
25713
26167
  }
25714
26168
 
25715
- function buildGeoParquetColumns(features, writer) {
26169
+ function buildGeoParquetColumns(features, includeGeometry) {
25716
26170
  var geometryName = 'geometry';
25717
26171
  var names = getPropertyNames(features);
25718
26172
  var columnData = [];
25719
- columnData.push({
25720
- name: geometryName,
25721
- data: features.map(function(feat) {
25722
- return feat.geometry || null;
25723
- }),
25724
- type: 'GEOMETRY'
25725
- });
26173
+ if (features.length === 0) {
26174
+ stop$1('GeoParquet export requires at least one record');
26175
+ }
26176
+ if (includeGeometry) {
26177
+ columnData.push({
26178
+ name: geometryName,
26179
+ data: features.map(function(feat) {
26180
+ return feat.geometry || null;
26181
+ }),
26182
+ type: 'GEOMETRY'
26183
+ });
26184
+ }
25726
26185
  names.forEach(function(name) {
25727
26186
  var values = features.map(function(feat) {
25728
26187
  return feat.properties ? feat.properties[name] : null;
25729
26188
  });
25730
26189
  columnData.push(buildAttributeColumn(name, values));
25731
26190
  });
26191
+ if (columnData.length === 0) {
26192
+ stop$1('GeoParquet export requires geometry or attribute data');
26193
+ }
25732
26194
  return {columnData: columnData, geometryColumn: geometryName};
25733
26195
  }
25734
26196
 
@@ -25886,6 +26348,99 @@ ${svg}
25886
26348
  return nodeMod.default && !nodeMod.parquetWriteBuffer ? nodeMod.default : nodeMod;
25887
26349
  }
25888
26350
 
26351
+ async function getGeoParquetCompression(opts) {
26352
+ var codec = normalizeGeoParquetCompression(opts.compression);
26353
+ var level = validateGeoParquetCompressionLevel(opts.level, codec);
26354
+ if (codec != 'ZSTD') {
26355
+ return {codec: codec, compressors: null};
26356
+ }
26357
+ var zstd = await loadZstdLib();
26358
+ if (!zstd || typeof zstd.compress != 'function') {
26359
+ stop$1('GeoParquet ZSTD compressor is not loaded');
26360
+ }
26361
+ return {
26362
+ codec: codec,
26363
+ pageSize: getGeoParquetPageSize(level),
26364
+ compressors: {
26365
+ ZSTD: function(bytes) {
26366
+ return compressZstdPage(zstd, bytes, level);
26367
+ }
26368
+ }
26369
+ };
26370
+ }
26371
+
26372
+ function compressZstdPage(zstd, bytes, level) {
26373
+ var compressed;
26374
+ try {
26375
+ compressed = zstd.compress(bytes, level);
26376
+ } catch (e) {
26377
+ stop$1('Unable to apply GeoParquet ZSTD compression. Try a lower level= value.');
26378
+ }
26379
+ if (!compressed) {
26380
+ stop$1('Unable to apply GeoParquet ZSTD compression. Try a lower level= value.');
26381
+ }
26382
+ return compressed;
26383
+ }
26384
+
26385
+ function getGeoParquetPageSize(level) {
26386
+ return level >= 10 ? 64 * 1024 : undefined;
26387
+ }
26388
+
26389
+ function normalizeGeoParquetCompression(compression) {
26390
+ var str = compression === undefined || compression === null ? 'snappy' : String(compression).toLowerCase();
26391
+ if (str == 'snappy') return 'SNAPPY';
26392
+ if (str == 'zstd') return 'ZSTD';
26393
+ if (str == 'none' || str == 'uncompressed') return null;
26394
+ stop$1('Unsupported GeoParquet compression:', compression);
26395
+ }
26396
+
26397
+ function validateGeoParquetCompressionLevel(level, codec) {
26398
+ if (level === undefined) return undefined;
26399
+ if (codec != 'ZSTD') {
26400
+ stop$1('The level= option only applies with compression=zstd');
26401
+ }
26402
+ if (level >= 1 && level <= 22 && Math.floor(level) === level) {
26403
+ return level;
26404
+ }
26405
+ stop$1('GeoParquet ZSTD level= option must be an integer from 1 to 22');
26406
+ }
26407
+
26408
+ async function loadZstdLib() {
26409
+ var mod;
26410
+ if (runningInBrowser()) {
26411
+ mod = require$1('zstd-codec');
26412
+ } else {
26413
+ if (!zstdPromise) {
26414
+ zstdPromise = dynamicImportModule$1('zstd-codec');
26415
+ }
26416
+ mod = await zstdPromise;
26417
+ }
26418
+ if (mod && mod.default && !mod.ZstdCodec) {
26419
+ mod = mod.default;
26420
+ }
26421
+ if (!mod || !mod.ZstdCodec || typeof mod.ZstdCodec.run != 'function') {
26422
+ stop$1('GeoParquet ZSTD compressor is not loaded');
26423
+ }
26424
+ return initZstdCodec(mod.ZstdCodec);
26425
+ }
26426
+
26427
+ function initZstdCodec(codec) {
26428
+ return new Promise(function(resolve, reject) {
26429
+ try {
26430
+ codec.run(function(zstd) {
26431
+ var simple = new zstd.Simple();
26432
+ resolve({
26433
+ compress: function(bytes, level) {
26434
+ return simple.compress(bytes, level);
26435
+ }
26436
+ });
26437
+ });
26438
+ } catch (e) {
26439
+ reject(e);
26440
+ }
26441
+ });
26442
+ }
26443
+
25889
26444
  function getOutputFormat(dataset, opts) {
25890
26445
  var outFile = opts.file || null,
25891
26446
  inFmt = dataset.info && dataset.info.input_formats && dataset.info.input_formats[0],
@@ -25991,6 +26546,9 @@ ${svg}
25991
26546
  async function exportDatasets(datasets, opts) {
25992
26547
  var format = getOutputFormat(datasets[0], opts);
25993
26548
  var files;
26549
+ if (format != 'geoparquet' && (opts.compression || opts.level !== undefined)) {
26550
+ error('The compression= and level= options only apply to GeoParquet output');
26551
+ }
25994
26552
  if (format == PACKAGE_EXT) {
25995
26553
  opts = utils.defaults({compact: true}, opts);
25996
26554
  return exportPackedDatasets(datasets, opts);
@@ -28669,6 +29227,13 @@ ${svg}
28669
29227
  type: 'flag',
28670
29228
  describe: '[CSV] export numbers with decimal commas not points'
28671
29229
  })
29230
+ .option('compression', {
29231
+ describe: '[GeoParquet] options: snappy,zstd,none (default is snappy)'
29232
+ })
29233
+ .option('level', {
29234
+ describe: '[GeoParquet] zstd compression level',
29235
+ type: 'integer'
29236
+ })
28672
29237
  .option('show-all', {
28673
29238
  type: 'flag',
28674
29239
  describe: '[Snapshot] show all layers in Web UI'
@@ -29344,20 +29909,30 @@ ${svg}
29344
29909
  .option('no-replace', noReplaceOpt);
29345
29910
 
29346
29911
  parser.command('grid')
29347
- .describe('create a grid of square or hexagonal polygons')
29912
+ .describe('create a grid of square, hexagonal, rhombus or triangle polygons')
29348
29913
  .option('type', {
29349
- describe: 'square, hex or hex2 (default is square)'
29914
+ describe: 'square, square2, hex, hex2, rhombus, rhombus2, triangle or triangle2 (default is square)'
29350
29915
  })
29351
29916
  .option('interval', {
29352
29917
  describe: 'side length (e.g. 500m, 12km)',
29353
29918
  type: 'distance'
29354
29919
  })
29355
29920
  .option('cols', {
29921
+ describe: 'target number of grid columns',
29356
29922
  type: 'integer'
29357
29923
  })
29358
29924
  .option('rows', {
29925
+ describe: 'target number of grid rows',
29926
+ type: 'integer'
29927
+ })
29928
+ .option('cells', {
29929
+ describe: 'target number of grid cells',
29359
29930
  type: 'integer'
29360
29931
  })
29932
+ .option('cell-scale', {
29933
+ describe: 'scale factor for cells, between 0 and 2',
29934
+ type: 'number'
29935
+ })
29361
29936
  // .option('bbox', {
29362
29937
  // type: 'bbox',
29363
29938
  // describe: 'xmin,ymin,xmax,ymax (default is bbox of data)'
@@ -32416,10 +32991,12 @@ ${svg}
32416
32991
  function parseGeoJSON(reader, cb) {
32417
32992
  var src = ByteReader(reader, 0);
32418
32993
  var isObject = seekObjectStart(src);
32994
+ var type;
32419
32995
  if (!isObject) {
32420
32996
  stop$1('File is not GeoJSON');
32421
32997
  }
32422
- var obj = readObject(src, cb);
32998
+ type = readTopLevelType(src);
32999
+ var obj = readObject(src, cb, type);
32423
33000
  if (obj.type == 'FeatureCollection' || obj.type == 'GeometryCollection') {
32424
33001
  return obj;
32425
33002
  }
@@ -32500,6 +33077,17 @@ ${svg}
32500
33077
  return arr;
32501
33078
  }
32502
33079
 
33080
+ function skipArray(src) {
33081
+ var c;
33082
+ eatChar(src, LBRACK);
33083
+ c = scanForSyntaxChar(src, RBRACK);
33084
+ while (c != RBRACK) {
33085
+ src.refresh();
33086
+ skipValue(src);
33087
+ c = scanForAorB(src, COMMA, RBRACK);
33088
+ }
33089
+ }
33090
+
32503
33091
  function readCollectionArray(src, cb) {
32504
33092
  var c;
32505
33093
  eatChar(src, LBRACK);
@@ -32588,6 +33176,18 @@ ${svg}
32588
33176
  return val;
32589
33177
  }
32590
33178
 
33179
+ function skipValue(src) {
33180
+ var c = src.peek();
33181
+ if (isFirstNumChar(c)) readNumber(src);
33182
+ else if (c == LBRACK) skipArray(src);
33183
+ else if (c == DQUOTE) readString(src);
33184
+ else if (c == LBRACE) skipObject(src);
33185
+ else if (c == 110) readNull(src); // "n" -> null
33186
+ else if (c == 116) readTrue(src); // "t" -> true
33187
+ else if (c == 102) readFalse(src); // "f" -> false
33188
+ else unexpectedCharAt(c, src.index());
33189
+ }
33190
+
32591
33191
  function readTrue(src) {
32592
33192
  eatChars(src, 'true');
32593
33193
  return true;
@@ -32614,7 +33214,7 @@ ${svg}
32614
33214
 
32615
33215
  // cb: optional callback for returning GeoJSON features or geometries
32616
33216
  //
32617
- function readObject(src, cb) {
33217
+ function readObject(src, cb, type) {
32618
33218
  var o = {};
32619
33219
  var key, c;
32620
33220
  eatChar(src, LBRACE);
@@ -32625,7 +33225,8 @@ ${svg}
32625
33225
  skipWS(src);
32626
33226
  eatChar(src, 58); // ":"
32627
33227
  skipWS(src);
32628
- if ((key == 'features' || key == 'geometries') &&
33228
+ if ((type == 'FeatureCollection' && key == 'features' ||
33229
+ type == 'GeometryCollection' && key == 'geometries') &&
32629
33230
  src.peek() == LBRACK && cb) {
32630
33231
  readCollectionArray(src, cb);
32631
33232
  o[key] = null;
@@ -32639,6 +33240,43 @@ ${svg}
32639
33240
  return o;
32640
33241
  }
32641
33242
 
33243
+ function skipObject(src) {
33244
+ var c;
33245
+ eatChar(src, LBRACE);
33246
+ c = scanForSyntaxChar(src, RBRACE);
33247
+ while (c != RBRACE) {
33248
+ src.refresh();
33249
+ readKeywordString(src);
33250
+ skipWS(src);
33251
+ eatChar(src, 58); // ":"
33252
+ skipWS(src);
33253
+ skipValue(src);
33254
+ c = scanForAorB(src, COMMA, RBRACE);
33255
+ }
33256
+ }
33257
+
33258
+ function readTopLevelType(src) {
33259
+ var i = src.index();
33260
+ var type, key, c;
33261
+ eatChar(src, LBRACE);
33262
+ c = scanForSyntaxChar(src, RBRACE);
33263
+ while (c != RBRACE) {
33264
+ src.refresh();
33265
+ key = readKeywordString(src);
33266
+ skipWS(src);
33267
+ eatChar(src, 58); // ":"
33268
+ skipWS(src);
33269
+ if (key == 'type' && src.peek() == DQUOTE) {
33270
+ type = readKeywordString(src);
33271
+ break;
33272
+ }
33273
+ skipValue(src);
33274
+ c = scanForAorB(src, COMMA, RBRACE);
33275
+ }
33276
+ src.index(i);
33277
+ return type;
33278
+ }
33279
+
32642
33280
  function growReserve() {
32643
33281
  RESERVE *= 2;
32644
33282
  return RESERVE <= MAX_STRLEN;
@@ -33675,6 +34313,7 @@ ${svg}
33675
34313
  }
33676
34314
 
33677
34315
  var hyparquetPromise = null;
34316
+ var compressorsPromise = null;
33678
34317
  var dynamicImportModule = Function('id', 'return import(id)');
33679
34318
  var mproj = null;
33680
34319
 
@@ -33685,8 +34324,10 @@ ${svg}
33685
34324
  var file = await getHyparquetFile(source, hyparquet);
33686
34325
  var metadata = await hyparquet.parquetMetadataAsync(file);
33687
34326
  var geo = parseGeoParquetMetadata(metadata);
34327
+ var compressors = await loadHyparquetCompressors();
33688
34328
  var rows = await hyparquet.parquetReadObjects({
33689
34329
  file: file,
34330
+ compressors: compressors,
33690
34331
  rowFormat: 'object'
33691
34332
  });
33692
34333
  var geometryColumn = getGeoParquetGeometryColumn(rows, geo);
@@ -33726,6 +34367,26 @@ ${svg}
33726
34367
  return nodeMod.default && !nodeMod.parquetReadObjects ? nodeMod.default : nodeMod;
33727
34368
  }
33728
34369
 
34370
+ async function loadHyparquetCompressors() {
34371
+ if (runningInBrowser()) {
34372
+ return getHyparquetCompressors(require$1('hyparquet-compressors'));
34373
+ }
34374
+ if (!compressorsPromise) {
34375
+ compressorsPromise = dynamicImportModule('hyparquet-compressors');
34376
+ }
34377
+ return getHyparquetCompressors(await compressorsPromise);
34378
+ }
34379
+
34380
+ function getHyparquetCompressors(mod) {
34381
+ if (mod && mod.default && !mod.compressors) {
34382
+ mod = mod.default;
34383
+ }
34384
+ if (!mod || !mod.compressors) {
34385
+ stop$1('GeoParquet compression library is not loaded');
34386
+ }
34387
+ return mod.compressors;
34388
+ }
34389
+
33729
34390
  function getGeoParquetSource(input) {
33730
34391
  if (!input) return null;
33731
34392
  if (Object.prototype.hasOwnProperty.call(input, 'content')) {
@@ -35714,6 +36375,22 @@ ${svg}
35714
36375
  var datasets = [],
35715
36376
  defaultTargets = [];// saved default command targets [{layers:[], dataset}, ...]
35716
36377
 
36378
+ this.getUndoId = function() {
36379
+ return getUndoId(this);
36380
+ };
36381
+
36382
+ this.getUndoRevision = function() {
36383
+ return getUndoRevision(this);
36384
+ };
36385
+
36386
+ this.captureCatalogBefore = function(detail) {
36387
+ noteCatalogWillChange(this, detail);
36388
+ };
36389
+
36390
+ this.markCatalogChanged = function(detail) {
36391
+ return markCatalogChanged(this, detail);
36392
+ };
36393
+
35717
36394
  this.forEachLayer = function(cb) {
35718
36395
  var i = 0;
35719
36396
  datasets.forEach(function(dataset) {
@@ -35725,16 +36402,19 @@ ${svg}
35725
36402
 
35726
36403
  // remove a layer from a dataset
35727
36404
  this.deleteLayer = function(lyr, dataset) {
36405
+ this.captureCatalogBefore({operation: 'deleteLayer'});
35728
36406
  // if deleting first target layer (selected in gui) -- switch to some other layer
35729
36407
  if (this.getActiveLayer().layer == lyr) {
35730
36408
  defaultTargets = [];
35731
36409
  }
35732
36410
 
35733
36411
  // remove layer from its dataset
36412
+ noteDatasetWillChange(dataset, {operation: 'deleteLayer', unit: 'layers'});
35734
36413
  dataset.layers.splice(dataset.layers.indexOf(lyr), 1);
35735
36414
  if (dataset.layers.length === 0) {
35736
- this.removeDataset(dataset);
36415
+ removeDatasetRaw(dataset);
35737
36416
  }
36417
+ markDatasetChanged(dataset, {operation: 'deleteLayer', unit: 'layers'});
35738
36418
 
35739
36419
  // remove layer from defaultTargets
35740
36420
  defaultTargets = defaultTargets.filter(function(targ) {
@@ -35743,6 +36423,7 @@ ${svg}
35743
36423
  targ.layers.splice(i, 1);
35744
36424
  return targ.layers.length > 0;
35745
36425
  });
36426
+ this.markCatalogChanged({operation: 'deleteLayer'});
35746
36427
  };
35747
36428
 
35748
36429
  // @arg: a layer object or a test function
@@ -35771,17 +36452,16 @@ ${svg}
35771
36452
  };
35772
36453
 
35773
36454
  this.clear = function() {
36455
+ this.captureCatalogBefore({operation: 'clear'});
35774
36456
  datasets = [];
35775
36457
  defaultTargets = [];
36458
+ this.markCatalogChanged({operation: 'clear'});
35776
36459
  };
35777
36460
 
35778
36461
  this.removeDataset = function(dataset) {
35779
- defaultTargets = defaultTargets.filter(function(targ) {
35780
- return targ.dataset != dataset;
35781
- });
35782
- datasets = datasets.filter(function(d) {
35783
- return d != dataset;
35784
- });
36462
+ this.captureCatalogBefore({operation: 'removeDataset'});
36463
+ removeDatasetRaw(dataset);
36464
+ this.markCatalogChanged({operation: 'removeDataset'});
35785
36465
  };
35786
36466
 
35787
36467
  this.getDatasets = function() {
@@ -35843,12 +36523,15 @@ ${svg}
35843
36523
 
35844
36524
  // arr: array of target objects {layers:[], dataset:{}}
35845
36525
  this.setDefaultTargets = function(arr) {
36526
+ if (targetsAreSame(defaultTargets, arr)) return;
36527
+ this.captureCatalogBefore({operation: 'setDefaultTargets'});
35846
36528
  arr.forEach(function(target) {
35847
36529
  if (datasets.indexOf(target.dataset) == -1) {
35848
36530
  datasets.push(target.dataset);
35849
36531
  }
35850
36532
  });
35851
36533
  defaultTargets = arr;
36534
+ this.markCatalogChanged({operation: 'setDefaultTargets'});
35852
36535
  };
35853
36536
 
35854
36537
  // should be in gui-model.js, moved here for testing
@@ -35873,6 +36556,24 @@ ${svg}
35873
36556
  };
35874
36557
  }
35875
36558
 
36559
+ function targetsAreSame(a, b) {
36560
+ if (a.length != b.length) return false;
36561
+ for (var i = 0; i < a.length; i++) {
36562
+ if (a[i].dataset != b[i].dataset || !layersAreSame(a[i].layers, b[i].layers)) {
36563
+ return false;
36564
+ }
36565
+ }
36566
+ return true;
36567
+ }
36568
+
36569
+ function layersAreSame(a, b) {
36570
+ if (a.length != b.length) return false;
36571
+ for (var i = 0; i < a.length; i++) {
36572
+ if (a[i] != b[i]) return false;
36573
+ }
36574
+ return true;
36575
+ }
36576
+
35876
36577
  function indexOfLayer(lyr, layers) {
35877
36578
  var idx = -1;
35878
36579
  layers.forEach(function(o, i) {
@@ -35880,6 +36581,15 @@ ${svg}
35880
36581
  });
35881
36582
  return idx;
35882
36583
  }
36584
+
36585
+ function removeDatasetRaw(dataset) {
36586
+ defaultTargets = defaultTargets.filter(function(targ) {
36587
+ return targ.dataset != dataset;
36588
+ });
36589
+ datasets = datasets.filter(function(d) {
36590
+ return d != dataset;
36591
+ });
36592
+ }
35883
36593
  }
35884
36594
 
35885
36595
  function getFormattedLayerList(catalog) {
@@ -36124,6 +36834,7 @@ ${svg}
36124
36834
  var targetShapes = [];
36125
36835
  var otherShapes = [];
36126
36836
  var targetPoints = [];
36837
+ var targetPointLayers = [];
36127
36838
  var targetFlags, otherFlags, transform;
36128
36839
  dataset.layers.filter(layerHasGeometry).forEach(function(lyr) {
36129
36840
  var hits = [],
@@ -36141,6 +36852,7 @@ ${svg}
36141
36852
  }
36142
36853
  if (lyr.geometry_type == 'point') {
36143
36854
  targetPoints = targetPoints.concat(hits);
36855
+ if (hits.length > 0) targetPointLayers.push(lyr);
36144
36856
  } else {
36145
36857
  targetShapes = targetShapes.concat(hits);
36146
36858
  otherShapes = otherShapes.concat(misses);
@@ -36160,7 +36872,9 @@ ${svg}
36160
36872
  if (otherShapes.length > 0) {
36161
36873
  countArcsInShapes(otherShapes, otherFlags);
36162
36874
  applyArrayMask(otherFlags, targetFlags);
36875
+ noteDatasetWillChange(dataset, {operation: 'affine', unit: 'arcs'});
36163
36876
  dataset.arcs = duplicateSelectedArcs(otherShapes, arcs, otherFlags);
36877
+ markDatasetChanged(dataset, {operation: 'affine', unit: 'arcs'});
36164
36878
  }
36165
36879
  dataset.arcs.transformPoints(function(x, y, arcId) {
36166
36880
  if (arcId < targetFlags.length && targetFlags[arcId] > 0) {
@@ -36168,11 +36882,17 @@ ${svg}
36168
36882
  }
36169
36883
  });
36170
36884
  }
36885
+ targetPointLayers.forEach(function(lyr) {
36886
+ noteLayerWillChange(lyr, {operation: 'affine', unit: 'shapes'});
36887
+ });
36171
36888
  forEachPoint(targetPoints, function(p) {
36172
36889
  var p2 = transform(p[0], p[1]);
36173
36890
  p[0] = p2[0];
36174
36891
  p[1] = p2[1];
36175
36892
  });
36893
+ targetPointLayers.forEach(function(lyr) {
36894
+ markLayerChanged(lyr, {operation: 'affine', unit: 'shapes'});
36895
+ });
36176
36896
  };
36177
36897
 
36178
36898
  function getAffineAnchor(dataset, opts) {
@@ -37014,7 +37734,9 @@ ${svg}
37014
37734
  var dataset = getPolygonDataset$2(pointLyr, filter, opts);
37015
37735
  var merged = mergeDatasets([targetDataset, dataset]);
37016
37736
  var lyr = merged.layers.pop();
37737
+ noteDatasetWillChange(targetDataset, {operation: 'alpha-shapes', unit: 'arcs'});
37017
37738
  targetDataset.arcs = merged.arcs;
37739
+ markDatasetChanged(targetDataset, {operation: 'alpha-shapes', unit: 'arcs'});
37018
37740
  setOutputLayerName(lyr, pointLyr, null, opts);
37019
37741
  return lyr;
37020
37742
  };
@@ -41750,9 +42472,11 @@ ${svg}
41750
42472
  // message('Use save-as=<field> to save to a different field');
41751
42473
  }
41752
42474
 
42475
+ lyr.data.captureSchemaBefore({operation: 'classify', field: outputField});
41753
42476
  records.forEach(function(d, i) {
41754
42477
  d[outputField] = classifyByRecordId(i);
41755
42478
  });
42479
+ lyr.data.markSchemaChanged({operation: 'classify', field: outputField});
41756
42480
  };
41757
42481
 
41758
42482
  function formatValuesForLogging(arr) {
@@ -41794,7 +42518,9 @@ ${svg}
41794
42518
  }
41795
42519
  };
41796
42520
 
42521
+ noteLayerWillChange(lyr, {operation: 'filter-slivers', unit: 'shapes'});
41797
42522
  editShapes(lyr.shapes, pathFilter);
42523
+ markLayerChanged(lyr, {operation: 'filter-slivers', unit: 'shapes'});
41798
42524
  message(utils.format("Removed %'d sliver%s using %s", removed, utils.pluralSuffix(removed), filterData.label));
41799
42525
 
41800
42526
  // Remove null shapes (likely removed by clipping/erasing, although possibly already present)
@@ -41829,7 +42555,9 @@ ${svg}
41829
42555
  };
41830
42556
 
41831
42557
  countArcsInShapes(clipLyr.shapes, flags);
42558
+ noteLayerWillChange(lyr, {operation: 'filter-clip-slivers', unit: 'shapes'});
41832
42559
  editShapes(lyr.shapes, pathFilter);
42560
+ markLayerChanged(lyr, {operation: 'filter-clip-slivers', unit: 'shapes'});
41833
42561
  return removed;
41834
42562
  }
41835
42563
 
@@ -42429,7 +43157,9 @@ ${svg}
42429
43157
  clipLyr = mergedDataset.layers[mergedDataset.layers.length-1];
42430
43158
  if (usingPathClip) {
42431
43159
  nodes = addIntersectionCuts(mergedDataset, opts);
43160
+ noteDatasetWillChange(targetDataset, {operation: type, unit: 'arcs'});
42432
43161
  targetDataset.arcs = mergedDataset.arcs;
43162
+ markDatasetChanged(targetDataset, {operation: type, unit: 'arcs'});
42433
43163
  profileStart('clipDissolvePolygonLayer2');
42434
43164
  clipLyr = utils.defaults({data: null}, clipLyr);
42435
43165
  clipLyr = dissolvePolygonLayer2(clipLyr, mergedDataset, {quiet: true, silent: true});
@@ -42909,7 +43639,9 @@ ${svg}
42909
43639
  var exp = `this.geojson = splitFeature(this.geojson)`;
42910
43640
  requirePolylineLayer(lyr);
42911
43641
  defs.splitFeature = getSplitFeatureFunction(crs, opts);
43642
+ noteLayerWillChange(lyr, {operation: 'dashlines', unit: 'shapes'});
42912
43643
  cmd.evaluateEachFeature(lyr, dataset, exp, opts);
43644
+ markLayerChanged(lyr, {operation: 'dashlines', unit: 'shapes'});
42913
43645
  delete defs.splitFeature;
42914
43646
  };
42915
43647
 
@@ -43050,6 +43782,7 @@ ${svg}
43050
43782
  var getNeighbors = getNeighborLookupFunction(lyr, arcs);
43051
43783
  var fillCount, islandCount;
43052
43784
 
43785
+ lyr.data.captureFieldsBefore([field], {operation: 'data-fill'});
43053
43786
  // get function to check if a shape was empty before data-fill
43054
43787
  var initiallyEmpty = (function() {
43055
43788
  var flags = lyr.data.getRecords().map(function(rec) {
@@ -43068,6 +43801,7 @@ ${svg}
43068
43801
  if (opts.contiguous) {
43069
43802
  islandCount = dataFillIslandGroups(field, lyr, arcs, getNeighbors, opts);
43070
43803
  }
43804
+ lyr.data.markFieldsChanged([field], {operation: 'data-fill'});
43071
43805
 
43072
43806
  message('Filled', fillCount, 'empty polygons' + utils.pluralSuffix(fillCount));
43073
43807
  if (islandCount > 0) {
@@ -44587,6 +45321,19 @@ ${svg}
44587
45321
  calc = getJoinCalc(src, opts.calc);
44588
45322
  }
44589
45323
 
45324
+ if (useDuplication) {
45325
+ noteLayerWillChange(destLyr, {operation: 'join', duplication: true});
45326
+ } else if (opts.calc) {
45327
+ dest.captureTableBefore({operation: 'join', calc: true});
45328
+ } else {
45329
+ dest.captureSchemaBefore({
45330
+ operation: 'join',
45331
+ fields: copyFields.concat(sumFields).map(function(field) {
45332
+ return prefix + field;
45333
+ })
45334
+ });
45335
+ }
45336
+
44590
45337
  // join source records to target records
44591
45338
  n = destRecords.length;
44592
45339
  for (i=0; i<n; i++) {
@@ -44680,6 +45427,13 @@ ${svg}
44680
45427
  data: new DataTable(unmatchedRecords)
44681
45428
  };
44682
45429
  }
45430
+ if (useDuplication) {
45431
+ markLayerChanged(destLyr, {operation: 'join', duplication: true});
45432
+ } else if (opts.calc) {
45433
+ dest.markChanged({operation: 'join', calc: true});
45434
+ } else {
45435
+ dest.markSchemaChanged({operation: 'join'});
45436
+ }
44683
45437
  return retn;
44684
45438
  }
44685
45439
 
@@ -44932,12 +45686,17 @@ ${svg}
44932
45686
 
44933
45687
  cmd.divide = function(targetLayers, targetDataset, source, opts) {
44934
45688
  targetLayers.forEach(requirePolylineLayer);
45689
+ targetLayers.forEach(function(lyr) {
45690
+ noteLayerWillChange(lyr, {operation: 'divide', unit: 'shapes-data'});
45691
+ });
44935
45692
  var mergedDataset = mergeLayersForOverlay(targetLayers, targetDataset, source, opts);
44936
45693
  var nodes = addIntersectionCuts(mergedDataset, opts);
44937
45694
  var polygonLyr = mergedDataset.layers.pop();
44938
45695
  requirePolygonLayer(polygonLyr);
44939
45696
  // Assume that topology is now built
45697
+ noteDatasetWillChange(targetDataset, {operation: 'divide', unit: 'arcs'});
44940
45698
  targetDataset.arcs = mergedDataset.arcs;
45699
+ markDatasetChanged(targetDataset, {operation: 'divide', unit: 'arcs'});
44941
45700
  targetLayers.forEach(function(polylineLyr) {
44942
45701
  dividePolylineLayer(polylineLyr, polygonLyr, nodes, opts);
44943
45702
  });
@@ -44966,12 +45725,13 @@ ${svg}
44966
45725
  forEachShapePart(shp, onPart);
44967
45726
  outputLines.forEach(function(shape2, i) {
44968
45727
  shapes2.push(shape2);
44969
- records2.push(i > 0 ? utils.extend({}, rec) : rec); // assume input data is being replaced
45728
+ records2.push(utils.extend({}, rec));
44970
45729
  index2.push(outputMatches[i]);
44971
45730
  });
44972
45731
  });
44973
45732
  polylineLyr.shapes = shapes2;
44974
45733
  polylineLyr.data = new DataTable(records2);
45734
+ markLayerChanged(polylineLyr, {operation: 'divide', unit: 'shapes-data'});
44975
45735
  joinTables(polylineLyr.data, polygonLyr.data, function(i) {
44976
45736
  return index2[i] || [];
44977
45737
  }, opts);
@@ -45746,17 +46506,23 @@ ${svg}
45746
46506
  var allFields = fields && fieldListContainsAll(fields, lyr.data.getFields());
45747
46507
  var deletion = !fields && !opts.geometry && !opts.holes || allFields && opts.geometry;
45748
46508
  if (opts.geometry) {
46509
+ noteLayerWillChange(lyr, {operation: 'drop-geometry'});
45749
46510
  updateArcs |= layerHasPaths(lyr);
45750
46511
  delete lyr.shapes;
45751
46512
  delete lyr.geometry_type;
46513
+ markLayerChanged(lyr, {operation: 'drop-geometry'});
45752
46514
  }
45753
46515
  if (opts.holes && lyr.geometry_type == 'polygon') {
46516
+ noteLayerWillChange(lyr, {operation: 'drop-holes'});
45754
46517
  deleteHoles(lyr, dataset.arcs);
46518
+ markLayerChanged(lyr, {operation: 'drop-holes'});
45755
46519
  }
45756
46520
  if (deletion) {
45757
46521
  catalog.deleteLayer(lyr, dataset);
45758
46522
  } else if (allFields) {
46523
+ noteLayerWillChange(lyr, {operation: 'drop-fields'});
45759
46524
  delete lyr.data;
46525
+ markLayerChanged(lyr, {operation: 'drop-fields'});
45760
46526
  } else if (fields) {
45761
46527
  opts.fields.forEach(lyr.data.deleteField, lyr.data);
45762
46528
  }
@@ -45849,7 +46615,9 @@ ${svg}
45849
46615
 
45850
46616
  function filterByBoundsIntersection(lyr, arcs, opts) {
45851
46617
  var filter = getBoundsIntersectionFilter(opts.bbox, lyr, arcs);
46618
+ noteLayerWillChange(lyr, {operation: 'filter-geom', unit: 'shapes'});
45852
46619
  editShapes(lyr.shapes, filter);
46620
+ markLayerChanged(lyr, {operation: 'filter-geom', unit: 'shapes'});
45853
46621
  }
45854
46622
 
45855
46623
  function getBoundsIntersectionFilter(bbox, lyr, arcs) {
@@ -45991,8 +46759,14 @@ ${svg}
45991
46759
  }
45992
46760
  });
45993
46761
 
46762
+ if (filteredLyr == lyr) {
46763
+ noteLayerWillChange(lyr, {operation: 'filter'});
46764
+ }
45994
46765
  filteredLyr.shapes = filteredShapes;
45995
46766
  filteredLyr.data = filteredRecords ? new DataTable(filteredRecords) : null;
46767
+ if (filteredLyr == lyr) {
46768
+ markLayerChanged(lyr, {operation: 'filter'});
46769
+ }
45996
46770
  if (opts.no_replace) {
45997
46771
  // if adding a layer, don't share objects between source and filtered layer
45998
46772
  filteredLyr = copyLayer(filteredLyr);
@@ -46020,8 +46794,10 @@ ${svg}
46020
46794
  if (records) filteredRecords.push(records[shapeId] || null);
46021
46795
  }
46022
46796
  });
46797
+ noteLayerWillChange(lyr, {operation: 'filterLayerInPlace'});
46023
46798
  lyr.shapes = filteredShapes;
46024
46799
  lyr.data = filteredRecords ? new DataTable(filteredRecords) : null;
46800
+ markLayerChanged(lyr, {operation: 'filterLayerInPlace'});
46025
46801
  }
46026
46802
 
46027
46803
  function getIdFilter(ids) {
@@ -46074,12 +46850,18 @@ ${svg}
46074
46850
  filter = combineFilters(filter, getIdFilter(opts.ids));
46075
46851
  }
46076
46852
  compiled = compileFeatureExpression(exp, lyr, arcs, exprOpts);
46853
+ if (lyr.data) {
46854
+ lyr.data.captureTableBefore({operation: 'each'});
46855
+ }
46077
46856
  // call compiled expression with id of each record
46078
46857
  for (var i=0; i<n; i++) {
46079
46858
  if (!filter || filter(i)) {
46080
46859
  compiled(i);
46081
46860
  }
46082
46861
  }
46862
+ if (lyr.data) {
46863
+ lyr.data.markChanged({operation: 'each'});
46864
+ }
46083
46865
 
46084
46866
  var replacement = exprOpts.geojson_editor ? exprOpts.geojson_editor.done() : null;
46085
46867
  if (replacement) {
@@ -46246,8 +47028,12 @@ ${svg}
46246
47028
  this.done = function() {
46247
47029
  dataset.layers = layers;
46248
47030
  if (arcs.length) {
47031
+ noteDatasetWillChange(dataset, {operation: 'DatasetEditor.done', unit: 'arcs'});
46249
47032
  dataset.arcs = new ArcCollection(arcs);
46250
- buildTopology(dataset);
47033
+ markDatasetChanged(dataset, {operation: 'DatasetEditor.done', unit: 'arcs'});
47034
+ withActiveUndoTransaction(null, function() {
47035
+ buildTopology(dataset);
47036
+ });
46251
47037
  }
46252
47038
  };
46253
47039
 
@@ -46274,7 +47060,9 @@ ${svg}
46274
47060
  }
46275
47061
  return shape2.length > 0 ? shape2 : null;
46276
47062
  });
47063
+ noteLayerWillChange(lyr, {operation: 'DatasetEditor.editLayer', unit: 'shapes'});
46277
47064
  layers.push(Object.assign(lyr, {shapes: shapes}));
47065
+ markLayerChanged(lyr, {operation: 'DatasetEditor.editLayer', unit: 'shapes'});
46278
47066
  };
46279
47067
 
46280
47068
  function extendPathShape(shape, parts) {
@@ -46844,7 +47632,9 @@ ${svg}
46844
47632
  } else {
46845
47633
  filter = getVertexCountTest(opts.min_vertices, arcs);
46846
47634
  }
47635
+ noteLayerWillChange(lyr, {operation: 'filter-islands', unit: 'shapes'});
46847
47636
  removed += filterIslands(lyr, arcs, filter);
47637
+ markLayerChanged(lyr, {operation: 'filter-islands', unit: 'shapes'});
46848
47638
  if (opts.remove_empty) {
46849
47639
  cmd.filterFeatures(lyr, arcs, {remove_empty: true, verbose: false});
46850
47640
  }
@@ -46925,7 +47715,9 @@ ${svg}
46925
47715
  } else {
46926
47716
  filter = getVertexCountTest(opts.min_vertices, arcs);
46927
47717
  }
47718
+ noteLayerWillChange(lyr, {operation: 'filter-islands2', unit: 'shapes'});
46928
47719
  removed += filterIslands2(lyr, arcs, filter);
47720
+ markLayerChanged(lyr, {operation: 'filter-islands2', unit: 'shapes'});
46929
47721
  if (opts.remove_empty) {
46930
47722
  cmd.filterFeatures(lyr, arcs, {remove_empty: true, verbose: false});
46931
47723
  }
@@ -46968,16 +47760,30 @@ ${svg}
46968
47760
  if (opts.invert) {
46969
47761
  map = invertFieldMap(map, table.getFields());
46970
47762
  }
46971
- lyr.data.update(getRecordMapper(map));
47763
+ replaceTableFields(table, map, {operation: 'filter-fields'});
46972
47764
  };
46973
47765
 
46974
47766
  cmd.renameFields = function(lyr, names) {
46975
47767
  var map = mapFieldNames(names);
46976
47768
  requireDataFields(lyr.data, Object.keys(map));
46977
47769
  utils.defaults(map, mapFieldNames(lyr.data.getFields()));
46978
- lyr.data.update(getRecordMapper(map));
47770
+ replaceTableFields(lyr.data, map, {operation: 'rename-fields'});
46979
47771
  };
46980
47772
 
47773
+ function replaceTableFields(table, map, detail) {
47774
+ var fields = table.getFields();
47775
+ var records = table.getRecords();
47776
+ var mapper = getRecordMapper(map);
47777
+ var columnDetail = Object.assign({schema_transform: true}, detail);
47778
+ table.captureSchemaBefore(detail);
47779
+ table.captureFieldsBefore(fields, columnDetail);
47780
+ for (var i=0, n=records.length; i<n; i++) {
47781
+ records[i] = mapper(records[i], i);
47782
+ }
47783
+ table.markFieldsChanged(fields, columnDetail);
47784
+ table.markSchemaChanged(detail);
47785
+ }
47786
+
46981
47787
  function invertFieldMap(map, fields) {
46982
47788
  return fields.reduce(function(memo, name) {
46983
47789
  if (!(name in map)) {
@@ -47432,8 +48238,10 @@ ${svg}
47432
48238
  });
47433
48239
  editor.done();
47434
48240
  if (!opts.debug) {
47435
- buildTopology(dataset);
47436
- cleanProjectedPathLayers(dataset);
48241
+ withActiveUndoTransaction(null, function() {
48242
+ buildTopology(dataset);
48243
+ cleanProjectedPathLayers(dataset);
48244
+ });
47437
48245
  }
47438
48246
  }
47439
48247
 
@@ -47557,7 +48365,9 @@ ${svg}
47557
48365
  });
47558
48366
  }
47559
48367
  var merged = mergeDatasets([dataset, importGeoJSON(geojson, {})]);
48368
+ noteDatasetWillChange(dataset, {operation: 'lines-segments', unit: 'arcs'});
47560
48369
  dataset.arcs = merged.arcs;
48370
+ markDatasetChanged(dataset, {operation: 'lines-segments', unit: 'arcs'});
47561
48371
  // buildTopology(dataset);
47562
48372
  return merged.layers.pop();
47563
48373
  }
@@ -48137,6 +48947,7 @@ ${svg}
48137
48947
  dataset.arcs.flatten(); // bake in any pending simplification
48138
48948
  target.arcs = modifyCopy ? dataset.arcs.getCopy() : dataset.arcs;
48139
48949
  }
48950
+ noteDatasetWillChange(dataset, {operation: 'proj'});
48140
48951
 
48141
48952
  target.layers = dataset.layers.map(function(lyr) {
48142
48953
  if (modifyCopy) {
@@ -48146,16 +48957,45 @@ ${svg}
48146
48957
  return lyr;
48147
48958
  });
48148
48959
 
48149
- projectDataset(target, srcInfo.crs, destInfo.crs, opts || {});
48960
+ withActiveUndoTransaction(null, function() {
48961
+ projectDataset(target, srcInfo.crs, destInfo.crs, opts || {});
48150
48962
 
48151
- // dataset.info.wkt1 = destInfo.wkt1; // may be undefined
48152
- setDatasetCrsInfo(target, destInfo);
48963
+ // dataset.info.wkt1 = destInfo.wkt1; // may be undefined
48964
+ setDatasetCrsInfo(target, destInfo);
48965
+ });
48153
48966
 
48154
48967
  dataset.arcs = target.arcs;
48155
48968
  originals.forEach(function(lyr, i) {
48156
48969
  // replace original layers with modified layers
48157
- utils.extend(lyr, target.layers[i]);
48970
+ if (layerWasChangedByProjection(lyr, target.layers[i])) {
48971
+ noteLayerWillChange(lyr, {operation: 'proj'});
48972
+ utils.extend(lyr, target.layers[i]);
48973
+ markLayerChanged(lyr, {operation: 'proj'});
48974
+ }
48158
48975
  });
48976
+ markDatasetChanged(dataset, {operation: 'proj'});
48977
+ return true;
48978
+ }
48979
+
48980
+ function layerWasChangedByProjection(a, b) {
48981
+ return a.name != b.name ||
48982
+ a.geometry_type != b.geometry_type ||
48983
+ a.data !== b.data ||
48984
+ !arraysAreEqual(a.shapes, b.shapes);
48985
+ }
48986
+
48987
+ function arraysAreEqual(a, b) {
48988
+ var val;
48989
+ if (a === b) return true;
48990
+ if (!a || !b || a.length !== b.length) return false;
48991
+ for (var i = 0; i < a.length; i++) {
48992
+ val = a[i];
48993
+ if (Array.isArray(val)) {
48994
+ if (!arraysAreEqual(val, b[i])) return false;
48995
+ } else if (val !== b[i]) {
48996
+ return false;
48997
+ }
48998
+ }
48159
48999
  return true;
48160
49000
  }
48161
49001
 
@@ -48618,7 +49458,9 @@ ${svg}
48618
49458
  lyr2.name = lyr0.name;
48619
49459
  return lyr2;
48620
49460
  });
49461
+ noteDatasetWillChange(targetDataset, {operation: 'inlay', unit: 'arcs'});
48621
49462
  targetDataset.arcs = mergedDataset.arcs;
49463
+ markDatasetChanged(targetDataset, {operation: 'inlay', unit: 'arcs'});
48622
49464
  return outputLayers;
48623
49465
  };
48624
49466
 
@@ -49051,6 +49893,10 @@ ${svg}
49051
49893
  cmd.createPointLayer = function(srcLyr, dataset, opts) {
49052
49894
  var destLyr = getOutputLayer(srcLyr, opts);
49053
49895
  var arcs = dataset.arcs;
49896
+ var replacingSource = destLyr == srcLyr;
49897
+ if (replacingSource) {
49898
+ noteLayerWillChange(destLyr, {operation: 'createPointLayer', unit: 'geometry'});
49899
+ }
49054
49900
  if (opts.intersections) {
49055
49901
  testIntersections(arcs);
49056
49902
  destLyr = srcLyr;
@@ -49090,6 +49936,9 @@ ${svg}
49090
49936
  if (srcLyr.data) {
49091
49937
  destLyr.data = opts.no_replace ? srcLyr.data.clone() : srcLyr.data;
49092
49938
  }
49939
+ if (replacingSource) {
49940
+ markLayerChanged(destLyr, {operation: 'createPointLayer', unit: 'geometry'});
49941
+ }
49093
49942
  return destLyr;
49094
49943
  };
49095
49944
 
@@ -50072,8 +50921,8 @@ ${svg}
50072
50921
  var v1 = bottom + vi * minorInterval;
50073
50922
  var v2 = v1 + minorInterval;
50074
50923
  var orientation = cwBar ?
50075
- orient2D(u1, v1, u2, v2, u, v) :
50076
- orient2D(u2, v1, u1, v2, u, v);
50924
+ orient2D$1(u1, v1, u2, v2, u, v) :
50925
+ orient2D$1(u2, v1, u1, v2, u, v);
50077
50926
  var colId = orientation > 0 ? ui - 1 : ui;
50078
50927
  var rowId = Math.floor(vi / 2);
50079
50928
  return [colId, rowId];
@@ -50398,16 +51247,28 @@ ${svg}
50398
51247
  var geojson, dataset;
50399
51248
  if (params.type == 'square') {
50400
51249
  geojson = getSquareGridGeoJSON(getSquareGridCoordinates(params));
51250
+ } else if (params.type == 'square2') {
51251
+ geojson = getRotatedSquareGridGeoJSON(params);
50401
51252
  } else if (params.type == 'hex') {
50402
51253
  geojson = getHexGridGeoJSON(getHexGridCoordinates(params));
50403
51254
  } else if (params.type == 'hex2') {
50404
51255
  // use rotated grid
50405
51256
  geojson = getHexGridGeoJSON(getHexGridCoordinates(swapGridParams(params)));
50406
51257
  swapPolygonCoords(geojson);
51258
+ } else if (params.type == 'rhombus') {
51259
+ geojson = getRhombusGridGeoJSON(params, false);
51260
+ } else if (params.type == 'rhombus2') {
51261
+ geojson = getRhombusGridGeoJSON(params, true);
51262
+ } else if (params.type == 'triangle') {
51263
+ geojson = getTriangleGridGeoJSON(params, false);
51264
+ } else if (params.type == 'triangle2') {
51265
+ geojson = getTriangleGridGeoJSON(params, true);
50407
51266
  } else {
50408
51267
  stop$1('Unsupported grid type');
50409
51268
  }
51269
+ scaleGridCells(geojson, params.cellScale);
50410
51270
  alignGridToBounds(geojson, params.bbox);
51271
+ cullGridCells(geojson, params.bbox);
50411
51272
  dataset = importGeoJSON(geojson, {});
50412
51273
  buildTopology(dataset);
50413
51274
  return dataset;
@@ -50433,11 +51294,6 @@ ${svg}
50433
51294
  function getGridParams(layers, dataset, opts) {
50434
51295
  var params = {};
50435
51296
  var crs = dataset ? getDatasetCRS(dataset) : null;
50436
- if (opts.interval) {
50437
- params.interval = convertIntervalParam(opts.interval, crs);
50438
- } else {
50439
- stop$1('Missing required interval option');
50440
- }
50441
51297
  if (opts.bbox) {
50442
51298
  params.bbox = opts.bbox;
50443
51299
  } else if (dataset) {
@@ -50449,9 +51305,143 @@ ${svg}
50449
51305
  params.width = params.bbox[2] - params.bbox[0];
50450
51306
  params.height = params.bbox[3] - params.bbox[1];
50451
51307
  params.type = opts.type || 'square';
51308
+ params.interval = getGridInterval(params, opts, crs);
51309
+ params.cellScale = getCellScale(opts);
50452
51310
  return params;
50453
51311
  }
50454
51312
 
51313
+ function getCellScale(opts) {
51314
+ var scale = opts.cell_scale == null ? 1 : opts.cell_scale;
51315
+ if (scale > 0 && scale < 2) return scale;
51316
+ stop$1('cell-scale= option should be greater than 0 and less than 2');
51317
+ }
51318
+
51319
+ function getGridInterval(params, opts, crs) {
51320
+ var interval = opts.interval ? convertIntervalParam(opts.interval, crs) : 0;
51321
+ var hasSizeOpt = opts.cols != null || opts.rows != null || opts.cells != null;
51322
+ var sizeOpts = 0;
51323
+ if (opts.cols > 0) sizeOpts++;
51324
+ if (opts.rows > 0) sizeOpts++;
51325
+ if (opts.cells > 0) sizeOpts++;
51326
+ if (interval && hasSizeOpt) {
51327
+ stop$1('Use interval= or cols=/rows=/cells=, not both');
51328
+ }
51329
+ if (interval) return interval;
51330
+ validateGridSizeOpt('cols', opts.cols);
51331
+ validateGridSizeOpt('rows', opts.rows);
51332
+ validateGridSizeOpt('cells', opts.cells);
51333
+ if (sizeOpts === 0) {
51334
+ stop$1('Missing required interval, cols, rows or cells option');
51335
+ }
51336
+ return getIntervalFromGridSize(params, opts);
51337
+ }
51338
+
51339
+ function validateGridSizeOpt(name, value) {
51340
+ if (value > 0 || value == null) return;
51341
+ stop$1(name + '= option should be a positive integer');
51342
+ }
51343
+
51344
+ function getIntervalFromGridSize(params, opts) {
51345
+ var factor = getCellAreaFactor(params.type);
51346
+ var intervals = [];
51347
+ if (opts.cols > 0) intervals.push(params.width / opts.cols / Math.sqrt(factor));
51348
+ if (opts.rows > 0) intervals.push(params.height / opts.rows / Math.sqrt(factor));
51349
+ if (opts.cells > 0) intervals.push(Math.sqrt(params.width * params.height / opts.cells / factor));
51350
+ return Math.min.apply(null, intervals);
51351
+ }
51352
+
51353
+ function getCellAreaFactor(type) {
51354
+ if (type == 'square' || type == 'square2') return 1;
51355
+ if (type == 'hex' || type == 'hex2') return 3 * Math.sqrt(3) / 2;
51356
+ if (type == 'rhombus' || type == 'rhombus2') return Math.sqrt(3) / 2;
51357
+ if (type == 'triangle' || type == 'triangle2') return Math.sqrt(3) / 4;
51358
+ stop$1('Unsupported grid type');
51359
+ }
51360
+
51361
+ function scaleGridCells(geojson, scale) {
51362
+ if (scale == 1) return;
51363
+ geojson.geometries.forEach(function(geom) {
51364
+ if (geom.type == 'Polygon') {
51365
+ geom.coordinates[0] = scalePolygonRing(geom.coordinates[0], scale);
51366
+ }
51367
+ });
51368
+ }
51369
+
51370
+ function scalePolygonRing(coords, scale) {
51371
+ var center = getPolygonRingCenter(coords);
51372
+ return coords.map(function(p) {
51373
+ return [
51374
+ center[0] + (p[0] - center[0]) * scale,
51375
+ center[1] + (p[1] - center[1]) * scale
51376
+ ];
51377
+ });
51378
+ }
51379
+
51380
+ function cullGridCells(geojson, bbox) {
51381
+ geojson.geometries = geojson.geometries.filter(function(geom) {
51382
+ return geom.type != 'Polygon' || polygonIntersectsBBox(geom.coordinates[0], bbox);
51383
+ });
51384
+ }
51385
+
51386
+ function polygonIntersectsBBox(coords, bbox) {
51387
+ var rect = [[bbox[0], bbox[1]], [bbox[2], bbox[1]], [bbox[2], bbox[3]], [bbox[0], bbox[3]]];
51388
+ var i, j;
51389
+ for (i=0; i<coords.length - 1; i++) {
51390
+ if (pointInBBox(coords[i], bbox)) return true;
51391
+ }
51392
+ for (i=0; i<rect.length; i++) {
51393
+ if (pointInPolygon(rect[i], coords)) return true;
51394
+ }
51395
+ for (i=0; i<coords.length - 1; i++) {
51396
+ for (j=0; j<rect.length; j++) {
51397
+ if (segmentsIntersect(coords[i], coords[i + 1], rect[j], rect[(j + 1) % rect.length])) {
51398
+ return true;
51399
+ }
51400
+ }
51401
+ }
51402
+ return false;
51403
+ }
51404
+
51405
+ function pointInBBox(p, bbox) {
51406
+ return p[0] >= bbox[0] && p[0] <= bbox[2] && p[1] >= bbox[1] && p[1] <= bbox[3];
51407
+ }
51408
+
51409
+ function pointInPolygon(p, coords) {
51410
+ var isInside = false;
51411
+ var a, b;
51412
+ for (var i=0, n=coords.length - 1, j=n - 1; i<n; j=i++) {
51413
+ a = coords[i];
51414
+ b = coords[j];
51415
+ if (orient2D(a, b, p) === 0 && pointOnSegment(p, a, b)) return true;
51416
+ if (a[1] > p[1] != b[1] > p[1] &&
51417
+ p[0] < (b[0] - a[0]) * (p[1] - a[1]) / (b[1] - a[1]) + a[0]) {
51418
+ isInside = !isInside;
51419
+ }
51420
+ }
51421
+ return isInside;
51422
+ }
51423
+
51424
+ function segmentsIntersect(a, b, c, d) {
51425
+ var ab_c = orient2D(a, b, c);
51426
+ var ab_d = orient2D(a, b, d);
51427
+ var cd_a = orient2D(c, d, a);
51428
+ var cd_b = orient2D(c, d, b);
51429
+ if (ab_c === 0 && pointOnSegment(c, a, b)) return true;
51430
+ if (ab_d === 0 && pointOnSegment(d, a, b)) return true;
51431
+ if (cd_a === 0 && pointOnSegment(a, c, d)) return true;
51432
+ if (cd_b === 0 && pointOnSegment(b, c, d)) return true;
51433
+ return ab_c > 0 != ab_d > 0 && cd_a > 0 != cd_b > 0;
51434
+ }
51435
+
51436
+ function orient2D(a, b, c) {
51437
+ return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
51438
+ }
51439
+
51440
+ function pointOnSegment(p, a, b) {
51441
+ return p[0] >= Math.min(a[0], b[0]) && p[0] <= Math.max(a[0], b[0]) &&
51442
+ p[1] >= Math.min(a[1], b[1]) && p[1] <= Math.max(a[1], b[1]);
51443
+ }
51444
+
50455
51445
  function getPointGridGeoJSON(arr) {
50456
51446
  var geometries = [];
50457
51447
  arr.forEach(function(row) {
@@ -50490,6 +51480,72 @@ ${svg}
50490
51480
  return {type: 'GeometryCollection', geometries: geometries};
50491
51481
  }
50492
51482
 
51483
+ function getRhombusGridGeoJSON(params, rotated) {
51484
+ var geojson;
51485
+ if (rotated) {
51486
+ geojson = getHexGridGeoJSON(getHexGridCoordinates(swapGridParams(params)));
51487
+ swapPolygonCoords(geojson);
51488
+ } else {
51489
+ geojson = getHexGridGeoJSON(getHexGridCoordinates(params));
51490
+ }
51491
+ geojson.geometries = geojson.geometries.reduce(function(memo, geom) {
51492
+ return memo.concat(subdivideHexagon(geom.coordinates[0]));
51493
+ }, []);
51494
+ return geojson;
51495
+ }
51496
+
51497
+ function getTriangleGridGeoJSON(params, rotated) {
51498
+ var geojson;
51499
+ if (rotated) {
51500
+ geojson = getHexGridGeoJSON(getHexGridCoordinates(swapGridParams(params)));
51501
+ swapPolygonCoords(geojson);
51502
+ } else {
51503
+ geojson = getHexGridGeoJSON(getHexGridCoordinates(params));
51504
+ }
51505
+ geojson.geometries = geojson.geometries.reduce(function(memo, geom) {
51506
+ return memo.concat(triangulateHexagon(geom.coordinates[0]));
51507
+ }, []);
51508
+ return geojson;
51509
+ }
51510
+
51511
+ function subdivideHexagon(coords) {
51512
+ var center = getPolygonRingCenter(coords);
51513
+ return [
51514
+ getRhombusCell(coords, center, 0),
51515
+ getRhombusCell(coords, center, 2),
51516
+ getRhombusCell(coords, center, 4)
51517
+ ];
51518
+ }
51519
+
51520
+ function getRhombusCell(coords, center, i) {
51521
+ return {
51522
+ type: 'Polygon',
51523
+ coordinates: [[center, coords[i], coords[i + 1], coords[(i + 2) % 6], center]]
51524
+ };
51525
+ }
51526
+
51527
+ function triangulateHexagon(coords) {
51528
+ var triangles = [];
51529
+ var center = getPolygonRingCenter(coords);
51530
+ for (var i=0; i<6; i++) {
51531
+ triangles.push({
51532
+ type: 'Polygon',
51533
+ coordinates: [[center, coords[i], coords[(i + 1) % 6], center]]
51534
+ });
51535
+ }
51536
+ return triangles;
51537
+ }
51538
+
51539
+ function getPolygonRingCenter(coords) {
51540
+ var x = 0, y = 0;
51541
+ var n = coords.length - 1; // ignore closing point
51542
+ for (var i=0; i<n; i++) {
51543
+ x += coords[i][0];
51544
+ y += coords[i][1];
51545
+ }
51546
+ return [x / n, y / n];
51547
+ }
51548
+
50493
51549
  function getSquareGridGeoJSON(arr) {
50494
51550
  var geometries = [], a, b, c, d;
50495
51551
  for (var row = 0, rows = arr.length - 1; row < rows; row++) {
@@ -50507,6 +51563,35 @@ ${svg}
50507
51563
  return {type: 'GeometryCollection', geometries: geometries};
50508
51564
  }
50509
51565
 
51566
+ function getRotatedSquareGridGeoJSON(params) {
51567
+ var interval = params.interval;
51568
+ var radius = interval * Math.SQRT1_2;
51569
+ var geometries = [];
51570
+ var row = 0;
51571
+ var x, y, x0;
51572
+ for (y = -radius; y <= params.height + radius; y += radius) {
51573
+ x0 = -2 * radius + (row % 2 ? radius : 0);
51574
+ for (x = x0; x <= params.width + 2 * radius; x += 2 * radius) {
51575
+ geometries.push(getRotatedSquareCell(x, y, radius));
51576
+ }
51577
+ row++;
51578
+ }
51579
+ return {type: 'GeometryCollection', geometries: geometries};
51580
+ }
51581
+
51582
+ function getRotatedSquareCell(x, y, radius) {
51583
+ return {
51584
+ type: 'Polygon',
51585
+ coordinates: [[
51586
+ [x, y - radius],
51587
+ [x + radius, y],
51588
+ [x, y + radius],
51589
+ [x - radius, y],
51590
+ [x, y - radius]
51591
+ ]]
51592
+ };
51593
+ }
51594
+
50510
51595
  function getHexGridCoordinates(params) {
50511
51596
  var xInterval = params.interval;
50512
51597
  var yInterval = Math.sqrt(3) * xInterval / 2;
@@ -50702,7 +51787,7 @@ ${svg}
50702
51787
  // Returns a function that receives a cell index and returns indices of points
50703
51788
  // within a given distance of the cell.
50704
51789
  function getGridToPointIndex(points, grid, radius) {
50705
- var Flatbush = require('flatbush');
51790
+ var Flatbush = require$1('flatbush');
50706
51791
  var gridIndex = new IdTestIndex(grid.cells());
50707
51792
  var bboxIndex = new Flatbush(points.length);
50708
51793
  var empty = [];
@@ -50990,6 +52075,7 @@ ${svg}
50990
52075
  function createPolygonLayerFromRings(lyr, dataset) {
50991
52076
  var arcs = dataset.arcs;
50992
52077
  var openCount = 0;
52078
+ noteLayerWillChange(lyr, {operation: 'polygons-from-rings', unit: 'geometry'});
50993
52079
  editShapes(lyr.shapes, function(part) {
50994
52080
  if (geom.pathIsClosed(part, arcs)) {
50995
52081
  return part;
@@ -51002,6 +52088,7 @@ ${svg}
51002
52088
  }
51003
52089
  lyr.geometry_type = 'polygon';
51004
52090
  rewindPolygons(lyr, arcs);
52091
+ markLayerChanged(lyr, {operation: 'polygons-from-rings', unit: 'geometry'});
51005
52092
  return lyr;
51006
52093
  }
51007
52094
 
@@ -51038,7 +52125,9 @@ ${svg}
51038
52125
  var index = mapLayerNames(names);
51039
52126
  catalog.forEachLayer(function(lyr) {
51040
52127
  if (index[lyr.name]) {
52128
+ noteLayerMetadataWillChange(lyr, {operation: 'rename-layers'});
51041
52129
  lyr.name = index[lyr.name];
52130
+ markLayerMetadataChanged(lyr, {operation: 'rename-layers'});
51042
52131
  }
51043
52132
  });
51044
52133
  }
@@ -51054,7 +52143,9 @@ ${svg}
51054
52143
  if (name && nameCount < layers.length && (i >= nameCount - 1)) {
51055
52144
  suffix = (suffix || 0) + 1;
51056
52145
  }
52146
+ noteLayerMetadataWillChange(lyr, {operation: 'rename-layers'});
51057
52147
  lyr.name = name + suffix;
52148
+ markLayerMetadataChanged(lyr, {operation: 'rename-layers'});
51058
52149
  });
51059
52150
  }
51060
52151
 
@@ -52383,6 +53474,8 @@ ${svg}
52383
53474
  var implicitlySimplifiedNames = getImplicitlyTargetedLayerNames(dataset, targetLayers, layerHasPaths);
52384
53475
  if (!arcs || arcs.size() === 0) return; // removed in v0.4.125: stop("Missing path data");
52385
53476
  opts = getStandardSimplifyOpts(dataset, opts); // standardize options
53477
+ noteArcsSimplificationWillChange(arcs, {operation: 'simplify'});
53478
+ noteDatasetInfoWillChange(dataset, {operation: 'simplify'});
52386
53479
  simplifyPaths(arcs, opts);
52387
53480
 
52388
53481
  // calculate and apply simplification interval
@@ -52399,6 +53492,8 @@ ${svg}
52399
53492
  }
52400
53493
 
52401
53494
  finalizeSimplification(dataset, opts);
53495
+ markArcsSimplificationChanged(arcs, {operation: 'simplify'});
53496
+ markDatasetInfoChanged(dataset, {operation: 'simplify'});
52402
53497
  if (implicitlySimplifiedNames.length > 0) {
52403
53498
  message(
52404
53499
  'Also simplified non-target layer' + utils.pluralSuffix(implicitlySimplifiedNames.length) +
@@ -52624,14 +53719,27 @@ ${svg}
52624
53719
  });
52625
53720
 
52626
53721
  var ids = utils.getSortedIds(values, ascending);
53722
+ var undoIds = invertIds$1(ids);
52627
53723
  if (lyr.shapes) {
53724
+ noteLayerOrderWillChange(lyr, undoIds, {operation: 'sort'});
52628
53725
  utils.reorderArray(lyr.shapes, ids);
53726
+ markLayerOrderChanged(lyr, ids, {operation: 'sort'});
52629
53727
  }
52630
53728
  if (lyr.data) {
53729
+ noteTableOrderWillChange(lyr.data, undoIds, {operation: 'sort'});
52631
53730
  utils.reorderArray(lyr.data.getRecords(), ids);
53731
+ markTableOrderChanged(lyr.data, ids, {operation: 'sort'});
52632
53732
  }
52633
53733
  };
52634
53734
 
53735
+ function invertIds$1(ids) {
53736
+ var inverse = [];
53737
+ ids.forEach(function(id, i) {
53738
+ inverse[id] = i;
53739
+ });
53740
+ return inverse;
53741
+ }
53742
+
52635
53743
  cmd.snap = function(target, opts) {
52636
53744
  var interval = 0;
52637
53745
  var snapCount = 0;
@@ -52701,6 +53809,7 @@ ${svg}
52701
53809
  return [lyr0];
52702
53810
  }
52703
53811
 
53812
+ noteLayerWillChange(lyr0, {operation: 'split', unit: 'shared-shapes-data'});
52704
53813
  utils.repeat(n, function(i) {
52705
53814
  var name = namer(i),
52706
53815
  lyr;
@@ -52724,6 +53833,7 @@ ${svg}
52724
53833
  lyr.data.getRecords().push(properties[i]);
52725
53834
  }
52726
53835
  });
53836
+ markLayerChanged(lyr0, {operation: 'split', unit: 'shared-shapes-data'});
52727
53837
 
52728
53838
  return splitLayers;
52729
53839
  };
@@ -52797,7 +53907,7 @@ ${svg}
52797
53907
  };
52798
53908
 
52799
53909
  cmd.svgStyle = function(lyr, dataset, opts) {
52800
- var filterFn;
53910
+ var filterFn, table, fields, hasNewFields;
52801
53911
  if (getFeatureCount(lyr) === 0) {
52802
53912
  return;
52803
53913
  }
@@ -52810,6 +53920,20 @@ ${svg}
52810
53920
  if (opts.clear) {
52811
53921
  lyr.data.getFields().filter(isSupportedSvgStyleProperty).forEach(lyr.data.deleteField, lyr.data);
52812
53922
  }
53923
+ table = getLayerDataTable(lyr);
53924
+ fields = Object.keys(opts).map(function(optName) {
53925
+ return optName.replace('_', '-');
53926
+ }).filter(isSupportedSvgStyleProperty);
53927
+ hasNewFields = fields.some(function(field) {
53928
+ return !table.fieldExists(field);
53929
+ });
53930
+ if (fields.length > 0) {
53931
+ if (hasNewFields) {
53932
+ table.captureSchemaBefore({operation: 'style', fields: fields});
53933
+ } else {
53934
+ table.captureFieldsBefore(fields, {operation: 'style'});
53935
+ }
53936
+ }
52813
53937
  Object.keys(opts).forEach(function(optName) {
52814
53938
  var svgName = optName.replace('_', '-'); // undo cli parser name conversion
52815
53939
  if (!isSupportedSvgStyleProperty(svgName)) {
@@ -52817,7 +53941,7 @@ ${svg}
52817
53941
  }
52818
53942
  var strVal = opts[optName].trim();
52819
53943
  var accessor = getSymbolPropertyAccessor(strVal, svgName, lyr);
52820
- getLayerDataTable(lyr).getRecords().forEach(function(rec, i) {
53944
+ table.getRecords().forEach(function(rec, i) {
52821
53945
  if (filterFn && !filterFn(i)) {
52822
53946
  // make sure field exists if record is excluded by filter
52823
53947
  if (svgName in rec === false) {
@@ -52828,6 +53952,13 @@ ${svg}
52828
53952
  }
52829
53953
  });
52830
53954
  });
53955
+ if (fields.length > 0) {
53956
+ if (hasNewFields) {
53957
+ table.markSchemaChanged({operation: 'style'});
53958
+ } else {
53959
+ table.markFieldsChanged(fields, {operation: 'style'});
53960
+ }
53961
+ }
52831
53962
  };
52832
53963
 
52833
53964
  var roundCoord$1 = getRoundingFunction(0.01);
@@ -53270,7 +54401,19 @@ ${svg}
53270
54401
  requireProjectedDataset(dataset);
53271
54402
  metersPerPx = opts.pixel_scale || getMetersPerPixel(lyr);
53272
54403
  }
53273
- var records = getLayerDataTable(lyr).getRecords();
54404
+ var table = getLayerDataTable(lyr);
54405
+ var symbolFields = getSymbolOutputFields(shapeMode, opts);
54406
+ var hasNewSymbolFields = symbolFields.some(function(field) {
54407
+ return !table.fieldExists(field);
54408
+ });
54409
+ if (symbolFields.length > 0) {
54410
+ if (hasNewSymbolFields) {
54411
+ table.captureSchemaBefore({operation: 'symbols', fields: symbolFields});
54412
+ } else {
54413
+ table.captureFieldsBefore(symbolFields, {operation: 'symbols'});
54414
+ }
54415
+ }
54416
+ var records = table.getRecords();
53274
54417
  var getSymbolData = getSymbolDataAccessor(lyr, opts);
53275
54418
  var geometries = lyr.shapes.map(function(shp, i) {
53276
54419
  if (!shp) return null;
@@ -53330,9 +54473,23 @@ ${svg}
53330
54473
  } else {
53331
54474
  outputLyr = lyr;
53332
54475
  }
54476
+ if (symbolFields.length > 0) {
54477
+ if (hasNewSymbolFields) {
54478
+ table.markSchemaChanged({operation: 'symbols'});
54479
+ } else {
54480
+ table.markFieldsChanged(symbolFields, {operation: 'symbols'});
54481
+ }
54482
+ }
53333
54483
  return [outputLyr];
53334
54484
  };
53335
54485
 
54486
+ function getSymbolOutputFields(shapeMode, opts) {
54487
+ if (!shapeMode) return ['svg-symbol'];
54488
+ return ['fill', 'stroke', 'opacity'].filter(function(field) {
54489
+ return field in opts;
54490
+ });
54491
+ }
54492
+
53336
54493
  function importGeometries(geometries, records) {
53337
54494
  var features = geometries.map(function(geom, i) {
53338
54495
  records[i];
@@ -53530,12 +54687,18 @@ ${svg}
53530
54687
  }
53531
54688
  });
53532
54689
 
54690
+ if (lyr.shapes || records) {
54691
+ noteLayerWillChange(lyr, {operation: 'uniq'});
54692
+ }
53533
54693
  if (lyr.shapes) {
53534
54694
  lyr.shapes = lyr.shapes.filter(filter);
53535
54695
  }
53536
54696
  if (records) {
53537
54697
  lyr.data = new DataTable(records.filter(filter));
53538
54698
  }
54699
+ if (lyr.shapes || records) {
54700
+ markLayerChanged(lyr, {operation: 'uniq'});
54701
+ }
53539
54702
  if (opts.verbose !== false) {
53540
54703
  message(utils.format('Retained %,d of %,d features', getFeatureCount(lyr), n));
53541
54704
  }
@@ -53569,7 +54732,9 @@ ${svg}
53569
54732
  arcThresholds = calculateVariableThresholds(lyr, arcs, getShapeThreshold);
53570
54733
  applyArcThresholds(arcs, arcThresholds);
53571
54734
  arcs.setRetainedInterval(1e20); // set to a huge value
54735
+ noteDatasetInfoWillChange(dataset, {operation: 'variable-simplify'});
53572
54736
  finalizeSimplification(dataset, opts);
54737
+ markDatasetInfoChanged(dataset, {operation: 'variable-simplify'});
53573
54738
  arcs.flatten(); // bake in simplification (different from standard -simplify)
53574
54739
  };
53575
54740
 
@@ -53664,16 +54829,20 @@ ${svg}
53664
54829
  }
53665
54830
 
53666
54831
  if (!lyr.data) {
54832
+ noteLayerWillChange(lyr, {operation: 'split-on-grid', unit: 'data'});
53667
54833
  lyr.data = new DataTable(shapes.length);
54834
+ markLayerChanged(lyr, {operation: 'split-on-grid', unit: 'data'});
53668
54835
  }
53669
54836
  properties = lyr.data.getRecords();
53670
54837
 
54838
+ lyr.data.captureSchemaBefore({operation: 'split-on-grid', field: fieldName});
53671
54839
  lyr.shapes.forEach(function(shp, i) {
53672
54840
  var bounds = type == 'point' ? getPointBounds$1([shp]) : arcs.getMultiShapeBounds(shp);
53673
54841
  var name = bounds.hasBounds() ? classify(bounds) : '';
53674
54842
  var rec = properties[i] = properties[i] || {};
53675
54843
  rec[fieldName] = name;
53676
54844
  });
54845
+ lyr.data.markSchemaChanged({operation: 'split-on-grid', field: fieldName});
53677
54846
 
53678
54847
  if (setId) return lyr; // don't split layer (instead assign cell ids)
53679
54848
 
@@ -54214,7 +55383,9 @@ ${svg}
54214
55383
  error('Command returned invalid output');
54215
55384
  }
54216
55385
 
55386
+ noteDatasetWillChange(targetDataset, {operation: 'appendOutputLayers', command: name});
54217
55387
  targetDataset.layers = targetDataset.layers.concat(outputLayers);
55388
+ markDatasetChanged(targetDataset, {operation: 'appendOutputLayers', command: name});
54218
55389
  } else {
54219
55390
  // TODO: consider replacing old layers as they are generated, for gc
54220
55391
  replaceLayers(targetDataset, targetLayers, outputLayers);
@@ -54267,7 +55438,7 @@ ${svg}
54267
55438
  });
54268
55439
  }
54269
55440
 
54270
- var version = "0.7.8";
55441
+ var version = "0.7.10";
54271
55442
 
54272
55443
  // Parse command line args into commands and run them
54273
55444
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
@@ -54954,6 +56125,744 @@ ${svg}
54954
56125
  return false;
54955
56126
  }
54956
56127
 
56128
+ function UndoTransaction(label) {
56129
+ this.label = label || '';
56130
+ this.units = [];
56131
+ this._captured = {};
56132
+ }
56133
+
56134
+ UndoTransaction.prototype = {
56135
+ run: function(cb) {
56136
+ return withActiveUndoTransaction(this, cb);
56137
+ },
56138
+
56139
+ getCapturedUnits: function() {
56140
+ return this.units.slice();
56141
+ },
56142
+
56143
+ restore: function() {
56144
+ restoreCapturedUnits(this.units);
56145
+ },
56146
+
56147
+ captureCurrentState: function() {
56148
+ return captureCurrentUnits(this.units);
56149
+ },
56150
+
56151
+ captureTableBefore: function(table, detail) {
56152
+ var key = unitKey('table', table);
56153
+ if (this._captured[key]) return;
56154
+ this._captured[key] = true;
56155
+ this.units.push({
56156
+ type: 'table',
56157
+ target: table,
56158
+ id: getUndoId(table),
56159
+ revision: getUndoRevision(table),
56160
+ detail: copyDetail(detail),
56161
+ records: table.getRecords().map(copyRecord)
56162
+ });
56163
+ },
56164
+
56165
+ captureTableRecordsBefore: function(table, detail) {
56166
+ var ids = uniqueNumbers(detail.ids);
56167
+ var captured = [];
56168
+ var records = table.getRecords();
56169
+ ids.forEach(function(id) {
56170
+ var key = unitKey('table-record', table, id);
56171
+ if (this._captured[key]) return;
56172
+ this._captured[key] = true;
56173
+ captured.push({
56174
+ id: id,
56175
+ record: copyRecord(records[id])
56176
+ });
56177
+ }, this);
56178
+ if (captured.length > 0) {
56179
+ this.units.push({
56180
+ type: 'table-records',
56181
+ target: table,
56182
+ id: getUndoId(table),
56183
+ revision: getUndoRevision(table),
56184
+ detail: copyDetail(detail),
56185
+ records: captured
56186
+ });
56187
+ }
56188
+ },
56189
+
56190
+ captureTableFieldsBefore: function(table, detail) {
56191
+ var fields = uniqueStrings(detail.fields);
56192
+ var records = table.getRecords();
56193
+ var columns = [];
56194
+ fields.forEach(function(field) {
56195
+ var key = unitKey('table-field', table, field);
56196
+ if (this._captured[key]) return;
56197
+ this._captured[key] = true;
56198
+ columns.push({
56199
+ field: field,
56200
+ values: records.map(function(rec) {
56201
+ return rec ? rec[field] : undefined;
56202
+ })
56203
+ });
56204
+ }, this);
56205
+ if (columns.length > 0) {
56206
+ this.units.push({
56207
+ type: 'table-fields',
56208
+ target: table,
56209
+ id: getUndoId(table),
56210
+ revision: getUndoRevision(table),
56211
+ detail: copyDetail(detail),
56212
+ columns: columns
56213
+ });
56214
+ }
56215
+ },
56216
+
56217
+ captureTableOrderBefore: function(table, detail) {
56218
+ captureOrderUnit(this, 'table-order', table, detail);
56219
+ },
56220
+
56221
+ captureTableSchemaBefore: function(table, detail) {
56222
+ var key = unitKey('table-schema', table);
56223
+ if (this._captured[key]) return;
56224
+ this._captured[key] = true;
56225
+ this.units.push({
56226
+ type: 'table-schema',
56227
+ target: table,
56228
+ id: getUndoId(table),
56229
+ revision: getUndoRevision(table),
56230
+ detail: copyDetail(detail),
56231
+ fields: table.getFields()
56232
+ });
56233
+ if (detail && (detail.field || detail.fields)) {
56234
+ this.captureTableFieldsBefore(table, {
56235
+ fields: detail.fields || [detail.field],
56236
+ operation: detail.operation
56237
+ });
56238
+ }
56239
+ },
56240
+
56241
+ captureArcsBefore: function(arcs, detail) {
56242
+ var data, key;
56243
+ key = unitKey('arcs', arcs);
56244
+ if (this._captured[key]) return;
56245
+ this._captured[key] = true;
56246
+ data = arcs.getVertexData();
56247
+ this.units.push({
56248
+ type: 'arcs',
56249
+ target: arcs,
56250
+ id: getUndoId(arcs),
56251
+ revision: getUndoRevision(arcs),
56252
+ detail: copyDetail(detail),
56253
+ nn: new Uint32Array(data.nn),
56254
+ xx: new Float64Array(data.xx),
56255
+ yy: new Float64Array(data.yy),
56256
+ zz: data.zz ? new Float64Array(data.zz) : null,
56257
+ zlimit: arcs.getRetainedInterval()
56258
+ });
56259
+ },
56260
+
56261
+ captureArcsSimplificationBefore: function(arcs, detail) {
56262
+ var data, key;
56263
+ key = unitKey('arcs-simplification', arcs);
56264
+ if (this._captured[key]) return;
56265
+ this._captured[key] = true;
56266
+ data = arcs.getVertexData();
56267
+ this.units.push({
56268
+ type: 'arcs-simplification',
56269
+ target: arcs,
56270
+ id: getUndoId(arcs),
56271
+ revision: getUndoRevision(arcs),
56272
+ detail: copyDetail(detail),
56273
+ zz: data.zz ? new Float64Array(data.zz) : null,
56274
+ zlimit: arcs.getRetainedInterval()
56275
+ });
56276
+ },
56277
+
56278
+ captureCatalogBefore: function(catalog, detail) {
56279
+ var key = unitKey('catalog', catalog);
56280
+ if (this._captured[key]) return;
56281
+ this._captured[key] = true;
56282
+ this.units.push({
56283
+ type: 'catalog',
56284
+ target: catalog,
56285
+ id: getUndoId(catalog),
56286
+ revision: getUndoRevision(catalog),
56287
+ detail: copyDetail(detail),
56288
+ datasets: catalog.getDatasets().slice(),
56289
+ targets: catalog.getDefaultTargets().map(function(target) {
56290
+ return {
56291
+ dataset: target.dataset,
56292
+ layers: target.layers.slice()
56293
+ };
56294
+ })
56295
+ });
56296
+ },
56297
+
56298
+ captureDatasetBefore: function(dataset, detail) {
56299
+ var key = unitKey('dataset', dataset, detail && detail.unit || '');
56300
+ if (this._captured[key]) return;
56301
+ this._captured[key] = true;
56302
+ this.units.push({
56303
+ type: 'dataset',
56304
+ target: dataset,
56305
+ id: getUndoId(dataset),
56306
+ revision: getUndoRevision(dataset),
56307
+ detail: copyDetail(detail),
56308
+ layers: dataset.layers ? dataset.layers.slice() : null,
56309
+ arcs: dataset.arcs || null,
56310
+ info: dataset.info ? copyRecord(dataset.info) : null
56311
+ });
56312
+ },
56313
+
56314
+ captureDatasetInfoBefore: function(dataset, detail) {
56315
+ var key = unitKey('dataset-info', dataset);
56316
+ if (this._captured[key]) return;
56317
+ this._captured[key] = true;
56318
+ this.units.push({
56319
+ type: 'dataset-info',
56320
+ target: dataset,
56321
+ id: getUndoId(dataset),
56322
+ revision: getUndoRevision(dataset),
56323
+ detail: copyDetail(detail),
56324
+ info: dataset.info ? copyRecord(dataset.info) : null
56325
+ });
56326
+ },
56327
+
56328
+ captureLayerBefore: function(layer, detail) {
56329
+ var key = unitKey('layer', layer, detail && detail.unit || '');
56330
+ if (this._captured[key]) return;
56331
+ this._captured[key] = true;
56332
+ this.units.push({
56333
+ type: 'layer',
56334
+ target: layer,
56335
+ id: getUndoId(layer),
56336
+ revision: getUndoRevision(layer),
56337
+ detail: copyDetail(detail),
56338
+ name: layer.name,
56339
+ geometry_type: layer.geometry_type,
56340
+ shapes: layer.shapes ? cloneShapes(layer.shapes) : null,
56341
+ data: layer.data || null
56342
+ });
56343
+ },
56344
+
56345
+ captureLayerMetadataBefore: function(layer, detail) {
56346
+ var key = unitKey('layer-metadata', layer, detail && detail.unit || '');
56347
+ if (this._captured[key]) return;
56348
+ this._captured[key] = true;
56349
+ this.units.push({
56350
+ type: 'layer-metadata',
56351
+ target: layer,
56352
+ id: getUndoId(layer),
56353
+ revision: getUndoRevision(layer),
56354
+ detail: copyDetail(detail),
56355
+ name: layer.name,
56356
+ geometry_type: layer.geometry_type
56357
+ });
56358
+ },
56359
+
56360
+ captureLayerOrderBefore: function(layer, detail) {
56361
+ captureOrderUnit(this, 'layer-order', layer, detail);
56362
+ },
56363
+
56364
+ markChanged: function(obj, detail) {
56365
+ this.units.push({
56366
+ type: 'changed',
56367
+ id: getUndoId(obj),
56368
+ revision: getUndoRevision(obj),
56369
+ detail: copyDetail(detail)
56370
+ });
56371
+ }
56372
+ };
56373
+
56374
+ function restoreCapturedUnits(units) {
56375
+ for (var i = units.length - 1; i >= 0; i--) {
56376
+ restoreUnit(units[i]);
56377
+ }
56378
+ }
56379
+
56380
+ function captureCurrentUnits(units) {
56381
+ var captured = [];
56382
+ units.forEach(function(unit) {
56383
+ var current = captureCurrentUnit(unit);
56384
+ if (current) captured.push(current);
56385
+ });
56386
+ return captured;
56387
+ }
56388
+
56389
+ function filterUnchangedRestoreUnits(units) {
56390
+ var arcChanges = getArcsUnitChangeIndex(units);
56391
+ var normalized = units.map(function(unit) {
56392
+ return normalizeUnchangedDatasetArcs(unit, arcChanges);
56393
+ });
56394
+ var protectedArcs = getDatasetRestoreArcsIndex(normalized);
56395
+ var protectedSimplification = getArcsRestoreIndex(normalized);
56396
+ return normalized.filter(function(unit) {
56397
+ return unit.type == 'changed' ||
56398
+ restoreUnitHasChanged(unit, protectedArcs, protectedSimplification);
56399
+ });
56400
+ }
56401
+
56402
+ function normalizeUnchangedDatasetArcs(unit, arcChanges) {
56403
+ var arcsId = unit.arcs && getUndoId(unit.arcs);
56404
+ if (unit.type == 'dataset' &&
56405
+ unit.arcs &&
56406
+ unit.target.arcs &&
56407
+ unit.arcs !== unit.target.arcs &&
56408
+ arcChanges[arcsId] === false &&
56409
+ arcCollectionsAreEqual(unit.arcs, unit.target.arcs)) {
56410
+ return Object.assign({}, unit, {arcs: unit.target.arcs});
56411
+ }
56412
+ return unit;
56413
+ }
56414
+
56415
+ function restoreUnitHasChanged(unit, protectedArcs, protectedSimplification) {
56416
+ if (unit.type == 'arcs') {
56417
+ if (protectedArcs[getUndoId(unit.target)]) return true;
56418
+ return arcsUnitHasChanged(unit);
56419
+ } else if (unit.type == 'arcs-simplification') {
56420
+ if (protectedSimplification[getUndoId(unit.target)]) return true;
56421
+ return arcsSimplificationUnitHasChanged(unit);
56422
+ }
56423
+ return true;
56424
+ }
56425
+
56426
+ function getArcsUnitChangeIndex(units) {
56427
+ var index = {};
56428
+ units.forEach(function(unit) {
56429
+ if (unit.type == 'arcs') {
56430
+ index[getUndoId(unit.target)] = arcsUnitHasChanged(unit);
56431
+ }
56432
+ });
56433
+ return index;
56434
+ }
56435
+
56436
+ function getDatasetRestoreArcsIndex(units) {
56437
+ var index = {};
56438
+ units.forEach(function(unit) {
56439
+ if (unit.type == 'dataset' && unit.arcs && unit.target.arcs !== unit.arcs) {
56440
+ index[getUndoId(unit.arcs)] = true;
56441
+ }
56442
+ });
56443
+ return index;
56444
+ }
56445
+
56446
+ function getArcsRestoreIndex(units) {
56447
+ var index = {};
56448
+ units.forEach(function(unit) {
56449
+ if (unit.type == 'arcs') {
56450
+ index[getUndoId(unit.target)] = true;
56451
+ }
56452
+ });
56453
+ return index;
56454
+ }
56455
+
56456
+ function arcCollectionsAreEqual(a, b) {
56457
+ var dataA, dataB;
56458
+ if (a === b) return true;
56459
+ if (!a || !b) return !a && !b;
56460
+ dataA = a.getVertexData();
56461
+ dataB = b.getVertexData();
56462
+ return a.getRetainedInterval() == b.getRetainedInterval() &&
56463
+ arrayLikeDataIsEqual(dataA.nn, dataB.nn) &&
56464
+ arrayLikeDataIsEqual(dataA.xx, dataB.xx) &&
56465
+ arrayLikeDataIsEqual(dataA.yy, dataB.yy) &&
56466
+ arrayLikeDataIsEqual(dataA.zz, dataB.zz);
56467
+ }
56468
+
56469
+ function arcsUnitHasChanged(unit) {
56470
+ var data = unit.target.getVertexData();
56471
+ return unit.zlimit != unit.target.getRetainedInterval() ||
56472
+ !arrayLikeDataIsEqual(unit.nn, data.nn) ||
56473
+ !arrayLikeDataIsEqual(unit.xx, data.xx) ||
56474
+ !arrayLikeDataIsEqual(unit.yy, data.yy) ||
56475
+ !arrayLikeDataIsEqual(unit.zz, data.zz);
56476
+ }
56477
+
56478
+ function arcsSimplificationUnitHasChanged(unit) {
56479
+ var data = unit.target.getVertexData();
56480
+ return unit.zlimit != unit.target.getRetainedInterval() ||
56481
+ !arrayLikeDataIsEqual(unit.zz, data.zz);
56482
+ }
56483
+
56484
+ function arrayLikeDataIsEqual(a, b) {
56485
+ if (a === b) return true;
56486
+ if (!a || !b) return !a && !b;
56487
+ if (a.length !== b.length) return false;
56488
+ for (var i = 0; i < a.length; i++) {
56489
+ if (a[i] !== b[i]) return false;
56490
+ }
56491
+ return true;
56492
+ }
56493
+
56494
+ function captureCurrentUnit(unit) {
56495
+ if (unit.type == 'changed') return null;
56496
+ if (unit.type == 'table') {
56497
+ return captureCurrentTable(unit);
56498
+ } else if (unit.type == 'table-records') {
56499
+ return captureCurrentTableRecords(unit);
56500
+ } else if (unit.type == 'table-fields') {
56501
+ return captureCurrentTableFields(unit);
56502
+ } else if (unit.type == 'table-order') {
56503
+ return captureCurrentOrder(unit);
56504
+ } else if (unit.type == 'table-schema') {
56505
+ return captureCurrentTableSchema(unit);
56506
+ } else if (unit.type == 'arcs') {
56507
+ return captureCurrentArcs(unit);
56508
+ } else if (unit.type == 'arcs-simplification') {
56509
+ return captureCurrentArcsSimplification(unit);
56510
+ } else if (unit.type == 'catalog') {
56511
+ return captureCurrentCatalog(unit);
56512
+ } else if (unit.type == 'dataset') {
56513
+ return captureCurrentDataset(unit);
56514
+ } else if (unit.type == 'dataset-info') {
56515
+ return captureCurrentDatasetInfo(unit);
56516
+ } else if (unit.type == 'layer') {
56517
+ return captureCurrentLayer(unit);
56518
+ } else if (unit.type == 'layer-metadata') {
56519
+ return captureCurrentLayerMetadata(unit);
56520
+ } else if (unit.type == 'layer-order') {
56521
+ return captureCurrentOrder(unit);
56522
+ }
56523
+ return null;
56524
+ }
56525
+
56526
+ function captureCurrentTable(unit) {
56527
+ return Object.assign({}, unit, {
56528
+ revision: getUndoRevision(unit.target),
56529
+ records: unit.target.getRecords().map(copyRecord)
56530
+ });
56531
+ }
56532
+
56533
+ function captureCurrentTableRecords(unit) {
56534
+ var records = unit.target.getRecords();
56535
+ return Object.assign({}, unit, {
56536
+ revision: getUndoRevision(unit.target),
56537
+ records: unit.records.map(function(item) {
56538
+ return {
56539
+ id: item.id,
56540
+ record: copyRecord(records[item.id])
56541
+ };
56542
+ })
56543
+ });
56544
+ }
56545
+
56546
+ function captureCurrentTableFields(unit) {
56547
+ var records = unit.target.getRecords();
56548
+ var fields = unit.detail && unit.detail.schema_transform ?
56549
+ unit.target.getFields() :
56550
+ unit.columns.map(function(column) { return column.field; });
56551
+ return Object.assign({}, unit, {
56552
+ revision: getUndoRevision(unit.target),
56553
+ columns: fields.map(function(field) {
56554
+ return {
56555
+ field: field,
56556
+ values: records.map(function(rec) {
56557
+ return rec ? rec[field] : undefined;
56558
+ })
56559
+ };
56560
+ })
56561
+ });
56562
+ }
56563
+
56564
+ function captureCurrentOrder(unit) {
56565
+ return Object.assign({}, unit, {
56566
+ revision: getUndoRevision(unit.target),
56567
+ ids: invertIds(unit.ids)
56568
+ });
56569
+ }
56570
+
56571
+ function captureCurrentTableSchema(unit) {
56572
+ return Object.assign({}, unit, {
56573
+ revision: getUndoRevision(unit.target),
56574
+ fields: unit.target.getFields()
56575
+ });
56576
+ }
56577
+
56578
+ function captureCurrentArcs(unit) {
56579
+ var data = unit.target.getVertexData();
56580
+ return Object.assign({}, unit, {
56581
+ revision: getUndoRevision(unit.target),
56582
+ nn: new Uint32Array(data.nn),
56583
+ xx: new Float64Array(data.xx),
56584
+ yy: new Float64Array(data.yy),
56585
+ zz: data.zz ? new Float64Array(data.zz) : null,
56586
+ zlimit: unit.target.getRetainedInterval()
56587
+ });
56588
+ }
56589
+
56590
+ function captureCurrentArcsSimplification(unit) {
56591
+ var data = unit.target.getVertexData();
56592
+ return Object.assign({}, unit, {
56593
+ revision: getUndoRevision(unit.target),
56594
+ zz: data.zz ? new Float64Array(data.zz) : null,
56595
+ zlimit: unit.target.getRetainedInterval()
56596
+ });
56597
+ }
56598
+
56599
+ function captureCurrentCatalog(unit) {
56600
+ return Object.assign({}, unit, {
56601
+ revision: getUndoRevision(unit.target),
56602
+ datasets: unit.target.getDatasets().slice(),
56603
+ targets: unit.target.getDefaultTargets().map(function(target) {
56604
+ return {
56605
+ dataset: target.dataset,
56606
+ layers: target.layers.slice()
56607
+ };
56608
+ })
56609
+ });
56610
+ }
56611
+
56612
+ function captureCurrentDataset(unit) {
56613
+ return Object.assign({}, unit, {
56614
+ revision: getUndoRevision(unit.target),
56615
+ layers: unit.target.layers ? unit.target.layers.slice() : null,
56616
+ arcs: unit.target.arcs || null,
56617
+ info: unit.target.info ? copyRecord(unit.target.info) : null
56618
+ });
56619
+ }
56620
+
56621
+ function captureCurrentDatasetInfo(unit) {
56622
+ return Object.assign({}, unit, {
56623
+ revision: getUndoRevision(unit.target),
56624
+ info: unit.target.info ? copyRecord(unit.target.info) : null
56625
+ });
56626
+ }
56627
+
56628
+ function captureCurrentLayer(unit) {
56629
+ return Object.assign({}, unit, {
56630
+ revision: getUndoRevision(unit.target),
56631
+ name: unit.target.name,
56632
+ geometry_type: unit.target.geometry_type,
56633
+ shapes: unit.target.shapes ? cloneShapes(unit.target.shapes) : null,
56634
+ data: unit.target.data || null
56635
+ });
56636
+ }
56637
+
56638
+ function captureCurrentLayerMetadata(unit) {
56639
+ return Object.assign({}, unit, {
56640
+ revision: getUndoRevision(unit.target),
56641
+ name: unit.target.name,
56642
+ geometry_type: unit.target.geometry_type
56643
+ });
56644
+ }
56645
+
56646
+ function restoreUnit(unit) {
56647
+ if (unit.type == 'changed') return;
56648
+ if (unit.type == 'table') {
56649
+ restoreTable(unit);
56650
+ } else if (unit.type == 'table-records') {
56651
+ restoreTableRecords(unit);
56652
+ } else if (unit.type == 'table-fields') {
56653
+ restoreTableFields(unit);
56654
+ } else if (unit.type == 'table-order') {
56655
+ restoreTableOrder(unit);
56656
+ } else if (unit.type == 'table-schema') {
56657
+ restoreTableSchema(unit);
56658
+ } else if (unit.type == 'arcs') {
56659
+ restoreArcs(unit);
56660
+ } else if (unit.type == 'arcs-simplification') {
56661
+ restoreArcsSimplification(unit);
56662
+ } else if (unit.type == 'catalog') {
56663
+ restoreCatalog(unit);
56664
+ } else if (unit.type == 'dataset') {
56665
+ restoreDataset(unit);
56666
+ } else if (unit.type == 'dataset-info') {
56667
+ restoreDatasetInfo(unit);
56668
+ } else if (unit.type == 'layer') {
56669
+ restoreLayer(unit);
56670
+ } else if (unit.type == 'layer-metadata') {
56671
+ restoreLayerMetadata(unit);
56672
+ } else if (unit.type == 'layer-order') {
56673
+ restoreLayerOrder(unit);
56674
+ }
56675
+ }
56676
+
56677
+ function restoreTable(unit) {
56678
+ var records = unit.target.getRecords();
56679
+ records.splice(0, records.length);
56680
+ unit.records.forEach(function(rec) {
56681
+ records.push(copyRecord(rec));
56682
+ });
56683
+ }
56684
+
56685
+ function restoreTableRecords(unit) {
56686
+ var records = unit.target.getRecords();
56687
+ unit.records.forEach(function(item) {
56688
+ records[item.id] = copyRecord(item.record);
56689
+ });
56690
+ }
56691
+
56692
+ function restoreTableFields(unit) {
56693
+ var records = unit.target.getRecords();
56694
+ unit.columns.forEach(function(column) {
56695
+ column.values.forEach(function(val, i) {
56696
+ if (!records[i]) records[i] = {};
56697
+ records[i][column.field] = val;
56698
+ });
56699
+ });
56700
+ }
56701
+
56702
+ function restoreTableOrder(unit) {
56703
+ reorderArray(unit.target.getRecords(), unit.ids);
56704
+ }
56705
+
56706
+ function restoreTableSchema(unit) {
56707
+ var records = unit.target.getRecords();
56708
+ records.forEach(function(rec, i) {
56709
+ var reordered = {};
56710
+ unit.fields.forEach(function(field) {
56711
+ reordered[field] = rec ? rec[field] : undefined;
56712
+ });
56713
+ records[i] = reordered;
56714
+ });
56715
+ }
56716
+
56717
+ function restoreArcs(unit) {
56718
+ unit.target.updateVertexData(
56719
+ new Uint32Array(unit.nn),
56720
+ new Float64Array(unit.xx),
56721
+ new Float64Array(unit.yy),
56722
+ unit.zz ? new Float64Array(unit.zz) : null
56723
+ );
56724
+ unit.target.setRetainedInterval(unit.zlimit);
56725
+ }
56726
+
56727
+ function restoreArcsSimplification(unit) {
56728
+ unit.target.setThresholds(unit.zz ? new Float64Array(unit.zz) : null);
56729
+ unit.target.setRetainedInterval(unit.zlimit);
56730
+ }
56731
+
56732
+ function restoreCatalog(unit) {
56733
+ var datasets = unit.target.getDatasets();
56734
+ datasets.splice(0, datasets.length);
56735
+ unit.datasets.forEach(function(dataset) {
56736
+ datasets.push(dataset);
56737
+ });
56738
+ unit.target.setDefaultTargets(unit.targets.map(function(target) {
56739
+ return {
56740
+ dataset: target.dataset,
56741
+ layers: target.layers.slice()
56742
+ };
56743
+ }));
56744
+ }
56745
+
56746
+ function restoreDataset(unit) {
56747
+ unit.target.layers = unit.layers ? unit.layers.slice() : null;
56748
+ unit.target.arcs = unit.arcs;
56749
+ unit.target.info = unit.info ? copyRecord(unit.info) : null;
56750
+ }
56751
+
56752
+ function restoreDatasetInfo(unit) {
56753
+ unit.target.info = unit.info ? copyRecord(unit.info) : null;
56754
+ }
56755
+
56756
+ function restoreLayer(unit) {
56757
+ unit.target.name = unit.name;
56758
+ unit.target.geometry_type = unit.geometry_type;
56759
+ unit.target.shapes = unit.shapes ? cloneShapes(unit.shapes) : null;
56760
+ unit.target.data = unit.data;
56761
+ }
56762
+
56763
+ function restoreLayerMetadata(unit) {
56764
+ unit.target.name = unit.name;
56765
+ unit.target.geometry_type = unit.geometry_type;
56766
+ }
56767
+
56768
+ function restoreLayerOrder(unit) {
56769
+ reorderArray(unit.target.shapes, unit.ids);
56770
+ }
56771
+
56772
+ function unitKey(type, obj, extra) {
56773
+ return type + ':' + getUndoId(obj) + (extra == null ? '' : ':' + extra);
56774
+ }
56775
+
56776
+ function captureOrderUnit(tx, type, target, detail) {
56777
+ var unit = findOrderUnit(tx.units, type, target);
56778
+ var ids = uniquePermutation(detail.ids);
56779
+ if (unit) {
56780
+ unit.ids = composeIds(ids, unit.ids);
56781
+ unit.detail = copyDetail(detail);
56782
+ return;
56783
+ }
56784
+ tx.units.push({
56785
+ type: type,
56786
+ target: target,
56787
+ id: getUndoId(target),
56788
+ revision: getUndoRevision(target),
56789
+ detail: copyDetail(detail),
56790
+ ids: ids
56791
+ });
56792
+ }
56793
+
56794
+ function findOrderUnit(units, type, target) {
56795
+ for (var i = units.length - 1; i >= 0; i--) {
56796
+ if (units[i].type == type && units[i].target == target) {
56797
+ return units[i];
56798
+ }
56799
+ }
56800
+ return null;
56801
+ }
56802
+
56803
+ function reorderArray(arr, ids) {
56804
+ var copy = ids.map(function(id) {
56805
+ return arr[id];
56806
+ });
56807
+ arr.splice.apply(arr, [0, arr.length].concat(copy));
56808
+ }
56809
+
56810
+ function composeIds(a, b) {
56811
+ return b.map(function(id) {
56812
+ return a[id];
56813
+ });
56814
+ }
56815
+
56816
+ function invertIds(ids) {
56817
+ var inverse = [];
56818
+ ids.forEach(function(id, i) {
56819
+ inverse[id] = i;
56820
+ });
56821
+ return inverse;
56822
+ }
56823
+
56824
+ function uniquePermutation(ids) {
56825
+ var index = {};
56826
+ ids = ids || [];
56827
+ ids.forEach(function(id) {
56828
+ if (id < 0 || id >= ids.length || index[id]) {
56829
+ throw new Error('Invalid undo order permutation');
56830
+ }
56831
+ index[id] = true;
56832
+ });
56833
+ return ids.slice();
56834
+ }
56835
+
56836
+ function copyDetail(detail) {
56837
+ return Object.assign({}, detail || {});
56838
+ }
56839
+
56840
+ function uniqueNumbers(ids) {
56841
+ var index = {};
56842
+ ids = ids || [];
56843
+ ids.forEach(function(id) {
56844
+ if (id >= 0) index[id] = true;
56845
+ });
56846
+ return Object.keys(index).map(Number);
56847
+ }
56848
+
56849
+ function uniqueStrings(fields) {
56850
+ var index = {};
56851
+ fields = fields || [];
56852
+ fields.forEach(function(field) {
56853
+ index[field] = true;
56854
+ });
56855
+ return Object.keys(index);
56856
+ }
56857
+
56858
+ var UndoTransaction$1 = /*#__PURE__*/Object.freeze({
56859
+ __proto__: null,
56860
+ UndoTransaction: UndoTransaction,
56861
+ captureCurrentUnits: captureCurrentUnits,
56862
+ filterUnchangedRestoreUnits: filterUnchangedRestoreUnits,
56863
+ restoreCapturedUnits: restoreCapturedUnits
56864
+ });
56865
+
54957
56866
  // Attach functions exported by modules to the "internal" object,
54958
56867
  // so they can be run by tests and by the GUI.
54959
56868
  // TODO: rewrite tests to import functions directly from modules,
@@ -55111,6 +57020,8 @@ ${svg}
55111
57020
  TopojsonExport,
55112
57021
  TopojsonImport,
55113
57022
  Topology,
57023
+ UndoTransaction$1,
57024
+ UndoTracking,
55114
57025
  Units,
55115
57026
  SvgHatch,
55116
57027
  SvgEffect,