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 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.112";
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
- return renderer(sym, 0, 0);
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
- return renderStyledLabel(d);
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 || []).reduce(function(memo, o) {
14467
- var sym = renderSymbol(o, x, y);
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 memo.concat(sym);
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
- var d, o;
14590
- for (var i=0; i<coords.length; i++) {
14591
- d = o ? o.properties.d + ' ' : '';
14592
- o = importLineString(coords[i]);
14593
- o.properties.d = d + o.properties.d + ' Z';
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)) { // assume href is a relative path
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" stroke-linecap="${capStyle}" stroke-linejoin="round">${style}
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('polygons', {
20451
- describe: 'generate symbols as polygons instead of SVG objects',
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-per-pixel (for polygons option)',
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 getSymbolColor(d) {
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
- // export function getStickArrowCoords(d, totalLen) {
38794
- // var minStemRatio = getMinStemRatio(d);
38795
- // var headAngle = d['arrow-head-angle'] || 90;
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
- // rotateCoords(stem, d.rotation);
38811
- // rotateCoords(head, d.rotation);
38812
- // return [stem, head];
38813
- // }
38843
+ function getFilledArrowCoords(d) {
38844
+ return getArrowCoords(d, 'standard');
38845
+ }
38814
38846
 
38815
- // function getStickArrowTip(totalLen, curve) {
38816
- // // curve/2 intersects the arrowhead at 90deg (trigonometry)
38817
- // var theta = Math.abs(curve/2) / 180 * Math.PI;
38818
- // var dx = totalLen * Math.sin(theta) * (curve > 0 ? -1 : 1);
38819
- // var dy = totalLen * Math.cos(theta);
38820
- // return [dx, dy];
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
- // function addPoints(a, b) {
38824
- // return [a[0] + b[0], a[1] + b[1]];
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 > 0) {
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 duplicatoin with svg-symbols.js
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: getSymbolColor(d),
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 = getSymbolColor(d);
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 makePolygonSymbol(coords, properties, geojsonType) {
39134
- if (geojsonType == 'MultiPolygon') {
39135
- coords = convertMultiPolygonCoords(coords);
39136
- } else if (geojsonType != 'Polygon') {
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(coords);
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 polygonMode = !!opts.polygons;
39203
+ var shapeMode = !!opts.geographic;
39158
39204
  var metersPerPx;
39159
- if (polygonMode) {
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 (!polygonMode && d.type == 'circle') {
39217
+ if (!shapeMode && d.type == 'circle') {
39172
39218
  rec['svg-symbol'] = makeCircleSymbol(d, opts);
39173
39219
  return;
39174
39220
  }
39175
- if (!polygonMode && d.type == 'ring') {
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 (!polygonMode) {
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 (polygonMode) {
39250
+ if (shapeMode) {
39202
39251
  scaleAndShiftCoords(coords, metersPerPx, shp[0]);
39203
- if (d.tfill) rec.fill = d.fill;
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'] = makePolygonSymbol(coords, d, geojsonType);
39256
+ rec['svg-symbol'] = makePathSymbol(coords, d, geojsonType);
39207
39257
  }
39208
39258
  });
39209
39259
 
39210
39260
  var outputLyr, dataset2;
39211
- if (polygonMode) {
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
- return bounds.width() / 800;
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({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.5.112",
3
+ "version": "0.5.113",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",