mapshaper 0.5.79 → 0.5.80
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/CHANGELOG.md +3 -0
- package/mapshaper.js +595 -346
- package/package.json +1 -1
- package/www/mapshaper.js +595 -346
package/mapshaper.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
|
|
3
|
-
var VERSION = "0.5.
|
|
3
|
+
var VERSION = "0.5.79";
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
var utils = /*#__PURE__*/Object.freeze({
|
|
@@ -1745,9 +1745,17 @@
|
|
|
1745
1745
|
return [xmin, ymin, xmax, ymax];
|
|
1746
1746
|
}
|
|
1747
1747
|
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1748
|
+
var WGS84 = {
|
|
1749
|
+
// https://en.wikipedia.org/wiki/Earth_radius
|
|
1750
|
+
SEMIMAJOR_AXIS: 6378137,
|
|
1751
|
+
SEMIMINOR_AXIS: 6356752.3142,
|
|
1752
|
+
AUTHALIC_RADIUS: 6371007.2,
|
|
1753
|
+
VOLUMETRIC_RADIUS: 6371000.8
|
|
1754
|
+
};
|
|
1755
|
+
|
|
1756
|
+
// TODO: remove this constant, use actual data from dataset CRS,
|
|
1757
|
+
// also consider using ellipsoidal formulas where greater accuracy might be important.
|
|
1758
|
+
var R$1 = WGS84.SEMIMAJOR_AXIS;
|
|
1751
1759
|
var D2R = Math.PI / 180;
|
|
1752
1760
|
var R2D = 180 / Math.PI;
|
|
1753
1761
|
|
|
@@ -2342,12 +2350,12 @@
|
|
|
2342
2350
|
}, 0);
|
|
2343
2351
|
}
|
|
2344
2352
|
|
|
2345
|
-
function getSphericalShapeArea(shp, arcs) {
|
|
2353
|
+
function getSphericalShapeArea(shp, arcs, R) {
|
|
2346
2354
|
if (arcs.isPlanar()) {
|
|
2347
2355
|
error("[getSphericalShapeArea()] Function requires decimal degree coordinates");
|
|
2348
2356
|
}
|
|
2349
2357
|
return (shp || []).reduce(function(area, ids) {
|
|
2350
|
-
return area + getSphericalPathArea(ids, arcs);
|
|
2358
|
+
return area + getSphericalPathArea(ids, arcs, R);
|
|
2351
2359
|
}, 0);
|
|
2352
2360
|
}
|
|
2353
2361
|
|
|
@@ -2464,16 +2472,17 @@
|
|
|
2464
2472
|
return (arcs.isPlanar() ? getPlanarPathArea : getSphericalPathArea)(ids, arcs);
|
|
2465
2473
|
}
|
|
2466
2474
|
|
|
2467
|
-
function getSphericalPathArea(ids, arcs) {
|
|
2475
|
+
function getSphericalPathArea(ids, arcs, R) {
|
|
2468
2476
|
var iter = arcs.getShapeIter(ids);
|
|
2469
|
-
return getSphericalPathArea2(iter);
|
|
2477
|
+
return getSphericalPathArea2(iter, R);
|
|
2470
2478
|
}
|
|
2471
2479
|
|
|
2472
|
-
function getSphericalPathArea2(iter) {
|
|
2480
|
+
function getSphericalPathArea2(iter, R) {
|
|
2473
2481
|
var sum = 0,
|
|
2474
2482
|
started = false,
|
|
2475
2483
|
deg2rad = Math.PI / 180,
|
|
2476
2484
|
x, y, xp, yp;
|
|
2485
|
+
R = R || WGS84.SEMIMAJOR_AXIS;
|
|
2477
2486
|
while (iter.hasNext()) {
|
|
2478
2487
|
x = iter.x * deg2rad;
|
|
2479
2488
|
y = Math.sin(iter.y * deg2rad);
|
|
@@ -2485,7 +2494,7 @@
|
|
|
2485
2494
|
xp = x;
|
|
2486
2495
|
yp = y;
|
|
2487
2496
|
}
|
|
2488
|
-
return sum / 2 *
|
|
2497
|
+
return sum / 2 * R * R;
|
|
2489
2498
|
}
|
|
2490
2499
|
|
|
2491
2500
|
// Get path area from an array of [x, y] points
|
|
@@ -3949,7 +3958,7 @@
|
|
|
3949
3958
|
function requireSinglePointLayer(lyr, msg) {
|
|
3950
3959
|
requirePointLayer(lyr);
|
|
3951
3960
|
if (countMultiPartFeatures(lyr) > 0) {
|
|
3952
|
-
stop(msg || 'This command requires single points');
|
|
3961
|
+
stop(msg || 'This command requires single points; layer contains multi-point features.');
|
|
3953
3962
|
}
|
|
3954
3963
|
}
|
|
3955
3964
|
|
|
@@ -4004,6 +4013,7 @@
|
|
|
4004
4013
|
return opts && opts.no_replace ? {geometry_type: src.geometry_type} : src;
|
|
4005
4014
|
}
|
|
4006
4015
|
|
|
4016
|
+
//
|
|
4007
4017
|
function setOutputLayerName(dest, src, defName, opts) {
|
|
4008
4018
|
opts = opts || {};
|
|
4009
4019
|
if (opts.name) {
|
|
@@ -4080,6 +4090,7 @@
|
|
|
4080
4090
|
return counts;
|
|
4081
4091
|
}
|
|
4082
4092
|
|
|
4093
|
+
// Returns a Bounds object
|
|
4083
4094
|
function getLayerBounds(lyr, arcs) {
|
|
4084
4095
|
var bounds = null;
|
|
4085
4096
|
if (lyr.geometry_type == 'point') {
|
|
@@ -6151,9 +6162,11 @@
|
|
|
6151
6162
|
arcCount += n;
|
|
6152
6163
|
});
|
|
6153
6164
|
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6165
|
+
if (arcSources.length > 0) {
|
|
6166
|
+
mergedArcs = mergeArcs(arcSources);
|
|
6167
|
+
if (mergedArcs.size() != arcCount) {
|
|
6168
|
+
error("[mergeDatasets()] Arc indexing error");
|
|
6169
|
+
}
|
|
6157
6170
|
}
|
|
6158
6171
|
|
|
6159
6172
|
return {
|
|
@@ -6172,6 +6185,8 @@
|
|
|
6172
6185
|
}
|
|
6173
6186
|
|
|
6174
6187
|
function mergeArcs(arr) {
|
|
6188
|
+
// Returning the original causes a test to fail
|
|
6189
|
+
// if (arr.length < 2) return arr[0];
|
|
6175
6190
|
var dataArr = arr.map(function(arcs) {
|
|
6176
6191
|
if (arcs.getRetainedInterval() > 0) {
|
|
6177
6192
|
verbose("Baking-in simplification setting.");
|
|
@@ -6776,31 +6791,47 @@
|
|
|
6776
6791
|
dataset.layers = currLayers;
|
|
6777
6792
|
}
|
|
6778
6793
|
|
|
6779
|
-
// Replace a layer
|
|
6794
|
+
// Replace a layer with a layer from a second dataset
|
|
6795
|
+
// (in-place)
|
|
6780
6796
|
// (Typically, the second dataset is imported from dynamically generated GeoJSON and contains one layer)
|
|
6781
6797
|
function replaceLayerContents(lyr, dataset, dataset2) {
|
|
6798
|
+
var lyr2 = mergeOutputLayerIntoDataset(lyr, dataset, dataset2, {});
|
|
6799
|
+
if (layerHasPaths(lyr2)) {
|
|
6800
|
+
buildTopology(dataset);
|
|
6801
|
+
}
|
|
6802
|
+
}
|
|
6803
|
+
|
|
6804
|
+
function mergeOutputLayerIntoDataset(lyr, dataset, dataset2, opts) {
|
|
6782
6805
|
if (!dataset2 || dataset2.layers.length != 1) {
|
|
6783
|
-
error('Invalid
|
|
6806
|
+
error('Invalid source dataset');
|
|
6784
6807
|
}
|
|
6785
6808
|
if (dataset.layers.includes(lyr) === false) {
|
|
6786
6809
|
error('Invalid target layer');
|
|
6787
6810
|
}
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
mergeDatasetsIntoDataset(dataset, [dataset2]);
|
|
6792
|
-
}
|
|
6811
|
+
// this command returns merged layers instead of adding them to target dataset
|
|
6812
|
+
var outputLayers = mergeDatasetsIntoDataset(dataset, [dataset2]);
|
|
6813
|
+
var lyr2 = outputLayers[0];
|
|
6793
6814
|
|
|
6794
|
-
|
|
6815
|
+
// TODO: find a more reliable way of knowing when to copy data
|
|
6816
|
+
var copyData = !lyr2.data && lyr.data && getFeatureCount(lyr2) == lyr.data.size();
|
|
6795
6817
|
|
|
6796
|
-
if (
|
|
6797
|
-
|
|
6818
|
+
if (copyData) {
|
|
6819
|
+
lyr2.data = opts.no_replace ? lyr.data.clone() : lyr.data;
|
|
6798
6820
|
}
|
|
6799
|
-
if (
|
|
6800
|
-
//
|
|
6801
|
-
|
|
6802
|
-
|
|
6821
|
+
if (opts.no_replace) {
|
|
6822
|
+
// dataset.layers.push(lyr2);
|
|
6823
|
+
|
|
6824
|
+
} else {
|
|
6825
|
+
lyr2 = Object.assign(lyr, {data: null, shapes: null}, lyr2);
|
|
6826
|
+
if (layerHasPaths(lyr)) {
|
|
6827
|
+
// Remove unused arcs from replaced layer
|
|
6828
|
+
// TODO: consider using clean insead of this
|
|
6829
|
+
dissolveArcs(dataset);
|
|
6830
|
+
}
|
|
6803
6831
|
}
|
|
6832
|
+
|
|
6833
|
+
lyr2.name = opts.name || lyr2.name;
|
|
6834
|
+
return lyr2;
|
|
6804
6835
|
}
|
|
6805
6836
|
|
|
6806
6837
|
// Transform the points in a dataset in-place; don't clean up corrupted shapes
|
|
@@ -6829,6 +6860,7 @@
|
|
|
6829
6860
|
pruneArcs: pruneArcs,
|
|
6830
6861
|
replaceLayers: replaceLayers,
|
|
6831
6862
|
replaceLayerContents: replaceLayerContents,
|
|
6863
|
+
mergeOutputLayerIntoDataset: mergeOutputLayerIntoDataset,
|
|
6832
6864
|
transformPoints: transformPoints
|
|
6833
6865
|
});
|
|
6834
6866
|
|
|
@@ -9412,6 +9444,12 @@
|
|
|
9412
9444
|
area: function() {
|
|
9413
9445
|
return _isPlanar ? ctx.planarArea : geom.getSphericalShapeArea(_ids, arcs);
|
|
9414
9446
|
},
|
|
9447
|
+
// area2: function() {
|
|
9448
|
+
// return _isPlanar ? ctx.planarArea : geom.getSphericalShapeArea(_ids, arcs, WGS84.SEMIMINOR_RADIUS);
|
|
9449
|
+
// },
|
|
9450
|
+
// area3: function() {
|
|
9451
|
+
// return _isPlanar ? ctx.planarArea : geom.getSphericalShapeArea(_ids, arcs, WGS84.AUTHALIC_RADIUS);
|
|
9452
|
+
// },
|
|
9415
9453
|
perimeter: function() {
|
|
9416
9454
|
return geom.getShapePerimeter(_ids, arcs);
|
|
9417
9455
|
},
|
|
@@ -13332,14 +13370,14 @@
|
|
|
13332
13370
|
return mergeArcs([arcs, new ArcCollection(nn, xx, yy)]);
|
|
13333
13371
|
}
|
|
13334
13372
|
|
|
13335
|
-
var roundCoord$
|
|
13373
|
+
var roundCoord$2 = getRoundingFunction(0.01);
|
|
13336
13374
|
|
|
13337
13375
|
function stringifyVertex(p) {
|
|
13338
|
-
return ' ' + roundCoord$
|
|
13376
|
+
return ' ' + roundCoord$2(p[0]) + ' ' + roundCoord$2(p[1]);
|
|
13339
13377
|
}
|
|
13340
13378
|
|
|
13341
13379
|
function stringifyCP(p) {
|
|
13342
|
-
return ' ' + roundCoord$
|
|
13380
|
+
return ' ' + roundCoord$2(p[2]) + ' ' + roundCoord$2(p[3]);
|
|
13343
13381
|
}
|
|
13344
13382
|
|
|
13345
13383
|
function isCubicCtrl(p) {
|
|
@@ -13374,40 +13412,9 @@
|
|
|
13374
13412
|
stringifyCP(p2) + stringifyVertex(p2);
|
|
13375
13413
|
}
|
|
13376
13414
|
|
|
13377
|
-
function findArcCenter(p1, p2, degrees) {
|
|
13378
|
-
var p3 = [(p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2], // midpoint betw. p1, p2
|
|
13379
|
-
tan = 1 / Math.tan(degrees / 180 * Math.PI / 2),
|
|
13380
|
-
cp = getAffineTransform(90, tan, [0, 0], p3)(p2[0], p2[1]);
|
|
13381
|
-
return cp;
|
|
13382
|
-
}
|
|
13383
|
-
|
|
13384
|
-
// export function addBezierArcControlPoints(p1, p2, degrees) {
|
|
13385
|
-
function addBezierArcControlPoints(points, degrees) {
|
|
13386
|
-
// source: https://stackoverflow.com/questions/734076/how-to-best-approximate-a-geometrical-arc-with-a-bezier-curve
|
|
13387
|
-
var p2 = points.pop(),
|
|
13388
|
-
p1 = points.pop(),
|
|
13389
|
-
cp = findArcCenter(p1, p2, degrees),
|
|
13390
|
-
xc = cp[0],
|
|
13391
|
-
yc = cp[1],
|
|
13392
|
-
ax = p1[0] - xc,
|
|
13393
|
-
ay = p1[1] - yc,
|
|
13394
|
-
bx = p2[0] - xc,
|
|
13395
|
-
by = p2[1] - yc,
|
|
13396
|
-
q1 = ax * ax + ay * ay,
|
|
13397
|
-
q2 = q1 + ax * bx + ay * by,
|
|
13398
|
-
k2 = 4/3 * (Math.sqrt(2 * q1 * q2) - q2) / (ax * by - ay * bx);
|
|
13399
|
-
|
|
13400
|
-
points.push(p1);
|
|
13401
|
-
points.push([xc + ax - k2 * ay, yc + ay + k2 * ax, 'C']);
|
|
13402
|
-
points.push([xc + bx + k2 * by, yc + by - k2 * bx, 'C']);
|
|
13403
|
-
points.push(p2);
|
|
13404
|
-
}
|
|
13405
|
-
|
|
13406
13415
|
var SvgPathUtils = /*#__PURE__*/Object.freeze({
|
|
13407
13416
|
__proto__: null,
|
|
13408
|
-
stringifyLineStringCoords: stringifyLineStringCoords
|
|
13409
|
-
findArcCenter: findArcCenter,
|
|
13410
|
-
addBezierArcControlPoints: addBezierArcControlPoints
|
|
13417
|
+
stringifyLineStringCoords: stringifyLineStringCoords
|
|
13411
13418
|
});
|
|
13412
13419
|
|
|
13413
13420
|
/* example patterns
|
|
@@ -13684,11 +13691,15 @@
|
|
|
13684
13691
|
type: null,
|
|
13685
13692
|
length: 'number', // e.g. arrow length
|
|
13686
13693
|
rotation: 'number',
|
|
13694
|
+
radius: 'number',
|
|
13695
|
+
'arrow-length': 'number',
|
|
13696
|
+
'arrow-direction': 'number',
|
|
13687
13697
|
'arrow-head-angle': 'number',
|
|
13688
13698
|
'arrow-head-width': 'number',
|
|
13689
13699
|
'arrow-stem-width': 'number',
|
|
13690
13700
|
'arrow-stem-curve': 'number', // degrees of arc
|
|
13691
13701
|
'arrow-stem-taper': 'number',
|
|
13702
|
+
'arrow-min-stem': 'number',
|
|
13692
13703
|
'arrow-scaling': 'number',
|
|
13693
13704
|
effect: null // e.g. "fade"
|
|
13694
13705
|
}, stylePropertyTypes);
|
|
@@ -13730,8 +13741,8 @@
|
|
|
13730
13741
|
if (!isSupportedSvgSymbolProperty(svgName)) {
|
|
13731
13742
|
return;
|
|
13732
13743
|
}
|
|
13733
|
-
var
|
|
13734
|
-
functions[svgName] = getSymbolPropertyAccessor(
|
|
13744
|
+
var val = opts[optName];
|
|
13745
|
+
functions[svgName] = getSymbolPropertyAccessor(val, svgName, lyr);
|
|
13735
13746
|
properties.push(svgName);
|
|
13736
13747
|
});
|
|
13737
13748
|
|
|
@@ -13754,7 +13765,8 @@
|
|
|
13754
13765
|
return /[(){}.+-/*?:&|=\[]/.test(str);
|
|
13755
13766
|
}
|
|
13756
13767
|
|
|
13757
|
-
function getSymbolPropertyAccessor(
|
|
13768
|
+
function getSymbolPropertyAccessor(val, svgName, lyr) {
|
|
13769
|
+
var strVal = String(val).trim();
|
|
13758
13770
|
var typeHint = symbolPropertyTypes[svgName];
|
|
13759
13771
|
var fields = lyr.data ? lyr.data.getFields() : [];
|
|
13760
13772
|
var literalVal = null;
|
|
@@ -13856,7 +13868,6 @@
|
|
|
13856
13868
|
isSvgColor: isSvgColor
|
|
13857
13869
|
});
|
|
13858
13870
|
|
|
13859
|
-
var symbolBuilders = {};
|
|
13860
13871
|
var symbolRenderers = {};
|
|
13861
13872
|
|
|
13862
13873
|
function getTransform(xy, scale) {
|
|
@@ -13869,7 +13880,6 @@
|
|
|
13869
13880
|
|
|
13870
13881
|
var SvgCommon = /*#__PURE__*/Object.freeze({
|
|
13871
13882
|
__proto__: null,
|
|
13872
|
-
symbolBuilders: symbolBuilders,
|
|
13873
13883
|
symbolRenderers: symbolRenderers,
|
|
13874
13884
|
getTransform: getTransform
|
|
13875
13885
|
});
|
|
@@ -19479,6 +19489,13 @@ ${svg}
|
|
|
19479
19489
|
})
|
|
19480
19490
|
.option('target', targetOpt);
|
|
19481
19491
|
|
|
19492
|
+
parser.command('include')
|
|
19493
|
+
.describe('import JS data and functions for use in JS expressions')
|
|
19494
|
+
.option('file', {
|
|
19495
|
+
DEFAULT: true,
|
|
19496
|
+
describe: 'file containing a JS object with key:value pairs to import'
|
|
19497
|
+
});
|
|
19498
|
+
|
|
19482
19499
|
parser.command('inlay')
|
|
19483
19500
|
.describe('inscribe a polygon layer inside another polygon layer')
|
|
19484
19501
|
.option('source', {
|
|
@@ -19791,6 +19808,7 @@ ${svg}
|
|
|
19791
19808
|
})
|
|
19792
19809
|
.option('target', targetOpt);
|
|
19793
19810
|
|
|
19811
|
+
|
|
19794
19812
|
parser.command('simplify')
|
|
19795
19813
|
.validate(validateSimplifyOpts)
|
|
19796
19814
|
.example('Retain 10% of removable vertices\n$ mapshaper input.shp -simplify 10%')
|
|
@@ -20029,26 +20047,81 @@ ${svg}
|
|
|
20029
20047
|
.option('target', targetOpt);
|
|
20030
20048
|
|
|
20031
20049
|
parser.command('symbols')
|
|
20032
|
-
// .describe('
|
|
20050
|
+
// .describe('symbolize points as polygons, circles, stars or arrows')
|
|
20033
20051
|
.option('type', {
|
|
20034
|
-
describe: 'symbol type'
|
|
20052
|
+
describe: 'symbol type (e.g. star, polygon, circle, arrow)'
|
|
20053
|
+
})
|
|
20054
|
+
.option('scale', {
|
|
20055
|
+
describe: 'scale symbols by a factor',
|
|
20056
|
+
type: 'number'
|
|
20057
|
+
})
|
|
20058
|
+
.option('pixel-scale', {
|
|
20059
|
+
describe: 'symbol scale in meters-per-pixel (see polygons option)',
|
|
20060
|
+
type: 'number',
|
|
20061
|
+
})
|
|
20062
|
+
.option('polygons', {
|
|
20063
|
+
describe: 'generate symbols as polygons instead of SVG objects',
|
|
20064
|
+
type: 'flag'
|
|
20065
|
+
})
|
|
20066
|
+
.option('radius', {
|
|
20067
|
+
describe: 'distance from center to farthest point on the symbol',
|
|
20068
|
+
type: 'distance'
|
|
20069
|
+
})
|
|
20070
|
+
.option('sides', {
|
|
20071
|
+
describe: 'sides of a polygon or star symbol',
|
|
20072
|
+
type: 'number'
|
|
20073
|
+
})
|
|
20074
|
+
.option('rotation', {
|
|
20075
|
+
describe: 'rotation of symbol in degrees'
|
|
20076
|
+
})
|
|
20077
|
+
.option('orientation', {
|
|
20078
|
+
describe: 'use orientation=b for a rotated or flipped orientation'
|
|
20079
|
+
})
|
|
20080
|
+
.option('length', {
|
|
20081
|
+
// alias for arrow-length
|
|
20082
|
+
})
|
|
20083
|
+
.option('star-ratio', {
|
|
20084
|
+
describe: 'ratio of major to minor radius of star',
|
|
20085
|
+
type: 'number'
|
|
20086
|
+
})
|
|
20087
|
+
.option('arrow-length', {
|
|
20088
|
+
describe: 'length of arrows in pixels (use with type=arrow)'
|
|
20089
|
+
})
|
|
20090
|
+
.option('arrow-direction', {
|
|
20091
|
+
describe: 'angle off of vertical (-90 = left-pointing arrow)'
|
|
20092
|
+
})
|
|
20093
|
+
.option('arrow-head-angle', {
|
|
20094
|
+
describe: 'angle of tip of arrow (default is 40 degrees)'
|
|
20095
|
+
})
|
|
20096
|
+
.option('arrow-head-width', {
|
|
20097
|
+
describe: 'size of arrow head from side to side'
|
|
20098
|
+
})
|
|
20099
|
+
.option('arrow-stem-width', {
|
|
20100
|
+
describe: 'width of stem at its widest point'
|
|
20101
|
+
})
|
|
20102
|
+
.option('arrow-stem-taper', {
|
|
20103
|
+
describe: 'factor for tapering the width of the stem'
|
|
20104
|
+
})
|
|
20105
|
+
.option('arrow-stem-curve', {
|
|
20106
|
+
describe: 'curvature in degrees (arrows are straight by default)'
|
|
20107
|
+
})
|
|
20108
|
+
.option('arrow-min-stem', {
|
|
20109
|
+
describe: 'min ratio of stem to total length (for small arrows)',
|
|
20110
|
+
type: 'number'
|
|
20035
20111
|
})
|
|
20036
20112
|
.option('stroke', {})
|
|
20037
20113
|
.option('stroke-width', {})
|
|
20038
|
-
.option('fill', {
|
|
20039
|
-
|
|
20040
|
-
|
|
20114
|
+
.option('fill', {
|
|
20115
|
+
describe: 'symbol fill color'
|
|
20116
|
+
})
|
|
20041
20117
|
.option('effect', {})
|
|
20042
|
-
.option('
|
|
20043
|
-
.option('
|
|
20044
|
-
.option('
|
|
20045
|
-
.option('
|
|
20046
|
-
.option('arrow-stem-taper', {})
|
|
20047
|
-
.option('arrow-scaling', {})
|
|
20048
|
-
.option('where', whereOpt)
|
|
20049
|
-
.option('target', targetOpt);
|
|
20118
|
+
// .option('where', whereOpt)
|
|
20119
|
+
.option('name', nameOpt)
|
|
20120
|
+
.option('target', targetOpt)
|
|
20121
|
+
.option('no-replace', noReplaceOpt);
|
|
20050
20122
|
// .option('name', nameOpt);
|
|
20051
20123
|
|
|
20124
|
+
|
|
20052
20125
|
parser.command('target')
|
|
20053
20126
|
.describe('set active layer (or layers)')
|
|
20054
20127
|
.option('target', {
|
|
@@ -20199,13 +20272,6 @@ ${svg}
|
|
|
20199
20272
|
})
|
|
20200
20273
|
.option('name', nameOpt);
|
|
20201
20274
|
|
|
20202
|
-
parser.command('include')
|
|
20203
|
-
.describe('import JS data and functions for use in JS expressions')
|
|
20204
|
-
.option('file', {
|
|
20205
|
-
DEFAULT: true,
|
|
20206
|
-
describe: 'file containing a JS object with key:value pairs to import'
|
|
20207
|
-
});
|
|
20208
|
-
|
|
20209
20275
|
parser.command('fuzzy-join')
|
|
20210
20276
|
.describe('join points to polygons, with data fill and fuzzy match')
|
|
20211
20277
|
.option('source', {
|
|
@@ -20341,16 +20407,6 @@ ${svg}
|
|
|
20341
20407
|
})
|
|
20342
20408
|
.option('name', nameOpt);
|
|
20343
20409
|
|
|
20344
|
-
// parser.command('shapes')
|
|
20345
|
-
// .describe('convert points to shapes')
|
|
20346
|
-
// .option('type', {
|
|
20347
|
-
// })
|
|
20348
|
-
// .option('size', {
|
|
20349
|
-
// })
|
|
20350
|
-
// .option('rotation', {
|
|
20351
|
-
// })
|
|
20352
|
-
|
|
20353
|
-
|
|
20354
20410
|
parser.command('subdivide')
|
|
20355
20411
|
.describe('recursively split a layer using a JS expression')
|
|
20356
20412
|
.validate(validateExpressionOpt)
|
|
@@ -21214,12 +21270,13 @@ ${svg}
|
|
|
21214
21270
|
if (this instanceof ShpReader === false) {
|
|
21215
21271
|
return new ShpReader(shpSrc, shxSrc);
|
|
21216
21272
|
}
|
|
21217
|
-
|
|
21218
21273
|
var shpFile = utils.isString(shpSrc) ? new FileReader(shpSrc) : new BufferReader(shpSrc);
|
|
21219
21274
|
var header = parseHeader(shpFile.readToBinArray(0, 100));
|
|
21220
|
-
var
|
|
21221
|
-
var
|
|
21222
|
-
var
|
|
21275
|
+
var shpType = header.type;
|
|
21276
|
+
var shpOffset = 100; // used when reading .shp without .shx
|
|
21277
|
+
var recordCount = 0;
|
|
21278
|
+
var badRecordNumberCount = 0;
|
|
21279
|
+
var RecordClass = new ShpRecordClass(shpType);
|
|
21223
21280
|
var shxBin, shxFile;
|
|
21224
21281
|
|
|
21225
21282
|
if (shxSrc) {
|
|
@@ -21227,8 +21284,6 @@ ${svg}
|
|
|
21227
21284
|
shxBin = shxFile.readToBinArray(0, shxFile.size()).bigEndian();
|
|
21228
21285
|
}
|
|
21229
21286
|
|
|
21230
|
-
reset();
|
|
21231
|
-
|
|
21232
21287
|
this.header = function() {
|
|
21233
21288
|
return header;
|
|
21234
21289
|
};
|
|
@@ -21246,37 +21301,35 @@ ${svg}
|
|
|
21246
21301
|
|
|
21247
21302
|
// Iterator interface for reading shape records
|
|
21248
21303
|
this.nextShape = function() {
|
|
21249
|
-
var shape
|
|
21250
|
-
if (
|
|
21251
|
-
|
|
21252
|
-
|
|
21253
|
-
shpFile.close();
|
|
21254
|
-
reset();
|
|
21304
|
+
var shape;
|
|
21305
|
+
if (!shpFile) {
|
|
21306
|
+
error('Tried to read from a used ShpReader');
|
|
21307
|
+
// return null; // this reader was already used
|
|
21255
21308
|
}
|
|
21309
|
+
shape = readNextShape(recordCount);
|
|
21310
|
+
if (!shape) {
|
|
21311
|
+
done();
|
|
21312
|
+
return null;
|
|
21313
|
+
}
|
|
21314
|
+
recordCount++;
|
|
21256
21315
|
return shape;
|
|
21257
21316
|
};
|
|
21258
21317
|
|
|
21259
21318
|
// Returns a shape record or null if no more shapes can be read
|
|
21319
|
+
// i: Expected 0-based index of the next record
|
|
21260
21320
|
//
|
|
21261
21321
|
function readNextShape(i) {
|
|
21262
|
-
|
|
21263
|
-
|
|
21264
|
-
|
|
21265
|
-
if (shxFile.size() <= 100 + i * 8) return null; // done
|
|
21266
|
-
shxBin.position(100 + i * 8);
|
|
21267
|
-
offset = shxBin.readUint32() * 2;
|
|
21268
|
-
shape = readIndexedShape(shpFile, offset, expectedId);
|
|
21269
|
-
} else {
|
|
21270
|
-
// Reading without a .shx file (returns null at end-of-file)
|
|
21271
|
-
offset = shpOffset;
|
|
21272
|
-
shape = readNonIndexedShape(shpFile, offset, expectedId);
|
|
21273
|
-
}
|
|
21274
|
-
return shape || null;
|
|
21322
|
+
return shxBin ?
|
|
21323
|
+
readIndexedShape(shpFile, shxBin, i) :
|
|
21324
|
+
readNonIndexedShape(shpFile, shpOffset, i);
|
|
21275
21325
|
}
|
|
21276
21326
|
|
|
21277
|
-
function
|
|
21278
|
-
|
|
21279
|
-
|
|
21327
|
+
function done() {
|
|
21328
|
+
shpFile.close();
|
|
21329
|
+
shpFile = shxFile = shxBin = null;
|
|
21330
|
+
if (badRecordNumberCount > 0) {
|
|
21331
|
+
message(`Warning: ${badRecordNumberCount}/${recordCount} features have non-standard record numbers in the .shp file.`);
|
|
21332
|
+
}
|
|
21280
21333
|
}
|
|
21281
21334
|
|
|
21282
21335
|
function parseHeader(bin) {
|
|
@@ -21307,33 +21360,35 @@ ${svg}
|
|
|
21307
21360
|
|
|
21308
21361
|
|
|
21309
21362
|
function readShapeAtOffset(shpFile, offset) {
|
|
21310
|
-
var
|
|
21311
|
-
|
|
21312
|
-
|
|
21313
|
-
|
|
21314
|
-
|
|
21315
|
-
|
|
21316
|
-
|
|
21317
|
-
|
|
21318
|
-
|
|
21319
|
-
|
|
21320
|
-
|
|
21321
|
-
if (goodSize && goodType) {
|
|
21322
|
-
bin = shpFile.readToBinArray(offset, recordSize);
|
|
21323
|
-
shape = new RecordClass(bin, recordSize);
|
|
21324
|
-
}
|
|
21363
|
+
var fileSize = shpFile.size();
|
|
21364
|
+
if (offset + 12 > fileSize) return null; // reached end-of-file
|
|
21365
|
+
var bin = shpFile.readToBinArray(offset, 12);
|
|
21366
|
+
var recordId = bin.bigEndian().readUint32();
|
|
21367
|
+
// record size is bytes in content section + 8 header bytes
|
|
21368
|
+
var recordSize = bin.readUint32() * 2 + 8;
|
|
21369
|
+
var recordType = bin.littleEndian().readUint32();
|
|
21370
|
+
var goodSize = offset + recordSize <= fileSize && recordSize >= 12;
|
|
21371
|
+
var goodType = recordType === 0 || recordType == shpType;
|
|
21372
|
+
if (!goodSize || !goodType) {
|
|
21373
|
+
return null;
|
|
21325
21374
|
}
|
|
21326
|
-
|
|
21375
|
+
bin = shpFile.readToBinArray(offset, recordSize);
|
|
21376
|
+
return new RecordClass(bin, recordSize);
|
|
21327
21377
|
}
|
|
21328
21378
|
|
|
21329
|
-
function readIndexedShape(shpFile,
|
|
21379
|
+
function readIndexedShape(shpFile, shxBin, i) {
|
|
21380
|
+
if (shxBin.size() <= 100 + i * 8) return null; // done
|
|
21381
|
+
shxBin.position(100 + i * 8);
|
|
21382
|
+
var expectedId = i + 1;
|
|
21383
|
+
var offset = shxBin.readUint32() * 2;
|
|
21384
|
+
var recLen = shxBin.readUint32() * 2; // TODO: match this to recLen in .shp
|
|
21330
21385
|
var shape = readShapeAtOffset(shpFile, offset);
|
|
21331
21386
|
if (!shape) {
|
|
21332
21387
|
stop('Index of Shapefile record', expectedId, 'in the .shx file is invalid.');
|
|
21333
21388
|
}
|
|
21334
21389
|
if (shape.id != expectedId) {
|
|
21335
|
-
|
|
21336
|
-
|
|
21390
|
+
badRecordNumberCount++;
|
|
21391
|
+
verbose(`Warning: A feature has a different record number in .shx (${expectedId}) and .shp (${shape.id}).`);
|
|
21337
21392
|
}
|
|
21338
21393
|
// TODO: consider printing verbose message if a .shp file contains garbage bytes
|
|
21339
21394
|
// example files:
|
|
@@ -21346,11 +21401,12 @@ ${svg}
|
|
|
21346
21401
|
// in consecutive sequence in the .shp file. This is a problem when the .shx
|
|
21347
21402
|
// index file is not present.
|
|
21348
21403
|
//
|
|
21349
|
-
// Here, we try to scan past invalid content to find the next record.
|
|
21404
|
+
// Here, we try to scan past any invalid content to find the next record.
|
|
21350
21405
|
// Records are required to be in sequential order.
|
|
21351
21406
|
//
|
|
21352
|
-
function readNonIndexedShape(shpFile, start,
|
|
21353
|
-
var
|
|
21407
|
+
function readNonIndexedShape(shpFile, start, i) {
|
|
21408
|
+
var expectedId = i + 1, // Shapefile ids are 1-based
|
|
21409
|
+
offset = start,
|
|
21354
21410
|
fileSize = shpFile.size(),
|
|
21355
21411
|
shape = null,
|
|
21356
21412
|
bin, recordId, recordType, isValidType;
|
|
@@ -21358,7 +21414,7 @@ ${svg}
|
|
|
21358
21414
|
bin = shpFile.readToBinArray(offset, 12);
|
|
21359
21415
|
recordId = bin.bigEndian().readUint32();
|
|
21360
21416
|
recordType = bin.littleEndian().skipBytes(4).readUint32();
|
|
21361
|
-
isValidType = recordType ==
|
|
21417
|
+
isValidType = recordType == shpType || recordType === 0;
|
|
21362
21418
|
if (!isValidType || recordId != expectedId && recordType === 0) {
|
|
21363
21419
|
offset += 4; // keep scanning -- try next integer position
|
|
21364
21420
|
continue;
|
|
@@ -21385,22 +21441,6 @@ ${svg}
|
|
|
21385
21441
|
return this.header().type;
|
|
21386
21442
|
};
|
|
21387
21443
|
|
|
21388
|
-
ShpReader.prototype.getCounts = function() {
|
|
21389
|
-
var counts = {
|
|
21390
|
-
nullCount: 0,
|
|
21391
|
-
partCount: 0,
|
|
21392
|
-
shapeCount: 0,
|
|
21393
|
-
pointCount: 0
|
|
21394
|
-
};
|
|
21395
|
-
this.forEachShape(function(shp) {
|
|
21396
|
-
if (shp.isNull) counts.nullCount++;
|
|
21397
|
-
counts.pointCount += shp.pointCount;
|
|
21398
|
-
counts.partCount += shp.partCount;
|
|
21399
|
-
counts.shapeCount++;
|
|
21400
|
-
});
|
|
21401
|
-
return counts;
|
|
21402
|
-
};
|
|
21403
|
-
|
|
21404
21444
|
// Apply snapping, remove duplicate coords and clean up defective paths in a dataset
|
|
21405
21445
|
// Assumes that any CRS info has been added to the dataset
|
|
21406
21446
|
// @opts: import options
|
|
@@ -26209,7 +26249,6 @@ ${svg}
|
|
|
26209
26249
|
if (constTol) return constTol;
|
|
26210
26250
|
return constTol ? constTol : meterDist * pctOfRadius;
|
|
26211
26251
|
};
|
|
26212
|
-
|
|
26213
26252
|
}
|
|
26214
26253
|
|
|
26215
26254
|
function getBufferDistanceFunction(lyr, dataset, opts) {
|
|
@@ -27382,7 +27421,7 @@ ${svg}
|
|
|
27382
27421
|
cmd.buffer = makeBufferLayer;
|
|
27383
27422
|
|
|
27384
27423
|
function makeBufferLayer(lyr, dataset, opts) {
|
|
27385
|
-
var dataset2
|
|
27424
|
+
var dataset2;
|
|
27386
27425
|
if (lyr.geometry_type == 'point') {
|
|
27387
27426
|
dataset2 = makePointBuffer(lyr, dataset, opts);
|
|
27388
27427
|
} else if (lyr.geometry_type == 'polyline') {
|
|
@@ -27392,13 +27431,9 @@ ${svg}
|
|
|
27392
27431
|
} else {
|
|
27393
27432
|
stop("Unsupported geometry type");
|
|
27394
27433
|
}
|
|
27395
|
-
|
|
27396
|
-
lyr2 =
|
|
27397
|
-
|
|
27398
|
-
if (lyr.data && !lyr2.data) {
|
|
27399
|
-
lyr2.data = opts.no_replace ? lyr.data.clone() : lyr.data;
|
|
27400
|
-
}
|
|
27401
|
-
return outputLayers;
|
|
27434
|
+
|
|
27435
|
+
var lyr2 = mergeOutputLayerIntoDataset(lyr, dataset, dataset2, opts);
|
|
27436
|
+
return [lyr2];
|
|
27402
27437
|
}
|
|
27403
27438
|
|
|
27404
27439
|
// TODO: support three or more stops
|
|
@@ -33983,7 +34018,7 @@ ${svg}
|
|
|
33983
34018
|
|
|
33984
34019
|
function createMeridianPart(x, ymin, ymax) {
|
|
33985
34020
|
var coords = densifyPathByInterval([[x, ymin], [x, ymax]], precision);
|
|
33986
|
-
meridians.push(graticuleFeature(coords, {type: 'meridian', value: roundCoord(x)}));
|
|
34021
|
+
meridians.push(graticuleFeature(coords, {type: 'meridian', value: roundCoord$1(x)}));
|
|
33987
34022
|
}
|
|
33988
34023
|
|
|
33989
34024
|
function createParallel(y) {
|
|
@@ -33993,7 +34028,7 @@ ${svg}
|
|
|
33993
34028
|
}
|
|
33994
34029
|
|
|
33995
34030
|
// remove tiny offsets
|
|
33996
|
-
function roundCoord(x) {
|
|
34031
|
+
function roundCoord$1(x) {
|
|
33997
34032
|
return +x.toFixed(3) || 0;
|
|
33998
34033
|
}
|
|
33999
34034
|
|
|
@@ -36644,6 +36679,99 @@ ${svg}
|
|
|
36644
36679
|
parseScalebarLabelToKm: parseScalebarLabelToKm
|
|
36645
36680
|
});
|
|
36646
36681
|
|
|
36682
|
+
cmd.shape = function(targetDataset, opts) {
|
|
36683
|
+
var geojson, dataset;
|
|
36684
|
+
if (opts.coordinates) {
|
|
36685
|
+
geojson = makeShapeFromCoords(opts);
|
|
36686
|
+
} else if (opts.type == 'circle') {
|
|
36687
|
+
geojson = makeCircle(opts);
|
|
36688
|
+
} else if (opts.type == 'rectangle' && opts.bbox) {
|
|
36689
|
+
geojson = getRectangleGeoJSON(opts);
|
|
36690
|
+
} else {
|
|
36691
|
+
stop('Missing coordinates parameter');
|
|
36692
|
+
}
|
|
36693
|
+
// TODO: project shape if targetDataset is projected
|
|
36694
|
+
dataset = importGeoJSON(geojson, {});
|
|
36695
|
+
if (opts.rotation) {
|
|
36696
|
+
rotateDatasetCoords(dataset, opts.rotation);
|
|
36697
|
+
}
|
|
36698
|
+
dataset.layers[0].name = opts.name || opts.type || 'shape';
|
|
36699
|
+
return dataset;
|
|
36700
|
+
};
|
|
36701
|
+
|
|
36702
|
+
function getRectangleGeoJSON(opts) {
|
|
36703
|
+
var bbox = opts.bbox,
|
|
36704
|
+
xmin = bbox[0],
|
|
36705
|
+
ymin = bbox[1],
|
|
36706
|
+
xmax = bbox[2],
|
|
36707
|
+
ymax = bbox[3],
|
|
36708
|
+
interval = 0.5,
|
|
36709
|
+
coords = [],
|
|
36710
|
+
type = opts.geometry == 'polyline' ? 'LineString' : 'Polygon';
|
|
36711
|
+
addSide(xmin, ymin, xmin, ymax);
|
|
36712
|
+
addSide(xmin, ymax, xmax, ymax);
|
|
36713
|
+
addSide(xmax, ymax, xmax, ymin);
|
|
36714
|
+
addSide(xmax, ymin, xmin, ymin);
|
|
36715
|
+
coords.push([xmin, ymin]);
|
|
36716
|
+
return {
|
|
36717
|
+
type: type,
|
|
36718
|
+
coordinates: type == 'Polygon' ? [coords] : coords
|
|
36719
|
+
};
|
|
36720
|
+
|
|
36721
|
+
function addSide(x1, y1, x2, y2) {
|
|
36722
|
+
var dx = x2 - x1,
|
|
36723
|
+
dy = y2 - y1,
|
|
36724
|
+
n = Math.ceil(Math.max(Math.abs(dx) / interval, Math.abs(dy) / interval)),
|
|
36725
|
+
xint = dx / n,
|
|
36726
|
+
yint = dy / n;
|
|
36727
|
+
for (var i=0; i<n; i++) {
|
|
36728
|
+
coords.push([x1 + i * xint, y1 + i * yint]);
|
|
36729
|
+
}
|
|
36730
|
+
}
|
|
36731
|
+
}
|
|
36732
|
+
|
|
36733
|
+
function makeCircle(opts) {
|
|
36734
|
+
if (opts.radius > 0 === false && opts.radius_angle > 0 === false) {
|
|
36735
|
+
stop('Missing required radius parameter.');
|
|
36736
|
+
}
|
|
36737
|
+
var cp = opts.center || [0, 0];
|
|
36738
|
+
var radius = opts.radius || getCircleRadiusFromAngle(getCRS('wgs84'), opts.radius_angle);
|
|
36739
|
+
return getCircleGeoJSON(cp, radius, null, {geometry_type : opts.geometry || 'polygon'});
|
|
36740
|
+
}
|
|
36741
|
+
|
|
36742
|
+
function makeShapeFromCoords(opts) {
|
|
36743
|
+
var coordinates = [];
|
|
36744
|
+
var offsets = opts.offsets || [];
|
|
36745
|
+
var coords = opts.coordinates;
|
|
36746
|
+
var type, i, x, y;
|
|
36747
|
+
if (coords.length >= 2 === false) {
|
|
36748
|
+
stop('Invalid coordinates parameter.');
|
|
36749
|
+
}
|
|
36750
|
+
for (i=0; i<coords.length; i+= 2) {
|
|
36751
|
+
x = coords[i];
|
|
36752
|
+
y = coords[i + 1];
|
|
36753
|
+
coordinates.push([x, y]);
|
|
36754
|
+
}
|
|
36755
|
+
for (i=0; i<offsets.length; i+=2) {
|
|
36756
|
+
x += offsets[i];
|
|
36757
|
+
y += offsets[i + 1];
|
|
36758
|
+
coordinates.push([x, y]);
|
|
36759
|
+
}
|
|
36760
|
+
if (GeoJSON.pathIsRing(coordinates)) {
|
|
36761
|
+
type = 'Polygon';
|
|
36762
|
+
} else if (opts.closed && coordinates.length >= 3) {
|
|
36763
|
+
type = 'Polygon';
|
|
36764
|
+
coordinates.push(coordinates[0]);
|
|
36765
|
+
} else {
|
|
36766
|
+
type = 'LineString';
|
|
36767
|
+
}
|
|
36768
|
+
return {
|
|
36769
|
+
type: type,
|
|
36770
|
+
coordinates: type == 'Polygon' ? [coordinates] : coordinates
|
|
36771
|
+
};
|
|
36772
|
+
|
|
36773
|
+
}
|
|
36774
|
+
|
|
36647
36775
|
function calcSimplifyStats(arcs, use3D) {
|
|
36648
36776
|
var distSq = use3D ? pointSegGeoDistSq : geom.pointSegDistSq,
|
|
36649
36777
|
calcAngle = use3D ? geom.signedAngleSph : geom.signedAngle,
|
|
@@ -37731,56 +37859,83 @@ ${svg}
|
|
|
37731
37859
|
});
|
|
37732
37860
|
};
|
|
37733
37861
|
|
|
37734
|
-
|
|
37735
|
-
var len = 'length' in d ? d.length : 10;
|
|
37736
|
-
var filled = 'fill' in d;
|
|
37737
|
-
return filled ? getFilledArrow(d, len) : getStickArrow(d, len);
|
|
37738
|
-
};
|
|
37862
|
+
var roundCoord = getRoundingFunction(0.01);
|
|
37739
37863
|
|
|
37740
|
-
function
|
|
37741
|
-
|
|
37742
|
-
|
|
37743
|
-
|
|
37744
|
-
|
|
37745
|
-
|
|
37746
|
-
}
|
|
37864
|
+
function forEachSymbolCoord(coords, cb) {
|
|
37865
|
+
var isPoint = coords && utils.isNumber(coords[0]);
|
|
37866
|
+
var isNested = !isPoint && coords && Array.isArray(coords[0]);
|
|
37867
|
+
if (isPoint) return cb(coords);
|
|
37868
|
+
for (var i=0; i<coords.length; i++) {
|
|
37869
|
+
if (isNested) forEachSymbolCoord(coords[i], cb);
|
|
37870
|
+
}
|
|
37747
37871
|
}
|
|
37748
37872
|
|
|
37749
|
-
function
|
|
37750
|
-
|
|
37751
|
-
|
|
37752
|
-
|
|
37753
|
-
fill: d.fill || 'magenta'
|
|
37754
|
-
};
|
|
37873
|
+
function flipY(coords) {
|
|
37874
|
+
forEachSymbolCoord(coords, function(p) {
|
|
37875
|
+
p[1] = -p[1];
|
|
37876
|
+
});
|
|
37755
37877
|
}
|
|
37756
37878
|
|
|
37757
|
-
function
|
|
37758
|
-
|
|
37759
|
-
|
|
37760
|
-
|
|
37761
|
-
|
|
37762
|
-
}
|
|
37763
|
-
return 1;
|
|
37879
|
+
function scaleAndShiftCoords(coords, scale, shift) {
|
|
37880
|
+
forEachSymbolCoord(coords, function(xy) {
|
|
37881
|
+
xy[0] = xy[0] * scale + shift[0];
|
|
37882
|
+
xy[1] = xy[1] * scale + shift[1];
|
|
37883
|
+
});
|
|
37764
37884
|
}
|
|
37765
37885
|
|
|
37766
|
-
function
|
|
37767
|
-
|
|
37768
|
-
|
|
37769
|
-
|
|
37770
|
-
|
|
37771
|
-
return [dx, dy];
|
|
37886
|
+
function roundCoordsForSVG(coords) {
|
|
37887
|
+
forEachSymbolCoord(coords, function(p) {
|
|
37888
|
+
p[0] = roundCoord(p[0]);
|
|
37889
|
+
p[1] = roundCoord(p[1]);
|
|
37890
|
+
});
|
|
37772
37891
|
}
|
|
37773
37892
|
|
|
37774
|
-
function
|
|
37775
|
-
|
|
37893
|
+
function rotateCoords(coords, rotation) {
|
|
37894
|
+
if (!rotation) return;
|
|
37895
|
+
var f = getAffineTransform(rotation, 1, [0, 0], [0, 0]);
|
|
37896
|
+
forEachSymbolCoord(coords, function(p) {
|
|
37897
|
+
var p2 = f(p[0], p[1]);
|
|
37898
|
+
p[0] = p2[0];
|
|
37899
|
+
p[1] = p2[1];
|
|
37900
|
+
});
|
|
37901
|
+
}
|
|
37902
|
+
|
|
37903
|
+
function findArcCenter(p1, p2, degrees) {
|
|
37904
|
+
var p3 = [(p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2], // midpoint betw. p1, p2
|
|
37905
|
+
tan = 1 / Math.tan(degrees / 180 * Math.PI / 2),
|
|
37906
|
+
cp = getAffineTransform(90, tan, [0, 0], p3)(p2[0], p2[1]);
|
|
37907
|
+
return cp;
|
|
37908
|
+
}
|
|
37909
|
+
|
|
37910
|
+
// export function addBezierArcControlPoints(p1, p2, degrees) {
|
|
37911
|
+
function addBezierArcControlPoints(points, degrees) {
|
|
37912
|
+
// source: https://stackoverflow.com/questions/734076/how-to-best-approximate-a-geometrical-arc-with-a-bezier-curve
|
|
37913
|
+
var p2 = points.pop(),
|
|
37914
|
+
p1 = points.pop(),
|
|
37915
|
+
cp = findArcCenter(p1, p2, degrees),
|
|
37916
|
+
xc = cp[0],
|
|
37917
|
+
yc = cp[1],
|
|
37918
|
+
ax = p1[0] - xc,
|
|
37919
|
+
ay = p1[1] - yc,
|
|
37920
|
+
bx = p2[0] - xc,
|
|
37921
|
+
by = p2[1] - yc,
|
|
37922
|
+
q1 = ax * ax + ay * ay,
|
|
37923
|
+
q2 = q1 + ax * bx + ay * by,
|
|
37924
|
+
k2 = 4/3 * (Math.sqrt(2 * q1 * q2) - q2) / (ax * by - ay * bx);
|
|
37925
|
+
|
|
37926
|
+
points.push(p1);
|
|
37927
|
+
points.push([xc + ax - k2 * ay, yc + ay + k2 * ax, 'C']);
|
|
37928
|
+
points.push([xc + bx + k2 * by, yc + by - k2 * bx, 'C']);
|
|
37929
|
+
points.push(p2);
|
|
37776
37930
|
}
|
|
37777
37931
|
|
|
37778
37932
|
function getStickArrowCoords(d, totalLen) {
|
|
37933
|
+
var minStemRatio = getMinStemRatio(d);
|
|
37779
37934
|
var headAngle = d['arrow-head-angle'] || 90;
|
|
37780
37935
|
var curve = d['arrow-stem-curve'] || 0;
|
|
37781
37936
|
var unscaledHeadWidth = d['arrow-head-width'] || 9;
|
|
37782
37937
|
var unscaledHeadLen = getHeadLength(unscaledHeadWidth, headAngle);
|
|
37783
|
-
var scale = getScale(totalLen, unscaledHeadLen);
|
|
37938
|
+
var scale = getScale(totalLen, unscaledHeadLen, minStemRatio);
|
|
37784
37939
|
var headWidth = unscaledHeadWidth * scale;
|
|
37785
37940
|
var headLen = unscaledHeadLen * scale;
|
|
37786
37941
|
var tip = getStickArrowTip(totalLen, curve);
|
|
@@ -37791,84 +37946,268 @@ ${svg}
|
|
|
37791
37946
|
if (!headLen) return [stem];
|
|
37792
37947
|
var head = [addPoints([-headWidth / 2, -headLen], tip), tip.concat(), addPoints([headWidth / 2, -headLen], tip)];
|
|
37793
37948
|
|
|
37794
|
-
|
|
37795
|
-
|
|
37949
|
+
rotateCoords(stem, d.rotation);
|
|
37950
|
+
rotateCoords(head, d.rotation);
|
|
37796
37951
|
return [stem, head];
|
|
37797
37952
|
}
|
|
37798
37953
|
|
|
37799
|
-
function
|
|
37800
|
-
|
|
37801
|
-
return headWidth * headRatio;
|
|
37954
|
+
function getMinStemRatio(d) {
|
|
37955
|
+
return d['arrow-min-stem'] >= 0 ? d['arrow-min-stem'] : 0.4;
|
|
37802
37956
|
}
|
|
37803
37957
|
|
|
37804
|
-
function getFilledArrowCoords(
|
|
37805
|
-
var
|
|
37958
|
+
function getFilledArrowCoords(totalLen, d) {
|
|
37959
|
+
var minStemRatio = getMinStemRatio(d),
|
|
37960
|
+
headAngle = d['arrow-head-angle'] || 40,
|
|
37961
|
+
direction = d.rotation || d['arrow-direction'] || 0,
|
|
37806
37962
|
unscaledStemWidth = d['arrow-stem-width'] || 2,
|
|
37807
37963
|
unscaledHeadWidth = d['arrow-head-width'] || unscaledStemWidth * 3,
|
|
37808
37964
|
unscaledHeadLen = getHeadLength(unscaledHeadWidth, headAngle),
|
|
37809
|
-
scale = getScale(totalLen, unscaledHeadLen),
|
|
37965
|
+
scale = getScale(totalLen, unscaledHeadLen, minStemRatio),
|
|
37810
37966
|
headWidth = unscaledHeadWidth * scale,
|
|
37811
37967
|
headLen = unscaledHeadLen * scale,
|
|
37812
37968
|
stemWidth = unscaledStemWidth * scale,
|
|
37813
37969
|
stemTaper = d['arrow-stem-taper'] || 0,
|
|
37970
|
+
stemCurve = d['arrow-stem-curve'] || 0,
|
|
37814
37971
|
stemLen = totalLen - headLen;
|
|
37815
37972
|
|
|
37816
37973
|
var headDx = headWidth / 2,
|
|
37817
37974
|
stemDx = stemWidth / 2,
|
|
37818
37975
|
baseDx = stemDx * (1 - stemTaper);
|
|
37819
37976
|
|
|
37820
|
-
var coords
|
|
37977
|
+
var coords;
|
|
37978
|
+
|
|
37979
|
+
if (!stemCurve || Math.abs(stemCurve) > 90) {
|
|
37980
|
+
coords = [[baseDx, 0], [stemDx, stemLen], [headDx, stemLen], [0, stemLen + headLen],
|
|
37821
37981
|
[-headDx, stemLen], [-stemDx, stemLen], [-baseDx, 0], [baseDx, 0]];
|
|
37982
|
+
} else {
|
|
37983
|
+
if (direction > 0) stemCurve = -stemCurve;
|
|
37984
|
+
coords = getCurvedArrowCoords(stemLen, headLen, stemCurve, stemDx, headDx, baseDx);
|
|
37985
|
+
}
|
|
37822
37986
|
|
|
37823
|
-
|
|
37987
|
+
rotateCoords(coords, direction);
|
|
37824
37988
|
return [coords];
|
|
37825
37989
|
}
|
|
37826
37990
|
|
|
37827
|
-
|
|
37828
|
-
|
|
37829
|
-
var
|
|
37830
|
-
|
|
37831
|
-
|
|
37832
|
-
|
|
37833
|
-
|
|
37834
|
-
|
|
37991
|
+
|
|
37992
|
+
function getScale(totalLen, headLen, minStemRatio) {
|
|
37993
|
+
var maxHeadPct = 1 - minStemRatio;
|
|
37994
|
+
var headPct = headLen / totalLen;
|
|
37995
|
+
if (headPct > maxHeadPct) {
|
|
37996
|
+
return maxHeadPct / headPct;
|
|
37997
|
+
}
|
|
37998
|
+
return 1;
|
|
37835
37999
|
}
|
|
37836
38000
|
|
|
37837
|
-
|
|
37838
|
-
|
|
37839
|
-
var
|
|
37840
|
-
|
|
37841
|
-
|
|
37842
|
-
|
|
37843
|
-
|
|
37844
|
-
|
|
38001
|
+
function getStickArrowTip(totalLen, curve) {
|
|
38002
|
+
// curve/2 intersects the arrowhead at 90deg (trigonometry)
|
|
38003
|
+
var theta = Math.abs(curve/2) / 180 * Math.PI;
|
|
38004
|
+
var dx = totalLen * Math.sin(theta) * (curve > 0 ? -1 : 1);
|
|
38005
|
+
var dy = totalLen * Math.cos(theta);
|
|
38006
|
+
return [dx, dy];
|
|
38007
|
+
}
|
|
38008
|
+
|
|
38009
|
+
function addPoints(a, b) {
|
|
38010
|
+
return [a[0] + b[0], a[1] + b[1]];
|
|
38011
|
+
}
|
|
38012
|
+
|
|
38013
|
+
|
|
38014
|
+
function getHeadLength(headWidth, headAngle) {
|
|
38015
|
+
var headRatio = 1 / Math.tan(Math.PI * headAngle / 180 / 2) / 2; // length-to-width head ratio
|
|
38016
|
+
return headWidth * headRatio;
|
|
38017
|
+
}
|
|
38018
|
+
|
|
38019
|
+
function getCurvedArrowCoords(stemLen, headLen, curvature, stemDx, headDx, baseDx) {
|
|
38020
|
+
// coordinates go counter clockwise, starting from the leftmost head coordinate
|
|
38021
|
+
var theta = Math.abs(curvature) / 180 * Math.PI;
|
|
38022
|
+
var sign = curvature > 0 ? 1 : -1;
|
|
38023
|
+
var dx = stemLen * Math.sin(theta / 2) * sign;
|
|
38024
|
+
var dy = stemLen * Math.cos(theta / 2);
|
|
38025
|
+
var head = [[stemDx + dx, dy], [headDx + dx, dy],
|
|
38026
|
+
[dx, headLen + dy], [-headDx + dx, dy], [-stemDx + dx, dy]];
|
|
38027
|
+
var ax = baseDx * Math.cos(theta); // rotate arrow base
|
|
38028
|
+
var ay = baseDx * Math.sin(theta) * -sign;
|
|
38029
|
+
var leftStem = getCurvedStemCoords(-ax, -ay, -stemDx + dx, dy, theta);
|
|
38030
|
+
var rightStem = getCurvedStemCoords(ax, ay, stemDx + dx, dy, theta);
|
|
38031
|
+
// if (stemTaper == 1) leftStem.pop();
|
|
38032
|
+
var stem = leftStem.concat(rightStem.reverse());
|
|
38033
|
+
stem.pop();
|
|
38034
|
+
return stem.concat(head);
|
|
38035
|
+
}
|
|
38036
|
+
|
|
38037
|
+
// ax, ay: point on the base
|
|
38038
|
+
// bx, by: point on the stem
|
|
38039
|
+
function getCurvedStemCoords(ax, ay, bx, by, theta0) {
|
|
38040
|
+
var dx = bx - ax,
|
|
38041
|
+
dy = by - ay,
|
|
38042
|
+
dy1 = (dy * dy - dx * dx) / (2 * dy),
|
|
38043
|
+
dy2 = dy - dy1,
|
|
38044
|
+
dx2 = Math.sqrt(dx * dx + dy * dy) / 2,
|
|
38045
|
+
theta = Math.PI - Math.asin(dx2 / dy2) * 2,
|
|
38046
|
+
degrees = theta * 180 / Math.PI,
|
|
38047
|
+
radius = dy2 / Math.tan(theta / 2),
|
|
38048
|
+
leftBend = bx > ax,
|
|
38049
|
+
sign = leftBend ? 1 : -1,
|
|
38050
|
+
points = Math.round(degrees / 5) + 2,
|
|
38051
|
+
// points = theta > 2 && 7 || theta > 1 && 6 || 5,
|
|
38052
|
+
increment = theta / (points + 1);
|
|
38053
|
+
|
|
38054
|
+
var coords = [[bx, by]];
|
|
38055
|
+
for (var i=1; i<= points; i++) {
|
|
38056
|
+
var phi = i * increment / 2;
|
|
38057
|
+
var sinPhi = Math.sin(phi);
|
|
38058
|
+
var cosPhi = Math.cos(phi);
|
|
38059
|
+
var c = sinPhi * radius * 2;
|
|
38060
|
+
var a = sinPhi * c;
|
|
38061
|
+
var b = cosPhi * c;
|
|
38062
|
+
coords.push([bx - a * sign, by - b]);
|
|
38063
|
+
}
|
|
38064
|
+
coords.push([ax, ay]);
|
|
38065
|
+
return coords;
|
|
38066
|
+
}
|
|
38067
|
+
|
|
38068
|
+
// sides: e.g. 5-pointed star has 10 sides
|
|
38069
|
+
// radius: distance from center to point
|
|
38070
|
+
//
|
|
38071
|
+
function getPolygonCoords(radius, opts) {
|
|
38072
|
+
var type = opts.type;
|
|
38073
|
+
var sides = +opts.sides || getDefaultSides(type);
|
|
38074
|
+
var isStar = type == 'star';
|
|
38075
|
+
if (isStar && (sides < 6 || sides % 2 !== 0)) {
|
|
38076
|
+
stop(`Invalid number of sides for a star (${sides})`);
|
|
38077
|
+
} else if (sides >= 3 === false) {
|
|
38078
|
+
stop(`Invalid number of sides (${sides})`);
|
|
37845
38079
|
}
|
|
37846
|
-
|
|
37847
|
-
|
|
37848
|
-
|
|
37849
|
-
|
|
37850
|
-
|
|
38080
|
+
var coords = [],
|
|
38081
|
+
angle = 360 / sides,
|
|
38082
|
+
b = isStar ? 1 : 0.5,
|
|
38083
|
+
theta, even, len;
|
|
38084
|
+
if (opts.orientation == 'b') {
|
|
38085
|
+
b = 0;
|
|
38086
|
+
}
|
|
38087
|
+
for (var i=0; i<sides; i++) {
|
|
38088
|
+
even = i % 2 == 0;
|
|
38089
|
+
len = radius;
|
|
38090
|
+
if (isStar && even) {
|
|
38091
|
+
len *= (opts.star_ratio || 0.5);
|
|
38092
|
+
}
|
|
38093
|
+
theta = (i + b) * angle % 360;
|
|
38094
|
+
coords.push(getPlanarSegmentEndpoint(0, 0, theta, len));
|
|
38095
|
+
}
|
|
38096
|
+
coords.push(coords[0].concat());
|
|
38097
|
+
return [coords];
|
|
38098
|
+
}
|
|
38099
|
+
|
|
38100
|
+
function getDefaultSides(type) {
|
|
38101
|
+
return {
|
|
38102
|
+
star: 10,
|
|
38103
|
+
circle: 72,
|
|
38104
|
+
triangle: 3,
|
|
38105
|
+
square: 4,
|
|
38106
|
+
pentagon: 5,
|
|
38107
|
+
hexagon: 6,
|
|
38108
|
+
heptagon: 7,
|
|
38109
|
+
octagon: 8,
|
|
38110
|
+
nonagon: 9,
|
|
38111
|
+
decagon: 10
|
|
38112
|
+
}[type] || 4;
|
|
38113
|
+
}
|
|
38114
|
+
|
|
38115
|
+
// TODO: refactor to remove duplication in mapshaper-svg-style.js
|
|
38116
|
+
cmd.symbols = function(inputLyr, dataset, opts) {
|
|
38117
|
+
requireSinglePointLayer(inputLyr);
|
|
38118
|
+
var lyr = opts.no_replace ? copyLayer(inputLyr) : inputLyr;
|
|
38119
|
+
var polygonMode = !!opts.polygons;
|
|
38120
|
+
var metersPerPx;
|
|
38121
|
+
if (polygonMode) {
|
|
38122
|
+
requireProjectedDataset(dataset);
|
|
38123
|
+
metersPerPx = opts.pixel_scale || getMetersPerPixel(lyr, dataset);
|
|
38124
|
+
}
|
|
38125
|
+
var records = getLayerDataTable(lyr).getRecords();
|
|
38126
|
+
var getSymbolData = getSymbolDataAccessor(lyr, opts);
|
|
38127
|
+
var geometries = lyr.shapes.map(function(shp, i) {
|
|
38128
|
+
if (!shp) return null;
|
|
38129
|
+
var d = getSymbolData(i);
|
|
38130
|
+
var rec = records[i] || {};
|
|
38131
|
+
var coords, size, constructor;
|
|
38132
|
+
if (d.type == 'arrow') {
|
|
38133
|
+
size = d.radius || d.length || d['arrow-length'] || d.r;
|
|
38134
|
+
constructor = getFilledArrowCoords;
|
|
37851
38135
|
} else {
|
|
37852
|
-
|
|
38136
|
+
size = d.radius || d.length || d.r;
|
|
38137
|
+
constructor = getPolygonCoords;
|
|
38138
|
+
}
|
|
38139
|
+
if (size > 0 === false) return null;
|
|
38140
|
+
coords = constructor(size, d, opts);
|
|
38141
|
+
rotateCoords(coords, +d.rotation || 0);
|
|
38142
|
+
if (!polygonMode) {
|
|
38143
|
+
flipY(coords);
|
|
38144
|
+
}
|
|
38145
|
+
if (+opts.scale) {
|
|
38146
|
+
scaleAndShiftCoords(coords, +opts.scale, [0, 0]);
|
|
38147
|
+
}
|
|
38148
|
+
if (polygonMode) {
|
|
38149
|
+
scaleAndShiftCoords(coords, metersPerPx, shp[0]);
|
|
38150
|
+
if (d.tfill) rec.fill = d.fill;
|
|
38151
|
+
return createGeometry(coords);
|
|
38152
|
+
} else {
|
|
38153
|
+
rec['svg-symbol'] = makeSvgSymbol(coords, d);
|
|
37853
38154
|
}
|
|
37854
38155
|
});
|
|
38156
|
+
|
|
38157
|
+
var outputLyr, dataset2;
|
|
38158
|
+
if (polygonMode) {
|
|
38159
|
+
dataset2 = importGeometries(geometries, records);
|
|
38160
|
+
outputLyr = mergeOutputLayerIntoDataset(inputLyr, dataset, dataset2, opts);
|
|
38161
|
+
outputLyr.data = lyr.data;
|
|
38162
|
+
} else {
|
|
38163
|
+
outputLyr = lyr;
|
|
38164
|
+
}
|
|
38165
|
+
return [outputLyr];
|
|
37855
38166
|
};
|
|
37856
38167
|
|
|
38168
|
+
function importGeometries(geometries, records) {
|
|
38169
|
+
var features = geometries.map(function(geom, i) {
|
|
38170
|
+
var d = records[i];
|
|
38171
|
+
return {
|
|
38172
|
+
type: 'Feature',
|
|
38173
|
+
properties: records[i] || null,
|
|
38174
|
+
geometry: geom
|
|
38175
|
+
};
|
|
38176
|
+
});
|
|
38177
|
+
var geojson = {
|
|
38178
|
+
type: 'FeatureCollection',
|
|
38179
|
+
features: features
|
|
38180
|
+
};
|
|
38181
|
+
return importGeoJSON(geojson);
|
|
38182
|
+
}
|
|
38183
|
+
|
|
38184
|
+
function createGeometry(coords) {
|
|
38185
|
+
return {
|
|
38186
|
+
type: 'Polygon',
|
|
38187
|
+
coordinates: coords
|
|
38188
|
+
};
|
|
38189
|
+
}
|
|
38190
|
+
|
|
38191
|
+
function getMetersPerPixel(lyr, dataset) {
|
|
38192
|
+
|
|
38193
|
+
// TODO: handle single point, no extent
|
|
38194
|
+
var bounds = getLayerBounds(lyr);
|
|
38195
|
+
return bounds.width() / 800;
|
|
38196
|
+
}
|
|
38197
|
+
|
|
37857
38198
|
// Returns an svg-symbol data object for one symbol
|
|
37858
|
-
function
|
|
37859
|
-
|
|
37860
|
-
|
|
37861
|
-
|
|
37862
|
-
|
|
37863
|
-
|
|
37864
|
-
|
|
37865
|
-
}
|
|
37866
|
-
return f(properties);
|
|
38199
|
+
function makeSvgSymbol(coords, properties) {
|
|
38200
|
+
roundCoordsForSVG(coords);
|
|
38201
|
+
return {
|
|
38202
|
+
type: 'polygon',
|
|
38203
|
+
coordinates: coords,
|
|
38204
|
+
fill: properties.fill || 'magenta'
|
|
38205
|
+
};
|
|
37867
38206
|
}
|
|
37868
38207
|
|
|
37869
38208
|
var Symbols = /*#__PURE__*/Object.freeze({
|
|
37870
38209
|
__proto__: null,
|
|
37871
|
-
|
|
38210
|
+
makeSvgSymbol: makeSvgSymbol
|
|
37872
38211
|
});
|
|
37873
38212
|
|
|
37874
38213
|
cmd.target = function(catalog, opts) {
|
|
@@ -38014,99 +38353,6 @@ ${svg}
|
|
|
38014
38353
|
}
|
|
38015
38354
|
};
|
|
38016
38355
|
|
|
38017
|
-
cmd.shape = function(targetDataset, opts) {
|
|
38018
|
-
var geojson, dataset;
|
|
38019
|
-
if (opts.coordinates) {
|
|
38020
|
-
geojson = makeShapeFromCoords(opts);
|
|
38021
|
-
} else if (opts.type == 'circle') {
|
|
38022
|
-
geojson = makeCircle(opts);
|
|
38023
|
-
} else if (opts.type == 'rectangle' && opts.bbox) {
|
|
38024
|
-
geojson = getRectangleGeoJSON(opts);
|
|
38025
|
-
} else {
|
|
38026
|
-
stop('Missing coordinates parameter');
|
|
38027
|
-
}
|
|
38028
|
-
// TODO: project shape if targetDataset is projected
|
|
38029
|
-
dataset = importGeoJSON(geojson, {});
|
|
38030
|
-
if (opts.rotation) {
|
|
38031
|
-
rotateDatasetCoords(dataset, opts.rotation);
|
|
38032
|
-
}
|
|
38033
|
-
dataset.layers[0].name = opts.name || opts.type || 'shape';
|
|
38034
|
-
return dataset;
|
|
38035
|
-
};
|
|
38036
|
-
|
|
38037
|
-
function getRectangleGeoJSON(opts) {
|
|
38038
|
-
var bbox = opts.bbox,
|
|
38039
|
-
xmin = bbox[0],
|
|
38040
|
-
ymin = bbox[1],
|
|
38041
|
-
xmax = bbox[2],
|
|
38042
|
-
ymax = bbox[3],
|
|
38043
|
-
interval = 0.5,
|
|
38044
|
-
coords = [],
|
|
38045
|
-
type = opts.geometry == 'polyline' ? 'LineString' : 'Polygon';
|
|
38046
|
-
addSide(xmin, ymin, xmin, ymax);
|
|
38047
|
-
addSide(xmin, ymax, xmax, ymax);
|
|
38048
|
-
addSide(xmax, ymax, xmax, ymin);
|
|
38049
|
-
addSide(xmax, ymin, xmin, ymin);
|
|
38050
|
-
coords.push([xmin, ymin]);
|
|
38051
|
-
return {
|
|
38052
|
-
type: type,
|
|
38053
|
-
coordinates: type == 'Polygon' ? [coords] : coords
|
|
38054
|
-
};
|
|
38055
|
-
|
|
38056
|
-
function addSide(x1, y1, x2, y2) {
|
|
38057
|
-
var dx = x2 - x1,
|
|
38058
|
-
dy = y2 - y1,
|
|
38059
|
-
n = Math.ceil(Math.max(Math.abs(dx) / interval, Math.abs(dy) / interval)),
|
|
38060
|
-
xint = dx / n,
|
|
38061
|
-
yint = dy / n;
|
|
38062
|
-
for (var i=0; i<n; i++) {
|
|
38063
|
-
coords.push([x1 + i * xint, y1 + i * yint]);
|
|
38064
|
-
}
|
|
38065
|
-
}
|
|
38066
|
-
}
|
|
38067
|
-
|
|
38068
|
-
function makeCircle(opts) {
|
|
38069
|
-
if (opts.radius > 0 === false && opts.radius_angle > 0 === false) {
|
|
38070
|
-
stop('Missing required radius parameter.');
|
|
38071
|
-
}
|
|
38072
|
-
var cp = opts.center || [0, 0];
|
|
38073
|
-
var radius = opts.radius || getCircleRadiusFromAngle(getCRS('wgs84'), opts.radius_angle);
|
|
38074
|
-
return getCircleGeoJSON(cp, radius, null, {geometry_type : opts.geometry || 'polygon'});
|
|
38075
|
-
}
|
|
38076
|
-
|
|
38077
|
-
function makeShapeFromCoords(opts) {
|
|
38078
|
-
var coordinates = [];
|
|
38079
|
-
var offsets = opts.offsets || [];
|
|
38080
|
-
var coords = opts.coordinates;
|
|
38081
|
-
var type, i, x, y;
|
|
38082
|
-
if (coords.length >= 2 === false) {
|
|
38083
|
-
stop('Invalid coordinates parameter.');
|
|
38084
|
-
}
|
|
38085
|
-
for (i=0; i<coords.length; i+= 2) {
|
|
38086
|
-
x = coords[i];
|
|
38087
|
-
y = coords[i + 1];
|
|
38088
|
-
coordinates.push([x, y]);
|
|
38089
|
-
}
|
|
38090
|
-
for (i=0; i<offsets.length; i+=2) {
|
|
38091
|
-
x += offsets[i];
|
|
38092
|
-
y += offsets[i + 1];
|
|
38093
|
-
coordinates.push([x, y]);
|
|
38094
|
-
}
|
|
38095
|
-
if (GeoJSON.pathIsRing(coordinates)) {
|
|
38096
|
-
type = 'Polygon';
|
|
38097
|
-
} else if (opts.closed && coordinates.length >= 3) {
|
|
38098
|
-
type = 'Polygon';
|
|
38099
|
-
coordinates.push(coordinates[0]);
|
|
38100
|
-
} else {
|
|
38101
|
-
type = 'LineString';
|
|
38102
|
-
}
|
|
38103
|
-
return {
|
|
38104
|
-
type: type,
|
|
38105
|
-
coordinates: type == 'Polygon' ? [coordinates] : coordinates
|
|
38106
|
-
};
|
|
38107
|
-
|
|
38108
|
-
}
|
|
38109
|
-
|
|
38110
38356
|
cmd.variableSimplify = function(layers, dataset, opts) {
|
|
38111
38357
|
var lyr = layers[0];
|
|
38112
38358
|
var arcs = dataset.arcs;
|
|
@@ -38648,6 +38894,9 @@ ${svg}
|
|
|
38648
38894
|
} else if (name == 'shape') {
|
|
38649
38895
|
catalog.addDataset(cmd.shape(targetDataset, opts));
|
|
38650
38896
|
|
|
38897
|
+
} else if (name == 'shapes') {
|
|
38898
|
+
outputLayers = applyCommandToEachLayer(cmd.shapes, targetLayers, targetDataset, opts);
|
|
38899
|
+
|
|
38651
38900
|
} else if (name == 'simplify') {
|
|
38652
38901
|
if (opts.variable) {
|
|
38653
38902
|
cmd.variableSimplify(targetLayers, targetDataset, opts);
|
|
@@ -38677,7 +38926,7 @@ ${svg}
|
|
|
38677
38926
|
applyCommandToEachLayer(cmd.svgStyle, targetLayers, targetDataset, opts);
|
|
38678
38927
|
|
|
38679
38928
|
} else if (name == 'symbols') {
|
|
38680
|
-
applyCommandToEachLayer(cmd.symbols, targetLayers, opts);
|
|
38929
|
+
outputLayers = applyCommandToEachLayer(cmd.symbols, targetLayers, targetDataset, opts);
|
|
38681
38930
|
|
|
38682
38931
|
} else if (name == 'subdivide') {
|
|
38683
38932
|
outputLayers = applyCommandToEachLayer(cmd.subdivideLayer, targetLayers, arcs, opts.expression);
|