mapshaper 0.6.99 → 0.6.101
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 +754 -572
- package/package.json +1 -1
- package/www/mapshaper-gui.js +249 -151
- package/www/mapshaper.js +754 -572
- package/www/page.css +4 -3
package/mapshaper.js
CHANGED
|
@@ -5165,12 +5165,12 @@
|
|
|
5165
5165
|
return getDatasetCrsInfo(dataset).crs;
|
|
5166
5166
|
}
|
|
5167
5167
|
|
|
5168
|
-
function requireDatasetsHaveCompatibleCRS(arr) {
|
|
5168
|
+
function requireDatasetsHaveCompatibleCRS(arr, msg) {
|
|
5169
5169
|
arr.reduce(function(memo, dataset) {
|
|
5170
5170
|
var P = getDatasetCRS(dataset);
|
|
5171
5171
|
if (memo && P) {
|
|
5172
5172
|
if (isLatLngCRS(memo) != isLatLngCRS(P)) {
|
|
5173
|
-
stop("Unable to combine projected and unprojected datasets");
|
|
5173
|
+
stop(msg || "Unable to combine projected and unprojected datasets");
|
|
5174
5174
|
}
|
|
5175
5175
|
}
|
|
5176
5176
|
return P || memo;
|
|
@@ -13640,7 +13640,7 @@
|
|
|
13640
13640
|
var px = units == 'in' && num * 72 ||
|
|
13641
13641
|
units == 'cm' && Math.round(num * 28.3465) ||
|
|
13642
13642
|
num;
|
|
13643
|
-
if (px
|
|
13643
|
+
if (px >= 0 === false || !units) {
|
|
13644
13644
|
stop('Invalid size:', str);
|
|
13645
13645
|
}
|
|
13646
13646
|
return px;
|
|
@@ -14715,6 +14715,7 @@
|
|
|
14715
14715
|
}
|
|
14716
14716
|
|
|
14717
14717
|
|
|
14718
|
+
|
|
14718
14719
|
function findCommandTargets(layers, pattern, type) {
|
|
14719
14720
|
var matches = findMatchingLayers(layers, pattern, true);
|
|
14720
14721
|
if (type) {
|
|
@@ -18154,6 +18155,9 @@
|
|
|
18154
18155
|
if (ids) {
|
|
18155
18156
|
obj.id = ids[i];
|
|
18156
18157
|
}
|
|
18158
|
+
if (opts.no_null_props && !obj.properties) {
|
|
18159
|
+
obj.properties = {};
|
|
18160
|
+
}
|
|
18157
18161
|
} else if (!geom) {
|
|
18158
18162
|
return memo; // don't add null objects to GeometryCollection
|
|
18159
18163
|
} else {
|
|
@@ -18499,7 +18503,8 @@
|
|
|
18499
18503
|
var bounds = getLayerBounds(lyr, arcs);
|
|
18500
18504
|
var d = lyr.data.getReadOnlyRecordAt(0);
|
|
18501
18505
|
var w = d.width || 800;
|
|
18502
|
-
|
|
18506
|
+
// prevent rounding errors (like 1000.0000000002)
|
|
18507
|
+
var h = Math.round(w * bounds.height() / bounds.width());
|
|
18503
18508
|
return {
|
|
18504
18509
|
type: 'frame',
|
|
18505
18510
|
width: w,
|
|
@@ -18511,17 +18516,19 @@
|
|
|
18511
18516
|
|
|
18512
18517
|
function calcFrameData(dataset, opts) {
|
|
18513
18518
|
var bounds;
|
|
18514
|
-
|
|
18515
|
-
|
|
18519
|
+
var outputBbox = opts.svg_bbox || opts.output_bbox || null;
|
|
18520
|
+
if (outputBbox) {
|
|
18521
|
+
bounds = new Bounds(outputBbox);
|
|
18516
18522
|
opts = Object.assign({margin: 0}, opts); // prevent default pixel margin around content
|
|
18517
18523
|
} else {
|
|
18518
18524
|
bounds = getDatasetBounds(dataset);
|
|
18519
18525
|
}
|
|
18520
|
-
var
|
|
18526
|
+
var outputBounds = calcOutputSizeInPixels(bounds, opts);
|
|
18521
18527
|
return {
|
|
18522
18528
|
bbox: bounds.toArray(),
|
|
18523
|
-
|
|
18524
|
-
|
|
18529
|
+
bbox2: outputBounds.toArray(),
|
|
18530
|
+
width: Math.round(outputBounds.width()),
|
|
18531
|
+
height: Math.round(outputBounds.height()) || 1,
|
|
18525
18532
|
type: 'frame'
|
|
18526
18533
|
};
|
|
18527
18534
|
}
|
|
@@ -18703,6 +18710,7 @@
|
|
|
18703
18710
|
__proto__: null,
|
|
18704
18711
|
getFrameData: getFrameData,
|
|
18705
18712
|
getFrameLayerData: getFrameLayerData,
|
|
18713
|
+
calcFrameData: calcFrameData,
|
|
18706
18714
|
getFrameSize: getFrameSize,
|
|
18707
18715
|
isFrameLayer: isFrameLayer,
|
|
18708
18716
|
findFrameLayerInDataset: findFrameLayerInDataset,
|
|
@@ -18766,6 +18774,9 @@
|
|
|
18766
18774
|
geometry_type: 'point', shapes: targetPoints}, {geometry_type: 'polyline',
|
|
18767
18775
|
shapes: targetShapes}]}, opts);
|
|
18768
18776
|
transform = getAffineTransform(rotateArg, scaleArg, shiftArg, anchorArg);
|
|
18777
|
+
if (opts.fit_bbox) {
|
|
18778
|
+
transform = getFitBoxTransform(opts.fit_bbox, targetPoints, targetShapes, arcs);
|
|
18779
|
+
}
|
|
18769
18780
|
if (targetShapes.length > 0) {
|
|
18770
18781
|
targetFlags = new Uint8Array(arcs.size());
|
|
18771
18782
|
otherFlags = new Uint8Array(arcs.size());
|
|
@@ -18815,6 +18826,33 @@
|
|
|
18815
18826
|
};
|
|
18816
18827
|
}
|
|
18817
18828
|
|
|
18829
|
+
function getFitBoxTransform(bbox, points, shapes, arcs) {
|
|
18830
|
+
var dataset = {
|
|
18831
|
+
arcs: arcs,
|
|
18832
|
+
info: {},
|
|
18833
|
+
layers: []
|
|
18834
|
+
};
|
|
18835
|
+
if (points && points.length) {
|
|
18836
|
+
dataset.layers.push({
|
|
18837
|
+
geometry_type: 'point',
|
|
18838
|
+
shapes: points
|
|
18839
|
+
});
|
|
18840
|
+
}
|
|
18841
|
+
if (shapes && shapes.length) {
|
|
18842
|
+
dataset.layers.push({
|
|
18843
|
+
geometry_type: 'polyline',
|
|
18844
|
+
shapes: shapes
|
|
18845
|
+
});
|
|
18846
|
+
}
|
|
18847
|
+
var frame = calcFrameData(dataset, {fit_bbox: bbox});
|
|
18848
|
+
var fromBounds = new Bounds(frame.bbox);
|
|
18849
|
+
var toBounds = new Bounds(frame.bbox2);
|
|
18850
|
+
var fwd = fromBounds.getTransform(toBounds, false);
|
|
18851
|
+
return function(x, y) {
|
|
18852
|
+
return fwd.transform(x, y);
|
|
18853
|
+
};
|
|
18854
|
+
}
|
|
18855
|
+
|
|
18818
18856
|
function applyArrayMask(destArr, maskArr) {
|
|
18819
18857
|
for (var i=0, n=destArr.length; i<n; i++) {
|
|
18820
18858
|
if (maskArr[i] === 0) destArr[i] = 0;
|
|
@@ -18934,7 +18972,7 @@
|
|
|
18934
18972
|
parts.shift();
|
|
18935
18973
|
var colors = [];
|
|
18936
18974
|
var background = parts.pop();
|
|
18937
|
-
var spacing =
|
|
18975
|
+
var spacing = parseNum(parts.pop());
|
|
18938
18976
|
var tmp;
|
|
18939
18977
|
while (parts.length > 0) {
|
|
18940
18978
|
tmp = parts.pop();
|
|
@@ -18945,11 +18983,11 @@
|
|
|
18945
18983
|
colors.push(tmp);
|
|
18946
18984
|
}
|
|
18947
18985
|
}
|
|
18948
|
-
var width =
|
|
18949
|
-
var dashes = [
|
|
18986
|
+
var width = parseNum(parts.pop());
|
|
18987
|
+
var dashes = [parseNum(parts.pop()), parseNum(parts.pop())].reverse();
|
|
18950
18988
|
var rotation = 45;
|
|
18951
18989
|
if (parts.length > 0) {
|
|
18952
|
-
rotation =
|
|
18990
|
+
rotation = parseNum(parts.pop());
|
|
18953
18991
|
}
|
|
18954
18992
|
if (parts.length > 0) {
|
|
18955
18993
|
return null;
|
|
@@ -18974,10 +19012,10 @@
|
|
|
18974
19012
|
// 1px red 1px white 1px black
|
|
18975
19013
|
// -45deg 3 #eee 3 rgb(0,0,0)
|
|
18976
19014
|
parts.shift();
|
|
18977
|
-
var rot = parts.length % 2 == 1 ?
|
|
19015
|
+
var rot = parts.length % 2 == 1 ? parseNum(parts.shift()) : 45, // default is 45
|
|
18978
19016
|
colors = [], widths = [];
|
|
18979
19017
|
for (var i=0; i<parts.length; i+=2) {
|
|
18980
|
-
widths.push(
|
|
19018
|
+
widths.push(parseNum(parts[i]));
|
|
18981
19019
|
colors.push(parts[i+1]);
|
|
18982
19020
|
}
|
|
18983
19021
|
if (Math.min.apply(null, widths) > 0 === false) return null;
|
|
@@ -18991,7 +19029,7 @@
|
|
|
18991
19029
|
}
|
|
18992
19030
|
|
|
18993
19031
|
function isSize(str) {
|
|
18994
|
-
return
|
|
19032
|
+
return parseNum(str) > 0;
|
|
18995
19033
|
}
|
|
18996
19034
|
|
|
18997
19035
|
function parseDots(parts) {
|
|
@@ -19004,11 +19042,11 @@
|
|
|
19004
19042
|
var type = parts.shift();
|
|
19005
19043
|
var rot = 0;
|
|
19006
19044
|
if (isSize(parts[1])) { // if rotation is present, there are two numbers
|
|
19007
|
-
rot =
|
|
19045
|
+
rot = parseNum(parts.shift());
|
|
19008
19046
|
}
|
|
19009
|
-
var size =
|
|
19047
|
+
var size = parseNum(parts.shift());
|
|
19010
19048
|
var bg = parts.pop();
|
|
19011
|
-
var spacing =
|
|
19049
|
+
var spacing = parseNum(parts.pop());
|
|
19012
19050
|
while (parts.length > 0) {
|
|
19013
19051
|
colors.push(parts.shift());
|
|
19014
19052
|
}
|
|
@@ -19026,6 +19064,12 @@
|
|
|
19026
19064
|
};
|
|
19027
19065
|
}
|
|
19028
19066
|
|
|
19067
|
+
function parseNum(str) {
|
|
19068
|
+
// return parseNum(str);
|
|
19069
|
+
// support sub-pixel sizes
|
|
19070
|
+
return parseFloat(str) || 0;
|
|
19071
|
+
}
|
|
19072
|
+
|
|
19029
19073
|
function splitPattern(str) {
|
|
19030
19074
|
// split apart space and comma-delimited tokens
|
|
19031
19075
|
// ... but don't split rgb(...) colors
|
|
@@ -20116,7 +20160,7 @@
|
|
|
20116
20160
|
|
|
20117
20161
|
function fitDatasetToFrame(dataset, frame, opts) {
|
|
20118
20162
|
var bounds = new Bounds(frame.bbox);
|
|
20119
|
-
var bounds2 = new Bounds(0, 0, frame.width, frame.height);
|
|
20163
|
+
var bounds2 = frame.bbox2 ? new Bounds(frame.bbox2) : new Bounds(0, 0, frame.width, frame.height);
|
|
20120
20164
|
var fwd = bounds.getTransform(bounds2, opts.invert_y);
|
|
20121
20165
|
transformPoints(dataset, function(x, y) {
|
|
20122
20166
|
return fwd.transform(x, y);
|
|
@@ -24502,6 +24546,10 @@ ${svg}
|
|
|
24502
24546
|
.option('geojson-type', {
|
|
24503
24547
|
describe: '[GeoJSON] FeatureCollection, GeometryCollection or Feature'
|
|
24504
24548
|
})
|
|
24549
|
+
.option('no-null-props', {
|
|
24550
|
+
describe: '[GeoJSON] use "properties":{} when a Feature has no data',
|
|
24551
|
+
type: 'flag'
|
|
24552
|
+
})
|
|
24505
24553
|
.option('hoist', {
|
|
24506
24554
|
describe: '[GeoJSON] move properties to the root level of each Feature',
|
|
24507
24555
|
type: 'strings'
|
|
@@ -24593,6 +24641,10 @@ ${svg}
|
|
|
24593
24641
|
type: 'numbers',
|
|
24594
24642
|
describe: 'center of rotation/scaling (default is center of selected shapes)'
|
|
24595
24643
|
})
|
|
24644
|
+
.option('fit-bbox', {
|
|
24645
|
+
type: 'bbox',
|
|
24646
|
+
describe: 'scale and shift coordinates to fit a bbox'
|
|
24647
|
+
})
|
|
24596
24648
|
.option('where', whereOpt)
|
|
24597
24649
|
.option('target', targetOpt);
|
|
24598
24650
|
|
|
@@ -26064,15 +26116,32 @@ ${svg}
|
|
|
26064
26116
|
});
|
|
26065
26117
|
|
|
26066
26118
|
parser.command('frame')
|
|
26067
|
-
|
|
26119
|
+
.describe('create a rectangular map frame layer at a given display width')
|
|
26120
|
+
.option('width', {
|
|
26121
|
+
describe: 'width of frame (e.g. 5in, 10cm, 600px; default is 800px)'
|
|
26122
|
+
})
|
|
26123
|
+
.option('height', {
|
|
26124
|
+
describe: '(optional) height of frame; similar to width= option'
|
|
26125
|
+
})
|
|
26126
|
+
.option('aspect-ratio', {
|
|
26127
|
+
describe: '(optional) aspect ratio of frame, if height= or width= is omitted',
|
|
26128
|
+
type: 'number'
|
|
26129
|
+
})
|
|
26068
26130
|
.option('bbox', {
|
|
26069
26131
|
describe: 'frame coordinates (xmin,ymin,xmax,ymax)',
|
|
26070
26132
|
type: 'bbox'
|
|
26071
26133
|
})
|
|
26072
|
-
|
|
26073
|
-
|
|
26074
|
-
|
|
26134
|
+
.option('offset', {
|
|
26135
|
+
describe: 'padding in display units or pct of width, e.g. 5cm 20px 5%',
|
|
26136
|
+
type: 'strings'
|
|
26075
26137
|
})
|
|
26138
|
+
.option('offsets', {
|
|
26139
|
+
describe: 'separate offsets for each side, in l,b,r,t order',
|
|
26140
|
+
type: 'strings'
|
|
26141
|
+
})
|
|
26142
|
+
.option('name', nameOpt)
|
|
26143
|
+
.option('target', targetOpt);
|
|
26144
|
+
|
|
26076
26145
|
// .option('height', {
|
|
26077
26146
|
// describe: 'pixel height of output (may be a range)'
|
|
26078
26147
|
// })
|
|
@@ -26083,7 +26152,6 @@ ${svg}
|
|
|
26083
26152
|
// .option('source', {
|
|
26084
26153
|
// describe: 'name of layer to enclose'
|
|
26085
26154
|
// })
|
|
26086
|
-
.option('name', nameOpt);
|
|
26087
26155
|
|
|
26088
26156
|
parser.command('fuzzy-join')
|
|
26089
26157
|
.describe('join points to polygons, with data fill and fuzzy match')
|
|
@@ -35780,7 +35848,10 @@ ${svg}
|
|
|
35780
35848
|
getColorizerFunction: getColorizerFunction
|
|
35781
35849
|
});
|
|
35782
35850
|
|
|
35783
|
-
cmd.comment = function() {
|
|
35851
|
+
cmd.comment = function(opts) {
|
|
35852
|
+
// TODO: print the comment in verbose mode
|
|
35853
|
+
// message('[comment]', opts.message);
|
|
35854
|
+
}; // no-op, so -comment doesn't trigger a parsing error
|
|
35784
35855
|
|
|
35785
35856
|
cmd.dashlines = function(lyr, dataset, opts) {
|
|
35786
35857
|
var crs = getDatasetCRS(dataset);
|
|
@@ -38158,46 +38229,637 @@ ${svg}
|
|
|
38158
38229
|
return parsed[0];
|
|
38159
38230
|
}
|
|
38160
38231
|
|
|
38161
|
-
|
|
38162
|
-
|
|
38163
|
-
|
|
38164
|
-
|
|
38165
|
-
|
|
38166
|
-
|
|
38167
|
-
|
|
38168
|
-
|
|
38169
|
-
|
|
38170
|
-
|
|
38171
|
-
|
|
38172
|
-
|
|
38173
|
-
|
|
38174
|
-
|
|
38175
|
-
|
|
38176
|
-
|
|
38177
|
-
|
|
38178
|
-
|
|
38179
|
-
|
|
38180
|
-
|
|
38181
|
-
}
|
|
38182
|
-
|
|
38183
|
-
|
|
38184
|
-
|
|
38185
|
-
|
|
38186
|
-
|
|
38187
|
-
|
|
38188
|
-
|
|
38189
|
-
|
|
38190
|
-
|
|
38191
|
-
|
|
38192
|
-
|
|
38193
|
-
|
|
38194
|
-
|
|
38195
|
-
|
|
38196
|
-
|
|
38197
|
-
|
|
38198
|
-
|
|
38232
|
+
// Returns number of arcs that were removed
|
|
38233
|
+
function editArcs(arcs, onPoint) {
|
|
38234
|
+
var nn2 = [],
|
|
38235
|
+
xx2 = [],
|
|
38236
|
+
yy2 = [],
|
|
38237
|
+
errors = 0,
|
|
38238
|
+
n;
|
|
38239
|
+
|
|
38240
|
+
arcs.forEach(function(arc, i) {
|
|
38241
|
+
editArc(arc, onPoint);
|
|
38242
|
+
});
|
|
38243
|
+
arcs.updateVertexData(nn2, xx2, yy2);
|
|
38244
|
+
return errors;
|
|
38245
|
+
|
|
38246
|
+
function append(p) {
|
|
38247
|
+
if (p) {
|
|
38248
|
+
xx2.push(p[0]);
|
|
38249
|
+
yy2.push(p[1]);
|
|
38250
|
+
n++;
|
|
38251
|
+
}
|
|
38252
|
+
}
|
|
38253
|
+
|
|
38254
|
+
function editArc(arc, cb) {
|
|
38255
|
+
var x, y, xp, yp, retn;
|
|
38256
|
+
var valid = true;
|
|
38257
|
+
var i = 0;
|
|
38258
|
+
n = 0;
|
|
38259
|
+
while (arc.hasNext()) {
|
|
38260
|
+
x = arc.x;
|
|
38261
|
+
y = arc.y;
|
|
38262
|
+
retn = cb(append, x, y, xp, yp, i++);
|
|
38263
|
+
if (retn === false) {
|
|
38264
|
+
valid = false;
|
|
38265
|
+
// assumes that it's ok for the arc iterator to be interrupted.
|
|
38266
|
+
break;
|
|
38267
|
+
}
|
|
38268
|
+
xp = x;
|
|
38269
|
+
yp = y;
|
|
38270
|
+
}
|
|
38271
|
+
if (valid && n == 1) {
|
|
38272
|
+
// only one valid point was added to this arc (invalid)
|
|
38273
|
+
// e.g. this could happen during reprojection.
|
|
38274
|
+
// making this arc empty
|
|
38275
|
+
// error("An invalid arc was created");
|
|
38276
|
+
message("An invalid arc was created");
|
|
38277
|
+
valid = false;
|
|
38278
|
+
}
|
|
38279
|
+
if (valid) {
|
|
38280
|
+
nn2.push(n);
|
|
38281
|
+
} else {
|
|
38282
|
+
// remove any points that were added for an invalid arc
|
|
38283
|
+
while (n-- > 0) {
|
|
38284
|
+
xx2.pop();
|
|
38285
|
+
yy2.pop();
|
|
38286
|
+
}
|
|
38287
|
+
nn2.push(0); // add empty arc (to preserve mapping from paths to arcs)
|
|
38288
|
+
errors++;
|
|
38289
|
+
}
|
|
38290
|
+
}
|
|
38291
|
+
}
|
|
38292
|
+
|
|
38293
|
+
function DatasetEditor(dataset) {
|
|
38294
|
+
var layers = [];
|
|
38295
|
+
var arcs = [];
|
|
38296
|
+
|
|
38297
|
+
this.done = function() {
|
|
38298
|
+
dataset.layers = layers;
|
|
38299
|
+
if (arcs.length) {
|
|
38300
|
+
dataset.arcs = new ArcCollection(arcs);
|
|
38301
|
+
buildTopology(dataset);
|
|
38302
|
+
}
|
|
38303
|
+
};
|
|
38304
|
+
|
|
38305
|
+
this.editLayer = function(lyr, cb) {
|
|
38306
|
+
var type = lyr.geometry_type;
|
|
38307
|
+
if (dataset.layers.indexOf(lyr) != layers.length) {
|
|
38308
|
+
error('Layer was edited out-of-order');
|
|
38309
|
+
}
|
|
38310
|
+
if (!type) {
|
|
38311
|
+
layers.push(lyr);
|
|
38312
|
+
return;
|
|
38313
|
+
}
|
|
38314
|
+
var shapes = lyr.shapes.map(function(shape, shpId) {
|
|
38315
|
+
var shape2 = [], retn, input;
|
|
38316
|
+
for (var i=0, n=shape ? shape.length : 0; i<n; i++) {
|
|
38317
|
+
input = type == 'point' ? shape[i] : idsToCoords(shape[i]);
|
|
38318
|
+
retn = cb(input, i, shape);
|
|
38319
|
+
if (!Array.isArray(retn)) continue;
|
|
38320
|
+
if (type == 'point') {
|
|
38321
|
+
shape2.push(retn);
|
|
38322
|
+
} else if (type == 'polygon' || type == 'polyline') {
|
|
38323
|
+
extendPathShape(shape2, retn || []);
|
|
38324
|
+
}
|
|
38325
|
+
}
|
|
38326
|
+
return shape2.length > 0 ? shape2 : null;
|
|
38327
|
+
});
|
|
38328
|
+
layers.push(Object.assign(lyr, {shapes: shapes}));
|
|
38329
|
+
};
|
|
38330
|
+
|
|
38331
|
+
function extendPathShape(shape, parts) {
|
|
38332
|
+
for (var i=0; i<parts.length; i++) {
|
|
38333
|
+
shape.push([arcs.length]);
|
|
38334
|
+
arcs.push(parts[i]);
|
|
38335
|
+
}
|
|
38336
|
+
}
|
|
38337
|
+
|
|
38338
|
+
function idsToCoords(ids) {
|
|
38339
|
+
var coords = [];
|
|
38340
|
+
var iter = dataset.arcs.getShapeIter(ids);
|
|
38341
|
+
while (iter.hasNext()) {
|
|
38342
|
+
coords.push([iter.x, iter.y]);
|
|
38343
|
+
}
|
|
38344
|
+
return coords;
|
|
38345
|
+
}
|
|
38346
|
+
}
|
|
38347
|
+
|
|
38348
|
+
// Planar densification by an interval
|
|
38349
|
+
function densifyPathByInterval(coords, interval, interpolate) {
|
|
38350
|
+
if (findMaxPathInterval(coords) < interval) return coords;
|
|
38351
|
+
if (!interpolate) {
|
|
38352
|
+
interpolate = getIntervalInterpolator(interval);
|
|
38353
|
+
}
|
|
38354
|
+
var coords2 = [coords[0]], a, b;
|
|
38355
|
+
for (var i=1, n=coords.length; i<n; i++) {
|
|
38356
|
+
a = coords[i-1];
|
|
38357
|
+
b = coords[i];
|
|
38358
|
+
if (geom.distance2D(a[0], a[1], b[0], b[1]) > interval + 1e-4) {
|
|
38359
|
+
appendArr(coords2, interpolate(a, b));
|
|
38360
|
+
}
|
|
38361
|
+
coords2.push(b);
|
|
38362
|
+
}
|
|
38363
|
+
return coords2;
|
|
38364
|
+
}
|
|
38365
|
+
|
|
38366
|
+
function getIntervalInterpolator(interval) {
|
|
38367
|
+
return function(a, b) {
|
|
38368
|
+
var points = [];
|
|
38369
|
+
// var rev = a[0] == b[0] ? a[1] > b[1] : a[0] > b[0];
|
|
38370
|
+
var dist = geom.distance2D(a[0], a[1], b[0], b[1]);
|
|
38371
|
+
var n = Math.round(dist / interval) - 1;
|
|
38372
|
+
var dx = (b[0] - a[0]) / (n + 1),
|
|
38373
|
+
dy = (b[1] - a[1]) / (n + 1);
|
|
38374
|
+
for (var i=1; i<=n; i++) {
|
|
38375
|
+
points.push([a[0] + dx * i, a[1] + dy * i]);
|
|
38376
|
+
}
|
|
38377
|
+
return points;
|
|
38378
|
+
};
|
|
38379
|
+
}
|
|
38380
|
+
|
|
38381
|
+
|
|
38382
|
+
// Interpolate the same points regardless of segment direction
|
|
38383
|
+
function densifyAntimeridianSegment(a, b, interval) {
|
|
38384
|
+
var y1, y2;
|
|
38385
|
+
var coords = [];
|
|
38386
|
+
var ascending = a[1] < b[1];
|
|
38387
|
+
if (a[0] != b[0]) error('Expected an edge segment');
|
|
38388
|
+
if (interval > 0 === false) error('Expected a positive interval');
|
|
38389
|
+
if (ascending) {
|
|
38390
|
+
y1 = a[1];
|
|
38391
|
+
y2 = b[1];
|
|
38392
|
+
} else {
|
|
38393
|
+
y1 = b[1];
|
|
38394
|
+
y2 = a[1];
|
|
38395
|
+
}
|
|
38396
|
+
var y = Math.floor(y1 / interval) * interval + interval;
|
|
38397
|
+
while (y < y2) {
|
|
38398
|
+
coords.push([a[0], y]);
|
|
38399
|
+
y += interval;
|
|
38400
|
+
}
|
|
38401
|
+
if (!ascending) coords.reverse();
|
|
38402
|
+
return coords;
|
|
38403
|
+
}
|
|
38404
|
+
|
|
38405
|
+
function appendArr(dest, src) {
|
|
38406
|
+
for (var i=0; i<src.length; i++) dest.push(src[i]);
|
|
38407
|
+
}
|
|
38408
|
+
|
|
38409
|
+
function findMaxPathInterval(coords) {
|
|
38410
|
+
var maxSq = 0, intSq, a, b;
|
|
38411
|
+
for (var i=1, n=coords.length; i<n; i++) {
|
|
38412
|
+
a = coords[i-1];
|
|
38413
|
+
b = coords[i];
|
|
38414
|
+
intSq = geom.distanceSq(a[0], a[1], b[0], b[1]);
|
|
38415
|
+
if (intSq > maxSq) maxSq = intSq;
|
|
38416
|
+
}
|
|
38417
|
+
return Math.sqrt(maxSq);
|
|
38418
|
+
}
|
|
38419
|
+
|
|
38420
|
+
function projectAndDensifyArcs(arcs, proj) {
|
|
38421
|
+
var interval = getDefaultDensifyInterval(arcs, proj);
|
|
38422
|
+
var minIntervalSq = interval * interval * 25;
|
|
38423
|
+
var p;
|
|
38424
|
+
return editArcs(arcs, onPoint);
|
|
38425
|
+
|
|
38426
|
+
function onPoint(append, lng, lat, prevLng, prevLat, i) {
|
|
38427
|
+
var pp = p;
|
|
38428
|
+
p = proj(lng, lat);
|
|
38429
|
+
if (!p) return false; // signal that current arc contains an error
|
|
38430
|
+
|
|
38431
|
+
// Don't try to densify shorter segments (optimization)
|
|
38432
|
+
if (i > 0 && geom.distanceSq(p[0], p[1], pp[0], pp[1]) > minIntervalSq) {
|
|
38433
|
+
densifySegment(prevLng, prevLat, pp[0], pp[1], lng, lat, p[0], p[1], proj, interval)
|
|
38434
|
+
.forEach(append);
|
|
38435
|
+
}
|
|
38436
|
+
append(p);
|
|
38437
|
+
}
|
|
38438
|
+
}
|
|
38439
|
+
|
|
38440
|
+
// Use the median of intervals computed by projecting segments.
|
|
38441
|
+
// We're probing a number of points, because @proj might only be valid in
|
|
38442
|
+
// a sub-region of the dataset bbox (e.g. +proj=tpers)
|
|
38443
|
+
function findDensifyInterval(bounds, xy, proj) {
|
|
38444
|
+
var steps = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9];
|
|
38445
|
+
var points = [];
|
|
38446
|
+
for (var i=0; i<steps.length; i++) {
|
|
38447
|
+
for (var j=0; j<steps.length; j++) {
|
|
38448
|
+
points.push([steps[i], steps[j]]);
|
|
38449
|
+
}
|
|
38450
|
+
}
|
|
38451
|
+
var intervals = points.map(function(pos) {
|
|
38452
|
+
var x = bounds.xmin + bounds.width() * pos[0];
|
|
38453
|
+
var y = bounds.ymin + bounds.height() * pos[1];
|
|
38454
|
+
var a = proj(x, y);
|
|
38455
|
+
var b = proj(x + xy[0], y + xy[1]);
|
|
38456
|
+
return a && b ? geom.distance2D(a[0], a[1], b[0], b[1]) : Infinity;
|
|
38457
|
+
}).filter(function(int) {return int < Infinity;});
|
|
38458
|
+
return intervals.length > 0 ? utils.findMedian(intervals) : Infinity;
|
|
38459
|
+
}
|
|
38460
|
+
|
|
38461
|
+
// Kludgy way to get a useful interval for densifying a bounding box.
|
|
38462
|
+
// Uses a fraction of average bbox side length)
|
|
38463
|
+
// TODO: improve
|
|
38464
|
+
function findDensifyInterval2(bb, proj) {
|
|
38465
|
+
var a = proj(bb.centerX(), bb.centerY()),
|
|
38466
|
+
c = proj(bb.centerX(), bb.ymin), // right center
|
|
38467
|
+
d = proj(bb.xmax, bb.centerY()); // bottom center
|
|
38468
|
+
var interval = a && c && d ? (geom.distance2D(a[0], a[1], c[0], c[1]) +
|
|
38469
|
+
geom.distance2D(a[0], a[1], d[0], d[1])) / 5000 : Infinity;
|
|
38470
|
+
return interval;
|
|
38471
|
+
}
|
|
38472
|
+
|
|
38473
|
+
// Returns an interval in projected units
|
|
38474
|
+
function getDefaultDensifyInterval(arcs, proj) {
|
|
38475
|
+
var xy = getAvgSegment2(arcs),
|
|
38476
|
+
bb = arcs.getBounds(),
|
|
38477
|
+
intervalA = findDensifyInterval(bb, xy, proj),
|
|
38478
|
+
intervalB = findDensifyInterval2(bb, proj),
|
|
38479
|
+
interval = Math.min(intervalA, intervalB);
|
|
38480
|
+
if (interval == Infinity) {
|
|
38481
|
+
error('Densification error');
|
|
38482
|
+
}
|
|
38483
|
+
return interval;
|
|
38484
|
+
}
|
|
38485
|
+
|
|
38486
|
+
// Interpolate points into a projected line segment if needed to prevent large
|
|
38487
|
+
// deviations from path of original unprojected segment.
|
|
38488
|
+
// @points (optional) array of accumulated points
|
|
38489
|
+
function densifySegment(lng0, lat0, x0, y0, lng2, lat2, x2, y2, proj, interval, points) {
|
|
38490
|
+
// Find midpoint between two endpoints and project it (assumes longitude does
|
|
38491
|
+
// not wrap). TODO Consider bisecting along great circle path -- although this
|
|
38492
|
+
// would not be good for boundaries that follow line of constant latitude.
|
|
38493
|
+
var lng1 = (lng0 + lng2) / 2,
|
|
38494
|
+
lat1 = (lat0 + lat2) / 2,
|
|
38495
|
+
p = proj(lng1, lat1),
|
|
38496
|
+
distSq;
|
|
38497
|
+
if (!p) return; // TODO: consider if this is adequate for handling proj. errors
|
|
38498
|
+
distSq = geom.pointSegDistSq2(p[0], p[1], x0, y0, x2, y2); // sq displacement
|
|
38499
|
+
points = points || [];
|
|
38500
|
+
// Bisect current segment if the projected midpoint deviates from original
|
|
38501
|
+
// segment by more than the @interval parameter.
|
|
38502
|
+
// ... but don't bisect very small segments to prevent infinite recursion
|
|
38503
|
+
// (e.g. if projection function is discontinuous)
|
|
38504
|
+
if (distSq > interval * interval * 0.25 && geom.distance2D(lng0, lat0, lng2, lat2) > 0.01) {
|
|
38505
|
+
densifySegment(lng0, lat0, x0, y0, lng1, lat1, p[0], p[1], proj, interval, points);
|
|
38506
|
+
points.push(p);
|
|
38507
|
+
densifySegment(lng1, lat1, p[0], p[1], lng2, lat2, x2, y2, proj, interval, points);
|
|
38508
|
+
}
|
|
38509
|
+
return points;
|
|
38510
|
+
}
|
|
38511
|
+
|
|
38512
|
+
// Create rectangles around each feature in a layer
|
|
38513
|
+
cmd.rectangles = function(targetLyr, targetDataset, opts) {
|
|
38514
|
+
var crsInfo = getDatasetCrsInfo(targetDataset);
|
|
38515
|
+
var records = targetLyr.data ? targetLyr.data.getRecords() : null;
|
|
38516
|
+
var geometries;
|
|
38517
|
+
|
|
38518
|
+
if (opts.bbox) {
|
|
38519
|
+
geometries = bboxExpressionToGeometries(opts.bbox, targetLyr, targetDataset);
|
|
38520
|
+
|
|
38521
|
+
} else {
|
|
38522
|
+
if (!layerHasGeometry(targetLyr)) {
|
|
38523
|
+
stop("Layer is missing geometric shapes");
|
|
38524
|
+
}
|
|
38525
|
+
geometries = shapesToBoxGeometries(targetLyr, targetDataset, opts);
|
|
38526
|
+
}
|
|
38527
|
+
|
|
38528
|
+
var geojson = {
|
|
38529
|
+
type: 'FeatureCollection',
|
|
38530
|
+
features: geometries.map(function(geom, i) {
|
|
38531
|
+
var rec = records && records[i] || null;
|
|
38532
|
+
if (rec && opts.no_replace) {
|
|
38533
|
+
rec = utils.extend({}, rec); // make a copy
|
|
38534
|
+
}
|
|
38535
|
+
return {
|
|
38536
|
+
type: 'Feature',
|
|
38537
|
+
properties: rec,
|
|
38538
|
+
geometry: geom
|
|
38539
|
+
};
|
|
38540
|
+
})
|
|
38541
|
+
};
|
|
38542
|
+
var dataset = importGeoJSON(geojson, {});
|
|
38543
|
+
setDatasetCrsInfo(dataset, crsInfo);
|
|
38544
|
+
var outputLayers = mergeDatasetsIntoDataset(targetDataset, [dataset]);
|
|
38545
|
+
setOutputLayerName(outputLayers[0], targetLyr, null, opts);
|
|
38546
|
+
return outputLayers;
|
|
38547
|
+
};
|
|
38548
|
+
|
|
38549
|
+
|
|
38550
|
+
|
|
38551
|
+
|
|
38552
|
+
function shapesToBoxGeometries(lyr, dataset, opts) {
|
|
38553
|
+
var crsInfo = getDatasetCrsInfo(dataset);
|
|
38554
|
+
return lyr.shapes.map(function(shp) {
|
|
38555
|
+
var bounds = lyr.geometry_type == 'point' ?
|
|
38556
|
+
getPointFeatureBounds(shp) : dataset.arcs.getMultiShapeBounds(shp);
|
|
38557
|
+
bounds = applyRectangleOptions(bounds, crsInfo.crs, opts);
|
|
38558
|
+
if (!bounds) return null;
|
|
38559
|
+
return bboxToPolygon(bounds.toArray(), opts);
|
|
38560
|
+
});
|
|
38561
|
+
}
|
|
38562
|
+
|
|
38563
|
+
function bboxExpressionToGeometries(exp, lyr, dataset, opts) {
|
|
38564
|
+
var compiled = compileFeatureExpression(exp, lyr, dataset.arcs, {});
|
|
38565
|
+
var n = getFeatureCount(lyr);
|
|
38566
|
+
var result;
|
|
38567
|
+
var geometries = [];
|
|
38568
|
+
for (var i=0; i<n; i++) {
|
|
38569
|
+
result = compiled(i);
|
|
38570
|
+
if (!looksLikeBbox(result)) {
|
|
38571
|
+
stop('Invalid bbox value (expected a GeoJSON-type bbox):', result);
|
|
38572
|
+
}
|
|
38573
|
+
geometries.push(bboxToPolygon(result));
|
|
38574
|
+
}
|
|
38575
|
+
return geometries;
|
|
38576
|
+
}
|
|
38577
|
+
|
|
38578
|
+
function looksLikeBbox(o) {
|
|
38579
|
+
if (!o || o.length != 4) return false;
|
|
38580
|
+
if (o.some(isNaN)) return false;
|
|
38581
|
+
if (o[0] <= o[2] == false || o[1] <= o[3] == false) return false;
|
|
38582
|
+
return true;
|
|
38583
|
+
}
|
|
38584
|
+
|
|
38585
|
+
// Create rectangles around one or more target layers
|
|
38586
|
+
//
|
|
38587
|
+
cmd.rectangle2 = function(target, opts) {
|
|
38588
|
+
// if target layer is a rectangle and we're applying frame properties,
|
|
38589
|
+
// turn the target into a frame instead of creating a new rectangle
|
|
38590
|
+
if (target.layers.length == 1 && opts.width &&
|
|
38591
|
+
layerIsRectangle(target.layers[0], target.dataset.arcs)) {
|
|
38592
|
+
applyFrameProperties(target.layers[0], opts);
|
|
38593
|
+
return;
|
|
38594
|
+
}
|
|
38595
|
+
var datasets = target.layers.map(function(lyr) {
|
|
38596
|
+
var dataset = cmd.rectangle({layer: lyr, dataset: target.dataset}, opts);
|
|
38597
|
+
setOutputLayerName(dataset.layers[0], lyr, null, opts);
|
|
38598
|
+
if (!opts.no_replace) {
|
|
38599
|
+
dataset.layers[0].name = lyr.name || dataset.layers[0].name;
|
|
38600
|
+
}
|
|
38601
|
+
return dataset;
|
|
38602
|
+
});
|
|
38603
|
+
return mergeDatasetsIntoDataset(target.dataset, datasets);
|
|
38604
|
+
};
|
|
38605
|
+
|
|
38606
|
+
cmd.rectangle = function(target, opts) {
|
|
38607
|
+
var bounds, crsInfo;
|
|
38608
|
+
if (opts.bbox) {
|
|
38609
|
+
bounds = new Bounds(opts.bbox);
|
|
38610
|
+
crsInfo = target && getDatasetCrsInfo(target.dataset) ||
|
|
38611
|
+
probablyDecimalDegreeBounds(bounds) && getCrsInfo('wgs84') || {};
|
|
38612
|
+
} else if (target) {
|
|
38613
|
+
bounds = getLayerBounds(target.layer, target.dataset.arcs);
|
|
38614
|
+
crsInfo = getDatasetCrsInfo(target.dataset);
|
|
38615
|
+
}
|
|
38616
|
+
bounds = bounds && applyRectangleOptions(bounds, crsInfo.crs, opts);
|
|
38617
|
+
if (!bounds || !bounds.hasBounds()) {
|
|
38618
|
+
stop('Missing rectangle extent');
|
|
38619
|
+
}
|
|
38620
|
+
var feature = {
|
|
38621
|
+
type: 'Feature',
|
|
38622
|
+
properties: {},
|
|
38623
|
+
geometry: bboxToPolygon(bounds.toArray(), opts)
|
|
38624
|
+
};
|
|
38625
|
+
var dataset = importGeoJSON(feature, {});
|
|
38626
|
+
applyFrameProperties(dataset.layers[0], opts);
|
|
38627
|
+
dataset.layers[0].name = opts.name || 'rectangle';
|
|
38628
|
+
setDatasetCrsInfo(dataset, crsInfo);
|
|
38629
|
+
return dataset;
|
|
38199
38630
|
};
|
|
38200
38631
|
|
|
38632
|
+
function applyFrameProperties(lyr, opts) {
|
|
38633
|
+
if (!opts.width) return;
|
|
38634
|
+
if (!lyr.data) initDataTable(lyr);
|
|
38635
|
+
var d = lyr.data.getRecords()[0] || {};
|
|
38636
|
+
d.width = parseSizeParam(opts.width);
|
|
38637
|
+
d.type = 'frame';
|
|
38638
|
+
}
|
|
38639
|
+
|
|
38640
|
+
function applyRectangleOptions(bounds, crs, opts) {
|
|
38641
|
+
var isGeoBox = probablyDecimalDegreeBounds(bounds);
|
|
38642
|
+
if (opts.offset) {
|
|
38643
|
+
bounds = applyBoundsOffset(opts.offset, bounds, crs);
|
|
38644
|
+
}
|
|
38645
|
+
if (bounds.area() > 0 === false) return null;
|
|
38646
|
+
if (opts.aspect_ratio) {
|
|
38647
|
+
bounds = applyAspectRatio(opts.aspect_ratio, bounds);
|
|
38648
|
+
}
|
|
38649
|
+
if (isGeoBox) {
|
|
38650
|
+
bounds = clampToWorldBounds(bounds);
|
|
38651
|
+
}
|
|
38652
|
+
return bounds;
|
|
38653
|
+
}
|
|
38654
|
+
|
|
38655
|
+
// opt: aspect ratio as a single number or a range (e.g. "1,2");
|
|
38656
|
+
function applyAspectRatio(opt, bounds) {
|
|
38657
|
+
var range = String(opt).split(',').map(parseFloat),
|
|
38658
|
+
aspectRatio = bounds.width() / bounds.height(),
|
|
38659
|
+
min, max; // min is height limit, max is width limit
|
|
38660
|
+
if (range.length == 1) {
|
|
38661
|
+
range.push(range[0]);
|
|
38662
|
+
} else if (range[0] > range[1]) {
|
|
38663
|
+
range.reverse();
|
|
38664
|
+
}
|
|
38665
|
+
min = range[0];
|
|
38666
|
+
max = range[1];
|
|
38667
|
+
if (!min && !max) return bounds;
|
|
38668
|
+
if (!min) min = -Infinity;
|
|
38669
|
+
if (!max) max = Infinity;
|
|
38670
|
+
if (aspectRatio < min) {
|
|
38671
|
+
bounds.fillOut(min);
|
|
38672
|
+
} else if (aspectRatio > max) {
|
|
38673
|
+
bounds.fillOut(max);
|
|
38674
|
+
}
|
|
38675
|
+
return bounds;
|
|
38676
|
+
}
|
|
38677
|
+
|
|
38678
|
+
function applyBoundsOffset(offsetOpt, bounds, crs) {
|
|
38679
|
+
var offsets = convertFourSides(offsetOpt, crs, bounds);
|
|
38680
|
+
bounds.padBounds(offsets[0], offsets[1], offsets[2], offsets[3]);
|
|
38681
|
+
return bounds;
|
|
38682
|
+
}
|
|
38683
|
+
|
|
38684
|
+
function bboxToPolygon(bbox, optsArg) {
|
|
38685
|
+
var opts = optsArg || {};
|
|
38686
|
+
var coords = bboxToCoords(bbox);
|
|
38687
|
+
if (opts.interval > 0) {
|
|
38688
|
+
coords = densifyPathByInterval(coords, opts.interval);
|
|
38689
|
+
}
|
|
38690
|
+
return {
|
|
38691
|
+
type: 'Polygon',
|
|
38692
|
+
coordinates: [coords]
|
|
38693
|
+
};
|
|
38694
|
+
}
|
|
38695
|
+
|
|
38696
|
+
var Rectangle = /*#__PURE__*/Object.freeze({
|
|
38697
|
+
__proto__: null,
|
|
38698
|
+
applyAspectRatio: applyAspectRatio,
|
|
38699
|
+
bboxToPolygon: bboxToPolygon
|
|
38700
|
+
});
|
|
38701
|
+
|
|
38702
|
+
cmd.frame = function(catalog, targets, opts) {
|
|
38703
|
+
var widthPx, heightPx, aspectRatio, bbox;
|
|
38704
|
+
if (opts.width) {
|
|
38705
|
+
widthPx = parseSizeParam(opts.width);
|
|
38706
|
+
if (widthPx > 0 === false) {
|
|
38707
|
+
stop('Invalid width parameter:', opts.width);
|
|
38708
|
+
}
|
|
38709
|
+
}
|
|
38710
|
+
if (opts.height) {
|
|
38711
|
+
heightPx = parseSizeParam(opts.height);
|
|
38712
|
+
if (heightPx > 0 === false) {
|
|
38713
|
+
stop('Invalid height parameter:', opts.height);
|
|
38714
|
+
}
|
|
38715
|
+
}
|
|
38716
|
+
if (!widthPx && !heightPx) {
|
|
38717
|
+
widthPx = 800;
|
|
38718
|
+
message('Using default 800px frame width');
|
|
38719
|
+
}
|
|
38720
|
+
|
|
38721
|
+
if (opts.aspect_ratio) {
|
|
38722
|
+
if (opts.aspect_ratio > 0 === false) {
|
|
38723
|
+
stop('Invalid aspect-ratio parameter:', opts.aspect_ratio);
|
|
38724
|
+
}
|
|
38725
|
+
if (!heightPx) {
|
|
38726
|
+
heightPx = roundToDigits(widthPx / opts.aspect_ratio, 1);
|
|
38727
|
+
} else if (!widthPx) {
|
|
38728
|
+
widthPx = roundToDigits(heightPx * opts.aspect_ratio, 1);
|
|
38729
|
+
}
|
|
38730
|
+
}
|
|
38731
|
+
|
|
38732
|
+
if (opts.bbox) {
|
|
38733
|
+
bbox = opts.bbox;
|
|
38734
|
+
// TODO: validate
|
|
38735
|
+
} else {
|
|
38736
|
+
var datasets = utils.pluck(targets, 'dataset');
|
|
38737
|
+
requireDatasetsHaveCompatibleCRS(datasets, 'Targets include both projected and unprojected coordinates');
|
|
38738
|
+
bbox = getTargetBbox(targets);
|
|
38739
|
+
if (!bbox) {
|
|
38740
|
+
stop('Command target is missing geographical bounds');
|
|
38741
|
+
}
|
|
38742
|
+
}
|
|
38743
|
+
|
|
38744
|
+
applyPercentageOffsets(bbox, opts.offset || opts.offsets);
|
|
38745
|
+
applyPixelOffsets(bbox, widthPx, heightPx, opts.offset || opts.offsets);
|
|
38746
|
+
|
|
38747
|
+
if (bbox[3] - bbox[1] > 0 === false || bbox[2] - bbox[0] > 0 === false) {
|
|
38748
|
+
stop('Frame has a collapsed bbox');
|
|
38749
|
+
}
|
|
38750
|
+
|
|
38751
|
+
aspectRatio = (bbox[2] - bbox[0]) / (bbox[3] - bbox[1]);
|
|
38752
|
+
if (!widthPx) {
|
|
38753
|
+
widthPx = roundToDigits(heightPx * aspectRatio, 1);
|
|
38754
|
+
} else if (!heightPx) {
|
|
38755
|
+
heightPx = roundToDigits(widthPx / aspectRatio, 1);
|
|
38756
|
+
}
|
|
38757
|
+
|
|
38758
|
+
var feature = {
|
|
38759
|
+
type: 'Feature',
|
|
38760
|
+
properties: {type: 'frame', width: widthPx, height: heightPx},
|
|
38761
|
+
geometry: bboxToPolygon(bbox)
|
|
38762
|
+
};
|
|
38763
|
+
var frameDataset = importGeoJSON(feature);
|
|
38764
|
+
// set CRS from target dataset
|
|
38765
|
+
// TODO: handle case: targets have different projections
|
|
38766
|
+
// TODO: handle case: first target is missing CRS
|
|
38767
|
+
if (targets.length > 0) {
|
|
38768
|
+
var crsInfo = getDatasetCrsInfo(targets[0].dataset);
|
|
38769
|
+
setDatasetCrsInfo(frameDataset, crsInfo);
|
|
38770
|
+
}
|
|
38771
|
+
frameDataset.layers[0].name = opts.name || 'frame';
|
|
38772
|
+
catalog.addDataset(frameDataset);
|
|
38773
|
+
};
|
|
38774
|
+
|
|
38775
|
+
function fillOutBbox(bbox, widthPx, heightPx) {
|
|
38776
|
+
var hpad = 0, vpad = 0;
|
|
38777
|
+
var w = bbox[2] - bbox[0];
|
|
38778
|
+
var h = bbox[3] - bbox[1];
|
|
38779
|
+
if (widthPx / heightPx > w / h) { // need to add horizontal padding
|
|
38780
|
+
hpad = h * widthPx / heightPx - w;
|
|
38781
|
+
} else {
|
|
38782
|
+
vpad = w * heightPx / widthPx - h;
|
|
38783
|
+
}
|
|
38784
|
+
bbox[0] -= hpad / 2;
|
|
38785
|
+
bbox[1] -= vpad / 2;
|
|
38786
|
+
bbox[2] += hpad / 2;
|
|
38787
|
+
bbox[3] += vpad / 2;
|
|
38788
|
+
}
|
|
38789
|
+
|
|
38790
|
+
function applyPercentageOffsets(bbox, arg) {
|
|
38791
|
+
var sides = getPctOffsets(arg);
|
|
38792
|
+
var l = sides[0],
|
|
38793
|
+
b = sides[1],
|
|
38794
|
+
r = sides[2],
|
|
38795
|
+
t = sides[3],
|
|
38796
|
+
w2 = (bbox[2] - bbox[0]) / (1 - l - r),
|
|
38797
|
+
h2 = (bbox[3] - bbox[1]) / (1 - t - b);
|
|
38798
|
+
bbox[0] -= l * w2;
|
|
38799
|
+
bbox[1] -= b * h2;
|
|
38800
|
+
bbox[2] += r * w2;
|
|
38801
|
+
bbox[3] += t * h2;
|
|
38802
|
+
}
|
|
38803
|
+
|
|
38804
|
+
function applyPixelOffsets(bbox, widthPx, heightPx, arg) {
|
|
38805
|
+
var sides = getPixelOffsets(arg);
|
|
38806
|
+
var l = sides[0],
|
|
38807
|
+
b = sides[1],
|
|
38808
|
+
r = sides[2],
|
|
38809
|
+
t = sides[3],
|
|
38810
|
+
scale, w;
|
|
38811
|
+
|
|
38812
|
+
if (widthPx && heightPx) {
|
|
38813
|
+
// add padding to bbox to match pixel dimensions, if needed
|
|
38814
|
+
fillOutBbox(bbox, widthPx, heightPx);
|
|
38815
|
+
}
|
|
38816
|
+
|
|
38817
|
+
w = bbox[2] - bbox[0];
|
|
38818
|
+
bbox[3] - bbox[1];
|
|
38819
|
+
|
|
38820
|
+
if (widthPx) {
|
|
38821
|
+
scale = w / (widthPx - l - r);
|
|
38822
|
+
} else {
|
|
38823
|
+
scale = w / (heightPx - t - b);
|
|
38824
|
+
}
|
|
38825
|
+
|
|
38826
|
+
bbox[0] -= scale * l;
|
|
38827
|
+
bbox[1] -= scale * b;
|
|
38828
|
+
bbox[2] += scale * r;
|
|
38829
|
+
bbox[3] += scale * t;
|
|
38830
|
+
return scale;
|
|
38831
|
+
}
|
|
38832
|
+
|
|
38833
|
+
function getPctOffsets(arg) {
|
|
38834
|
+
return adjustOffsetsArg(arg).map(str => {
|
|
38835
|
+
return str.includes('%') ? utils.parsePercent(str) : 0;
|
|
38836
|
+
});
|
|
38837
|
+
}
|
|
38838
|
+
|
|
38839
|
+
function getPixelOffsets(arg) {
|
|
38840
|
+
return adjustOffsetsArg(arg).map(str => {
|
|
38841
|
+
return str.includes('%') ? 0 : parseSizeParam(str);
|
|
38842
|
+
});
|
|
38843
|
+
}
|
|
38844
|
+
|
|
38845
|
+
function adjustOffsetsArg(arg) {
|
|
38846
|
+
if (!arg) arg = ['0'];
|
|
38847
|
+
if (arg.length == 1) {
|
|
38848
|
+
return [arg[0], arg[0], arg[0], arg[0]];
|
|
38849
|
+
}
|
|
38850
|
+
if (arg.length != 4) {
|
|
38851
|
+
stop('List of offsets should have 4 values');
|
|
38852
|
+
}
|
|
38853
|
+
return arg;
|
|
38854
|
+
}
|
|
38855
|
+
|
|
38856
|
+
function getTargetBbox(targets) {
|
|
38857
|
+
var expanded = expandCommandTargets(targets);
|
|
38858
|
+
var bounds = expanded.reduce(function(memo, o) {
|
|
38859
|
+
return memo.mergeBounds(getLayerBounds(o.layer, o.dataset.arcs));
|
|
38860
|
+
}, new Bounds());
|
|
38861
|
+
return bounds.hasBounds() ? bounds.toArray() : null;
|
|
38862
|
+
}
|
|
38201
38863
|
|
|
38202
38864
|
// Convert width and height args to aspect ratio arg for the rectangle() function
|
|
38203
38865
|
function getAspectRatioArg(widthArg, heightArg) {
|
|
@@ -38211,21 +38873,6 @@ ${svg}
|
|
|
38211
38873
|
}).reverse().join(',');
|
|
38212
38874
|
}
|
|
38213
38875
|
|
|
38214
|
-
// export function renderFrame(d) {
|
|
38215
|
-
// var lineWidth = 1,
|
|
38216
|
-
// // inset stroke by half of line width
|
|
38217
|
-
// off = lineWidth / 2,
|
|
38218
|
-
// obj = importPolygon([[[off, off], [off, d.height - off],
|
|
38219
|
-
// [d.width - off, d.height - off],
|
|
38220
|
-
// [d.width - off, off], [off, off]]]);
|
|
38221
|
-
// utils.extend(obj.properties, {
|
|
38222
|
-
// fill: 'none',
|
|
38223
|
-
// stroke: d.stroke || 'black',
|
|
38224
|
-
// 'stroke-width': d['stroke-width'] || lineWidth
|
|
38225
|
-
// });
|
|
38226
|
-
// return [obj];
|
|
38227
|
-
// }
|
|
38228
|
-
|
|
38229
38876
|
var Frame = /*#__PURE__*/Object.freeze({
|
|
38230
38877
|
__proto__: null,
|
|
38231
38878
|
getAspectRatioArg: getAspectRatioArg
|
|
@@ -38679,473 +39326,6 @@ ${svg}
|
|
|
38679
39326
|
return maxValue;
|
|
38680
39327
|
}
|
|
38681
39328
|
|
|
38682
|
-
// Returns number of arcs that were removed
|
|
38683
|
-
function editArcs(arcs, onPoint) {
|
|
38684
|
-
var nn2 = [],
|
|
38685
|
-
xx2 = [],
|
|
38686
|
-
yy2 = [],
|
|
38687
|
-
errors = 0,
|
|
38688
|
-
n;
|
|
38689
|
-
|
|
38690
|
-
arcs.forEach(function(arc, i) {
|
|
38691
|
-
editArc(arc, onPoint);
|
|
38692
|
-
});
|
|
38693
|
-
arcs.updateVertexData(nn2, xx2, yy2);
|
|
38694
|
-
return errors;
|
|
38695
|
-
|
|
38696
|
-
function append(p) {
|
|
38697
|
-
if (p) {
|
|
38698
|
-
xx2.push(p[0]);
|
|
38699
|
-
yy2.push(p[1]);
|
|
38700
|
-
n++;
|
|
38701
|
-
}
|
|
38702
|
-
}
|
|
38703
|
-
|
|
38704
|
-
function editArc(arc, cb) {
|
|
38705
|
-
var x, y, xp, yp, retn;
|
|
38706
|
-
var valid = true;
|
|
38707
|
-
var i = 0;
|
|
38708
|
-
n = 0;
|
|
38709
|
-
while (arc.hasNext()) {
|
|
38710
|
-
x = arc.x;
|
|
38711
|
-
y = arc.y;
|
|
38712
|
-
retn = cb(append, x, y, xp, yp, i++);
|
|
38713
|
-
if (retn === false) {
|
|
38714
|
-
valid = false;
|
|
38715
|
-
// assumes that it's ok for the arc iterator to be interrupted.
|
|
38716
|
-
break;
|
|
38717
|
-
}
|
|
38718
|
-
xp = x;
|
|
38719
|
-
yp = y;
|
|
38720
|
-
}
|
|
38721
|
-
if (valid && n == 1) {
|
|
38722
|
-
// only one valid point was added to this arc (invalid)
|
|
38723
|
-
// e.g. this could happen during reprojection.
|
|
38724
|
-
// making this arc empty
|
|
38725
|
-
// error("An invalid arc was created");
|
|
38726
|
-
message("An invalid arc was created");
|
|
38727
|
-
valid = false;
|
|
38728
|
-
}
|
|
38729
|
-
if (valid) {
|
|
38730
|
-
nn2.push(n);
|
|
38731
|
-
} else {
|
|
38732
|
-
// remove any points that were added for an invalid arc
|
|
38733
|
-
while (n-- > 0) {
|
|
38734
|
-
xx2.pop();
|
|
38735
|
-
yy2.pop();
|
|
38736
|
-
}
|
|
38737
|
-
nn2.push(0); // add empty arc (to preserve mapping from paths to arcs)
|
|
38738
|
-
errors++;
|
|
38739
|
-
}
|
|
38740
|
-
}
|
|
38741
|
-
}
|
|
38742
|
-
|
|
38743
|
-
function DatasetEditor(dataset) {
|
|
38744
|
-
var layers = [];
|
|
38745
|
-
var arcs = [];
|
|
38746
|
-
|
|
38747
|
-
this.done = function() {
|
|
38748
|
-
dataset.layers = layers;
|
|
38749
|
-
if (arcs.length) {
|
|
38750
|
-
dataset.arcs = new ArcCollection(arcs);
|
|
38751
|
-
buildTopology(dataset);
|
|
38752
|
-
}
|
|
38753
|
-
};
|
|
38754
|
-
|
|
38755
|
-
this.editLayer = function(lyr, cb) {
|
|
38756
|
-
var type = lyr.geometry_type;
|
|
38757
|
-
if (dataset.layers.indexOf(lyr) != layers.length) {
|
|
38758
|
-
error('Layer was edited out-of-order');
|
|
38759
|
-
}
|
|
38760
|
-
if (!type) {
|
|
38761
|
-
layers.push(lyr);
|
|
38762
|
-
return;
|
|
38763
|
-
}
|
|
38764
|
-
var shapes = lyr.shapes.map(function(shape, shpId) {
|
|
38765
|
-
var shape2 = [], retn, input;
|
|
38766
|
-
for (var i=0, n=shape ? shape.length : 0; i<n; i++) {
|
|
38767
|
-
input = type == 'point' ? shape[i] : idsToCoords(shape[i]);
|
|
38768
|
-
retn = cb(input, i, shape);
|
|
38769
|
-
if (!Array.isArray(retn)) continue;
|
|
38770
|
-
if (type == 'point') {
|
|
38771
|
-
shape2.push(retn);
|
|
38772
|
-
} else if (type == 'polygon' || type == 'polyline') {
|
|
38773
|
-
extendPathShape(shape2, retn || []);
|
|
38774
|
-
}
|
|
38775
|
-
}
|
|
38776
|
-
return shape2.length > 0 ? shape2 : null;
|
|
38777
|
-
});
|
|
38778
|
-
layers.push(Object.assign(lyr, {shapes: shapes}));
|
|
38779
|
-
};
|
|
38780
|
-
|
|
38781
|
-
function extendPathShape(shape, parts) {
|
|
38782
|
-
for (var i=0; i<parts.length; i++) {
|
|
38783
|
-
shape.push([arcs.length]);
|
|
38784
|
-
arcs.push(parts[i]);
|
|
38785
|
-
}
|
|
38786
|
-
}
|
|
38787
|
-
|
|
38788
|
-
function idsToCoords(ids) {
|
|
38789
|
-
var coords = [];
|
|
38790
|
-
var iter = dataset.arcs.getShapeIter(ids);
|
|
38791
|
-
while (iter.hasNext()) {
|
|
38792
|
-
coords.push([iter.x, iter.y]);
|
|
38793
|
-
}
|
|
38794
|
-
return coords;
|
|
38795
|
-
}
|
|
38796
|
-
}
|
|
38797
|
-
|
|
38798
|
-
// Planar densification by an interval
|
|
38799
|
-
function densifyPathByInterval(coords, interval, interpolate) {
|
|
38800
|
-
if (findMaxPathInterval(coords) < interval) return coords;
|
|
38801
|
-
if (!interpolate) {
|
|
38802
|
-
interpolate = getIntervalInterpolator(interval);
|
|
38803
|
-
}
|
|
38804
|
-
var coords2 = [coords[0]], a, b;
|
|
38805
|
-
for (var i=1, n=coords.length; i<n; i++) {
|
|
38806
|
-
a = coords[i-1];
|
|
38807
|
-
b = coords[i];
|
|
38808
|
-
if (geom.distance2D(a[0], a[1], b[0], b[1]) > interval + 1e-4) {
|
|
38809
|
-
appendArr(coords2, interpolate(a, b));
|
|
38810
|
-
}
|
|
38811
|
-
coords2.push(b);
|
|
38812
|
-
}
|
|
38813
|
-
return coords2;
|
|
38814
|
-
}
|
|
38815
|
-
|
|
38816
|
-
function getIntervalInterpolator(interval) {
|
|
38817
|
-
return function(a, b) {
|
|
38818
|
-
var points = [];
|
|
38819
|
-
// var rev = a[0] == b[0] ? a[1] > b[1] : a[0] > b[0];
|
|
38820
|
-
var dist = geom.distance2D(a[0], a[1], b[0], b[1]);
|
|
38821
|
-
var n = Math.round(dist / interval) - 1;
|
|
38822
|
-
var dx = (b[0] - a[0]) / (n + 1),
|
|
38823
|
-
dy = (b[1] - a[1]) / (n + 1);
|
|
38824
|
-
for (var i=1; i<=n; i++) {
|
|
38825
|
-
points.push([a[0] + dx * i, a[1] + dy * i]);
|
|
38826
|
-
}
|
|
38827
|
-
return points;
|
|
38828
|
-
};
|
|
38829
|
-
}
|
|
38830
|
-
|
|
38831
|
-
|
|
38832
|
-
// Interpolate the same points regardless of segment direction
|
|
38833
|
-
function densifyAntimeridianSegment(a, b, interval) {
|
|
38834
|
-
var y1, y2;
|
|
38835
|
-
var coords = [];
|
|
38836
|
-
var ascending = a[1] < b[1];
|
|
38837
|
-
if (a[0] != b[0]) error('Expected an edge segment');
|
|
38838
|
-
if (interval > 0 === false) error('Expected a positive interval');
|
|
38839
|
-
if (ascending) {
|
|
38840
|
-
y1 = a[1];
|
|
38841
|
-
y2 = b[1];
|
|
38842
|
-
} else {
|
|
38843
|
-
y1 = b[1];
|
|
38844
|
-
y2 = a[1];
|
|
38845
|
-
}
|
|
38846
|
-
var y = Math.floor(y1 / interval) * interval + interval;
|
|
38847
|
-
while (y < y2) {
|
|
38848
|
-
coords.push([a[0], y]);
|
|
38849
|
-
y += interval;
|
|
38850
|
-
}
|
|
38851
|
-
if (!ascending) coords.reverse();
|
|
38852
|
-
return coords;
|
|
38853
|
-
}
|
|
38854
|
-
|
|
38855
|
-
function appendArr(dest, src) {
|
|
38856
|
-
for (var i=0; i<src.length; i++) dest.push(src[i]);
|
|
38857
|
-
}
|
|
38858
|
-
|
|
38859
|
-
function findMaxPathInterval(coords) {
|
|
38860
|
-
var maxSq = 0, intSq, a, b;
|
|
38861
|
-
for (var i=1, n=coords.length; i<n; i++) {
|
|
38862
|
-
a = coords[i-1];
|
|
38863
|
-
b = coords[i];
|
|
38864
|
-
intSq = geom.distanceSq(a[0], a[1], b[0], b[1]);
|
|
38865
|
-
if (intSq > maxSq) maxSq = intSq;
|
|
38866
|
-
}
|
|
38867
|
-
return Math.sqrt(maxSq);
|
|
38868
|
-
}
|
|
38869
|
-
|
|
38870
|
-
function projectAndDensifyArcs(arcs, proj) {
|
|
38871
|
-
var interval = getDefaultDensifyInterval(arcs, proj);
|
|
38872
|
-
var minIntervalSq = interval * interval * 25;
|
|
38873
|
-
var p;
|
|
38874
|
-
return editArcs(arcs, onPoint);
|
|
38875
|
-
|
|
38876
|
-
function onPoint(append, lng, lat, prevLng, prevLat, i) {
|
|
38877
|
-
var pp = p;
|
|
38878
|
-
p = proj(lng, lat);
|
|
38879
|
-
if (!p) return false; // signal that current arc contains an error
|
|
38880
|
-
|
|
38881
|
-
// Don't try to densify shorter segments (optimization)
|
|
38882
|
-
if (i > 0 && geom.distanceSq(p[0], p[1], pp[0], pp[1]) > minIntervalSq) {
|
|
38883
|
-
densifySegment(prevLng, prevLat, pp[0], pp[1], lng, lat, p[0], p[1], proj, interval)
|
|
38884
|
-
.forEach(append);
|
|
38885
|
-
}
|
|
38886
|
-
append(p);
|
|
38887
|
-
}
|
|
38888
|
-
}
|
|
38889
|
-
|
|
38890
|
-
// Use the median of intervals computed by projecting segments.
|
|
38891
|
-
// We're probing a number of points, because @proj might only be valid in
|
|
38892
|
-
// a sub-region of the dataset bbox (e.g. +proj=tpers)
|
|
38893
|
-
function findDensifyInterval(bounds, xy, proj) {
|
|
38894
|
-
var steps = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9];
|
|
38895
|
-
var points = [];
|
|
38896
|
-
for (var i=0; i<steps.length; i++) {
|
|
38897
|
-
for (var j=0; j<steps.length; j++) {
|
|
38898
|
-
points.push([steps[i], steps[j]]);
|
|
38899
|
-
}
|
|
38900
|
-
}
|
|
38901
|
-
var intervals = points.map(function(pos) {
|
|
38902
|
-
var x = bounds.xmin + bounds.width() * pos[0];
|
|
38903
|
-
var y = bounds.ymin + bounds.height() * pos[1];
|
|
38904
|
-
var a = proj(x, y);
|
|
38905
|
-
var b = proj(x + xy[0], y + xy[1]);
|
|
38906
|
-
return a && b ? geom.distance2D(a[0], a[1], b[0], b[1]) : Infinity;
|
|
38907
|
-
}).filter(function(int) {return int < Infinity;});
|
|
38908
|
-
return intervals.length > 0 ? utils.findMedian(intervals) : Infinity;
|
|
38909
|
-
}
|
|
38910
|
-
|
|
38911
|
-
// Kludgy way to get a useful interval for densifying a bounding box.
|
|
38912
|
-
// Uses a fraction of average bbox side length)
|
|
38913
|
-
// TODO: improve
|
|
38914
|
-
function findDensifyInterval2(bb, proj) {
|
|
38915
|
-
var a = proj(bb.centerX(), bb.centerY()),
|
|
38916
|
-
c = proj(bb.centerX(), bb.ymin), // right center
|
|
38917
|
-
d = proj(bb.xmax, bb.centerY()); // bottom center
|
|
38918
|
-
var interval = a && c && d ? (geom.distance2D(a[0], a[1], c[0], c[1]) +
|
|
38919
|
-
geom.distance2D(a[0], a[1], d[0], d[1])) / 5000 : Infinity;
|
|
38920
|
-
return interval;
|
|
38921
|
-
}
|
|
38922
|
-
|
|
38923
|
-
// Returns an interval in projected units
|
|
38924
|
-
function getDefaultDensifyInterval(arcs, proj) {
|
|
38925
|
-
var xy = getAvgSegment2(arcs),
|
|
38926
|
-
bb = arcs.getBounds(),
|
|
38927
|
-
intervalA = findDensifyInterval(bb, xy, proj),
|
|
38928
|
-
intervalB = findDensifyInterval2(bb, proj),
|
|
38929
|
-
interval = Math.min(intervalA, intervalB);
|
|
38930
|
-
if (interval == Infinity) {
|
|
38931
|
-
error('Densification error');
|
|
38932
|
-
}
|
|
38933
|
-
return interval;
|
|
38934
|
-
}
|
|
38935
|
-
|
|
38936
|
-
// Interpolate points into a projected line segment if needed to prevent large
|
|
38937
|
-
// deviations from path of original unprojected segment.
|
|
38938
|
-
// @points (optional) array of accumulated points
|
|
38939
|
-
function densifySegment(lng0, lat0, x0, y0, lng2, lat2, x2, y2, proj, interval, points) {
|
|
38940
|
-
// Find midpoint between two endpoints and project it (assumes longitude does
|
|
38941
|
-
// not wrap). TODO Consider bisecting along great circle path -- although this
|
|
38942
|
-
// would not be good for boundaries that follow line of constant latitude.
|
|
38943
|
-
var lng1 = (lng0 + lng2) / 2,
|
|
38944
|
-
lat1 = (lat0 + lat2) / 2,
|
|
38945
|
-
p = proj(lng1, lat1),
|
|
38946
|
-
distSq;
|
|
38947
|
-
if (!p) return; // TODO: consider if this is adequate for handling proj. errors
|
|
38948
|
-
distSq = geom.pointSegDistSq2(p[0], p[1], x0, y0, x2, y2); // sq displacement
|
|
38949
|
-
points = points || [];
|
|
38950
|
-
// Bisect current segment if the projected midpoint deviates from original
|
|
38951
|
-
// segment by more than the @interval parameter.
|
|
38952
|
-
// ... but don't bisect very small segments to prevent infinite recursion
|
|
38953
|
-
// (e.g. if projection function is discontinuous)
|
|
38954
|
-
if (distSq > interval * interval * 0.25 && geom.distance2D(lng0, lat0, lng2, lat2) > 0.01) {
|
|
38955
|
-
densifySegment(lng0, lat0, x0, y0, lng1, lat1, p[0], p[1], proj, interval, points);
|
|
38956
|
-
points.push(p);
|
|
38957
|
-
densifySegment(lng1, lat1, p[0], p[1], lng2, lat2, x2, y2, proj, interval, points);
|
|
38958
|
-
}
|
|
38959
|
-
return points;
|
|
38960
|
-
}
|
|
38961
|
-
|
|
38962
|
-
// Create rectangles around each feature in a layer
|
|
38963
|
-
cmd.rectangles = function(targetLyr, targetDataset, opts) {
|
|
38964
|
-
var crsInfo = getDatasetCrsInfo(targetDataset);
|
|
38965
|
-
var records = targetLyr.data ? targetLyr.data.getRecords() : null;
|
|
38966
|
-
var geometries;
|
|
38967
|
-
|
|
38968
|
-
if (opts.bbox) {
|
|
38969
|
-
geometries = bboxExpressionToGeometries(opts.bbox, targetLyr, targetDataset);
|
|
38970
|
-
|
|
38971
|
-
} else {
|
|
38972
|
-
if (!layerHasGeometry(targetLyr)) {
|
|
38973
|
-
stop("Layer is missing geometric shapes");
|
|
38974
|
-
}
|
|
38975
|
-
geometries = shapesToBoxGeometries(targetLyr, targetDataset, opts);
|
|
38976
|
-
}
|
|
38977
|
-
|
|
38978
|
-
var geojson = {
|
|
38979
|
-
type: 'FeatureCollection',
|
|
38980
|
-
features: geometries.map(function(geom, i) {
|
|
38981
|
-
var rec = records && records[i] || null;
|
|
38982
|
-
if (rec && opts.no_replace) {
|
|
38983
|
-
rec = utils.extend({}, rec); // make a copy
|
|
38984
|
-
}
|
|
38985
|
-
return {
|
|
38986
|
-
type: 'Feature',
|
|
38987
|
-
properties: rec,
|
|
38988
|
-
geometry: geom
|
|
38989
|
-
};
|
|
38990
|
-
})
|
|
38991
|
-
};
|
|
38992
|
-
var dataset = importGeoJSON(geojson, {});
|
|
38993
|
-
setDatasetCrsInfo(dataset, crsInfo);
|
|
38994
|
-
var outputLayers = mergeDatasetsIntoDataset(targetDataset, [dataset]);
|
|
38995
|
-
setOutputLayerName(outputLayers[0], targetLyr, null, opts);
|
|
38996
|
-
return outputLayers;
|
|
38997
|
-
};
|
|
38998
|
-
|
|
38999
|
-
function shapesToBoxGeometries(lyr, dataset, opts) {
|
|
39000
|
-
var crsInfo = getDatasetCrsInfo(dataset);
|
|
39001
|
-
return lyr.shapes.map(function(shp) {
|
|
39002
|
-
var bounds = lyr.geometry_type == 'point' ?
|
|
39003
|
-
getPointFeatureBounds(shp) : dataset.arcs.getMultiShapeBounds(shp);
|
|
39004
|
-
bounds = applyRectangleOptions(bounds, crsInfo.crs, opts);
|
|
39005
|
-
if (!bounds) return null;
|
|
39006
|
-
return bboxToPolygon(bounds.toArray(), opts);
|
|
39007
|
-
});
|
|
39008
|
-
}
|
|
39009
|
-
|
|
39010
|
-
function bboxExpressionToGeometries(exp, lyr, dataset, opts) {
|
|
39011
|
-
var compiled = compileFeatureExpression(exp, lyr, dataset.arcs, {});
|
|
39012
|
-
var n = getFeatureCount(lyr);
|
|
39013
|
-
var result;
|
|
39014
|
-
var geometries = [];
|
|
39015
|
-
for (var i=0; i<n; i++) {
|
|
39016
|
-
result = compiled(i);
|
|
39017
|
-
if (!looksLikeBbox(result)) {
|
|
39018
|
-
stop('Invalid bbox value (expected a GeoJSON-type bbox):', result);
|
|
39019
|
-
}
|
|
39020
|
-
geometries.push(bboxToPolygon(result));
|
|
39021
|
-
}
|
|
39022
|
-
return geometries;
|
|
39023
|
-
}
|
|
39024
|
-
|
|
39025
|
-
function looksLikeBbox(o) {
|
|
39026
|
-
if (!o || o.length != 4) return false;
|
|
39027
|
-
if (o.some(isNaN)) return false;
|
|
39028
|
-
if (o[0] <= o[2] == false || o[1] <= o[3] == false) return false;
|
|
39029
|
-
return true;
|
|
39030
|
-
}
|
|
39031
|
-
|
|
39032
|
-
// Create rectangles around one or more target layers
|
|
39033
|
-
//
|
|
39034
|
-
cmd.rectangle2 = function(target, opts) {
|
|
39035
|
-
// if target layer is a rectangle and we're applying frame properties,
|
|
39036
|
-
// turn the target into a frame instead of creating a new rectangle
|
|
39037
|
-
if (target.layers.length == 1 && opts.width &&
|
|
39038
|
-
layerIsRectangle(target.layers[0], target.dataset.arcs)) {
|
|
39039
|
-
applyFrameProperties(target.layers[0], opts);
|
|
39040
|
-
return;
|
|
39041
|
-
}
|
|
39042
|
-
var datasets = target.layers.map(function(lyr) {
|
|
39043
|
-
var dataset = cmd.rectangle({layer: lyr, dataset: target.dataset}, opts);
|
|
39044
|
-
setOutputLayerName(dataset.layers[0], lyr, null, opts);
|
|
39045
|
-
if (!opts.no_replace) {
|
|
39046
|
-
dataset.layers[0].name = lyr.name || dataset.layers[0].name;
|
|
39047
|
-
}
|
|
39048
|
-
return dataset;
|
|
39049
|
-
});
|
|
39050
|
-
return mergeDatasetsIntoDataset(target.dataset, datasets);
|
|
39051
|
-
};
|
|
39052
|
-
|
|
39053
|
-
cmd.rectangle = function(target, opts) {
|
|
39054
|
-
var bounds, crsInfo;
|
|
39055
|
-
if (opts.bbox) {
|
|
39056
|
-
bounds = new Bounds(opts.bbox);
|
|
39057
|
-
crsInfo = target && getDatasetCrsInfo(target.dataset) ||
|
|
39058
|
-
probablyDecimalDegreeBounds(bounds) && getCrsInfo('wgs84') || {};
|
|
39059
|
-
} else if (target) {
|
|
39060
|
-
bounds = getLayerBounds(target.layer, target.dataset.arcs);
|
|
39061
|
-
crsInfo = getDatasetCrsInfo(target.dataset);
|
|
39062
|
-
}
|
|
39063
|
-
bounds = bounds && applyRectangleOptions(bounds, crsInfo.crs, opts);
|
|
39064
|
-
if (!bounds || !bounds.hasBounds()) {
|
|
39065
|
-
stop('Missing rectangle extent');
|
|
39066
|
-
}
|
|
39067
|
-
var feature = {
|
|
39068
|
-
type: 'Feature',
|
|
39069
|
-
properties: {},
|
|
39070
|
-
geometry: bboxToPolygon(bounds.toArray(), opts)
|
|
39071
|
-
};
|
|
39072
|
-
var dataset = importGeoJSON(feature, {});
|
|
39073
|
-
applyFrameProperties(dataset.layers[0], opts);
|
|
39074
|
-
dataset.layers[0].name = opts.name || 'rectangle';
|
|
39075
|
-
setDatasetCrsInfo(dataset, crsInfo);
|
|
39076
|
-
return dataset;
|
|
39077
|
-
};
|
|
39078
|
-
|
|
39079
|
-
function applyFrameProperties(lyr, opts) {
|
|
39080
|
-
if (!opts.width) return;
|
|
39081
|
-
if (!lyr.data) initDataTable(lyr);
|
|
39082
|
-
var d = lyr.data.getRecords()[0] || {};
|
|
39083
|
-
d.width = parseSizeParam(opts.width);
|
|
39084
|
-
d.type = 'frame';
|
|
39085
|
-
}
|
|
39086
|
-
|
|
39087
|
-
function applyRectangleOptions(bounds, crs, opts) {
|
|
39088
|
-
var isGeoBox = probablyDecimalDegreeBounds(bounds);
|
|
39089
|
-
if (opts.offset) {
|
|
39090
|
-
bounds = applyBoundsOffset(opts.offset, bounds, crs);
|
|
39091
|
-
}
|
|
39092
|
-
if (bounds.area() > 0 === false) return null;
|
|
39093
|
-
if (opts.aspect_ratio) {
|
|
39094
|
-
bounds = applyAspectRatio(opts.aspect_ratio, bounds);
|
|
39095
|
-
}
|
|
39096
|
-
if (isGeoBox) {
|
|
39097
|
-
bounds = clampToWorldBounds(bounds);
|
|
39098
|
-
}
|
|
39099
|
-
return bounds;
|
|
39100
|
-
}
|
|
39101
|
-
|
|
39102
|
-
// opt: aspect ratio as a single number or a range (e.g. "1,2");
|
|
39103
|
-
function applyAspectRatio(opt, bounds) {
|
|
39104
|
-
var range = String(opt).split(',').map(parseFloat),
|
|
39105
|
-
aspectRatio = bounds.width() / bounds.height(),
|
|
39106
|
-
min, max; // min is height limit, max is width limit
|
|
39107
|
-
if (range.length == 1) {
|
|
39108
|
-
range.push(range[0]);
|
|
39109
|
-
} else if (range[0] > range[1]) {
|
|
39110
|
-
range.reverse();
|
|
39111
|
-
}
|
|
39112
|
-
min = range[0];
|
|
39113
|
-
max = range[1];
|
|
39114
|
-
if (!min && !max) return bounds;
|
|
39115
|
-
if (!min) min = -Infinity;
|
|
39116
|
-
if (!max) max = Infinity;
|
|
39117
|
-
if (aspectRatio < min) {
|
|
39118
|
-
bounds.fillOut(min);
|
|
39119
|
-
} else if (aspectRatio > max) {
|
|
39120
|
-
bounds.fillOut(max);
|
|
39121
|
-
}
|
|
39122
|
-
return bounds;
|
|
39123
|
-
}
|
|
39124
|
-
|
|
39125
|
-
function applyBoundsOffset(offsetOpt, bounds, crs) {
|
|
39126
|
-
var offsets = convertFourSides(offsetOpt, crs, bounds);
|
|
39127
|
-
bounds.padBounds(offsets[0], offsets[1], offsets[2], offsets[3]);
|
|
39128
|
-
return bounds;
|
|
39129
|
-
}
|
|
39130
|
-
|
|
39131
|
-
function bboxToPolygon(bbox, optsArg) {
|
|
39132
|
-
var opts = optsArg || {};
|
|
39133
|
-
var coords = bboxToCoords(bbox);
|
|
39134
|
-
if (opts.interval > 0) {
|
|
39135
|
-
coords = densifyPathByInterval(coords, opts.interval);
|
|
39136
|
-
}
|
|
39137
|
-
return {
|
|
39138
|
-
type: 'Polygon',
|
|
39139
|
-
coordinates: [coords]
|
|
39140
|
-
};
|
|
39141
|
-
}
|
|
39142
|
-
|
|
39143
|
-
var Rectangle = /*#__PURE__*/Object.freeze({
|
|
39144
|
-
__proto__: null,
|
|
39145
|
-
applyAspectRatio: applyAspectRatio,
|
|
39146
|
-
bboxToPolygon: bboxToPolygon
|
|
39147
|
-
});
|
|
39148
|
-
|
|
39149
39329
|
function getSemiMinorAxis(P) {
|
|
39150
39330
|
return P.a * Math.sqrt(1 - (P.es || 0));
|
|
39151
39331
|
}
|
|
@@ -44377,6 +44557,7 @@ ${svg}
|
|
|
44377
44557
|
if (!size) return null;
|
|
44378
44558
|
var stemLen = size.stemLen,
|
|
44379
44559
|
headLen = size.headLen,
|
|
44560
|
+
totalLen = stickArrow ? Math.max(stemLen, headLen) : stemLen + headLen,
|
|
44380
44561
|
headDx = size.headWidth / 2,
|
|
44381
44562
|
stemDx = size.stemWidth / 2,
|
|
44382
44563
|
baseDx = stemDx * (1 - stemTaper),
|
|
@@ -44429,11 +44610,10 @@ ${svg}
|
|
|
44429
44610
|
}
|
|
44430
44611
|
|
|
44431
44612
|
if (d.anchor == 'end') {
|
|
44432
|
-
scaleAndShiftCoords(coords, 1, [-dx, -
|
|
44613
|
+
scaleAndShiftCoords(coords, 1, [-dx, -totalLen]);
|
|
44433
44614
|
} else if (d.anchor == 'middle') {
|
|
44434
44615
|
// shift midpoint away from the head a bit for a more balanced placement
|
|
44435
|
-
|
|
44436
|
-
scaleAndShiftCoords(coords, 1, [-dx * 0.5, -dy * 0.5 - headLen * 0.25]);
|
|
44616
|
+
scaleAndShiftCoords(coords, 1, [-dx * 0.5, -dy * 0.5 - totalLen * 0.25]);
|
|
44437
44617
|
}
|
|
44438
44618
|
|
|
44439
44619
|
rotateCoords(coords, direction);
|
|
@@ -44450,15 +44630,16 @@ ${svg}
|
|
|
44450
44630
|
|
|
44451
44631
|
function calcArrowSize(d, stickArrow) {
|
|
44452
44632
|
// don't display arrows with negative length
|
|
44453
|
-
var
|
|
44454
|
-
|
|
44455
|
-
|
|
44633
|
+
var o = initArrowSize(d), // calc several parameters
|
|
44634
|
+
dataLen = Math.max(d.radius || d.length || d.r || 0),
|
|
44635
|
+
totalLen = Math.max(dataLen, o.headLen, 0),
|
|
44636
|
+
scale = 1;
|
|
44456
44637
|
if (totalLen >= 0) {
|
|
44457
44638
|
scale = calcScale(totalLen, o.headLen, d);
|
|
44458
44639
|
o.stemWidth *= scale;
|
|
44459
44640
|
o.headWidth *= scale;
|
|
44460
44641
|
o.headLen *= scale;
|
|
44461
|
-
o.stemLen = stickArrow ?
|
|
44642
|
+
o.stemLen = stickArrow ? dataLen : totalLen - o.headLen;
|
|
44462
44643
|
}
|
|
44463
44644
|
|
|
44464
44645
|
if (o.headWidth < o.stemWidth && o.headWidth > 0) {
|
|
@@ -44507,13 +44688,11 @@ ${svg}
|
|
|
44507
44688
|
return o;
|
|
44508
44689
|
}
|
|
44509
44690
|
|
|
44510
|
-
|
|
44511
44691
|
// Returns ratio of head length to head width
|
|
44512
44692
|
function getHeadSizeRatio(headAngle) {
|
|
44513
44693
|
return 1 / Math.tan(Math.PI * headAngle / 180 / 2) / 2;
|
|
44514
44694
|
}
|
|
44515
44695
|
|
|
44516
|
-
|
|
44517
44696
|
// ax, ay: point on the base
|
|
44518
44697
|
// bx, by: point on the stem
|
|
44519
44698
|
function getCurvedStemCoords(ax, ay, bx, by) {
|
|
@@ -45259,14 +45438,16 @@ ${svg}
|
|
|
45259
45438
|
}
|
|
45260
45439
|
|
|
45261
45440
|
function commandAcceptsMultipleTargetDatasets(name) {
|
|
45262
|
-
return name == 'rotate' || name == 'info' || name == 'proj' ||
|
|
45263
|
-
name == '
|
|
45264
|
-
name == '
|
|
45441
|
+
return name == 'rotate' || name == 'info' || name == 'proj' ||
|
|
45442
|
+
name == 'require' || name == 'drop' || name == 'target' ||
|
|
45443
|
+
name == 'if' || name == 'elif' || name == 'else' || name == 'endif' ||
|
|
45444
|
+
name == 'run' || name == 'i' || name == 'snap' || name == 'frame' ||
|
|
45445
|
+
name == 'comment';
|
|
45265
45446
|
}
|
|
45266
45447
|
|
|
45267
45448
|
function commandAcceptsEmptyTarget(name) {
|
|
45268
45449
|
return name == 'graticule' || name == 'i' || name == 'help' ||
|
|
45269
|
-
name == 'point-grid' || name == 'shape' || name == 'rectangle' ||
|
|
45450
|
+
name == 'point-grid' || name == 'shape' || name == 'rectangle' || name == 'frame' ||
|
|
45270
45451
|
name == 'require' || name == 'run' || name == 'define' ||
|
|
45271
45452
|
name == 'include' || name == 'print' || name == 'comment' || name == 'if' || name == 'elif' ||
|
|
45272
45453
|
name == 'else' || name == 'endif' || name == 'stop' || name == 'add-shape' ||
|
|
@@ -45290,12 +45471,14 @@ ${svg}
|
|
|
45290
45471
|
}
|
|
45291
45472
|
|
|
45292
45473
|
if (name == 'comment') {
|
|
45474
|
+
cmd.comment(opts);
|
|
45293
45475
|
return done(null);
|
|
45294
45476
|
}
|
|
45295
45477
|
|
|
45296
45478
|
if (!job) job = new Job();
|
|
45297
45479
|
job.startCommand(command);
|
|
45298
45480
|
|
|
45481
|
+
|
|
45299
45482
|
try { // catch errors from synchronous functions
|
|
45300
45483
|
T$1.start();
|
|
45301
45484
|
|
|
@@ -45394,8 +45577,8 @@ ${svg}
|
|
|
45394
45577
|
} else if (name == 'colorizer') {
|
|
45395
45578
|
outputLayers = cmd.colorizer(opts);
|
|
45396
45579
|
|
|
45397
|
-
} else if (name == 'comment') {
|
|
45398
|
-
|
|
45580
|
+
// } else if (name == 'comment') {
|
|
45581
|
+
// // no-op
|
|
45399
45582
|
|
|
45400
45583
|
} else if (name == 'dashlines') {
|
|
45401
45584
|
applyCommandToEachLayer(cmd.dashlines, targetLayers, targetDataset, opts);
|
|
@@ -45453,9 +45636,8 @@ ${svg}
|
|
|
45453
45636
|
} else if (name == 'filter-slivers') {
|
|
45454
45637
|
applyCommandToEachLayer(cmd.filterSlivers, targetLayers, targetDataset, opts);
|
|
45455
45638
|
|
|
45456
|
-
|
|
45457
|
-
|
|
45458
|
-
// cmd.frame(job.catalog, source, opts);
|
|
45639
|
+
} else if (name == 'frame') {
|
|
45640
|
+
cmd.frame(job.catalog, targets, opts);
|
|
45459
45641
|
|
|
45460
45642
|
} else if (name == 'fuzzy-join') {
|
|
45461
45643
|
applyCommandToEachLayer(cmd.fuzzyJoin, targetLayers, arcs, source, opts);
|
|
@@ -45557,10 +45739,7 @@ ${svg}
|
|
|
45557
45739
|
cmd.proj(targ.dataset, job.catalog, opts);
|
|
45558
45740
|
});
|
|
45559
45741
|
|
|
45560
|
-
} else if (name == 'rectangle'
|
|
45561
|
-
if (name == 'frame' && !opts.width) {
|
|
45562
|
-
stop('Command requires a width= argument');
|
|
45563
|
-
}
|
|
45742
|
+
} else if (name == 'rectangle') {
|
|
45564
45743
|
if (source || opts.bbox || targets.length === 0) {
|
|
45565
45744
|
job.catalog.addDataset(cmd.rectangle(source || targets?.[0], opts));
|
|
45566
45745
|
} else {
|
|
@@ -45726,7 +45905,7 @@ ${svg}
|
|
|
45726
45905
|
});
|
|
45727
45906
|
}
|
|
45728
45907
|
|
|
45729
|
-
var version = "0.6.
|
|
45908
|
+
var version = "0.6.101";
|
|
45730
45909
|
|
|
45731
45910
|
// Parse command line args into commands and run them
|
|
45732
45911
|
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.
|
|
@@ -45877,13 +46056,16 @@ ${svg}
|
|
|
45877
46056
|
|
|
45878
46057
|
function nextGroup(prevJob, commands, next) {
|
|
45879
46058
|
runParsedCommands(commands, new Job(), function(err, job) {
|
|
45880
|
-
err =
|
|
46059
|
+
err = handleNonFatalError(err);
|
|
45881
46060
|
next(err, job);
|
|
45882
46061
|
});
|
|
45883
46062
|
}
|
|
45884
46063
|
|
|
45885
46064
|
function done(err, job) {
|
|
45886
|
-
|
|
46065
|
+
if (job && inControlBlock(job)) {
|
|
46066
|
+
message('Warning: -if command is missing a matching -endif');
|
|
46067
|
+
}
|
|
46068
|
+
err = handleNonFatalError(err);
|
|
45887
46069
|
if (err) printError(err);
|
|
45888
46070
|
callback(err, job);
|
|
45889
46071
|
}
|
|
@@ -45960,7 +46142,7 @@ ${svg}
|
|
|
45960
46142
|
}
|
|
45961
46143
|
}
|
|
45962
46144
|
|
|
45963
|
-
function
|
|
46145
|
+
function handleNonFatalError(err) {
|
|
45964
46146
|
if (err && err.name == 'NonFatalError') {
|
|
45965
46147
|
printError(err);
|
|
45966
46148
|
return null;
|