mapshaper 0.6.23 → 0.6.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/mapshaper.js +398 -153
- package/package.json +2 -3
- package/www/index.html +1 -0
- package/www/mapshaper-gui.js +67 -38
- package/www/mapshaper.js +398 -153
package/www/mapshaper.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
|
|
3
|
-
var VERSION = "0.6.
|
|
3
|
+
var VERSION = "0.6.25";
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
var utils = /*#__PURE__*/Object.freeze({
|
|
@@ -2449,6 +2449,17 @@
|
|
|
2449
2449
|
getPathCentroid: getPathCentroid
|
|
2450
2450
|
});
|
|
2451
2451
|
|
|
2452
|
+
function testSegmentBoundsIntersection(a, b, bb) {
|
|
2453
|
+
if (bb.containsPoint(a[0], a[1])) {
|
|
2454
|
+
return true;
|
|
2455
|
+
}
|
|
2456
|
+
return !!(
|
|
2457
|
+
geom.segmentIntersection(a[0], a[1], b[0], b[1], bb.xmin, bb.ymin, bb.xmin, bb.ymax) ||
|
|
2458
|
+
geom.segmentIntersection(a[0], a[1], b[0], b[1], bb.xmin, bb.ymax, bb.xmax, bb.ymax) ||
|
|
2459
|
+
geom.segmentIntersection(a[0], a[1], b[0], b[1], bb.xmax, bb.ymax, bb.xmax, bb.ymin) ||
|
|
2460
|
+
geom.segmentIntersection(a[0], a[1], b[0], b[1], bb.xmax, bb.ymin, bb.xmin, bb.ymin));
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2452
2463
|
// A compactness measure designed for testing electoral districts for gerrymandering.
|
|
2453
2464
|
// Returns value in [0-1] range. 1 = perfect circle, 0 = collapsed polygon
|
|
2454
2465
|
function calcPolsbyPopperCompactness(area, perimeter) {
|
|
@@ -2496,21 +2507,32 @@
|
|
|
2496
2507
|
// }, 0);
|
|
2497
2508
|
// }
|
|
2498
2509
|
|
|
2510
|
+
// test if a rectangle is completely enclosed in a planar polygon
|
|
2511
|
+
function testBoundsInPolygon(bounds, shp, arcs) {
|
|
2512
|
+
if (!shp || !testPointInPolygon(bounds.xmin, bounds.ymin, shp, arcs)) return false;
|
|
2513
|
+
var isIn = true;
|
|
2514
|
+
shp.forEach(function(ids) {
|
|
2515
|
+
forEachSegmentInPath(ids, arcs, function(a, b, xx, yy) {
|
|
2516
|
+
isIn = isIn && !testSegmentBoundsIntersection([xx[a], yy[a]], [xx[b], yy[b]], bounds);
|
|
2517
|
+
});
|
|
2518
|
+
});
|
|
2519
|
+
return isIn;
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2499
2522
|
// Return true if point is inside or on boundary of a shape
|
|
2500
2523
|
//
|
|
2501
2524
|
function testPointInPolygon(x, y, shp, arcs) {
|
|
2502
2525
|
var isIn = false,
|
|
2503
2526
|
isOn = false;
|
|
2504
|
-
if (shp)
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
}
|
|
2527
|
+
if (!shp) return false;
|
|
2528
|
+
shp.forEach(function(ids) {
|
|
2529
|
+
var inRing = testPointInRing(x, y, ids, arcs);
|
|
2530
|
+
if (inRing == 1) {
|
|
2531
|
+
isIn = !isIn;
|
|
2532
|
+
} else if (inRing == -1) {
|
|
2533
|
+
isOn = true;
|
|
2534
|
+
}
|
|
2535
|
+
});
|
|
2514
2536
|
return isOn || isIn;
|
|
2515
2537
|
}
|
|
2516
2538
|
|
|
@@ -2518,6 +2540,8 @@
|
|
|
2518
2540
|
return ay + (x - ax) * (by - ay) / (bx - ax);
|
|
2519
2541
|
}
|
|
2520
2542
|
|
|
2543
|
+
|
|
2544
|
+
|
|
2521
2545
|
// Test if point (x, y) is inside, outside or on the boundary of a polygon ring
|
|
2522
2546
|
// Return 0: outside; 1: inside; -1: on boundary
|
|
2523
2547
|
//
|
|
@@ -2708,6 +2732,7 @@
|
|
|
2708
2732
|
getShapeArea: getShapeArea,
|
|
2709
2733
|
getPlanarShapeArea: getPlanarShapeArea,
|
|
2710
2734
|
getSphericalShapeArea: getSphericalShapeArea,
|
|
2735
|
+
testBoundsInPolygon: testBoundsInPolygon,
|
|
2711
2736
|
testPointInPolygon: testPointInPolygon,
|
|
2712
2737
|
testPointInRing: testPointInRing,
|
|
2713
2738
|
testRayIntersection: testRayIntersection,
|
|
@@ -7251,7 +7276,7 @@
|
|
|
7251
7276
|
let size = source.length;
|
|
7252
7277
|
let value = this ? this.unpack(source, size) : defaultUnpackr.unpack(source, size);
|
|
7253
7278
|
if (forEach) {
|
|
7254
|
-
forEach(value);
|
|
7279
|
+
if (forEach(value) === false) return;
|
|
7255
7280
|
while(position$1 < size) {
|
|
7256
7281
|
lastPosition = position$1;
|
|
7257
7282
|
if (forEach(checkedRead()) === false) {
|
|
@@ -8193,7 +8218,7 @@
|
|
|
8193
8218
|
let safeEnd;
|
|
8194
8219
|
let bundledStrings = null;
|
|
8195
8220
|
let writeStructSlots;
|
|
8196
|
-
const MAX_BUNDLE_SIZE =
|
|
8221
|
+
const MAX_BUNDLE_SIZE = 0x5500; // maximum characters such that the encoded bytes fits in 16 bits.
|
|
8197
8222
|
const hasNonLatin = /[\u0080-\uFFFF]/;
|
|
8198
8223
|
const RECORD_SYMBOL = Symbol('record-id');
|
|
8199
8224
|
class Packr extends Unpackr {
|
|
@@ -8222,7 +8247,7 @@
|
|
|
8222
8247
|
if (maxSharedStructures > 8160)
|
|
8223
8248
|
throw new Error('Maximum maxSharedStructure is 8160')
|
|
8224
8249
|
if (options.structuredClone && options.moreTypes == undefined) {
|
|
8225
|
-
|
|
8250
|
+
this.moreTypes = true;
|
|
8226
8251
|
}
|
|
8227
8252
|
let maxOwnStructures = options.maxOwnStructures;
|
|
8228
8253
|
if (maxOwnStructures == null)
|
|
@@ -8917,12 +8942,11 @@
|
|
|
8917
8942
|
if (notifySharedUpdate)
|
|
8918
8943
|
return hasSharedUpdate = true;
|
|
8919
8944
|
position = newPosition;
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
|
|
8923
|
-
|
|
8924
|
-
}
|
|
8925
|
-
pack(value);
|
|
8945
|
+
let startTarget = target;
|
|
8946
|
+
pack(value);
|
|
8947
|
+
if (startTarget !== target) {
|
|
8948
|
+
return { position, targetView, target }; // indicate the buffer was re-allocated
|
|
8949
|
+
}
|
|
8926
8950
|
return position;
|
|
8927
8951
|
}, this);
|
|
8928
8952
|
if (newPosition === 0) // bail and go to a msgpack object
|
|
@@ -10620,7 +10644,8 @@
|
|
|
10620
10644
|
buf = new Uint8Array(buf);
|
|
10621
10645
|
}
|
|
10622
10646
|
opts = opts || {};
|
|
10623
|
-
var
|
|
10647
|
+
var gunzip$1 = runningInBrowser() ? utils.promisify(gunzip) : utils.promisify(require('zlib').gunzip);
|
|
10648
|
+
var out = await gunzip$1(buf, opts);
|
|
10624
10649
|
if (opts.filename && !isImportableAsBinary(opts.filename)) {
|
|
10625
10650
|
out = strFromU8(out);
|
|
10626
10651
|
}
|
|
@@ -10628,8 +10653,6 @@
|
|
|
10628
10653
|
}
|
|
10629
10654
|
|
|
10630
10655
|
function gunzipSync(buf, filename) {
|
|
10631
|
-
// TODO: use native module in Node
|
|
10632
|
-
// require('zlib').
|
|
10633
10656
|
if (buf instanceof ArrayBuffer) {
|
|
10634
10657
|
buf = new Uint8Array(buf);
|
|
10635
10658
|
}
|
|
@@ -10768,16 +10791,39 @@
|
|
|
10768
10791
|
// gui: (optional) gui instance
|
|
10769
10792
|
//
|
|
10770
10793
|
async function exportDatasetsToPack(datasets, opts) {
|
|
10771
|
-
|
|
10794
|
+
var obj = {
|
|
10772
10795
|
version: 1,
|
|
10773
10796
|
created: (new Date).toISOString(),
|
|
10774
|
-
datasets: await Promise.all(datasets.map(
|
|
10797
|
+
datasets: await Promise.all(datasets.map(exportDataset))
|
|
10775
10798
|
};
|
|
10799
|
+
if (opts.compact) {
|
|
10800
|
+
await applyCompression(obj);
|
|
10801
|
+
}
|
|
10802
|
+
return obj;
|
|
10803
|
+
}
|
|
10804
|
+
|
|
10805
|
+
async function applyCompression(obj, opts) {
|
|
10806
|
+
var promises = [];
|
|
10807
|
+
obj.datasets.forEach(d => {
|
|
10808
|
+
if (d.arcs) promises.push(compressArcs(d.arcs, opts));
|
|
10809
|
+
});
|
|
10810
|
+
await Promise.all(promises);
|
|
10811
|
+
}
|
|
10812
|
+
|
|
10813
|
+
async function compressArcs(obj, opts) {
|
|
10814
|
+
var gzipOpts = Object.assign({level: 1, consume: false}, opts);
|
|
10815
|
+
var promises = [gzipAsync(obj.nn, gzipOpts), gzipAsync(obj.xx, gzipOpts), gzipAsync(obj.yy, gzipOpts)];
|
|
10816
|
+
if (obj.zz) promises.push(gzipAsync(obj.zz, gzipOpts));
|
|
10817
|
+
var results = await Promise.all(promises);
|
|
10818
|
+
obj.nn = results.shift();
|
|
10819
|
+
obj.xx = results.shift();
|
|
10820
|
+
obj.yy = results.shift();
|
|
10821
|
+
if (obj.zz) obj.zz = results.shift();
|
|
10776
10822
|
}
|
|
10777
10823
|
|
|
10778
10824
|
async function exportDataset(dataset, opts) {
|
|
10779
10825
|
return {
|
|
10780
|
-
arcs: dataset.arcs ?
|
|
10826
|
+
arcs: dataset.arcs ? exportArcs(dataset.arcs) : null,
|
|
10781
10827
|
info: dataset.info ? exportInfo(dataset.info) : null,
|
|
10782
10828
|
layers: await Promise.all((dataset.layers || []).map(exportLayer))
|
|
10783
10829
|
};
|
|
@@ -10787,38 +10833,22 @@
|
|
|
10787
10833
|
return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
10788
10834
|
}
|
|
10789
10835
|
|
|
10790
|
-
|
|
10836
|
+
function exportArcs(arcs) {
|
|
10791
10837
|
var data = arcs.getVertexData();
|
|
10792
|
-
|
|
10838
|
+
return {
|
|
10793
10839
|
nn: typedArrayToBuffer(data.nn),
|
|
10794
10840
|
xx: typedArrayToBuffer(data.xx),
|
|
10795
10841
|
yy: typedArrayToBuffer(data.yy),
|
|
10796
10842
|
zz: data.zz ? typedArrayToBuffer(data.zz) : null,
|
|
10797
10843
|
zlimit: arcs.getRetainedInterval()
|
|
10798
10844
|
};
|
|
10799
|
-
|
|
10800
|
-
// gzipping typically sees about 70% compression on unrounded coordinates
|
|
10801
|
-
// -- possibly not worth the time
|
|
10802
|
-
if (opts.compact) {
|
|
10803
|
-
var gzipOpts = {level: 1, consume: false};
|
|
10804
|
-
var promises = [gzipAsync(obj.nn, gzipOpts), gzipAsync(obj.xx, gzipOpts), gzipAsync(obj.yy, gzipOpts)];
|
|
10805
|
-
if (obj.zz) promises.push(gzipAsync(obj.zz, gzipOpts));
|
|
10806
|
-
var results = await Promise.all(promises);
|
|
10807
|
-
obj.nn = results.shift();
|
|
10808
|
-
obj.xx = results.shift();
|
|
10809
|
-
obj.yy = results.shift();
|
|
10810
|
-
if (obj.zz) obj.zz = results.shift();
|
|
10811
|
-
}
|
|
10812
|
-
return obj;
|
|
10813
10845
|
}
|
|
10814
10846
|
|
|
10815
10847
|
async function exportLayer(lyr) {
|
|
10816
|
-
// console.time('table')
|
|
10817
10848
|
var data = null;
|
|
10818
10849
|
if (lyr.data) {
|
|
10819
10850
|
data = await exportTable2(lyr.data);
|
|
10820
10851
|
}
|
|
10821
|
-
// console.timeEnd('table')
|
|
10822
10852
|
return {
|
|
10823
10853
|
name: lyr.name || null,
|
|
10824
10854
|
geometry_type: lyr.geometry_type || null,
|
|
@@ -10845,6 +10875,7 @@
|
|
|
10845
10875
|
exportPackedDatasets: exportPackedDatasets,
|
|
10846
10876
|
pack: pack,
|
|
10847
10877
|
exportDatasetsToPack: exportDatasetsToPack,
|
|
10878
|
+
applyCompression: applyCompression,
|
|
10848
10879
|
exportDataset: exportDataset
|
|
10849
10880
|
});
|
|
10850
10881
|
|
|
@@ -13722,6 +13753,11 @@
|
|
|
13722
13753
|
return rect.contains(bbox);
|
|
13723
13754
|
};
|
|
13724
13755
|
|
|
13756
|
+
// TODO
|
|
13757
|
+
// ctx.intersectsRectangle = function(a, b, c, d) {}; // paths... points too?
|
|
13758
|
+
// ctx.containsPoint = function(x, y) {}; // polygon only
|
|
13759
|
+
// ctx.containedByRectangle(a, b, c, d); // paths and points... how do multipart points work?
|
|
13760
|
+
|
|
13725
13761
|
addGetters(ctx, {
|
|
13726
13762
|
// TODO: count hole/s + containing ring as one part
|
|
13727
13763
|
partCount: function() {
|
|
@@ -24595,6 +24631,21 @@ ${svg}
|
|
|
24595
24631
|
// Experimental commands
|
|
24596
24632
|
parser.section('Experimental commands (may give unexpected results)');
|
|
24597
24633
|
|
|
24634
|
+
parser.command('add-shape')
|
|
24635
|
+
.describe('')
|
|
24636
|
+
.option('geojson', {
|
|
24637
|
+
|
|
24638
|
+
})
|
|
24639
|
+
.option('coordinates', {
|
|
24640
|
+
|
|
24641
|
+
})
|
|
24642
|
+
.option('properties', {
|
|
24643
|
+
|
|
24644
|
+
})
|
|
24645
|
+
.option('name', nameOpt)
|
|
24646
|
+
.option('target', targetOpt)
|
|
24647
|
+
.option('no-replace', noReplaceOpt);
|
|
24648
|
+
|
|
24598
24649
|
parser.command('alpha-shapes')
|
|
24599
24650
|
// .describe('convert points to alpha shapes (aka concave hulls)')
|
|
24600
24651
|
.option('interval', {
|
|
@@ -24922,6 +24973,9 @@ ${svg}
|
|
|
24922
24973
|
|
|
24923
24974
|
parser.command('info')
|
|
24924
24975
|
.describe('print information about data layers')
|
|
24976
|
+
.option('save-to', {
|
|
24977
|
+
describe: 'name of file to save info in JSON format'
|
|
24978
|
+
})
|
|
24925
24979
|
.option('target', targetOpt);
|
|
24926
24980
|
|
|
24927
24981
|
parser.command('inspect')
|
|
@@ -27457,12 +27511,14 @@ ${svg}
|
|
|
27457
27511
|
// Import datasets contained in a BSON blob
|
|
27458
27512
|
// Return command target as a dataset
|
|
27459
27513
|
//
|
|
27460
|
-
async function
|
|
27461
|
-
|
|
27514
|
+
async function unpackSessionData(buf) {
|
|
27515
|
+
return restoreSessionData(unpack(buf, {}));
|
|
27516
|
+
}
|
|
27517
|
+
|
|
27518
|
+
async function restoreSessionData(obj) {
|
|
27462
27519
|
if (!isValidSession(obj)) {
|
|
27463
27520
|
stop('Invalid mapshaper session data object');
|
|
27464
27521
|
}
|
|
27465
|
-
|
|
27466
27522
|
var datasets = await Promise.all(obj.datasets.map(importDataset));
|
|
27467
27523
|
return Object.assign(obj, {datasets: datasets});
|
|
27468
27524
|
}
|
|
@@ -27539,7 +27595,8 @@ ${svg}
|
|
|
27539
27595
|
|
|
27540
27596
|
var Unpack = /*#__PURE__*/Object.freeze({
|
|
27541
27597
|
__proto__: null,
|
|
27542
|
-
|
|
27598
|
+
unpackSessionData: unpackSessionData,
|
|
27599
|
+
restoreSessionData: restoreSessionData
|
|
27543
27600
|
});
|
|
27544
27601
|
|
|
27545
27602
|
cmd.importFiles = async function(catalog, opts) {
|
|
@@ -27593,7 +27650,7 @@ ${svg}
|
|
|
27593
27650
|
|
|
27594
27651
|
async function importMshpFile(file, catalog, opts) {
|
|
27595
27652
|
var buf = cli.readFile(file, null, opts.input);
|
|
27596
|
-
var obj = await
|
|
27653
|
+
var obj = await unpackSessionData(buf);
|
|
27597
27654
|
obj.datasets.forEach(catalog.addDataset, catalog);
|
|
27598
27655
|
return obj.target;
|
|
27599
27656
|
}
|
|
@@ -28063,6 +28120,136 @@ ${svg}
|
|
|
28063
28120
|
stashVar('input_files', job.input_files);
|
|
28064
28121
|
}
|
|
28065
28122
|
|
|
28123
|
+
cmd.addShape = addShape;
|
|
28124
|
+
|
|
28125
|
+
function addShape(targetLayers, targetDataset, opts) {
|
|
28126
|
+
if (targetLayers.length > 1) {
|
|
28127
|
+
stop('Command expects a single target layer');
|
|
28128
|
+
}
|
|
28129
|
+
var targetLyr = targetLayers[0]; // may be undefined
|
|
28130
|
+
var targetType = !opts.no_replace && targetLyr && targetLyr.geometry_type || null;
|
|
28131
|
+
var dataset = importGeoJSON(toFeature(opts, targetType));
|
|
28132
|
+
var outputLyr = mergeDatasetsIntoDataset(targetDataset, [dataset])[0];
|
|
28133
|
+
if (opts.no_replace || !targetLyr) {
|
|
28134
|
+
// create new layer
|
|
28135
|
+
setOutputLayerName(outputLyr, targetLyr && targetLyr.name, null, opts);
|
|
28136
|
+
return [outputLyr];
|
|
28137
|
+
}
|
|
28138
|
+
// merge into target layer
|
|
28139
|
+
return cmd.mergeLayers([targetLyr, outputLyr], {force: true});
|
|
28140
|
+
}
|
|
28141
|
+
|
|
28142
|
+
function toFeature(opts, geomType) {
|
|
28143
|
+
if (opts.geojson) {
|
|
28144
|
+
return parseArg(opts.geojson);
|
|
28145
|
+
}
|
|
28146
|
+
|
|
28147
|
+
var geom = opts.coordinates && parseCoordsAsGeometry(opts.coordinates) || null;
|
|
28148
|
+
|
|
28149
|
+
if (!geom) {
|
|
28150
|
+
stop('Missing required shape coordinates');
|
|
28151
|
+
}
|
|
28152
|
+
|
|
28153
|
+
if (geomType == 'point' && geom.type != 'Point') {
|
|
28154
|
+
stop('Expected point coordinates, received', geom.type);
|
|
28155
|
+
}
|
|
28156
|
+
|
|
28157
|
+
if (geomType == 'polygon' && geom.type != 'Polygon') {
|
|
28158
|
+
stop('Expected polygon coordinates, received', geom.type);
|
|
28159
|
+
}
|
|
28160
|
+
|
|
28161
|
+
if (geomType == 'polyline') {
|
|
28162
|
+
if (geom.type == 'Polygon') {
|
|
28163
|
+
geom.coordinates = geom.coordinates[0];
|
|
28164
|
+
geom.type = 'LineString';
|
|
28165
|
+
} else {
|
|
28166
|
+
stop('Expected polyline coordinates, received', geom.type);
|
|
28167
|
+
}
|
|
28168
|
+
}
|
|
28169
|
+
|
|
28170
|
+
return {
|
|
28171
|
+
type: 'Feature',
|
|
28172
|
+
properties: parseProperties(opts.properties),
|
|
28173
|
+
geometry: geom
|
|
28174
|
+
};
|
|
28175
|
+
}
|
|
28176
|
+
|
|
28177
|
+
function parseArg(obj) {
|
|
28178
|
+
return typeof obj == 'string' ? JSON.parse(obj) : obj;
|
|
28179
|
+
}
|
|
28180
|
+
|
|
28181
|
+
function parseProperties(arg) {
|
|
28182
|
+
if (!arg) return null;
|
|
28183
|
+
return parseArg(arg);
|
|
28184
|
+
}
|
|
28185
|
+
|
|
28186
|
+
function isArrayOfNumbers(arr) {
|
|
28187
|
+
return arr.length >= 2 && arr.every(utils.isNumber);
|
|
28188
|
+
}
|
|
28189
|
+
|
|
28190
|
+
function isClosedPath$1(arr) {
|
|
28191
|
+
return isArrayOfPoints(arr) && arr.length > 3 && samePoint$1(arr[0], arr[arr.length - 1]);
|
|
28192
|
+
}
|
|
28193
|
+
|
|
28194
|
+
function samePoint$1(a, b) {
|
|
28195
|
+
return a[0] == b[0] && a[1] == b[1];
|
|
28196
|
+
}
|
|
28197
|
+
|
|
28198
|
+
function isArrayOfPoints(arr) {
|
|
28199
|
+
return arr.every(isPoint);
|
|
28200
|
+
}
|
|
28201
|
+
|
|
28202
|
+
function isPoint(arr) {
|
|
28203
|
+
return arr && arr.length == 2 && isArrayOfNumbers(arr);
|
|
28204
|
+
}
|
|
28205
|
+
|
|
28206
|
+
function transposeCoords(arr) {
|
|
28207
|
+
var coords = [];
|
|
28208
|
+
for (var i=0; i<arr.length; i+=2) {
|
|
28209
|
+
coords.push([arr[i], arr[i+1]]);
|
|
28210
|
+
}
|
|
28211
|
+
if (!isArrayOfPoints(coords)) {
|
|
28212
|
+
stop('Unable to parse x,y,x,y... coordinates');
|
|
28213
|
+
}
|
|
28214
|
+
return coords;
|
|
28215
|
+
}
|
|
28216
|
+
|
|
28217
|
+
function parseCoordsAsGeometry(arg) {
|
|
28218
|
+
if (typeof arg == 'string') {
|
|
28219
|
+
arg = arg.trim();
|
|
28220
|
+
if (!arg.startsWith('[') && !arg.endsWith(']')) {
|
|
28221
|
+
arg = '[' + arg + ']';
|
|
28222
|
+
}
|
|
28223
|
+
}
|
|
28224
|
+
var arr = parseArg(arg);
|
|
28225
|
+
if (isPoint(arr)) {
|
|
28226
|
+
return {
|
|
28227
|
+
type: 'Point',
|
|
28228
|
+
coordinates: arr
|
|
28229
|
+
};
|
|
28230
|
+
}
|
|
28231
|
+
|
|
28232
|
+
if (isArrayOfNumbers(arr)) {
|
|
28233
|
+
arr = transposeCoords(arr);
|
|
28234
|
+
}
|
|
28235
|
+
|
|
28236
|
+
if (isClosedPath$1(arr)) {
|
|
28237
|
+
return {
|
|
28238
|
+
type: 'Polygon',
|
|
28239
|
+
coordinates: [arr]
|
|
28240
|
+
};
|
|
28241
|
+
}
|
|
28242
|
+
|
|
28243
|
+
if (isArrayOfPoints(arr)) {
|
|
28244
|
+
return {
|
|
28245
|
+
type: 'LineString',
|
|
28246
|
+
coordinates: arr
|
|
28247
|
+
};
|
|
28248
|
+
}
|
|
28249
|
+
|
|
28250
|
+
stop('Unable to import coordinates');
|
|
28251
|
+
}
|
|
28252
|
+
|
|
28066
28253
|
const epsilon = 1.1102230246251565e-16;
|
|
28067
28254
|
const splitter = 134217729;
|
|
28068
28255
|
const resulterrbound = (3 + 8 * epsilon) * epsilon;
|
|
@@ -29251,17 +29438,6 @@ ${svg}
|
|
|
29251
29438
|
bufferIntersection: bufferIntersection
|
|
29252
29439
|
});
|
|
29253
29440
|
|
|
29254
|
-
function testSegmentBoundsIntersection(a, b, bb) {
|
|
29255
|
-
if (bb.containsPoint(a[0], a[1])) {
|
|
29256
|
-
return true;
|
|
29257
|
-
}
|
|
29258
|
-
return !!(
|
|
29259
|
-
geom.segmentIntersection(a[0], a[1], b[0], b[1], bb.xmin, bb.ymin, bb.xmin, bb.ymax) ||
|
|
29260
|
-
geom.segmentIntersection(a[0], a[1], b[0], b[1], bb.xmin, bb.ymax, bb.xmax, bb.ymax) ||
|
|
29261
|
-
geom.segmentIntersection(a[0], a[1], b[0], b[1], bb.xmax, bb.ymax, bb.xmax, bb.ymin) ||
|
|
29262
|
-
geom.segmentIntersection(a[0], a[1], b[0], b[1], bb.xmax, bb.ymin, bb.xmin, bb.ymin));
|
|
29263
|
-
}
|
|
29264
|
-
|
|
29265
29441
|
function getPolylineBufferMaker2(arcs, geod, getBearing, opts) {
|
|
29266
29442
|
var makeLeftBuffer = getPathBufferMaker2(arcs, geod, getBearing, opts);
|
|
29267
29443
|
var geomType = opts.geometry_type;
|
|
@@ -37082,7 +37258,7 @@ ${svg}
|
|
|
37082
37258
|
editor.done();
|
|
37083
37259
|
if (!opts.debug) {
|
|
37084
37260
|
buildTopology(dataset);
|
|
37085
|
-
|
|
37261
|
+
cleanProjectedPathLayers(dataset);
|
|
37086
37262
|
}
|
|
37087
37263
|
}
|
|
37088
37264
|
|
|
@@ -37593,15 +37769,41 @@ ${svg}
|
|
|
37593
37769
|
} else {
|
|
37594
37770
|
clipData = getClippingDataset(src, dest, opts);
|
|
37595
37771
|
}
|
|
37772
|
+
// clip data to projection limits (some projections), if content exceeds the limit
|
|
37773
|
+
//
|
|
37596
37774
|
if (clipData) {
|
|
37597
|
-
|
|
37598
|
-
// the clipping shape. But how to tell?
|
|
37599
|
-
clipLayersInPlace(dataset.layers, clipData, dataset, 'clip');
|
|
37600
|
-
clipped = true;
|
|
37775
|
+
clipped = clipLayersIfNeeded(dataset, clipData);
|
|
37601
37776
|
}
|
|
37602
37777
|
return cut || clipped;
|
|
37603
37778
|
}
|
|
37604
37779
|
|
|
37780
|
+
function clipLayersIfNeeded(dataset, clipData) {
|
|
37781
|
+
// Avoid clipping layers that are fully enclosed within the projectable
|
|
37782
|
+
// coordinate space (represented by a dataset containing a single
|
|
37783
|
+
// polygon layer, @clipData). This avoids performing unnecessary intersection
|
|
37784
|
+
// tests on each line segment.
|
|
37785
|
+
var layers = dataset.layers.filter(function(lyr) {
|
|
37786
|
+
return !layerIsFullyEnclosed(lyr, dataset, clipData);
|
|
37787
|
+
});
|
|
37788
|
+
if (layers.length > 0) {
|
|
37789
|
+
clipLayersInPlace(layers, clipData, dataset, 'clip');
|
|
37790
|
+
return true;
|
|
37791
|
+
}
|
|
37792
|
+
return false;
|
|
37793
|
+
}
|
|
37794
|
+
|
|
37795
|
+
// @clipData: a dataset containing a polygon layer
|
|
37796
|
+
function layerIsFullyEnclosed(lyr, dataset, clipData) {
|
|
37797
|
+
// This test uses the layer's bounding box to represent the extent of the
|
|
37798
|
+
// layer, and can produce false negatives.
|
|
37799
|
+
var dataBounds = getLayerBounds(lyr, dataset.arcs);
|
|
37800
|
+
var enclosed = false;
|
|
37801
|
+
clipData.layers[0].shapes.forEach(function(shp, i) {
|
|
37802
|
+
enclosed = enclosed || testBoundsInPolygon(dataBounds, shp, clipData.arcs);
|
|
37803
|
+
});
|
|
37804
|
+
return enclosed;
|
|
37805
|
+
}
|
|
37806
|
+
|
|
37605
37807
|
|
|
37606
37808
|
function insertPreProjectionCuts(dataset, src, dest) {
|
|
37607
37809
|
var antimeridian = getAntimeridian(dest.lam0 * 180 / Math.PI);
|
|
@@ -37796,7 +37998,6 @@ ${svg}
|
|
|
37796
37998
|
var badArcs = 0;
|
|
37797
37999
|
var badPoints = 0;
|
|
37798
38000
|
var clipped = preProjectionClip(dataset, src, dest, opts);
|
|
37799
|
-
|
|
37800
38001
|
dataset.layers.forEach(function(lyr) {
|
|
37801
38002
|
if (layerHasPoints(lyr)) {
|
|
37802
38003
|
badPoints += projectPointLayer(lyr, proj); // v2 compatible (invalid points are removed)
|
|
@@ -37813,7 +38014,7 @@ ${svg}
|
|
|
37813
38014
|
if (clipped) {
|
|
37814
38015
|
// TODO: could more selective in cleaning clipped layers
|
|
37815
38016
|
// (probably only needed when clipped area crosses the antimeridian or includes a pole)
|
|
37816
|
-
|
|
38017
|
+
cleanProjectedPathLayers(dataset);
|
|
37817
38018
|
}
|
|
37818
38019
|
|
|
37819
38020
|
if (badArcs > 0 && !opts.quiet) {
|
|
@@ -37829,7 +38030,7 @@ ${svg}
|
|
|
37829
38030
|
// * Removes line intersections
|
|
37830
38031
|
// * TODO: what if a layer contains polygons with desired overlaps? should
|
|
37831
38032
|
// we ignore overlaps between different features?
|
|
37832
|
-
function
|
|
38033
|
+
function cleanProjectedPathLayers(dataset) {
|
|
37833
38034
|
// TODO: only clean affected polygons (cleaning all polygons can be slow)
|
|
37834
38035
|
var polygonLayers = dataset.layers.filter(lyr => lyr.geometry_type == 'polygon');
|
|
37835
38036
|
// clean options: force a topology update (by default, this only happens when
|
|
@@ -37897,7 +38098,7 @@ ${svg}
|
|
|
37897
38098
|
__proto__: null,
|
|
37898
38099
|
fetchCrsInfo: fetchCrsInfo,
|
|
37899
38100
|
projectDataset: projectDataset,
|
|
37900
|
-
|
|
38101
|
+
cleanProjectedPathLayers: cleanProjectedPathLayers,
|
|
37901
38102
|
projectPointLayer: projectPointLayer,
|
|
37902
38103
|
projectArcs: projectArcs,
|
|
37903
38104
|
projectArcs2: projectArcs2
|
|
@@ -38240,55 +38441,27 @@ ${svg}
|
|
|
38240
38441
|
|
|
38241
38442
|
var MAX_RULE_LEN = 50;
|
|
38242
38443
|
|
|
38243
|
-
cmd.
|
|
38244
|
-
var
|
|
38245
|
-
layers.
|
|
38246
|
-
|
|
38247
|
-
var tableStr = getAttributeTableInfo(o.layer);
|
|
38248
|
-
var tableWidth = measureLongestLine(tableStr);
|
|
38249
|
-
var ruleLen = Math.min(Math.max(title.length, tableWidth), MAX_RULE_LEN);
|
|
38250
|
-
str += '\n';
|
|
38251
|
-
str += utils.lpad('', ruleLen, '=') + '\n';
|
|
38252
|
-
str += title + '\n';
|
|
38253
|
-
str += utils.lpad('', ruleLen, '-') + '\n';
|
|
38254
|
-
str += getLayerInfo(o.layer, o.dataset);
|
|
38255
|
-
str += tableStr;
|
|
38444
|
+
cmd.info = function(targets, opts) {
|
|
38445
|
+
var layers = expandCommandTargets(targets);
|
|
38446
|
+
var arr = layers.map(function(o) {
|
|
38447
|
+
return getLayerInfo(o.layer, o.dataset);
|
|
38256
38448
|
});
|
|
38257
|
-
message(
|
|
38449
|
+
message(formatInfo(arr));
|
|
38450
|
+
if (opts.save_to) {
|
|
38451
|
+
var output = [{
|
|
38452
|
+
filename: opts.save_to + (opts.save_to.endsWith('.json') ? '' : '.json'),
|
|
38453
|
+
content: JSON.stringify(arr, null, 2)
|
|
38454
|
+
}];
|
|
38455
|
+
writeFiles(output, opts);
|
|
38456
|
+
}
|
|
38258
38457
|
};
|
|
38259
38458
|
|
|
38260
|
-
|
|
38261
|
-
return Math.max.apply(null, str.split('\n').map(function(line) {return stringDisplayWidth(line);}));
|
|
38262
|
-
}
|
|
38459
|
+
cmd.printInfo = cmd.info; // old name
|
|
38263
38460
|
|
|
38264
|
-
function
|
|
38265
|
-
var w = 0;
|
|
38266
|
-
for (var i = 0, n=str.length; i < n; i++) {
|
|
38267
|
-
w += charDisplayWidth(str.charCodeAt(i));
|
|
38268
|
-
}
|
|
38269
|
-
return w;
|
|
38270
|
-
}
|
|
38271
|
-
|
|
38272
|
-
// see https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
|
|
38273
|
-
// this is a simplified version, focusing on double-width CJK chars and ignoring nonprinting etc chars
|
|
38274
|
-
function charDisplayWidth(c) {
|
|
38275
|
-
if (c >= 0x1100 &&
|
|
38276
|
-
(c <= 0x115f || c == 0x2329 || c == 0x232a ||
|
|
38277
|
-
(c >= 0x2e80 && c <= 0xa4cf && c != 0x303f) || /* CJK ... Yi */
|
|
38278
|
-
(c >= 0xac00 && c <= 0xd7a3) || /* Hangul Syllables */
|
|
38279
|
-
(c >= 0xf900 && c <= 0xfaff) || /* CJK Compatibility Ideographs */
|
|
38280
|
-
(c >= 0xfe10 && c <= 0xfe19) || /* Vertical forms */
|
|
38281
|
-
(c >= 0xfe30 && c <= 0xfe6f) || /* CJK Compatibility Forms */
|
|
38282
|
-
(c >= 0xff00 && c <= 0xff60) || /* Fullwidth Forms */
|
|
38283
|
-
(c >= 0xffe0 && c <= 0xffe6) ||
|
|
38284
|
-
(c >= 0x20000 && c <= 0x2fffd) ||
|
|
38285
|
-
(c >= 0x30000 && c <= 0x3fffd))) return 2;
|
|
38286
|
-
return 1;
|
|
38287
|
-
}
|
|
38288
|
-
|
|
38289
|
-
function getLayerData(lyr, dataset) {
|
|
38461
|
+
function getLayerInfo(lyr, dataset) {
|
|
38290
38462
|
var n = getFeatureCount(lyr);
|
|
38291
38463
|
var o = {
|
|
38464
|
+
layer_name: lyr.name,
|
|
38292
38465
|
geometry_type: lyr.geometry_type,
|
|
38293
38466
|
feature_count: n,
|
|
38294
38467
|
null_shape_count: 0,
|
|
@@ -38296,31 +38469,47 @@ ${svg}
|
|
|
38296
38469
|
};
|
|
38297
38470
|
if (lyr.shapes && lyr.shapes.length > 0) {
|
|
38298
38471
|
o.null_shape_count = countNullShapes(lyr.shapes);
|
|
38299
|
-
o.bbox =getLayerBounds(lyr, dataset.arcs).toArray();
|
|
38472
|
+
o.bbox = getLayerBounds(lyr, dataset.arcs).toArray();
|
|
38300
38473
|
o.proj4 = getProjInfo(dataset);
|
|
38301
38474
|
}
|
|
38475
|
+
o.source_file = getLayerSourceFile(lyr, dataset) || null;
|
|
38476
|
+
o.attribute_data = getAttributeTableInfo(lyr);
|
|
38302
38477
|
return o;
|
|
38303
38478
|
}
|
|
38304
38479
|
|
|
38305
|
-
//
|
|
38306
|
-
function
|
|
38307
|
-
|
|
38308
|
-
|
|
38309
|
-
if (!shapes[i] || shapes[i].length === 0) count++;
|
|
38480
|
+
// i: (optional) record index
|
|
38481
|
+
function getAttributeTableInfo(lyr, i) {
|
|
38482
|
+
if (!lyr.data || lyr.data.size() === 0 || lyr.data.getFields().length === 0) {
|
|
38483
|
+
return null;
|
|
38310
38484
|
}
|
|
38311
|
-
|
|
38485
|
+
var fields = applyFieldOrder(lyr.data.getFields(), 'ascending');
|
|
38486
|
+
var valueName = i === undefined ? 'first_value' : 'value';
|
|
38487
|
+
return fields.map(function(fname) {
|
|
38488
|
+
return {
|
|
38489
|
+
field: fname,
|
|
38490
|
+
[valueName]: lyr.data.getReadOnlyRecordAt(i || 0)[fname]
|
|
38491
|
+
};
|
|
38492
|
+
});
|
|
38312
38493
|
}
|
|
38313
38494
|
|
|
38314
|
-
function
|
|
38315
|
-
var
|
|
38316
|
-
|
|
38317
|
-
|
|
38318
|
-
|
|
38319
|
-
|
|
38495
|
+
function formatInfo(arr) {
|
|
38496
|
+
var str = '';
|
|
38497
|
+
arr.forEach(function(info, i) {
|
|
38498
|
+
var title = 'Layer: ' + (info.layer_name || '[unnamed layer]');
|
|
38499
|
+
var tableStr = formatAttributeTableInfo(info.attribute_data);
|
|
38500
|
+
var tableWidth = measureLongestLine(tableStr);
|
|
38501
|
+
var ruleLen = Math.min(Math.max(title.length, tableWidth), MAX_RULE_LEN);
|
|
38502
|
+
str += '\n';
|
|
38503
|
+
str += utils.lpad('', ruleLen, '=') + '\n';
|
|
38504
|
+
str += title + '\n';
|
|
38505
|
+
str += utils.lpad('', ruleLen, '-') + '\n';
|
|
38506
|
+
str += formatLayerInfo(info);
|
|
38507
|
+
str += tableStr;
|
|
38508
|
+
});
|
|
38509
|
+
return str;
|
|
38320
38510
|
}
|
|
38321
38511
|
|
|
38322
|
-
function
|
|
38323
|
-
var data = getLayerData(lyr, dataset);
|
|
38512
|
+
function formatLayerInfo(data) {
|
|
38324
38513
|
var str = '';
|
|
38325
38514
|
str += "Type: " + (data.geometry_type || "tabular data") + "\n";
|
|
38326
38515
|
str += utils.format("Records: %,d\n",data.feature_count);
|
|
@@ -38331,21 +38520,19 @@ ${svg}
|
|
|
38331
38520
|
str += "Bounds: " + data.bbox.join(',') + "\n";
|
|
38332
38521
|
str += "CRS: " + data.proj4 + "\n";
|
|
38333
38522
|
}
|
|
38334
|
-
str += "Source: " + (
|
|
38523
|
+
str += "Source: " + (data.source_file || 'n/a') + "\n";
|
|
38335
38524
|
return str;
|
|
38336
38525
|
}
|
|
38337
38526
|
|
|
38338
|
-
function
|
|
38339
|
-
if (!
|
|
38340
|
-
|
|
38341
|
-
|
|
38342
|
-
|
|
38343
|
-
|
|
38344
|
-
|
|
38345
|
-
|
|
38346
|
-
|
|
38347
|
-
var vals = fields.map(function(fname) {
|
|
38348
|
-
return data.getReadOnlyRecordAt(i || 0)[fname];
|
|
38527
|
+
function formatAttributeTableInfo(arr) {
|
|
38528
|
+
if (!arr) return "Attribute data: [none]\n";
|
|
38529
|
+
var header = "\nAttribute data\n";
|
|
38530
|
+
var valKey = 'first_value' in arr[0] ? 'first_value' : 'value';
|
|
38531
|
+
var vals = [];
|
|
38532
|
+
var fields = [];
|
|
38533
|
+
arr.forEach(function(o) {
|
|
38534
|
+
fields.push(o.field);
|
|
38535
|
+
vals.push(o[valKey]);
|
|
38349
38536
|
});
|
|
38350
38537
|
var maxIntegralChars = vals.reduce(function(max, val) {
|
|
38351
38538
|
if (utils.isNumber(val)) {
|
|
@@ -38357,20 +38544,66 @@ ${svg}
|
|
|
38357
38544
|
var col2Arr = vals.reduce(function(memo, val) {
|
|
38358
38545
|
memo.push(formatTableValue(val, maxIntegralChars));
|
|
38359
38546
|
return memo;
|
|
38360
|
-
}, [
|
|
38547
|
+
}, [valKey == 'first_value' ? 'First value' : 'Value']);
|
|
38361
38548
|
var col1Chars = maxChars(col1Arr);
|
|
38362
38549
|
var col2Chars = maxChars(col2Arr);
|
|
38363
38550
|
var sepStr = (utils.rpad('', col1Chars + 2, '-') + '+' +
|
|
38364
38551
|
utils.rpad('', col2Chars + 2, '-')).substr(0, MAX_RULE_LEN);
|
|
38365
38552
|
var sepLine = sepStr + '\n';
|
|
38366
|
-
var table =
|
|
38553
|
+
var table = '';
|
|
38367
38554
|
col1Arr.forEach(function(col1, i) {
|
|
38368
38555
|
var w = stringDisplayWidth(col1);
|
|
38369
38556
|
table += ' ' + col1 + utils.rpad('', col1Chars - w, ' ') + ' | ' +
|
|
38370
38557
|
col2Arr[i] + '\n';
|
|
38371
38558
|
if (i === 0) table += sepLine; // separator after first line
|
|
38372
38559
|
});
|
|
38373
|
-
return table + sepLine;
|
|
38560
|
+
return header + sepLine + table + sepLine;
|
|
38561
|
+
}
|
|
38562
|
+
|
|
38563
|
+
function measureLongestLine(str) {
|
|
38564
|
+
return Math.max.apply(null, str.split('\n').map(function(line) {return stringDisplayWidth(line);}));
|
|
38565
|
+
}
|
|
38566
|
+
|
|
38567
|
+
function stringDisplayWidth(str) {
|
|
38568
|
+
var w = 0;
|
|
38569
|
+
for (var i = 0, n=str.length; i < n; i++) {
|
|
38570
|
+
w += charDisplayWidth(str.charCodeAt(i));
|
|
38571
|
+
}
|
|
38572
|
+
return w;
|
|
38573
|
+
}
|
|
38574
|
+
|
|
38575
|
+
// see https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
|
|
38576
|
+
// this is a simplified version, focusing on double-width CJK chars and ignoring nonprinting etc chars
|
|
38577
|
+
function charDisplayWidth(c) {
|
|
38578
|
+
if (c >= 0x1100 &&
|
|
38579
|
+
(c <= 0x115f || c == 0x2329 || c == 0x232a ||
|
|
38580
|
+
(c >= 0x2e80 && c <= 0xa4cf && c != 0x303f) || /* CJK ... Yi */
|
|
38581
|
+
(c >= 0xac00 && c <= 0xd7a3) || /* Hangul Syllables */
|
|
38582
|
+
(c >= 0xf900 && c <= 0xfaff) || /* CJK Compatibility Ideographs */
|
|
38583
|
+
(c >= 0xfe10 && c <= 0xfe19) || /* Vertical forms */
|
|
38584
|
+
(c >= 0xfe30 && c <= 0xfe6f) || /* CJK Compatibility Forms */
|
|
38585
|
+
(c >= 0xff00 && c <= 0xff60) || /* Fullwidth Forms */
|
|
38586
|
+
(c >= 0xffe0 && c <= 0xffe6) ||
|
|
38587
|
+
(c >= 0x20000 && c <= 0x2fffd) ||
|
|
38588
|
+
(c >= 0x30000 && c <= 0x3fffd))) return 2;
|
|
38589
|
+
return 1;
|
|
38590
|
+
}
|
|
38591
|
+
|
|
38592
|
+
// TODO: consider polygons with zero area or other invalid geometries
|
|
38593
|
+
function countNullShapes(shapes) {
|
|
38594
|
+
var count = 0;
|
|
38595
|
+
for (var i=0; i<shapes.length; i++) {
|
|
38596
|
+
if (!shapes[i] || shapes[i].length === 0) count++;
|
|
38597
|
+
}
|
|
38598
|
+
return count;
|
|
38599
|
+
}
|
|
38600
|
+
|
|
38601
|
+
function countNullRecords(records) {
|
|
38602
|
+
var count = 0;
|
|
38603
|
+
for (var i=0; i<records.length; i++) {
|
|
38604
|
+
if (!records[i]) count++;
|
|
38605
|
+
}
|
|
38606
|
+
return count;
|
|
38374
38607
|
}
|
|
38375
38608
|
|
|
38376
38609
|
function maxChars(arr) {
|
|
@@ -38424,8 +38657,9 @@ ${svg}
|
|
|
38424
38657
|
|
|
38425
38658
|
var Info = /*#__PURE__*/Object.freeze({
|
|
38426
38659
|
__proto__: null,
|
|
38427
|
-
|
|
38660
|
+
getLayerInfo: getLayerInfo,
|
|
38428
38661
|
getAttributeTableInfo: getAttributeTableInfo,
|
|
38662
|
+
formatAttributeTableInfo: formatAttributeTableInfo,
|
|
38429
38663
|
formatTableValue: formatTableValue
|
|
38430
38664
|
});
|
|
38431
38665
|
|
|
@@ -38477,7 +38711,7 @@ ${svg}
|
|
|
38477
38711
|
function getFeatureInfo(id, lyr, arcs) {
|
|
38478
38712
|
var msg = "Feature " + id + '\n';
|
|
38479
38713
|
msg += getShapeInfo(id, lyr, arcs);
|
|
38480
|
-
msg += getAttributeTableInfo(lyr, id);
|
|
38714
|
+
msg += formatAttributeTableInfo(getAttributeTableInfo(lyr, id));
|
|
38481
38715
|
return msg;
|
|
38482
38716
|
}
|
|
38483
38717
|
|
|
@@ -38838,12 +39072,12 @@ ${svg}
|
|
|
38838
39072
|
function polylineToMidpoints(shp, arcs, opts) {
|
|
38839
39073
|
if (!shp) return null;
|
|
38840
39074
|
var points = shp.map(function(path) {
|
|
38841
|
-
return findPathMidpoint(path, arcs);
|
|
39075
|
+
return findPathMidpoint(path, arcs, false);
|
|
38842
39076
|
});
|
|
38843
39077
|
return points;
|
|
38844
39078
|
}
|
|
38845
39079
|
|
|
38846
|
-
function findPathMidpoint(path, arcs) {
|
|
39080
|
+
function findPathMidpoint(path, arcs, useNearestVertex) {
|
|
38847
39081
|
var halfLen = calcPathLen(path, arcs, false) / 2;
|
|
38848
39082
|
var partialLen = 0;
|
|
38849
39083
|
var p;
|
|
@@ -38860,7 +39094,11 @@ ${svg}
|
|
|
38860
39094
|
var k;
|
|
38861
39095
|
if (partialLen + segLen >= halfLen) {
|
|
38862
39096
|
k = (halfLen - partialLen) / segLen;
|
|
38863
|
-
|
|
39097
|
+
if (useNearestVertex) {
|
|
39098
|
+
k = k < 0.5 ? 0 : 1;
|
|
39099
|
+
}
|
|
39100
|
+
// p = [a + k * (c - a), b + k * (d - b)];
|
|
39101
|
+
p = [(1 - k) * a + k * c, (1 - k) * b + k * d];
|
|
38864
39102
|
}
|
|
38865
39103
|
partialLen += segLen;
|
|
38866
39104
|
});
|
|
@@ -40589,7 +40827,7 @@ ${svg}
|
|
|
40589
40827
|
|
|
40590
40828
|
function getRunCommandData(target) {
|
|
40591
40829
|
var lyr = target.layers[0];
|
|
40592
|
-
var data =
|
|
40830
|
+
var data = getLayerInfo(lyr, target.dataset);
|
|
40593
40831
|
data.layer = lyr;
|
|
40594
40832
|
data.dataset = target.dataset;
|
|
40595
40833
|
return data;
|
|
@@ -42740,7 +42978,7 @@ ${svg}
|
|
|
42740
42978
|
name == 'point-grid' || name == 'shape' || name == 'rectangle' ||
|
|
42741
42979
|
name == 'require' || name == 'run' || name == 'define' ||
|
|
42742
42980
|
name == 'include' || name == 'print' || name == 'comment' || name == 'if' || name == 'elif' ||
|
|
42743
|
-
name == 'else' || name == 'endif' || name == 'stop';
|
|
42981
|
+
name == 'else' || name == 'endif' || name == 'stop' || name == 'add-shape';
|
|
42744
42982
|
}
|
|
42745
42983
|
|
|
42746
42984
|
async function runCommand(command, job) {
|
|
@@ -42814,7 +43052,14 @@ ${svg}
|
|
|
42814
43052
|
source = findCommandSource(convertSourceName(opts.source, targets), job.catalog, opts);
|
|
42815
43053
|
}
|
|
42816
43054
|
|
|
42817
|
-
if (name == '
|
|
43055
|
+
if (name == 'add-shape') {
|
|
43056
|
+
if (!targetDataset) {
|
|
43057
|
+
targetDataset = {info: {}, layers: []};
|
|
43058
|
+
targetLayers = targetDataset.layers;
|
|
43059
|
+
job.catalog.addDataset(targetDataset);
|
|
43060
|
+
}
|
|
43061
|
+
outputLayers = cmd.addShape(targetLayers, targetDataset, opts);
|
|
43062
|
+
} else if (name == 'affine') {
|
|
42818
43063
|
cmd.affine(targetLayers, targetDataset, opts);
|
|
42819
43064
|
|
|
42820
43065
|
} else if (name == 'alpha-shapes') {
|
|
@@ -42939,7 +43184,7 @@ ${svg}
|
|
|
42939
43184
|
cmd.include(opts);
|
|
42940
43185
|
|
|
42941
43186
|
} else if (name == 'info') {
|
|
42942
|
-
cmd.
|
|
43187
|
+
cmd.info(targets, opts);
|
|
42943
43188
|
|
|
42944
43189
|
} else if (name == 'inlay') {
|
|
42945
43190
|
outputLayers = cmd.inlay(targetLayers, source, targetDataset, opts);
|
|
@@ -43305,13 +43550,13 @@ ${svg}
|
|
|
43305
43550
|
commands = runAndRemoveInfoCommands(commands);
|
|
43306
43551
|
if (commands.length === 0) return done(null);
|
|
43307
43552
|
|
|
43308
|
-
// add options to -i -o -join -clip -erase commands to bypass file i/o
|
|
43553
|
+
// add options to -i -o -join -clip -erase etc. commands to bypass file i/o
|
|
43309
43554
|
// TODO: find a less kludgy solution
|
|
43310
43555
|
commands.forEach(function(cmd) {
|
|
43311
43556
|
if (commandTakesFileInput(cmd.name) && inputObj) {
|
|
43312
43557
|
cmd.options.input = inputObj;
|
|
43313
43558
|
}
|
|
43314
|
-
if (cmd.name == 'o' &&
|
|
43559
|
+
if (outputArr && (cmd.name == 'o' || cmd.name == 'info' && cmd.options.save_to)) {
|
|
43315
43560
|
cmd.options.output = outputArr;
|
|
43316
43561
|
}
|
|
43317
43562
|
});
|