mapshaper 0.6.99 → 0.6.100
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 +705 -558
- package/package.json +1 -1
- package/www/mapshaper-gui.js +249 -151
- package/www/mapshaper.js +705 -558
- 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,
|
|
@@ -18520,6 +18525,7 @@
|
|
|
18520
18525
|
var pixBounds = calcOutputSizeInPixels(bounds, opts);
|
|
18521
18526
|
return {
|
|
18522
18527
|
bbox: bounds.toArray(),
|
|
18528
|
+
bbox2: pixBounds.toArray(),
|
|
18523
18529
|
width: Math.round(pixBounds.width()),
|
|
18524
18530
|
height: Math.round(pixBounds.height()) || 1,
|
|
18525
18531
|
type: 'frame'
|
|
@@ -18934,7 +18940,7 @@
|
|
|
18934
18940
|
parts.shift();
|
|
18935
18941
|
var colors = [];
|
|
18936
18942
|
var background = parts.pop();
|
|
18937
|
-
var spacing =
|
|
18943
|
+
var spacing = parseNum(parts.pop());
|
|
18938
18944
|
var tmp;
|
|
18939
18945
|
while (parts.length > 0) {
|
|
18940
18946
|
tmp = parts.pop();
|
|
@@ -18945,11 +18951,11 @@
|
|
|
18945
18951
|
colors.push(tmp);
|
|
18946
18952
|
}
|
|
18947
18953
|
}
|
|
18948
|
-
var width =
|
|
18949
|
-
var dashes = [
|
|
18954
|
+
var width = parseNum(parts.pop());
|
|
18955
|
+
var dashes = [parseNum(parts.pop()), parseNum(parts.pop())].reverse();
|
|
18950
18956
|
var rotation = 45;
|
|
18951
18957
|
if (parts.length > 0) {
|
|
18952
|
-
rotation =
|
|
18958
|
+
rotation = parseNum(parts.pop());
|
|
18953
18959
|
}
|
|
18954
18960
|
if (parts.length > 0) {
|
|
18955
18961
|
return null;
|
|
@@ -18974,10 +18980,10 @@
|
|
|
18974
18980
|
// 1px red 1px white 1px black
|
|
18975
18981
|
// -45deg 3 #eee 3 rgb(0,0,0)
|
|
18976
18982
|
parts.shift();
|
|
18977
|
-
var rot = parts.length % 2 == 1 ?
|
|
18983
|
+
var rot = parts.length % 2 == 1 ? parseNum(parts.shift()) : 45, // default is 45
|
|
18978
18984
|
colors = [], widths = [];
|
|
18979
18985
|
for (var i=0; i<parts.length; i+=2) {
|
|
18980
|
-
widths.push(
|
|
18986
|
+
widths.push(parseNum(parts[i]));
|
|
18981
18987
|
colors.push(parts[i+1]);
|
|
18982
18988
|
}
|
|
18983
18989
|
if (Math.min.apply(null, widths) > 0 === false) return null;
|
|
@@ -18991,7 +18997,7 @@
|
|
|
18991
18997
|
}
|
|
18992
18998
|
|
|
18993
18999
|
function isSize(str) {
|
|
18994
|
-
return
|
|
19000
|
+
return parseNum(str) > 0;
|
|
18995
19001
|
}
|
|
18996
19002
|
|
|
18997
19003
|
function parseDots(parts) {
|
|
@@ -19004,11 +19010,11 @@
|
|
|
19004
19010
|
var type = parts.shift();
|
|
19005
19011
|
var rot = 0;
|
|
19006
19012
|
if (isSize(parts[1])) { // if rotation is present, there are two numbers
|
|
19007
|
-
rot =
|
|
19013
|
+
rot = parseNum(parts.shift());
|
|
19008
19014
|
}
|
|
19009
|
-
var size =
|
|
19015
|
+
var size = parseNum(parts.shift());
|
|
19010
19016
|
var bg = parts.pop();
|
|
19011
|
-
var spacing =
|
|
19017
|
+
var spacing = parseNum(parts.pop());
|
|
19012
19018
|
while (parts.length > 0) {
|
|
19013
19019
|
colors.push(parts.shift());
|
|
19014
19020
|
}
|
|
@@ -19026,6 +19032,12 @@
|
|
|
19026
19032
|
};
|
|
19027
19033
|
}
|
|
19028
19034
|
|
|
19035
|
+
function parseNum(str) {
|
|
19036
|
+
// return parseNum(str);
|
|
19037
|
+
// support sub-pixel sizes
|
|
19038
|
+
return parseFloat(str) || 0;
|
|
19039
|
+
}
|
|
19040
|
+
|
|
19029
19041
|
function splitPattern(str) {
|
|
19030
19042
|
// split apart space and comma-delimited tokens
|
|
19031
19043
|
// ... but don't split rgb(...) colors
|
|
@@ -20116,7 +20128,7 @@
|
|
|
20116
20128
|
|
|
20117
20129
|
function fitDatasetToFrame(dataset, frame, opts) {
|
|
20118
20130
|
var bounds = new Bounds(frame.bbox);
|
|
20119
|
-
var bounds2 = new Bounds(0, 0, frame.width, frame.height);
|
|
20131
|
+
var bounds2 = frame.bbox2 ? new Bounds(frame.bbox2) : new Bounds(0, 0, frame.width, frame.height);
|
|
20120
20132
|
var fwd = bounds.getTransform(bounds2, opts.invert_y);
|
|
20121
20133
|
transformPoints(dataset, function(x, y) {
|
|
20122
20134
|
return fwd.transform(x, y);
|
|
@@ -24502,6 +24514,10 @@ ${svg}
|
|
|
24502
24514
|
.option('geojson-type', {
|
|
24503
24515
|
describe: '[GeoJSON] FeatureCollection, GeometryCollection or Feature'
|
|
24504
24516
|
})
|
|
24517
|
+
.option('no-null-props', {
|
|
24518
|
+
describe: '[GeoJSON] use "properties":{} when a Feature has no data',
|
|
24519
|
+
type: 'flag'
|
|
24520
|
+
})
|
|
24505
24521
|
.option('hoist', {
|
|
24506
24522
|
describe: '[GeoJSON] move properties to the root level of each Feature',
|
|
24507
24523
|
type: 'strings'
|
|
@@ -26064,15 +26080,32 @@ ${svg}
|
|
|
26064
26080
|
});
|
|
26065
26081
|
|
|
26066
26082
|
parser.command('frame')
|
|
26067
|
-
|
|
26083
|
+
.describe('create a rectangular map frame layer at a given display width')
|
|
26084
|
+
.option('width', {
|
|
26085
|
+
describe: 'width of frame (e.g. 5in, 10cm, 600px; default is 800px)'
|
|
26086
|
+
})
|
|
26087
|
+
.option('height', {
|
|
26088
|
+
describe: '(optional) height of frame; similar to width= option'
|
|
26089
|
+
})
|
|
26090
|
+
.option('aspect-ratio', {
|
|
26091
|
+
describe: '(optional) aspect ratio of frame, if height= or width= is omitted',
|
|
26092
|
+
type: 'number'
|
|
26093
|
+
})
|
|
26068
26094
|
.option('bbox', {
|
|
26069
26095
|
describe: 'frame coordinates (xmin,ymin,xmax,ymax)',
|
|
26070
26096
|
type: 'bbox'
|
|
26071
26097
|
})
|
|
26072
|
-
|
|
26073
|
-
|
|
26074
|
-
|
|
26098
|
+
.option('offset', {
|
|
26099
|
+
describe: 'padding in display units or pct of width, e.g. 5cm 20px 5%',
|
|
26100
|
+
type: 'strings'
|
|
26101
|
+
})
|
|
26102
|
+
.option('offsets', {
|
|
26103
|
+
describe: 'separate offsets for each side, in l,b,r,t order',
|
|
26104
|
+
type: 'strings'
|
|
26075
26105
|
})
|
|
26106
|
+
.option('name', nameOpt)
|
|
26107
|
+
.option('target', targetOpt);
|
|
26108
|
+
|
|
26076
26109
|
// .option('height', {
|
|
26077
26110
|
// describe: 'pixel height of output (may be a range)'
|
|
26078
26111
|
// })
|
|
@@ -26083,7 +26116,6 @@ ${svg}
|
|
|
26083
26116
|
// .option('source', {
|
|
26084
26117
|
// describe: 'name of layer to enclose'
|
|
26085
26118
|
// })
|
|
26086
|
-
.option('name', nameOpt);
|
|
26087
26119
|
|
|
26088
26120
|
parser.command('fuzzy-join')
|
|
26089
26121
|
.describe('join points to polygons, with data fill and fuzzy match')
|
|
@@ -35780,7 +35812,10 @@ ${svg}
|
|
|
35780
35812
|
getColorizerFunction: getColorizerFunction
|
|
35781
35813
|
});
|
|
35782
35814
|
|
|
35783
|
-
cmd.comment = function() {
|
|
35815
|
+
cmd.comment = function(opts) {
|
|
35816
|
+
// TODO: print the comment in verbose mode
|
|
35817
|
+
// message('[comment]', opts.message);
|
|
35818
|
+
}; // no-op, so -comment doesn't trigger a parsing error
|
|
35784
35819
|
|
|
35785
35820
|
cmd.dashlines = function(lyr, dataset, opts) {
|
|
35786
35821
|
var crs = getDatasetCRS(dataset);
|
|
@@ -38158,46 +38193,637 @@ ${svg}
|
|
|
38158
38193
|
return parsed[0];
|
|
38159
38194
|
}
|
|
38160
38195
|
|
|
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
|
-
|
|
38196
|
+
// Returns number of arcs that were removed
|
|
38197
|
+
function editArcs(arcs, onPoint) {
|
|
38198
|
+
var nn2 = [],
|
|
38199
|
+
xx2 = [],
|
|
38200
|
+
yy2 = [],
|
|
38201
|
+
errors = 0,
|
|
38202
|
+
n;
|
|
38203
|
+
|
|
38204
|
+
arcs.forEach(function(arc, i) {
|
|
38205
|
+
editArc(arc, onPoint);
|
|
38206
|
+
});
|
|
38207
|
+
arcs.updateVertexData(nn2, xx2, yy2);
|
|
38208
|
+
return errors;
|
|
38209
|
+
|
|
38210
|
+
function append(p) {
|
|
38211
|
+
if (p) {
|
|
38212
|
+
xx2.push(p[0]);
|
|
38213
|
+
yy2.push(p[1]);
|
|
38214
|
+
n++;
|
|
38215
|
+
}
|
|
38216
|
+
}
|
|
38217
|
+
|
|
38218
|
+
function editArc(arc, cb) {
|
|
38219
|
+
var x, y, xp, yp, retn;
|
|
38220
|
+
var valid = true;
|
|
38221
|
+
var i = 0;
|
|
38222
|
+
n = 0;
|
|
38223
|
+
while (arc.hasNext()) {
|
|
38224
|
+
x = arc.x;
|
|
38225
|
+
y = arc.y;
|
|
38226
|
+
retn = cb(append, x, y, xp, yp, i++);
|
|
38227
|
+
if (retn === false) {
|
|
38228
|
+
valid = false;
|
|
38229
|
+
// assumes that it's ok for the arc iterator to be interrupted.
|
|
38230
|
+
break;
|
|
38231
|
+
}
|
|
38232
|
+
xp = x;
|
|
38233
|
+
yp = y;
|
|
38234
|
+
}
|
|
38235
|
+
if (valid && n == 1) {
|
|
38236
|
+
// only one valid point was added to this arc (invalid)
|
|
38237
|
+
// e.g. this could happen during reprojection.
|
|
38238
|
+
// making this arc empty
|
|
38239
|
+
// error("An invalid arc was created");
|
|
38240
|
+
message("An invalid arc was created");
|
|
38241
|
+
valid = false;
|
|
38242
|
+
}
|
|
38243
|
+
if (valid) {
|
|
38244
|
+
nn2.push(n);
|
|
38245
|
+
} else {
|
|
38246
|
+
// remove any points that were added for an invalid arc
|
|
38247
|
+
while (n-- > 0) {
|
|
38248
|
+
xx2.pop();
|
|
38249
|
+
yy2.pop();
|
|
38250
|
+
}
|
|
38251
|
+
nn2.push(0); // add empty arc (to preserve mapping from paths to arcs)
|
|
38252
|
+
errors++;
|
|
38253
|
+
}
|
|
38254
|
+
}
|
|
38255
|
+
}
|
|
38256
|
+
|
|
38257
|
+
function DatasetEditor(dataset) {
|
|
38258
|
+
var layers = [];
|
|
38259
|
+
var arcs = [];
|
|
38260
|
+
|
|
38261
|
+
this.done = function() {
|
|
38262
|
+
dataset.layers = layers;
|
|
38263
|
+
if (arcs.length) {
|
|
38264
|
+
dataset.arcs = new ArcCollection(arcs);
|
|
38265
|
+
buildTopology(dataset);
|
|
38266
|
+
}
|
|
38267
|
+
};
|
|
38268
|
+
|
|
38269
|
+
this.editLayer = function(lyr, cb) {
|
|
38270
|
+
var type = lyr.geometry_type;
|
|
38271
|
+
if (dataset.layers.indexOf(lyr) != layers.length) {
|
|
38272
|
+
error('Layer was edited out-of-order');
|
|
38273
|
+
}
|
|
38274
|
+
if (!type) {
|
|
38275
|
+
layers.push(lyr);
|
|
38276
|
+
return;
|
|
38277
|
+
}
|
|
38278
|
+
var shapes = lyr.shapes.map(function(shape, shpId) {
|
|
38279
|
+
var shape2 = [], retn, input;
|
|
38280
|
+
for (var i=0, n=shape ? shape.length : 0; i<n; i++) {
|
|
38281
|
+
input = type == 'point' ? shape[i] : idsToCoords(shape[i]);
|
|
38282
|
+
retn = cb(input, i, shape);
|
|
38283
|
+
if (!Array.isArray(retn)) continue;
|
|
38284
|
+
if (type == 'point') {
|
|
38285
|
+
shape2.push(retn);
|
|
38286
|
+
} else if (type == 'polygon' || type == 'polyline') {
|
|
38287
|
+
extendPathShape(shape2, retn || []);
|
|
38288
|
+
}
|
|
38289
|
+
}
|
|
38290
|
+
return shape2.length > 0 ? shape2 : null;
|
|
38291
|
+
});
|
|
38292
|
+
layers.push(Object.assign(lyr, {shapes: shapes}));
|
|
38293
|
+
};
|
|
38294
|
+
|
|
38295
|
+
function extendPathShape(shape, parts) {
|
|
38296
|
+
for (var i=0; i<parts.length; i++) {
|
|
38297
|
+
shape.push([arcs.length]);
|
|
38298
|
+
arcs.push(parts[i]);
|
|
38299
|
+
}
|
|
38300
|
+
}
|
|
38301
|
+
|
|
38302
|
+
function idsToCoords(ids) {
|
|
38303
|
+
var coords = [];
|
|
38304
|
+
var iter = dataset.arcs.getShapeIter(ids);
|
|
38305
|
+
while (iter.hasNext()) {
|
|
38306
|
+
coords.push([iter.x, iter.y]);
|
|
38307
|
+
}
|
|
38308
|
+
return coords;
|
|
38309
|
+
}
|
|
38310
|
+
}
|
|
38311
|
+
|
|
38312
|
+
// Planar densification by an interval
|
|
38313
|
+
function densifyPathByInterval(coords, interval, interpolate) {
|
|
38314
|
+
if (findMaxPathInterval(coords) < interval) return coords;
|
|
38315
|
+
if (!interpolate) {
|
|
38316
|
+
interpolate = getIntervalInterpolator(interval);
|
|
38317
|
+
}
|
|
38318
|
+
var coords2 = [coords[0]], a, b;
|
|
38319
|
+
for (var i=1, n=coords.length; i<n; i++) {
|
|
38320
|
+
a = coords[i-1];
|
|
38321
|
+
b = coords[i];
|
|
38322
|
+
if (geom.distance2D(a[0], a[1], b[0], b[1]) > interval + 1e-4) {
|
|
38323
|
+
appendArr(coords2, interpolate(a, b));
|
|
38324
|
+
}
|
|
38325
|
+
coords2.push(b);
|
|
38326
|
+
}
|
|
38327
|
+
return coords2;
|
|
38328
|
+
}
|
|
38329
|
+
|
|
38330
|
+
function getIntervalInterpolator(interval) {
|
|
38331
|
+
return function(a, b) {
|
|
38332
|
+
var points = [];
|
|
38333
|
+
// var rev = a[0] == b[0] ? a[1] > b[1] : a[0] > b[0];
|
|
38334
|
+
var dist = geom.distance2D(a[0], a[1], b[0], b[1]);
|
|
38335
|
+
var n = Math.round(dist / interval) - 1;
|
|
38336
|
+
var dx = (b[0] - a[0]) / (n + 1),
|
|
38337
|
+
dy = (b[1] - a[1]) / (n + 1);
|
|
38338
|
+
for (var i=1; i<=n; i++) {
|
|
38339
|
+
points.push([a[0] + dx * i, a[1] + dy * i]);
|
|
38340
|
+
}
|
|
38341
|
+
return points;
|
|
38342
|
+
};
|
|
38343
|
+
}
|
|
38344
|
+
|
|
38345
|
+
|
|
38346
|
+
// Interpolate the same points regardless of segment direction
|
|
38347
|
+
function densifyAntimeridianSegment(a, b, interval) {
|
|
38348
|
+
var y1, y2;
|
|
38349
|
+
var coords = [];
|
|
38350
|
+
var ascending = a[1] < b[1];
|
|
38351
|
+
if (a[0] != b[0]) error('Expected an edge segment');
|
|
38352
|
+
if (interval > 0 === false) error('Expected a positive interval');
|
|
38353
|
+
if (ascending) {
|
|
38354
|
+
y1 = a[1];
|
|
38355
|
+
y2 = b[1];
|
|
38356
|
+
} else {
|
|
38357
|
+
y1 = b[1];
|
|
38358
|
+
y2 = a[1];
|
|
38359
|
+
}
|
|
38360
|
+
var y = Math.floor(y1 / interval) * interval + interval;
|
|
38361
|
+
while (y < y2) {
|
|
38362
|
+
coords.push([a[0], y]);
|
|
38363
|
+
y += interval;
|
|
38364
|
+
}
|
|
38365
|
+
if (!ascending) coords.reverse();
|
|
38366
|
+
return coords;
|
|
38367
|
+
}
|
|
38368
|
+
|
|
38369
|
+
function appendArr(dest, src) {
|
|
38370
|
+
for (var i=0; i<src.length; i++) dest.push(src[i]);
|
|
38371
|
+
}
|
|
38372
|
+
|
|
38373
|
+
function findMaxPathInterval(coords) {
|
|
38374
|
+
var maxSq = 0, intSq, a, b;
|
|
38375
|
+
for (var i=1, n=coords.length; i<n; i++) {
|
|
38376
|
+
a = coords[i-1];
|
|
38377
|
+
b = coords[i];
|
|
38378
|
+
intSq = geom.distanceSq(a[0], a[1], b[0], b[1]);
|
|
38379
|
+
if (intSq > maxSq) maxSq = intSq;
|
|
38380
|
+
}
|
|
38381
|
+
return Math.sqrt(maxSq);
|
|
38382
|
+
}
|
|
38383
|
+
|
|
38384
|
+
function projectAndDensifyArcs(arcs, proj) {
|
|
38385
|
+
var interval = getDefaultDensifyInterval(arcs, proj);
|
|
38386
|
+
var minIntervalSq = interval * interval * 25;
|
|
38387
|
+
var p;
|
|
38388
|
+
return editArcs(arcs, onPoint);
|
|
38389
|
+
|
|
38390
|
+
function onPoint(append, lng, lat, prevLng, prevLat, i) {
|
|
38391
|
+
var pp = p;
|
|
38392
|
+
p = proj(lng, lat);
|
|
38393
|
+
if (!p) return false; // signal that current arc contains an error
|
|
38394
|
+
|
|
38395
|
+
// Don't try to densify shorter segments (optimization)
|
|
38396
|
+
if (i > 0 && geom.distanceSq(p[0], p[1], pp[0], pp[1]) > minIntervalSq) {
|
|
38397
|
+
densifySegment(prevLng, prevLat, pp[0], pp[1], lng, lat, p[0], p[1], proj, interval)
|
|
38398
|
+
.forEach(append);
|
|
38399
|
+
}
|
|
38400
|
+
append(p);
|
|
38401
|
+
}
|
|
38402
|
+
}
|
|
38403
|
+
|
|
38404
|
+
// Use the median of intervals computed by projecting segments.
|
|
38405
|
+
// We're probing a number of points, because @proj might only be valid in
|
|
38406
|
+
// a sub-region of the dataset bbox (e.g. +proj=tpers)
|
|
38407
|
+
function findDensifyInterval(bounds, xy, proj) {
|
|
38408
|
+
var steps = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9];
|
|
38409
|
+
var points = [];
|
|
38410
|
+
for (var i=0; i<steps.length; i++) {
|
|
38411
|
+
for (var j=0; j<steps.length; j++) {
|
|
38412
|
+
points.push([steps[i], steps[j]]);
|
|
38413
|
+
}
|
|
38414
|
+
}
|
|
38415
|
+
var intervals = points.map(function(pos) {
|
|
38416
|
+
var x = bounds.xmin + bounds.width() * pos[0];
|
|
38417
|
+
var y = bounds.ymin + bounds.height() * pos[1];
|
|
38418
|
+
var a = proj(x, y);
|
|
38419
|
+
var b = proj(x + xy[0], y + xy[1]);
|
|
38420
|
+
return a && b ? geom.distance2D(a[0], a[1], b[0], b[1]) : Infinity;
|
|
38421
|
+
}).filter(function(int) {return int < Infinity;});
|
|
38422
|
+
return intervals.length > 0 ? utils.findMedian(intervals) : Infinity;
|
|
38423
|
+
}
|
|
38424
|
+
|
|
38425
|
+
// Kludgy way to get a useful interval for densifying a bounding box.
|
|
38426
|
+
// Uses a fraction of average bbox side length)
|
|
38427
|
+
// TODO: improve
|
|
38428
|
+
function findDensifyInterval2(bb, proj) {
|
|
38429
|
+
var a = proj(bb.centerX(), bb.centerY()),
|
|
38430
|
+
c = proj(bb.centerX(), bb.ymin), // right center
|
|
38431
|
+
d = proj(bb.xmax, bb.centerY()); // bottom center
|
|
38432
|
+
var interval = a && c && d ? (geom.distance2D(a[0], a[1], c[0], c[1]) +
|
|
38433
|
+
geom.distance2D(a[0], a[1], d[0], d[1])) / 5000 : Infinity;
|
|
38434
|
+
return interval;
|
|
38435
|
+
}
|
|
38436
|
+
|
|
38437
|
+
// Returns an interval in projected units
|
|
38438
|
+
function getDefaultDensifyInterval(arcs, proj) {
|
|
38439
|
+
var xy = getAvgSegment2(arcs),
|
|
38440
|
+
bb = arcs.getBounds(),
|
|
38441
|
+
intervalA = findDensifyInterval(bb, xy, proj),
|
|
38442
|
+
intervalB = findDensifyInterval2(bb, proj),
|
|
38443
|
+
interval = Math.min(intervalA, intervalB);
|
|
38444
|
+
if (interval == Infinity) {
|
|
38445
|
+
error('Densification error');
|
|
38446
|
+
}
|
|
38447
|
+
return interval;
|
|
38448
|
+
}
|
|
38449
|
+
|
|
38450
|
+
// Interpolate points into a projected line segment if needed to prevent large
|
|
38451
|
+
// deviations from path of original unprojected segment.
|
|
38452
|
+
// @points (optional) array of accumulated points
|
|
38453
|
+
function densifySegment(lng0, lat0, x0, y0, lng2, lat2, x2, y2, proj, interval, points) {
|
|
38454
|
+
// Find midpoint between two endpoints and project it (assumes longitude does
|
|
38455
|
+
// not wrap). TODO Consider bisecting along great circle path -- although this
|
|
38456
|
+
// would not be good for boundaries that follow line of constant latitude.
|
|
38457
|
+
var lng1 = (lng0 + lng2) / 2,
|
|
38458
|
+
lat1 = (lat0 + lat2) / 2,
|
|
38459
|
+
p = proj(lng1, lat1),
|
|
38460
|
+
distSq;
|
|
38461
|
+
if (!p) return; // TODO: consider if this is adequate for handling proj. errors
|
|
38462
|
+
distSq = geom.pointSegDistSq2(p[0], p[1], x0, y0, x2, y2); // sq displacement
|
|
38463
|
+
points = points || [];
|
|
38464
|
+
// Bisect current segment if the projected midpoint deviates from original
|
|
38465
|
+
// segment by more than the @interval parameter.
|
|
38466
|
+
// ... but don't bisect very small segments to prevent infinite recursion
|
|
38467
|
+
// (e.g. if projection function is discontinuous)
|
|
38468
|
+
if (distSq > interval * interval * 0.25 && geom.distance2D(lng0, lat0, lng2, lat2) > 0.01) {
|
|
38469
|
+
densifySegment(lng0, lat0, x0, y0, lng1, lat1, p[0], p[1], proj, interval, points);
|
|
38470
|
+
points.push(p);
|
|
38471
|
+
densifySegment(lng1, lat1, p[0], p[1], lng2, lat2, x2, y2, proj, interval, points);
|
|
38472
|
+
}
|
|
38473
|
+
return points;
|
|
38474
|
+
}
|
|
38475
|
+
|
|
38476
|
+
// Create rectangles around each feature in a layer
|
|
38477
|
+
cmd.rectangles = function(targetLyr, targetDataset, opts) {
|
|
38478
|
+
var crsInfo = getDatasetCrsInfo(targetDataset);
|
|
38479
|
+
var records = targetLyr.data ? targetLyr.data.getRecords() : null;
|
|
38480
|
+
var geometries;
|
|
38481
|
+
|
|
38482
|
+
if (opts.bbox) {
|
|
38483
|
+
geometries = bboxExpressionToGeometries(opts.bbox, targetLyr, targetDataset);
|
|
38484
|
+
|
|
38485
|
+
} else {
|
|
38486
|
+
if (!layerHasGeometry(targetLyr)) {
|
|
38487
|
+
stop("Layer is missing geometric shapes");
|
|
38488
|
+
}
|
|
38489
|
+
geometries = shapesToBoxGeometries(targetLyr, targetDataset, opts);
|
|
38490
|
+
}
|
|
38491
|
+
|
|
38492
|
+
var geojson = {
|
|
38493
|
+
type: 'FeatureCollection',
|
|
38494
|
+
features: geometries.map(function(geom, i) {
|
|
38495
|
+
var rec = records && records[i] || null;
|
|
38496
|
+
if (rec && opts.no_replace) {
|
|
38497
|
+
rec = utils.extend({}, rec); // make a copy
|
|
38498
|
+
}
|
|
38499
|
+
return {
|
|
38500
|
+
type: 'Feature',
|
|
38501
|
+
properties: rec,
|
|
38502
|
+
geometry: geom
|
|
38503
|
+
};
|
|
38504
|
+
})
|
|
38505
|
+
};
|
|
38506
|
+
var dataset = importGeoJSON(geojson, {});
|
|
38507
|
+
setDatasetCrsInfo(dataset, crsInfo);
|
|
38508
|
+
var outputLayers = mergeDatasetsIntoDataset(targetDataset, [dataset]);
|
|
38509
|
+
setOutputLayerName(outputLayers[0], targetLyr, null, opts);
|
|
38510
|
+
return outputLayers;
|
|
38511
|
+
};
|
|
38512
|
+
|
|
38513
|
+
|
|
38514
|
+
|
|
38515
|
+
|
|
38516
|
+
function shapesToBoxGeometries(lyr, dataset, opts) {
|
|
38517
|
+
var crsInfo = getDatasetCrsInfo(dataset);
|
|
38518
|
+
return lyr.shapes.map(function(shp) {
|
|
38519
|
+
var bounds = lyr.geometry_type == 'point' ?
|
|
38520
|
+
getPointFeatureBounds(shp) : dataset.arcs.getMultiShapeBounds(shp);
|
|
38521
|
+
bounds = applyRectangleOptions(bounds, crsInfo.crs, opts);
|
|
38522
|
+
if (!bounds) return null;
|
|
38523
|
+
return bboxToPolygon(bounds.toArray(), opts);
|
|
38524
|
+
});
|
|
38525
|
+
}
|
|
38526
|
+
|
|
38527
|
+
function bboxExpressionToGeometries(exp, lyr, dataset, opts) {
|
|
38528
|
+
var compiled = compileFeatureExpression(exp, lyr, dataset.arcs, {});
|
|
38529
|
+
var n = getFeatureCount(lyr);
|
|
38530
|
+
var result;
|
|
38531
|
+
var geometries = [];
|
|
38532
|
+
for (var i=0; i<n; i++) {
|
|
38533
|
+
result = compiled(i);
|
|
38534
|
+
if (!looksLikeBbox(result)) {
|
|
38535
|
+
stop('Invalid bbox value (expected a GeoJSON-type bbox):', result);
|
|
38536
|
+
}
|
|
38537
|
+
geometries.push(bboxToPolygon(result));
|
|
38538
|
+
}
|
|
38539
|
+
return geometries;
|
|
38540
|
+
}
|
|
38541
|
+
|
|
38542
|
+
function looksLikeBbox(o) {
|
|
38543
|
+
if (!o || o.length != 4) return false;
|
|
38544
|
+
if (o.some(isNaN)) return false;
|
|
38545
|
+
if (o[0] <= o[2] == false || o[1] <= o[3] == false) return false;
|
|
38546
|
+
return true;
|
|
38547
|
+
}
|
|
38548
|
+
|
|
38549
|
+
// Create rectangles around one or more target layers
|
|
38550
|
+
//
|
|
38551
|
+
cmd.rectangle2 = function(target, opts) {
|
|
38552
|
+
// if target layer is a rectangle and we're applying frame properties,
|
|
38553
|
+
// turn the target into a frame instead of creating a new rectangle
|
|
38554
|
+
if (target.layers.length == 1 && opts.width &&
|
|
38555
|
+
layerIsRectangle(target.layers[0], target.dataset.arcs)) {
|
|
38556
|
+
applyFrameProperties(target.layers[0], opts);
|
|
38557
|
+
return;
|
|
38558
|
+
}
|
|
38559
|
+
var datasets = target.layers.map(function(lyr) {
|
|
38560
|
+
var dataset = cmd.rectangle({layer: lyr, dataset: target.dataset}, opts);
|
|
38561
|
+
setOutputLayerName(dataset.layers[0], lyr, null, opts);
|
|
38562
|
+
if (!opts.no_replace) {
|
|
38563
|
+
dataset.layers[0].name = lyr.name || dataset.layers[0].name;
|
|
38564
|
+
}
|
|
38565
|
+
return dataset;
|
|
38566
|
+
});
|
|
38567
|
+
return mergeDatasetsIntoDataset(target.dataset, datasets);
|
|
38568
|
+
};
|
|
38569
|
+
|
|
38570
|
+
cmd.rectangle = function(target, opts) {
|
|
38571
|
+
var bounds, crsInfo;
|
|
38572
|
+
if (opts.bbox) {
|
|
38573
|
+
bounds = new Bounds(opts.bbox);
|
|
38574
|
+
crsInfo = target && getDatasetCrsInfo(target.dataset) ||
|
|
38575
|
+
probablyDecimalDegreeBounds(bounds) && getCrsInfo('wgs84') || {};
|
|
38576
|
+
} else if (target) {
|
|
38577
|
+
bounds = getLayerBounds(target.layer, target.dataset.arcs);
|
|
38578
|
+
crsInfo = getDatasetCrsInfo(target.dataset);
|
|
38579
|
+
}
|
|
38580
|
+
bounds = bounds && applyRectangleOptions(bounds, crsInfo.crs, opts);
|
|
38581
|
+
if (!bounds || !bounds.hasBounds()) {
|
|
38582
|
+
stop('Missing rectangle extent');
|
|
38583
|
+
}
|
|
38584
|
+
var feature = {
|
|
38585
|
+
type: 'Feature',
|
|
38586
|
+
properties: {},
|
|
38587
|
+
geometry: bboxToPolygon(bounds.toArray(), opts)
|
|
38588
|
+
};
|
|
38589
|
+
var dataset = importGeoJSON(feature, {});
|
|
38590
|
+
applyFrameProperties(dataset.layers[0], opts);
|
|
38591
|
+
dataset.layers[0].name = opts.name || 'rectangle';
|
|
38592
|
+
setDatasetCrsInfo(dataset, crsInfo);
|
|
38593
|
+
return dataset;
|
|
38199
38594
|
};
|
|
38200
38595
|
|
|
38596
|
+
function applyFrameProperties(lyr, opts) {
|
|
38597
|
+
if (!opts.width) return;
|
|
38598
|
+
if (!lyr.data) initDataTable(lyr);
|
|
38599
|
+
var d = lyr.data.getRecords()[0] || {};
|
|
38600
|
+
d.width = parseSizeParam(opts.width);
|
|
38601
|
+
d.type = 'frame';
|
|
38602
|
+
}
|
|
38603
|
+
|
|
38604
|
+
function applyRectangleOptions(bounds, crs, opts) {
|
|
38605
|
+
var isGeoBox = probablyDecimalDegreeBounds(bounds);
|
|
38606
|
+
if (opts.offset) {
|
|
38607
|
+
bounds = applyBoundsOffset(opts.offset, bounds, crs);
|
|
38608
|
+
}
|
|
38609
|
+
if (bounds.area() > 0 === false) return null;
|
|
38610
|
+
if (opts.aspect_ratio) {
|
|
38611
|
+
bounds = applyAspectRatio(opts.aspect_ratio, bounds);
|
|
38612
|
+
}
|
|
38613
|
+
if (isGeoBox) {
|
|
38614
|
+
bounds = clampToWorldBounds(bounds);
|
|
38615
|
+
}
|
|
38616
|
+
return bounds;
|
|
38617
|
+
}
|
|
38618
|
+
|
|
38619
|
+
// opt: aspect ratio as a single number or a range (e.g. "1,2");
|
|
38620
|
+
function applyAspectRatio(opt, bounds) {
|
|
38621
|
+
var range = String(opt).split(',').map(parseFloat),
|
|
38622
|
+
aspectRatio = bounds.width() / bounds.height(),
|
|
38623
|
+
min, max; // min is height limit, max is width limit
|
|
38624
|
+
if (range.length == 1) {
|
|
38625
|
+
range.push(range[0]);
|
|
38626
|
+
} else if (range[0] > range[1]) {
|
|
38627
|
+
range.reverse();
|
|
38628
|
+
}
|
|
38629
|
+
min = range[0];
|
|
38630
|
+
max = range[1];
|
|
38631
|
+
if (!min && !max) return bounds;
|
|
38632
|
+
if (!min) min = -Infinity;
|
|
38633
|
+
if (!max) max = Infinity;
|
|
38634
|
+
if (aspectRatio < min) {
|
|
38635
|
+
bounds.fillOut(min);
|
|
38636
|
+
} else if (aspectRatio > max) {
|
|
38637
|
+
bounds.fillOut(max);
|
|
38638
|
+
}
|
|
38639
|
+
return bounds;
|
|
38640
|
+
}
|
|
38641
|
+
|
|
38642
|
+
function applyBoundsOffset(offsetOpt, bounds, crs) {
|
|
38643
|
+
var offsets = convertFourSides(offsetOpt, crs, bounds);
|
|
38644
|
+
bounds.padBounds(offsets[0], offsets[1], offsets[2], offsets[3]);
|
|
38645
|
+
return bounds;
|
|
38646
|
+
}
|
|
38647
|
+
|
|
38648
|
+
function bboxToPolygon(bbox, optsArg) {
|
|
38649
|
+
var opts = optsArg || {};
|
|
38650
|
+
var coords = bboxToCoords(bbox);
|
|
38651
|
+
if (opts.interval > 0) {
|
|
38652
|
+
coords = densifyPathByInterval(coords, opts.interval);
|
|
38653
|
+
}
|
|
38654
|
+
return {
|
|
38655
|
+
type: 'Polygon',
|
|
38656
|
+
coordinates: [coords]
|
|
38657
|
+
};
|
|
38658
|
+
}
|
|
38659
|
+
|
|
38660
|
+
var Rectangle = /*#__PURE__*/Object.freeze({
|
|
38661
|
+
__proto__: null,
|
|
38662
|
+
applyAspectRatio: applyAspectRatio,
|
|
38663
|
+
bboxToPolygon: bboxToPolygon
|
|
38664
|
+
});
|
|
38665
|
+
|
|
38666
|
+
cmd.frame = function(catalog, targets, opts) {
|
|
38667
|
+
var widthPx, heightPx, aspectRatio, bbox;
|
|
38668
|
+
if (opts.width) {
|
|
38669
|
+
widthPx = parseSizeParam(opts.width);
|
|
38670
|
+
if (widthPx > 0 === false) {
|
|
38671
|
+
stop('Invalid width parameter:', opts.width);
|
|
38672
|
+
}
|
|
38673
|
+
}
|
|
38674
|
+
if (opts.height) {
|
|
38675
|
+
heightPx = parseSizeParam(opts.height);
|
|
38676
|
+
if (heightPx > 0 === false) {
|
|
38677
|
+
stop('Invalid height parameter:', opts.height);
|
|
38678
|
+
}
|
|
38679
|
+
}
|
|
38680
|
+
if (!widthPx && !heightPx) {
|
|
38681
|
+
widthPx = 800;
|
|
38682
|
+
message('Using default 800px frame width');
|
|
38683
|
+
}
|
|
38684
|
+
|
|
38685
|
+
if (opts.aspect_ratio) {
|
|
38686
|
+
if (opts.aspect_ratio > 0 === false) {
|
|
38687
|
+
stop('Invalid aspect-ratio parameter:', opts.aspect_ratio);
|
|
38688
|
+
}
|
|
38689
|
+
if (!heightPx) {
|
|
38690
|
+
heightPx = roundToDigits(widthPx / opts.aspect_ratio, 1);
|
|
38691
|
+
} else if (!widthPx) {
|
|
38692
|
+
widthPx = roundToDigits(heightPx * opts.aspect_ratio, 1);
|
|
38693
|
+
}
|
|
38694
|
+
}
|
|
38695
|
+
|
|
38696
|
+
if (opts.bbox) {
|
|
38697
|
+
bbox = opts.bbox;
|
|
38698
|
+
// TODO: validate
|
|
38699
|
+
} else {
|
|
38700
|
+
var datasets = utils.pluck(targets, 'dataset');
|
|
38701
|
+
requireDatasetsHaveCompatibleCRS(datasets, 'Targets include both projected and unprojected coordinates');
|
|
38702
|
+
bbox = getTargetBbox(targets);
|
|
38703
|
+
if (!bbox) {
|
|
38704
|
+
stop('Command target is missing geographical bounds');
|
|
38705
|
+
}
|
|
38706
|
+
}
|
|
38707
|
+
|
|
38708
|
+
applyPercentageOffsets(bbox, opts.offset || opts.offsets);
|
|
38709
|
+
applyPixelOffsets(bbox, widthPx, heightPx, opts.offset || opts.offsets);
|
|
38710
|
+
|
|
38711
|
+
if (bbox[3] - bbox[1] > 0 === false || bbox[2] - bbox[0] > 0 === false) {
|
|
38712
|
+
stop('Frame has a collapsed bbox');
|
|
38713
|
+
}
|
|
38714
|
+
|
|
38715
|
+
aspectRatio = (bbox[2] - bbox[0]) / (bbox[3] - bbox[1]);
|
|
38716
|
+
if (!widthPx) {
|
|
38717
|
+
widthPx = roundToDigits(heightPx * aspectRatio, 1);
|
|
38718
|
+
} else if (!heightPx) {
|
|
38719
|
+
heightPx = roundToDigits(widthPx / aspectRatio, 1);
|
|
38720
|
+
}
|
|
38721
|
+
|
|
38722
|
+
var feature = {
|
|
38723
|
+
type: 'Feature',
|
|
38724
|
+
properties: {type: 'frame', width: widthPx, height: heightPx},
|
|
38725
|
+
geometry: bboxToPolygon(bbox)
|
|
38726
|
+
};
|
|
38727
|
+
var frameDataset = importGeoJSON(feature);
|
|
38728
|
+
// set CRS from target dataset
|
|
38729
|
+
// TODO: handle case: targets have different projections
|
|
38730
|
+
// TODO: handle case: first target is missing CRS
|
|
38731
|
+
if (targets.length > 0) {
|
|
38732
|
+
var crsInfo = getDatasetCrsInfo(targets[0].dataset);
|
|
38733
|
+
setDatasetCrsInfo(frameDataset, crsInfo);
|
|
38734
|
+
}
|
|
38735
|
+
frameDataset.layers[0].name = opts.name || 'frame';
|
|
38736
|
+
catalog.addDataset(frameDataset);
|
|
38737
|
+
};
|
|
38738
|
+
|
|
38739
|
+
function fillOutBbox(bbox, widthPx, heightPx) {
|
|
38740
|
+
var hpad = 0, vpad = 0;
|
|
38741
|
+
var w = bbox[2] - bbox[0];
|
|
38742
|
+
var h = bbox[3] - bbox[1];
|
|
38743
|
+
if (widthPx / heightPx > w / h) { // need to add horizontal padding
|
|
38744
|
+
hpad = h * widthPx / heightPx - w;
|
|
38745
|
+
} else {
|
|
38746
|
+
vpad = w * heightPx / widthPx - h;
|
|
38747
|
+
}
|
|
38748
|
+
bbox[0] -= hpad / 2;
|
|
38749
|
+
bbox[1] -= vpad / 2;
|
|
38750
|
+
bbox[2] += hpad / 2;
|
|
38751
|
+
bbox[3] += vpad / 2;
|
|
38752
|
+
}
|
|
38753
|
+
|
|
38754
|
+
function applyPercentageOffsets(bbox, arg) {
|
|
38755
|
+
var sides = getPctOffsets(arg);
|
|
38756
|
+
var l = sides[0],
|
|
38757
|
+
b = sides[1],
|
|
38758
|
+
r = sides[2],
|
|
38759
|
+
t = sides[3],
|
|
38760
|
+
w2 = (bbox[2] - bbox[0]) / (1 - l - r),
|
|
38761
|
+
h2 = (bbox[3] - bbox[1]) / (1 - t - b);
|
|
38762
|
+
bbox[0] -= l * w2;
|
|
38763
|
+
bbox[1] -= b * h2;
|
|
38764
|
+
bbox[2] += r * w2;
|
|
38765
|
+
bbox[3] += t * h2;
|
|
38766
|
+
}
|
|
38767
|
+
|
|
38768
|
+
function applyPixelOffsets(bbox, widthPx, heightPx, arg) {
|
|
38769
|
+
var sides = getPixelOffsets(arg);
|
|
38770
|
+
var l = sides[0],
|
|
38771
|
+
b = sides[1],
|
|
38772
|
+
r = sides[2],
|
|
38773
|
+
t = sides[3],
|
|
38774
|
+
scale, w;
|
|
38775
|
+
|
|
38776
|
+
if (widthPx && heightPx) {
|
|
38777
|
+
// add padding to bbox to match pixel dimensions, if needed
|
|
38778
|
+
fillOutBbox(bbox, widthPx, heightPx);
|
|
38779
|
+
}
|
|
38780
|
+
|
|
38781
|
+
w = bbox[2] - bbox[0];
|
|
38782
|
+
bbox[3] - bbox[1];
|
|
38783
|
+
|
|
38784
|
+
if (widthPx) {
|
|
38785
|
+
scale = w / (widthPx - l - r);
|
|
38786
|
+
} else {
|
|
38787
|
+
scale = w / (heightPx - t - b);
|
|
38788
|
+
}
|
|
38789
|
+
|
|
38790
|
+
bbox[0] -= scale * l;
|
|
38791
|
+
bbox[1] -= scale * b;
|
|
38792
|
+
bbox[2] += scale * r;
|
|
38793
|
+
bbox[3] += scale * t;
|
|
38794
|
+
return scale;
|
|
38795
|
+
}
|
|
38796
|
+
|
|
38797
|
+
function getPctOffsets(arg) {
|
|
38798
|
+
return adjustOffsetsArg(arg).map(str => {
|
|
38799
|
+
return str.includes('%') ? utils.parsePercent(str) : 0;
|
|
38800
|
+
});
|
|
38801
|
+
}
|
|
38802
|
+
|
|
38803
|
+
function getPixelOffsets(arg) {
|
|
38804
|
+
return adjustOffsetsArg(arg).map(str => {
|
|
38805
|
+
return str.includes('%') ? 0 : parseSizeParam(str);
|
|
38806
|
+
});
|
|
38807
|
+
}
|
|
38808
|
+
|
|
38809
|
+
function adjustOffsetsArg(arg) {
|
|
38810
|
+
if (!arg) arg = ['0'];
|
|
38811
|
+
if (arg.length == 1) {
|
|
38812
|
+
return [arg[0], arg[0], arg[0], arg[0]];
|
|
38813
|
+
}
|
|
38814
|
+
if (arg.length != 4) {
|
|
38815
|
+
stop('List of offsets should have 4 values');
|
|
38816
|
+
}
|
|
38817
|
+
return arg;
|
|
38818
|
+
}
|
|
38819
|
+
|
|
38820
|
+
function getTargetBbox(targets) {
|
|
38821
|
+
var expanded = expandCommandTargets(targets);
|
|
38822
|
+
var bounds = expanded.reduce(function(memo, o) {
|
|
38823
|
+
return memo.mergeBounds(getLayerBounds(o.layer, o.dataset.arcs));
|
|
38824
|
+
}, new Bounds());
|
|
38825
|
+
return bounds.hasBounds() ? bounds.toArray() : null;
|
|
38826
|
+
}
|
|
38201
38827
|
|
|
38202
38828
|
// Convert width and height args to aspect ratio arg for the rectangle() function
|
|
38203
38829
|
function getAspectRatioArg(widthArg, heightArg) {
|
|
@@ -38211,21 +38837,6 @@ ${svg}
|
|
|
38211
38837
|
}).reverse().join(',');
|
|
38212
38838
|
}
|
|
38213
38839
|
|
|
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
38840
|
var Frame = /*#__PURE__*/Object.freeze({
|
|
38230
38841
|
__proto__: null,
|
|
38231
38842
|
getAspectRatioArg: getAspectRatioArg
|
|
@@ -38679,473 +39290,6 @@ ${svg}
|
|
|
38679
39290
|
return maxValue;
|
|
38680
39291
|
}
|
|
38681
39292
|
|
|
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
39293
|
function getSemiMinorAxis(P) {
|
|
39150
39294
|
return P.a * Math.sqrt(1 - (P.es || 0));
|
|
39151
39295
|
}
|
|
@@ -45259,14 +45403,16 @@ ${svg}
|
|
|
45259
45403
|
}
|
|
45260
45404
|
|
|
45261
45405
|
function commandAcceptsMultipleTargetDatasets(name) {
|
|
45262
|
-
return name == 'rotate' || name == 'info' || name == 'proj' ||
|
|
45263
|
-
name == '
|
|
45264
|
-
name == '
|
|
45406
|
+
return name == 'rotate' || name == 'info' || name == 'proj' ||
|
|
45407
|
+
name == 'require' || name == 'drop' || name == 'target' ||
|
|
45408
|
+
name == 'if' || name == 'elif' || name == 'else' || name == 'endif' ||
|
|
45409
|
+
name == 'run' || name == 'i' || name == 'snap' || name == 'frame' ||
|
|
45410
|
+
name == 'comment';
|
|
45265
45411
|
}
|
|
45266
45412
|
|
|
45267
45413
|
function commandAcceptsEmptyTarget(name) {
|
|
45268
45414
|
return name == 'graticule' || name == 'i' || name == 'help' ||
|
|
45269
|
-
name == 'point-grid' || name == 'shape' || name == 'rectangle' ||
|
|
45415
|
+
name == 'point-grid' || name == 'shape' || name == 'rectangle' || name == 'frame' ||
|
|
45270
45416
|
name == 'require' || name == 'run' || name == 'define' ||
|
|
45271
45417
|
name == 'include' || name == 'print' || name == 'comment' || name == 'if' || name == 'elif' ||
|
|
45272
45418
|
name == 'else' || name == 'endif' || name == 'stop' || name == 'add-shape' ||
|
|
@@ -45290,12 +45436,14 @@ ${svg}
|
|
|
45290
45436
|
}
|
|
45291
45437
|
|
|
45292
45438
|
if (name == 'comment') {
|
|
45439
|
+
cmd.comment(opts);
|
|
45293
45440
|
return done(null);
|
|
45294
45441
|
}
|
|
45295
45442
|
|
|
45296
45443
|
if (!job) job = new Job();
|
|
45297
45444
|
job.startCommand(command);
|
|
45298
45445
|
|
|
45446
|
+
|
|
45299
45447
|
try { // catch errors from synchronous functions
|
|
45300
45448
|
T$1.start();
|
|
45301
45449
|
|
|
@@ -45394,8 +45542,8 @@ ${svg}
|
|
|
45394
45542
|
} else if (name == 'colorizer') {
|
|
45395
45543
|
outputLayers = cmd.colorizer(opts);
|
|
45396
45544
|
|
|
45397
|
-
} else if (name == 'comment') {
|
|
45398
|
-
|
|
45545
|
+
// } else if (name == 'comment') {
|
|
45546
|
+
// // no-op
|
|
45399
45547
|
|
|
45400
45548
|
} else if (name == 'dashlines') {
|
|
45401
45549
|
applyCommandToEachLayer(cmd.dashlines, targetLayers, targetDataset, opts);
|
|
@@ -45453,9 +45601,8 @@ ${svg}
|
|
|
45453
45601
|
} else if (name == 'filter-slivers') {
|
|
45454
45602
|
applyCommandToEachLayer(cmd.filterSlivers, targetLayers, targetDataset, opts);
|
|
45455
45603
|
|
|
45456
|
-
|
|
45457
|
-
|
|
45458
|
-
// cmd.frame(job.catalog, source, opts);
|
|
45604
|
+
} else if (name == 'frame') {
|
|
45605
|
+
cmd.frame(job.catalog, targets, opts);
|
|
45459
45606
|
|
|
45460
45607
|
} else if (name == 'fuzzy-join') {
|
|
45461
45608
|
applyCommandToEachLayer(cmd.fuzzyJoin, targetLayers, arcs, source, opts);
|
|
@@ -45557,10 +45704,7 @@ ${svg}
|
|
|
45557
45704
|
cmd.proj(targ.dataset, job.catalog, opts);
|
|
45558
45705
|
});
|
|
45559
45706
|
|
|
45560
|
-
} else if (name == 'rectangle'
|
|
45561
|
-
if (name == 'frame' && !opts.width) {
|
|
45562
|
-
stop('Command requires a width= argument');
|
|
45563
|
-
}
|
|
45707
|
+
} else if (name == 'rectangle') {
|
|
45564
45708
|
if (source || opts.bbox || targets.length === 0) {
|
|
45565
45709
|
job.catalog.addDataset(cmd.rectangle(source || targets?.[0], opts));
|
|
45566
45710
|
} else {
|
|
@@ -45726,7 +45870,7 @@ ${svg}
|
|
|
45726
45870
|
});
|
|
45727
45871
|
}
|
|
45728
45872
|
|
|
45729
|
-
var version = "0.6.
|
|
45873
|
+
var version = "0.6.100";
|
|
45730
45874
|
|
|
45731
45875
|
// Parse command line args into commands and run them
|
|
45732
45876
|
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.
|
|
@@ -45877,13 +46021,16 @@ ${svg}
|
|
|
45877
46021
|
|
|
45878
46022
|
function nextGroup(prevJob, commands, next) {
|
|
45879
46023
|
runParsedCommands(commands, new Job(), function(err, job) {
|
|
45880
|
-
err =
|
|
46024
|
+
err = handleNonFatalError(err);
|
|
45881
46025
|
next(err, job);
|
|
45882
46026
|
});
|
|
45883
46027
|
}
|
|
45884
46028
|
|
|
45885
46029
|
function done(err, job) {
|
|
45886
|
-
|
|
46030
|
+
if (job && inControlBlock(job)) {
|
|
46031
|
+
message('Warning: -if command is missing a matching -endif');
|
|
46032
|
+
}
|
|
46033
|
+
err = handleNonFatalError(err);
|
|
45887
46034
|
if (err) printError(err);
|
|
45888
46035
|
callback(err, job);
|
|
45889
46036
|
}
|
|
@@ -45960,7 +46107,7 @@ ${svg}
|
|
|
45960
46107
|
}
|
|
45961
46108
|
}
|
|
45962
46109
|
|
|
45963
|
-
function
|
|
46110
|
+
function handleNonFatalError(err) {
|
|
45964
46111
|
if (err && err.name == 'NonFatalError') {
|
|
45965
46112
|
printError(err);
|
|
45966
46113
|
return null;
|