mapshaper 0.5.112 → 0.5.113
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 +4 -0
- package/mapshaper.js +217 -166
- package/package.json +1 -1
- package/www/mapshaper-gui.js +12 -5
- package/www/mapshaper.js +217 -166
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
v0.5.113
|
|
2
|
+
* Added -symbols arrow-style=stick option, for making stick arrows
|
|
3
|
+
* Added -symbols stroke= and -symbols stroke-width= options for styling stick arrows
|
|
4
|
+
|
|
1
5
|
v0.5.112
|
|
2
6
|
* Added support for labels with symbols.
|
|
3
7
|
* Symbols created with the -symbols command have a default size (e.g. `-symbols type=star` creates star symbols with a default size).
|
package/mapshaper.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
|
|
3
|
-
var VERSION = "0.5.
|
|
3
|
+
var VERSION = "0.5.113";
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
var utils = /*#__PURE__*/Object.freeze({
|
|
@@ -13744,6 +13744,14 @@
|
|
|
13744
13744
|
return p.length > 2 && p[2] == 'C';
|
|
13745
13745
|
}
|
|
13746
13746
|
|
|
13747
|
+
function stringifyPolygonCoords(coords) {
|
|
13748
|
+
var parts = [];
|
|
13749
|
+
for (var i=0; i<coords.length; i++) {
|
|
13750
|
+
parts.push(stringifyLineStringCoords(coords[i]) + ' Z');
|
|
13751
|
+
}
|
|
13752
|
+
return parts.length > 0 ? parts.join(' ') : '';
|
|
13753
|
+
}
|
|
13754
|
+
|
|
13747
13755
|
function stringifyLineStringCoords(coords) {
|
|
13748
13756
|
if (coords.length === 0) return '';
|
|
13749
13757
|
var d = 'M';
|
|
@@ -13774,6 +13782,7 @@
|
|
|
13774
13782
|
|
|
13775
13783
|
var SvgPathUtils = /*#__PURE__*/Object.freeze({
|
|
13776
13784
|
__proto__: null,
|
|
13785
|
+
stringifyPolygonCoords: stringifyPolygonCoords,
|
|
13777
13786
|
stringifyLineStringCoords: stringifyLineStringCoords
|
|
13778
13787
|
});
|
|
13779
13788
|
|
|
@@ -14041,6 +14050,7 @@
|
|
|
14041
14050
|
'stroke-dasharray': 'dasharray',
|
|
14042
14051
|
'stroke-width': 'number',
|
|
14043
14052
|
'stroke-opacity': 'number',
|
|
14053
|
+
'stroke-miterlimit': 'number',
|
|
14044
14054
|
'fill-opacity': 'number',
|
|
14045
14055
|
'text-anchor': null
|
|
14046
14056
|
};
|
|
@@ -14075,7 +14085,7 @@
|
|
|
14075
14085
|
|
|
14076
14086
|
var propertiesBySymbolType = {
|
|
14077
14087
|
polygon: utils.arrayToIndex(commonProperties.concat('fill', 'fill-pattern')),
|
|
14078
|
-
polyline: utils.arrayToIndex(commonProperties.concat('stroke-linecap', 'stroke-linejoin')),
|
|
14088
|
+
polyline: utils.arrayToIndex(commonProperties.concat('stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit')),
|
|
14079
14089
|
point: utils.arrayToIndex(commonProperties.concat('fill', 'r')),
|
|
14080
14090
|
label: utils.arrayToIndex(commonProperties.concat(
|
|
14081
14091
|
'fill,font-family,font-size,text-anchor,font-weight,font-style,letter-spacing,dominant-baseline'.split(',')))
|
|
@@ -14377,16 +14387,24 @@
|
|
|
14377
14387
|
return empty();
|
|
14378
14388
|
}
|
|
14379
14389
|
|
|
14380
|
-
function renderComplexSymbol(sym) {
|
|
14390
|
+
function renderComplexSymbol(sym, x, y) {
|
|
14381
14391
|
if (utils.isString(sym)) {
|
|
14382
14392
|
sym = JSON.parse(sym);
|
|
14383
14393
|
}
|
|
14394
|
+
if (sym.tag) {
|
|
14395
|
+
// symbol appears to already use mapshaper's svg notation... pass through
|
|
14396
|
+
return sym;
|
|
14397
|
+
}
|
|
14384
14398
|
var renderer = symbolRenderers[sym.type];
|
|
14385
14399
|
if (!renderer) {
|
|
14386
14400
|
message(sym.type ? 'Unknown symbol type: ' + sym.type : 'Symbol is missing a type property');
|
|
14387
14401
|
return empty();
|
|
14388
14402
|
}
|
|
14389
|
-
|
|
14403
|
+
var o = renderer(sym, x || 0, y || 0);
|
|
14404
|
+
if (sym.opacity) {
|
|
14405
|
+
o.properties.opacity = sym.opacity;
|
|
14406
|
+
}
|
|
14407
|
+
return o;
|
|
14390
14408
|
}
|
|
14391
14409
|
|
|
14392
14410
|
function empty() {
|
|
@@ -14406,7 +14424,12 @@
|
|
|
14406
14424
|
}
|
|
14407
14425
|
|
|
14408
14426
|
function label(d, x, y) {
|
|
14409
|
-
|
|
14427
|
+
var o = renderStyledLabel(d);
|
|
14428
|
+
if (x || y) {
|
|
14429
|
+
o.properties.x = x || 0;
|
|
14430
|
+
o.properties.y = y || 0;
|
|
14431
|
+
}
|
|
14432
|
+
return o;
|
|
14410
14433
|
}
|
|
14411
14434
|
|
|
14412
14435
|
function image(d, x, y) {
|
|
@@ -14417,8 +14440,8 @@
|
|
|
14417
14440
|
properties: {
|
|
14418
14441
|
width: w,
|
|
14419
14442
|
height: h,
|
|
14420
|
-
x: x - w / 2,
|
|
14421
|
-
y: y - h / 2,
|
|
14443
|
+
x: (x || 0) - w / 2,
|
|
14444
|
+
y: (y || 0) - h / 2,
|
|
14422
14445
|
href: d.href || ''
|
|
14423
14446
|
}
|
|
14424
14447
|
};
|
|
@@ -14448,6 +14471,7 @@
|
|
|
14448
14471
|
return o;
|
|
14449
14472
|
}
|
|
14450
14473
|
|
|
14474
|
+
// polyline coords are like GeoJSON MultiLineString coords: an array of 0 or more paths
|
|
14451
14475
|
function polyline(d, x, y) {
|
|
14452
14476
|
var coords = d.coordinates || [];
|
|
14453
14477
|
var o = importMultiLineString(coords);
|
|
@@ -14455,6 +14479,7 @@
|
|
|
14455
14479
|
return o;
|
|
14456
14480
|
}
|
|
14457
14481
|
|
|
14482
|
+
// polygon coords are an array of rings (and holes), like flattened MultiPolygon coords
|
|
14458
14483
|
function polygon(d, x, y) {
|
|
14459
14484
|
var coords = d.coordinates || [];
|
|
14460
14485
|
var o = importPolygon(coords);
|
|
@@ -14463,14 +14488,15 @@
|
|
|
14463
14488
|
}
|
|
14464
14489
|
|
|
14465
14490
|
function group(d, x, y) {
|
|
14466
|
-
var parts = (d.parts || []).
|
|
14467
|
-
var sym =
|
|
14491
|
+
var parts = (d.parts || []).map(function(o) {
|
|
14492
|
+
var sym = renderComplexSymbol(o, x, y);
|
|
14468
14493
|
if (d.chained) {
|
|
14494
|
+
//if (o.chained) {
|
|
14469
14495
|
x += (o.dx || 0);
|
|
14470
14496
|
y += (o.dy || 0);
|
|
14471
14497
|
}
|
|
14472
|
-
return
|
|
14473
|
-
}
|
|
14498
|
+
return sym;
|
|
14499
|
+
});
|
|
14474
14500
|
if (parts.length == 1) return parts[0];
|
|
14475
14501
|
return {
|
|
14476
14502
|
tag: 'g',
|
|
@@ -14485,6 +14511,15 @@
|
|
|
14485
14511
|
renderPoint: renderPoint
|
|
14486
14512
|
});
|
|
14487
14513
|
|
|
14514
|
+
var geojsonImporters = {
|
|
14515
|
+
Point: importPoint,
|
|
14516
|
+
Polygon: importPolygon,
|
|
14517
|
+
LineString: importLineString,
|
|
14518
|
+
MultiPoint: importMultiPoint,
|
|
14519
|
+
MultiLineString: importMultiLineString,
|
|
14520
|
+
MultiPolygon: importMultiPolygon
|
|
14521
|
+
};
|
|
14522
|
+
|
|
14488
14523
|
function importGeoJSONFeatures(features, opts) {
|
|
14489
14524
|
opts = opts || {};
|
|
14490
14525
|
return features.map(function(obj, i) {
|
|
@@ -14557,18 +14592,6 @@
|
|
|
14557
14592
|
return children.length > 0 ? {tag: 'g', children: children} : null;
|
|
14558
14593
|
}
|
|
14559
14594
|
|
|
14560
|
-
function importMultiPath(coords, importer) {
|
|
14561
|
-
var o;
|
|
14562
|
-
for (var i=0; i<coords.length; i++) {
|
|
14563
|
-
if (i === 0) {
|
|
14564
|
-
o = importer(coords[i]);
|
|
14565
|
-
} else {
|
|
14566
|
-
o.properties.d += ' ' + importer(coords[i]).properties.d;
|
|
14567
|
-
}
|
|
14568
|
-
}
|
|
14569
|
-
return o;
|
|
14570
|
-
}
|
|
14571
|
-
|
|
14572
14595
|
function importLineString(coords) {
|
|
14573
14596
|
var d = stringifyLineStringCoords(coords);
|
|
14574
14597
|
return {
|
|
@@ -14585,40 +14608,38 @@
|
|
|
14585
14608
|
};
|
|
14586
14609
|
}
|
|
14587
14610
|
|
|
14611
|
+
function importMultiPolygon(coords) {
|
|
14612
|
+
return importPolygon(flattenMultiPolygonCoords(coords));
|
|
14613
|
+
}
|
|
14614
|
+
|
|
14615
|
+
function flattenMultiPolygonCoords(coords) {
|
|
14616
|
+
return coords.reduce(function(memo, poly) {
|
|
14617
|
+
return memo.concat(poly);
|
|
14618
|
+
}, []);
|
|
14619
|
+
}
|
|
14620
|
+
|
|
14588
14621
|
function importPolygon(coords) {
|
|
14589
|
-
|
|
14590
|
-
|
|
14591
|
-
|
|
14592
|
-
|
|
14593
|
-
|
|
14594
|
-
|
|
14622
|
+
if (coords.length === 0) return null;
|
|
14623
|
+
var o = {
|
|
14624
|
+
tag: 'path',
|
|
14625
|
+
properties: {
|
|
14626
|
+
d: stringifyPolygonCoords(coords)
|
|
14627
|
+
}
|
|
14628
|
+
};
|
|
14595
14629
|
if (coords.length > 1) {
|
|
14596
14630
|
o.properties['fill-rule'] = 'evenodd'; // support polygons with holes
|
|
14597
14631
|
}
|
|
14598
14632
|
return o;
|
|
14599
14633
|
}
|
|
14600
14634
|
|
|
14601
|
-
var geojsonImporters = {
|
|
14602
|
-
Point: importPoint,
|
|
14603
|
-
Polygon: importPolygon,
|
|
14604
|
-
LineString: importLineString,
|
|
14605
|
-
MultiPoint: function(coords, rec) {
|
|
14606
|
-
return importMultiPoint(coords, rec);
|
|
14607
|
-
},
|
|
14608
|
-
MultiLineString: function(coords) {
|
|
14609
|
-
return importMultiPath(coords, importLineString);
|
|
14610
|
-
},
|
|
14611
|
-
MultiPolygon: function(coords) {
|
|
14612
|
-
return importMultiPath(coords, importPolygon);
|
|
14613
|
-
}
|
|
14614
|
-
};
|
|
14615
|
-
|
|
14616
14635
|
var GeojsonToSvg = /*#__PURE__*/Object.freeze({
|
|
14617
14636
|
__proto__: null,
|
|
14618
14637
|
importGeoJSONFeatures: importGeoJSONFeatures,
|
|
14619
14638
|
importPoint: importPoint,
|
|
14620
14639
|
importLineString: importLineString,
|
|
14621
14640
|
importMultiLineString: importMultiLineString,
|
|
14641
|
+
importMultiPolygon: importMultiPolygon,
|
|
14642
|
+
flattenMultiPolygonCoords: flattenMultiPolygonCoords,
|
|
14622
14643
|
importPolygon: importPolygon
|
|
14623
14644
|
});
|
|
14624
14645
|
|
|
@@ -15077,6 +15098,7 @@
|
|
|
15077
15098
|
return svg;
|
|
15078
15099
|
}
|
|
15079
15100
|
|
|
15101
|
+
// href: A URL or a local path
|
|
15080
15102
|
// TODO: download SVG files asynchronously
|
|
15081
15103
|
// (currently, files are downloaded synchronously, which is obviously undesirable)
|
|
15082
15104
|
//
|
|
@@ -15084,7 +15106,7 @@
|
|
|
15084
15106
|
var content;
|
|
15085
15107
|
if (href.indexOf('http') === 0) {
|
|
15086
15108
|
content = fetchFileSync(href);
|
|
15087
|
-
} else if (require('fs').existsSync(href)) {
|
|
15109
|
+
} else if (require('fs').existsSync(href)) {
|
|
15088
15110
|
content = require('fs').readFileSync(href, 'utf8');
|
|
15089
15111
|
} else {
|
|
15090
15112
|
stop("Invalid SVG location:", href);
|
|
@@ -15159,15 +15181,25 @@
|
|
|
15159
15181
|
convertPropertiesToDefinitions(obj, defs);
|
|
15160
15182
|
return stringify(obj);
|
|
15161
15183
|
}).join('\n');
|
|
15184
|
+
|
|
15162
15185
|
if (defs.length > 0) {
|
|
15163
15186
|
svg = '<defs>\n' + utils.pluck(defs, 'svg').join('') + '</defs>\n' + svg;
|
|
15164
15187
|
}
|
|
15188
|
+
|
|
15165
15189
|
if (svg.includes('xlink:')) {
|
|
15166
15190
|
namespace += ' xmlns:xlink="http://www.w3.org/1999/xlink"';
|
|
15167
15191
|
}
|
|
15192
|
+
|
|
15193
|
+
// default line style properties
|
|
15168
15194
|
var capStyle = opts.default_linecap || 'round';
|
|
15195
|
+
var lineProps = `stroke-linecap="${capStyle}" stroke-linejoin="round"`;
|
|
15196
|
+
if (svg.includes('stroke-linejoin="miter"')) {
|
|
15197
|
+
// the default limit in Illustrator seems to be 10 -- too large for mapping
|
|
15198
|
+
// (Mapbox uses 2 as the default in their styles)
|
|
15199
|
+
lineProps += ' stroke-miterlimit="2"';
|
|
15200
|
+
}
|
|
15169
15201
|
var template = `<?xml version="1.0"?>
|
|
15170
|
-
<svg ${namespace} version="1.2" baseProfile="tiny" width="%d" height="%d" viewBox="%s %s %s %s"
|
|
15202
|
+
<svg ${namespace} version="1.2" baseProfile="tiny" width="%d" height="%d" viewBox="%s %s %s %s" ${lineProps}>${style}
|
|
15171
15203
|
${svg}
|
|
15172
15204
|
</svg>`;
|
|
15173
15205
|
svg = utils.format(template,size[0], size[1], 0, 0, size[0], size[1]);
|
|
@@ -20445,14 +20477,21 @@ ${svg}
|
|
|
20445
20477
|
.option('stroke', {})
|
|
20446
20478
|
.option('stroke-width', {})
|
|
20447
20479
|
.option('fill', {
|
|
20448
|
-
describe: 'symbol fill color'
|
|
20480
|
+
describe: 'symbol fill color (filled symbols only)'
|
|
20481
|
+
})
|
|
20482
|
+
.option('stroke', {
|
|
20483
|
+
describe: 'symbol line color (linear symbols only)'
|
|
20484
|
+
})
|
|
20485
|
+
.option('stroke-width', {
|
|
20486
|
+
describe: 'symbol line width (linear symbols only)'
|
|
20449
20487
|
})
|
|
20450
|
-
.option('
|
|
20451
|
-
|
|
20488
|
+
.option('geographic', {
|
|
20489
|
+
old_alias: 'polygons',
|
|
20490
|
+
describe: 'make geographic shapes instead of SVG objects',
|
|
20452
20491
|
type: 'flag'
|
|
20453
20492
|
})
|
|
20454
20493
|
.option('pixel-scale', {
|
|
20455
|
-
describe: 'set symbol scale in meters
|
|
20494
|
+
describe: 'set symbol scale in meters per pixel (geographic option)',
|
|
20456
20495
|
type: 'number',
|
|
20457
20496
|
})
|
|
20458
20497
|
// .option('flipped', {
|
|
@@ -20489,6 +20528,9 @@ ${svg}
|
|
|
20489
20528
|
.option('radii', {
|
|
20490
20529
|
describe: '(ring) comma-sep. list of concentric radii, ascending order'
|
|
20491
20530
|
})
|
|
20531
|
+
.option('arrow-style', {
|
|
20532
|
+
describe: '(arrow) options: stick, standard (default is standard)'
|
|
20533
|
+
})
|
|
20492
20534
|
.option('length', {
|
|
20493
20535
|
old_alias: 'arrow-length',
|
|
20494
20536
|
describe: '(arrow) length of arrow in pixels'
|
|
@@ -38713,10 +38755,14 @@ ${svg}
|
|
|
38713
38755
|
|
|
38714
38756
|
var roundCoord = getRoundingFunction(0.01);
|
|
38715
38757
|
|
|
38716
|
-
function
|
|
38758
|
+
function getSymbolFillColor(d) {
|
|
38717
38759
|
return d.fill || 'magenta';
|
|
38718
38760
|
}
|
|
38719
38761
|
|
|
38762
|
+
function getSymbolStrokeColor(d) {
|
|
38763
|
+
return d.stroke || d.fill || 'magenta';
|
|
38764
|
+
}
|
|
38765
|
+
|
|
38720
38766
|
function getSymbolRadius(d) {
|
|
38721
38767
|
if (d.radius === 0 || d.length === 0 || d.r === 0) return 0;
|
|
38722
38768
|
return d.radius || d.length || d.r || 5; // use a default value
|
|
@@ -38790,57 +38836,104 @@ ${svg}
|
|
|
38790
38836
|
points.push(p2);
|
|
38791
38837
|
}
|
|
38792
38838
|
|
|
38793
|
-
|
|
38794
|
-
|
|
38795
|
-
|
|
38796
|
-
// var curve = d['arrow-stem-curve'] || 0;
|
|
38797
|
-
// var unscaledHeadWidth = d['arrow-head-width'] || 9;
|
|
38798
|
-
// var unscaledHeadLen = getHeadLength(unscaledHeadWidth, headAngle);
|
|
38799
|
-
// var scale = getScale(totalLen, unscaledHeadLen, minStemRatio);
|
|
38800
|
-
// var headWidth = unscaledHeadWidth * scale;
|
|
38801
|
-
// var headLen = unscaledHeadLen * scale;
|
|
38802
|
-
// var tip = getStickArrowTip(totalLen, curve);
|
|
38803
|
-
// var stem = [[0, 0], tip.concat()];
|
|
38804
|
-
// if (curve) {
|
|
38805
|
-
// addBezierArcControlPoints(stem, curve);
|
|
38806
|
-
// }
|
|
38807
|
-
// if (!headLen) return [stem];
|
|
38808
|
-
// var head = [addPoints([-headWidth / 2, -headLen], tip), tip.concat(), addPoints([headWidth / 2, -headLen], tip)];
|
|
38839
|
+
function getStickArrowCoords(d) {
|
|
38840
|
+
return getArrowCoords(d, 'stick');
|
|
38841
|
+
}
|
|
38809
38842
|
|
|
38810
|
-
|
|
38811
|
-
|
|
38812
|
-
|
|
38813
|
-
// }
|
|
38843
|
+
function getFilledArrowCoords(d) {
|
|
38844
|
+
return getArrowCoords(d, 'standard');
|
|
38845
|
+
}
|
|
38814
38846
|
|
|
38815
|
-
|
|
38816
|
-
|
|
38817
|
-
|
|
38818
|
-
|
|
38819
|
-
|
|
38820
|
-
|
|
38821
|
-
|
|
38847
|
+
function getArrowCoords(d, style) {
|
|
38848
|
+
var stickArrow = style == 'stick',
|
|
38849
|
+
// direction = d.rotation || d.direction || 0,
|
|
38850
|
+
direction = d.direction || 0, // rotation is an independent parameter
|
|
38851
|
+
stemTaper = d['stem-taper'] || 0,
|
|
38852
|
+
curvature = d['stem-curve'] || 0,
|
|
38853
|
+
size = calcArrowSize(d, stickArrow);
|
|
38854
|
+
if (!size) return null;
|
|
38855
|
+
var stemLen = size.stemLen,
|
|
38856
|
+
headLen = size.headLen,
|
|
38857
|
+
headDx = size.headWidth / 2,
|
|
38858
|
+
stemDx = size.stemWidth / 2,
|
|
38859
|
+
baseDx = stemDx * (1 - stemTaper),
|
|
38860
|
+
head, stem, coords, dx, dy;
|
|
38822
38861
|
|
|
38823
|
-
|
|
38824
|
-
|
|
38825
|
-
|
|
38862
|
+
if (curvature) {
|
|
38863
|
+
// make curved stem
|
|
38864
|
+
if (direction > 0) curvature = -curvature;
|
|
38865
|
+
var theta = Math.abs(curvature) / 180 * Math.PI;
|
|
38866
|
+
var sign = curvature > 0 ? 1 : -1;
|
|
38867
|
+
var ax = baseDx * Math.cos(theta); // rotate arrow base
|
|
38868
|
+
var ay = baseDx * Math.sin(theta) * -sign;
|
|
38869
|
+
dx = stemLen * Math.sin(theta / 2) * sign;
|
|
38870
|
+
dy = stemLen * Math.cos(theta / 2);
|
|
38871
|
+
|
|
38872
|
+
if (stickArrow) {
|
|
38873
|
+
stem = getCurvedStemCoords(-ax, -ay, dx, dy, theta);
|
|
38874
|
+
} else {
|
|
38875
|
+
var leftStem = getCurvedStemCoords(-ax, -ay, -stemDx + dx, dy, theta);
|
|
38876
|
+
var rightStem = getCurvedStemCoords(ax, ay, stemDx + dx, dy, theta);
|
|
38877
|
+
stem = leftStem.concat(rightStem.reverse());
|
|
38878
|
+
}
|
|
38879
|
+
|
|
38880
|
+
} else {
|
|
38881
|
+
// make straight stem
|
|
38882
|
+
dx = 0;
|
|
38883
|
+
dy = stemLen;
|
|
38884
|
+
if (stickArrow) {
|
|
38885
|
+
stem = [[0, 0], [0, stemLen]];
|
|
38886
|
+
} else {
|
|
38887
|
+
stem = [[-baseDx, 0], [baseDx, 0]];
|
|
38888
|
+
}
|
|
38889
|
+
}
|
|
38890
|
+
|
|
38891
|
+
if (stickArrow) {
|
|
38892
|
+
// make stick arrow
|
|
38893
|
+
head = [[-headDx + dx, stemLen - headLen], [dx, stemLen], [headDx + dx, stemLen - headLen]];
|
|
38894
|
+
coords = [stem, head]; // MultiLineString coords
|
|
38895
|
+
} else {
|
|
38896
|
+
// make filled arrow
|
|
38897
|
+
// coordinates go counter clockwise, starting from the leftmost head coordinate
|
|
38898
|
+
head = [[stemDx + dx, dy], [headDx + dx, dy],
|
|
38899
|
+
[dx, headLen + dy], [-headDx + dx, dy], [-stemDx + dx, dy]];
|
|
38900
|
+
coords = stem.concat(head);
|
|
38901
|
+
coords.push(stem[0].concat()); // closed path
|
|
38902
|
+
coords = [coords]; // Polygon coords
|
|
38903
|
+
}
|
|
38904
|
+
|
|
38905
|
+
if (d.anchor == 'end') {
|
|
38906
|
+
scaleAndShiftCoords(coords, 1, [-dx, -dy - headLen]);
|
|
38907
|
+
} else if (d.anchor == 'middle') {
|
|
38908
|
+
// shift midpoint away from the head a bit for a more balanced placement
|
|
38909
|
+
// scaleAndShiftCoords(coords, 1, [-dx/2, (-dy - headLen)/2]);
|
|
38910
|
+
scaleAndShiftCoords(coords, 1, [-dx * 0.5, -dy * 0.5 - headLen * 0.25]);
|
|
38911
|
+
}
|
|
38912
|
+
|
|
38913
|
+
rotateCoords(coords, direction);
|
|
38914
|
+
if (d.flipped) {
|
|
38915
|
+
flipY(coords);
|
|
38916
|
+
}
|
|
38917
|
+
return coords;
|
|
38918
|
+
}
|
|
38826
38919
|
|
|
38827
38920
|
function calcStraightArrowCoords(stemLen, headLen, stemDx, headDx, baseDx) {
|
|
38828
38921
|
return [[baseDx, 0], [stemDx, stemLen], [headDx, stemLen], [0, stemLen + headLen],
|
|
38829
38922
|
[-headDx, stemLen], [-stemDx, stemLen], [-baseDx, 0], [baseDx, 0]];
|
|
38830
38923
|
}
|
|
38831
38924
|
|
|
38832
|
-
function calcArrowSize(d) {
|
|
38925
|
+
function calcArrowSize(d, stickArrow) {
|
|
38833
38926
|
// don't display arrows with negative length
|
|
38834
38927
|
var totalLen = Math.max(d.radius || d.length || d.r || 0, 0),
|
|
38835
38928
|
scale = 1,
|
|
38836
38929
|
o = initArrowSize(d); // calc several parameters
|
|
38837
38930
|
|
|
38838
|
-
if (totalLen
|
|
38931
|
+
if (totalLen >= 0) {
|
|
38839
38932
|
scale = calcScale(totalLen, o.headLen, d);
|
|
38840
38933
|
o.stemWidth *= scale;
|
|
38841
38934
|
o.headWidth *= scale;
|
|
38842
38935
|
o.headLen *= scale;
|
|
38843
|
-
o.stemLen = totalLen - o.headLen;
|
|
38936
|
+
o.stemLen = stickArrow ? totalLen : totalLen - o.headLen;
|
|
38844
38937
|
}
|
|
38845
38938
|
|
|
38846
38939
|
if (o.headWidth < o.stemWidth) {
|
|
@@ -38861,7 +38954,7 @@ ${svg}
|
|
|
38861
38954
|
} else if (stemLen + headLen > totalLen) {
|
|
38862
38955
|
scale = totalLen / (stemLen + headLen);
|
|
38863
38956
|
}
|
|
38864
|
-
return scale;
|
|
38957
|
+
return scale || 0;
|
|
38865
38958
|
}
|
|
38866
38959
|
|
|
38867
38960
|
function initArrowSize(d) {
|
|
@@ -38891,56 +38984,6 @@ ${svg}
|
|
|
38891
38984
|
return 1 / Math.tan(Math.PI * headAngle / 180 / 2) / 2;
|
|
38892
38985
|
}
|
|
38893
38986
|
|
|
38894
|
-
function getFilledArrowCoords(d) {
|
|
38895
|
-
var direction = d.rotation || d.direction || 0,
|
|
38896
|
-
stemTaper = d['stem-taper'] || 0,
|
|
38897
|
-
curvature = d['stem-curve'] || 0,
|
|
38898
|
-
size = calcArrowSize(d);
|
|
38899
|
-
if (!size) return null;
|
|
38900
|
-
var stemLen = size.stemLen,
|
|
38901
|
-
headLen = size.headLen,
|
|
38902
|
-
headDx = size.headWidth / 2,
|
|
38903
|
-
stemDx = size.stemWidth / 2,
|
|
38904
|
-
baseDx = stemDx * (1 - stemTaper),
|
|
38905
|
-
head, stem, coords, dx, dy;
|
|
38906
|
-
|
|
38907
|
-
if (curvature) {
|
|
38908
|
-
if (direction > 0) curvature = -curvature;
|
|
38909
|
-
var theta = Math.abs(curvature) / 180 * Math.PI;
|
|
38910
|
-
var sign = curvature > 0 ? 1 : -1;
|
|
38911
|
-
var ax = baseDx * Math.cos(theta); // rotate arrow base
|
|
38912
|
-
var ay = baseDx * Math.sin(theta) * -sign;
|
|
38913
|
-
dx = stemLen * Math.sin(theta / 2) * sign;
|
|
38914
|
-
dy = stemLen * Math.cos(theta / 2);
|
|
38915
|
-
var leftStem = getCurvedStemCoords(-ax, -ay, -stemDx + dx, dy, theta);
|
|
38916
|
-
var rightStem = getCurvedStemCoords(ax, ay, stemDx + dx, dy, theta);
|
|
38917
|
-
stem = leftStem.concat(rightStem.reverse());
|
|
38918
|
-
|
|
38919
|
-
} else {
|
|
38920
|
-
dx = 0;
|
|
38921
|
-
dy = stemLen;
|
|
38922
|
-
stem = [[-baseDx, 0], [baseDx, 0], [baseDx, 0]];
|
|
38923
|
-
}
|
|
38924
|
-
|
|
38925
|
-
// coordinates go counter clockwise, starting from the leftmost head coordinate
|
|
38926
|
-
head = [[stemDx + dx, dy], [headDx + dx, dy],
|
|
38927
|
-
[dx, headLen + dy], [-headDx + dx, dy], [-stemDx + dx, dy]];
|
|
38928
|
-
|
|
38929
|
-
coords = stem.concat(head);
|
|
38930
|
-
if (d.anchor == 'end') {
|
|
38931
|
-
scaleAndShiftCoords(coords, 1, [-dx, -dy - headLen]);
|
|
38932
|
-
} else if (d.anchor == 'middle') {
|
|
38933
|
-
// shift midpoint away from the head a bit for a more balanced placement
|
|
38934
|
-
// scaleAndShiftCoords(coords, 1, [-dx/2, (-dy - headLen)/2]);
|
|
38935
|
-
scaleAndShiftCoords(coords, 1, [-dx * 0.5, -dy * 0.5 - headLen * 0.25]);
|
|
38936
|
-
}
|
|
38937
|
-
|
|
38938
|
-
rotateCoords(coords, direction);
|
|
38939
|
-
if (d.flipped) {
|
|
38940
|
-
flipY(coords);
|
|
38941
|
-
}
|
|
38942
|
-
return [coords];
|
|
38943
|
-
}
|
|
38944
38987
|
|
|
38945
38988
|
// ax, ay: point on the base
|
|
38946
38989
|
// bx, by: point on the stem
|
|
@@ -38978,11 +39021,11 @@ ${svg}
|
|
|
38978
39021
|
|
|
38979
39022
|
function makeCircleSymbol(d, opts) {
|
|
38980
39023
|
var radius = getSymbolRadius(d);
|
|
38981
|
-
// TODO: remove
|
|
39024
|
+
// TODO: remove duplication with svg-symbols.js
|
|
38982
39025
|
if (+opts.scale) radius *= +opts.scale;
|
|
38983
39026
|
return {
|
|
38984
39027
|
type: 'circle',
|
|
38985
|
-
fill:
|
|
39028
|
+
fill: getSymbolFillColor(d),
|
|
38986
39029
|
r: radius
|
|
38987
39030
|
};
|
|
38988
39031
|
}
|
|
@@ -39070,11 +39113,12 @@ ${svg}
|
|
|
39070
39113
|
return 180 - centerAngle;
|
|
39071
39114
|
}
|
|
39072
39115
|
|
|
39116
|
+
// Returns a svg-symbol object
|
|
39073
39117
|
function makeRingSymbol(d, opts) {
|
|
39074
39118
|
var scale = +opts.scale || 1;
|
|
39075
39119
|
var radii = parseRings(d.radii || '2').map(function(r) { return r * scale; });
|
|
39076
39120
|
var solidCenter = utils.isOdd(radii.length);
|
|
39077
|
-
var color =
|
|
39121
|
+
var color = getSymbolFillColor(d);
|
|
39078
39122
|
var parts = [];
|
|
39079
39123
|
if (solidCenter) {
|
|
39080
39124
|
parts.push({
|
|
@@ -39130,33 +39174,35 @@ ${svg}
|
|
|
39130
39174
|
}
|
|
39131
39175
|
|
|
39132
39176
|
// Returns an svg-symbol data object for one symbol
|
|
39133
|
-
function
|
|
39134
|
-
|
|
39135
|
-
|
|
39136
|
-
|
|
39177
|
+
function makePathSymbol(coords, properties, geojsonType) {
|
|
39178
|
+
var sym;
|
|
39179
|
+
if (geojsonType == 'MultiPolygon' || geojsonType == 'Polygon') {
|
|
39180
|
+
sym = {
|
|
39181
|
+
type: 'polygon',
|
|
39182
|
+
fill: getSymbolFillColor(properties),
|
|
39183
|
+
coordinates: geojsonType == 'Polygon' ? coords : flattenMultiPolygonCoords(coords)
|
|
39184
|
+
};
|
|
39185
|
+
} else if (geojsonType == 'LineString' || geojsonType == 'MultiLineString') {
|
|
39186
|
+
sym = {
|
|
39187
|
+
type: 'polyline',
|
|
39188
|
+
stroke: getSymbolStrokeColor(properties),
|
|
39189
|
+
'stroke-width': properties['stroke-width'] || 2,
|
|
39190
|
+
coordinates: geojsonType == 'LineString' ? [coords] : coords
|
|
39191
|
+
};
|
|
39192
|
+
} else {
|
|
39137
39193
|
error('Unsupported type:', geojsonType);
|
|
39138
39194
|
}
|
|
39139
|
-
roundCoordsForSVG(
|
|
39140
|
-
return
|
|
39141
|
-
type: 'polygon',
|
|
39142
|
-
coordinates: coords,
|
|
39143
|
-
fill: getSymbolColor(properties)
|
|
39144
|
-
};
|
|
39145
|
-
}
|
|
39146
|
-
|
|
39147
|
-
function convertMultiPolygonCoords(coords) {
|
|
39148
|
-
return coords.reduce(function(memo, poly) {
|
|
39149
|
-
return memo.concat(poly);
|
|
39150
|
-
}, []);
|
|
39195
|
+
roundCoordsForSVG(sym.coordinates);
|
|
39196
|
+
return sym;
|
|
39151
39197
|
}
|
|
39152
39198
|
|
|
39153
39199
|
// TODO: refactor to remove duplication in mapshaper-svg-style.js
|
|
39154
39200
|
cmd.symbols = function(inputLyr, dataset, opts) {
|
|
39155
39201
|
requireSinglePointLayer(inputLyr);
|
|
39156
39202
|
var lyr = opts.no_replace ? copyLayer(inputLyr) : inputLyr;
|
|
39157
|
-
var
|
|
39203
|
+
var shapeMode = !!opts.geographic;
|
|
39158
39204
|
var metersPerPx;
|
|
39159
|
-
if (
|
|
39205
|
+
if (shapeMode) {
|
|
39160
39206
|
requireProjectedDataset(dataset);
|
|
39161
39207
|
metersPerPx = opts.pixel_scale || getMetersPerPixel(lyr, dataset);
|
|
39162
39208
|
}
|
|
@@ -39168,11 +39214,11 @@ ${svg}
|
|
|
39168
39214
|
var rec = records[i] || {};
|
|
39169
39215
|
|
|
39170
39216
|
// non-polygon symbols
|
|
39171
|
-
if (!
|
|
39217
|
+
if (!shapeMode && d.type == 'circle') {
|
|
39172
39218
|
rec['svg-symbol'] = makeCircleSymbol(d, opts);
|
|
39173
39219
|
return;
|
|
39174
39220
|
}
|
|
39175
|
-
if (!
|
|
39221
|
+
if (!shapeMode && d.type == 'ring') {
|
|
39176
39222
|
rec['svg-symbol'] = makeRingSymbol(d, opts);
|
|
39177
39223
|
return;
|
|
39178
39224
|
}
|
|
@@ -39180,7 +39226,10 @@ ${svg}
|
|
|
39180
39226
|
var geojsonType = 'Polygon';
|
|
39181
39227
|
var coords;
|
|
39182
39228
|
// these symbols get converted to polygon shapes
|
|
39183
|
-
if (d.type == 'arrow') {
|
|
39229
|
+
if (d.type == 'arrow' && opts.arrow_style == 'stick') {
|
|
39230
|
+
coords = getStickArrowCoords(d);
|
|
39231
|
+
geojsonType = 'MultiLineString';
|
|
39232
|
+
} else if (d.type == 'arrow') {
|
|
39184
39233
|
coords = getFilledArrowCoords(d);
|
|
39185
39234
|
} else if (d.type == 'ring') {
|
|
39186
39235
|
coords = getRingCoords(d);
|
|
@@ -39192,23 +39241,24 @@ ${svg}
|
|
|
39192
39241
|
}
|
|
39193
39242
|
if (!coords) return null;
|
|
39194
39243
|
rotateCoords(coords, +d.rotation || 0);
|
|
39195
|
-
if (!
|
|
39244
|
+
if (!shapeMode) {
|
|
39196
39245
|
flipY(coords);
|
|
39197
39246
|
}
|
|
39198
39247
|
if (+opts.scale) {
|
|
39199
39248
|
scaleAndShiftCoords(coords, +opts.scale, [0, 0]);
|
|
39200
39249
|
}
|
|
39201
|
-
if (
|
|
39250
|
+
if (shapeMode) {
|
|
39202
39251
|
scaleAndShiftCoords(coords, metersPerPx, shp[0]);
|
|
39203
|
-
if (d.
|
|
39252
|
+
if (d.fill) rec.fill = d.fill;
|
|
39253
|
+
if (d.stroke) rec.stroke = d.stroke;
|
|
39204
39254
|
return createGeometry(coords, geojsonType);
|
|
39205
39255
|
} else {
|
|
39206
|
-
rec['svg-symbol'] =
|
|
39256
|
+
rec['svg-symbol'] = makePathSymbol(coords, d, geojsonType);
|
|
39207
39257
|
}
|
|
39208
39258
|
});
|
|
39209
39259
|
|
|
39210
39260
|
var outputLyr, dataset2;
|
|
39211
|
-
if (
|
|
39261
|
+
if (shapeMode) {
|
|
39212
39262
|
dataset2 = importGeometries(geometries, records);
|
|
39213
39263
|
outputLyr = mergeOutputLayerIntoDataset(inputLyr, dataset, dataset2, opts);
|
|
39214
39264
|
outputLyr.data = lyr.data;
|
|
@@ -39242,9 +39292,10 @@ ${svg}
|
|
|
39242
39292
|
}
|
|
39243
39293
|
|
|
39244
39294
|
function getMetersPerPixel(lyr, dataset) {
|
|
39245
|
-
// TODO: handle single point, no extent
|
|
39246
39295
|
var bounds = getLayerBounds(lyr);
|
|
39247
|
-
|
|
39296
|
+
// TODO: need a better way to handle a single point with no extent
|
|
39297
|
+
var extent = bounds.width() || bounds.height() || 1000;
|
|
39298
|
+
return extent / 800;
|
|
39248
39299
|
}
|
|
39249
39300
|
|
|
39250
39301
|
var Symbols = /*#__PURE__*/Object.freeze({
|