mapshaper 0.5.84 → 0.5.85

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,6 @@
1
+ v0.5.85
2
+ * Improved arrow and star symbols.
3
+
1
4
  v0.5.84
2
5
  * Bug fixes
3
6
 
package/mapshaper.js CHANGED
@@ -13712,6 +13712,9 @@
13712
13712
  flipped: 'boolean',
13713
13713
  rotated: 'boolean',
13714
13714
  direction: 'number',
13715
+ sides: 'number', // polygons and stars
13716
+ points: 'number', // polygons and stars
13717
+ anchor: null, // arrows; takes start, middle, end
13715
13718
  'head-angle': 'number',
13716
13719
  'head-width': 'number',
13717
13720
  'head-length': 'number',
@@ -20111,10 +20114,11 @@ ${svg}
20111
20114
  type: 'distance'
20112
20115
  })
20113
20116
  .option('sides', {
20114
- describe: 'sides of a polygon or star symbol',
20117
+ describe: 'number of sides of a polygon symbol',
20115
20118
  type: 'number'
20116
20119
  })
20117
20120
  .option('orientation', {
20121
+ // TODO: removed (replaced by flipped and rotated)
20118
20122
  // describe: 'use orientation=b for a rotated or flipped orientation'
20119
20123
  })
20120
20124
  .option('flipped', {
@@ -20128,8 +20132,8 @@ ${svg}
20128
20132
  .option('rotation', {
20129
20133
  describe: 'rotation of symbol in degrees'
20130
20134
  })
20131
- .option('length', {
20132
- // alias for arrow-length
20135
+ .option('points', {
20136
+ describe: '(star) number of points'
20133
20137
  })
20134
20138
  .option('point-ratio', {
20135
20139
  old_alias: 'star-ratio',
@@ -20183,6 +20187,9 @@ ${svg}
20183
20187
  describe: '(arrow) min ratio of stem to total length',
20184
20188
  type: 'number'
20185
20189
  })
20190
+ .option('anchor', {
20191
+ describe: '(arrow) takes one of: start, middle, end (default is start)'
20192
+ })
20186
20193
  .option('stroke', {})
20187
20194
  .option('stroke-width', {})
20188
20195
  .option('fill', {
@@ -38075,33 +38082,6 @@ ${svg}
38075
38082
  // return [a[0] + b[0], a[1] + b[1]];
38076
38083
  // }
38077
38084
 
38078
- function getFilledArrowCoords(d) {
38079
- var direction = d.rotation || d.direction || 0,
38080
- stemTaper = d['stem-taper'] || 0,
38081
- stemCurve = d['stem-curve'] || 0,
38082
- size = calcArrowSize(d);
38083
-
38084
- if (!size) return null;
38085
-
38086
- var headDx = size.headWidth / 2,
38087
- stemDx = size.stemWidth / 2,
38088
- baseDx = stemDx * (1 - stemTaper),
38089
- coords;
38090
-
38091
- if (!stemCurve || Math.abs(stemCurve) > 90) {
38092
- coords = calcStraightArrowCoords(size.stemLen, size.headLen, stemDx, headDx, baseDx);
38093
- } else {
38094
- if (direction > 0) stemCurve = -stemCurve;
38095
- coords = getCurvedArrowCoords(size.stemLen, size.headLen, stemCurve, stemDx, headDx, baseDx);
38096
- }
38097
-
38098
- rotateCoords(coords, direction);
38099
- if (d.flipped) {
38100
- flipY(coords);
38101
- }
38102
- return [coords];
38103
- }
38104
-
38105
38085
  function calcStraightArrowCoords(stemLen, headLen, stemDx, headDx, baseDx) {
38106
38086
  return [[baseDx, 0], [stemDx, stemLen], [headDx, stemLen], [0, stemLen + headLen],
38107
38087
  [-headDx, stemLen], [-stemDx, stemLen], [-baseDx, 0], [baseDx, 0]];
@@ -38168,21 +38148,53 @@ ${svg}
38168
38148
  return 1 / Math.tan(Math.PI * headAngle / 180 / 2) / 2;
38169
38149
  }
38170
38150
 
38171
- function getCurvedArrowCoords(stemLen, headLen, curvature, stemDx, headDx, baseDx) {
38151
+ function getFilledArrowCoords(d) {
38152
+ var direction = d.rotation || d.direction || 0,
38153
+ stemTaper = d['stem-taper'] || 0,
38154
+ curvature = d['stem-curve'] || 0,
38155
+ size = calcArrowSize(d);
38156
+ if (!size) return null;
38157
+ var stemLen = size.stemLen,
38158
+ headLen = size.headLen,
38159
+ headDx = size.headWidth / 2,
38160
+ stemDx = size.stemWidth / 2,
38161
+ baseDx = stemDx * (1 - stemTaper),
38162
+ head, stem, coords, dx, dy;
38163
+
38164
+ if (curvature) {
38165
+ if (direction > 0) curvature = -curvature;
38166
+ var theta = Math.abs(curvature) / 180 * Math.PI;
38167
+ var sign = curvature > 0 ? 1 : -1;
38168
+ var ax = baseDx * Math.cos(theta); // rotate arrow base
38169
+ var ay = baseDx * Math.sin(theta) * -sign;
38170
+ dx = stemLen * Math.sin(theta / 2) * sign;
38171
+ dy = stemLen * Math.cos(theta / 2);
38172
+ var leftStem = getCurvedStemCoords(-ax, -ay, -stemDx + dx, dy, theta);
38173
+ var rightStem = getCurvedStemCoords(ax, ay, stemDx + dx, dy, theta);
38174
+ stem = leftStem.concat(rightStem.reverse());
38175
+
38176
+ } else {
38177
+ dx = 0;
38178
+ dy = stemLen;
38179
+ stem = [[-baseDx, 0], [baseDx, 0], [baseDx, 0]];
38180
+ }
38181
+
38172
38182
  // coordinates go counter clockwise, starting from the leftmost head coordinate
38173
- var theta = Math.abs(curvature) / 180 * Math.PI;
38174
- var sign = curvature > 0 ? 1 : -1;
38175
- var dx = stemLen * Math.sin(theta / 2) * sign;
38176
- var dy = stemLen * Math.cos(theta / 2);
38177
- var head = [[stemDx + dx, dy], [headDx + dx, dy],
38178
- [dx, headLen + dy], [-headDx + dx, dy], [-stemDx + dx, dy]];
38179
- var ax = baseDx * Math.cos(theta); // rotate arrow base
38180
- var ay = baseDx * Math.sin(theta) * -sign;
38181
- var leftStem = getCurvedStemCoords(-ax, -ay, -stemDx + dx, dy, theta);
38182
- var rightStem = getCurvedStemCoords(ax, ay, stemDx + dx, dy, theta);
38183
- var stem = leftStem.concat(rightStem.reverse());
38184
- // stem.pop();
38185
- return stem.concat(head);
38183
+ head = [[stemDx + dx, dy], [headDx + dx, dy],
38184
+ [dx, headLen + dy], [-headDx + dx, dy], [-stemDx + dx, dy]];
38185
+
38186
+ coords = stem.concat(head);
38187
+ if (d.anchor == 'end') {
38188
+ scaleAndShiftCoords(coords, 1, [-dx, -dy - headLen]);
38189
+ } else if (d.anchor == 'middle') {
38190
+ scaleAndShiftCoords(coords, 1, [-dx/2, (-dy - headLen)/2]);
38191
+ }
38192
+
38193
+ rotateCoords(coords, direction);
38194
+ if (d.flipped) {
38195
+ flipY(coords);
38196
+ }
38197
+ return [coords];
38186
38198
  }
38187
38199
 
38188
38200
  // ax, ay: point on the base
@@ -38219,6 +38231,30 @@ ${svg}
38219
38231
  return coords;
38220
38232
  }
38221
38233
 
38234
+ function getMinorRadius(points) {
38235
+ var innerAngle = 360 / points;
38236
+ var pointAngle = getDefaultPointAngle(points);
38237
+ var thetaA = Math.PI / 180 * innerAngle / 2;
38238
+ var thetaB = Math.PI / 180 * pointAngle / 2;
38239
+ var a = Math.tan(thetaB) / (Math.tan(thetaB) + Math.tan(thetaA));
38240
+ var c = a / Math.cos(thetaA);
38241
+ return c;
38242
+ }
38243
+
38244
+
38245
+ function getPointAngle(points, skip) {
38246
+ var unitAngle = 360 / points;
38247
+ var centerAngle = unitAngle * (skip + 1);
38248
+ return 180 - centerAngle;
38249
+ }
38250
+
38251
+ function getDefaultPointAngle(points) {
38252
+ var minSkip = 1;
38253
+ var maxSkip = Math.ceil(points / 2) - 2;
38254
+ var skip = Math.floor((maxSkip + minSkip) / 2);
38255
+ return getPointAngle(points, skip);
38256
+ }
38257
+
38222
38258
  // sides: e.g. 5-pointed star has 10 sides
38223
38259
  // radius: distance from center to point
38224
38260
  //
@@ -38228,8 +38264,12 @@ ${svg}
38228
38264
  var type = d.type;
38229
38265
  var sides = +d.sides || getDefaultSides(type);
38230
38266
  var isStar = type == 'star';
38231
- if (isStar && (sides < 6 || sides % 2 !== 0)) {
38232
- stop(`Invalid number of sides for a star (${sides})`);
38267
+ if (isStar && d.points > 0) {
38268
+ sides = d.points * 2;
38269
+ }
38270
+ var starRatio = isStar ? d.star_ratio || getMinorRadius(sides / 2) : 0;
38271
+ if (isStar && (sides < 10 || sides % 2 !== 0)) {
38272
+ stop(`Invalid number of points for a star (${sides / 2})`);
38233
38273
  } else if (sides >= 3 === false) {
38234
38274
  stop(`Invalid number of sides (${sides})`);
38235
38275
  }
@@ -38244,7 +38284,7 @@ ${svg}
38244
38284
  even = i % 2 == 0;
38245
38285
  len = radius;
38246
38286
  if (isStar && even) {
38247
- len *= (d.star_ratio || 0.5);
38287
+ len *= starRatio;
38248
38288
  }
38249
38289
  theta = (i + b) * angle % 360;
38250
38290
  coords.push(getPlanarSegmentEndpoint(0, 0, theta, len));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.5.84",
3
+ "version": "0.5.85",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
package/www/mapshaper.js CHANGED
@@ -13712,6 +13712,9 @@
13712
13712
  flipped: 'boolean',
13713
13713
  rotated: 'boolean',
13714
13714
  direction: 'number',
13715
+ sides: 'number', // polygons and stars
13716
+ points: 'number', // polygons and stars
13717
+ anchor: null, // arrows; takes start, middle, end
13715
13718
  'head-angle': 'number',
13716
13719
  'head-width': 'number',
13717
13720
  'head-length': 'number',
@@ -20111,10 +20114,11 @@ ${svg}
20111
20114
  type: 'distance'
20112
20115
  })
20113
20116
  .option('sides', {
20114
- describe: 'sides of a polygon or star symbol',
20117
+ describe: 'number of sides of a polygon symbol',
20115
20118
  type: 'number'
20116
20119
  })
20117
20120
  .option('orientation', {
20121
+ // TODO: removed (replaced by flipped and rotated)
20118
20122
  // describe: 'use orientation=b for a rotated or flipped orientation'
20119
20123
  })
20120
20124
  .option('flipped', {
@@ -20128,8 +20132,8 @@ ${svg}
20128
20132
  .option('rotation', {
20129
20133
  describe: 'rotation of symbol in degrees'
20130
20134
  })
20131
- .option('length', {
20132
- // alias for arrow-length
20135
+ .option('points', {
20136
+ describe: '(star) number of points'
20133
20137
  })
20134
20138
  .option('point-ratio', {
20135
20139
  old_alias: 'star-ratio',
@@ -20183,6 +20187,9 @@ ${svg}
20183
20187
  describe: '(arrow) min ratio of stem to total length',
20184
20188
  type: 'number'
20185
20189
  })
20190
+ .option('anchor', {
20191
+ describe: '(arrow) takes one of: start, middle, end (default is start)'
20192
+ })
20186
20193
  .option('stroke', {})
20187
20194
  .option('stroke-width', {})
20188
20195
  .option('fill', {
@@ -38075,33 +38082,6 @@ ${svg}
38075
38082
  // return [a[0] + b[0], a[1] + b[1]];
38076
38083
  // }
38077
38084
 
38078
- function getFilledArrowCoords(d) {
38079
- var direction = d.rotation || d.direction || 0,
38080
- stemTaper = d['stem-taper'] || 0,
38081
- stemCurve = d['stem-curve'] || 0,
38082
- size = calcArrowSize(d);
38083
-
38084
- if (!size) return null;
38085
-
38086
- var headDx = size.headWidth / 2,
38087
- stemDx = size.stemWidth / 2,
38088
- baseDx = stemDx * (1 - stemTaper),
38089
- coords;
38090
-
38091
- if (!stemCurve || Math.abs(stemCurve) > 90) {
38092
- coords = calcStraightArrowCoords(size.stemLen, size.headLen, stemDx, headDx, baseDx);
38093
- } else {
38094
- if (direction > 0) stemCurve = -stemCurve;
38095
- coords = getCurvedArrowCoords(size.stemLen, size.headLen, stemCurve, stemDx, headDx, baseDx);
38096
- }
38097
-
38098
- rotateCoords(coords, direction);
38099
- if (d.flipped) {
38100
- flipY(coords);
38101
- }
38102
- return [coords];
38103
- }
38104
-
38105
38085
  function calcStraightArrowCoords(stemLen, headLen, stemDx, headDx, baseDx) {
38106
38086
  return [[baseDx, 0], [stemDx, stemLen], [headDx, stemLen], [0, stemLen + headLen],
38107
38087
  [-headDx, stemLen], [-stemDx, stemLen], [-baseDx, 0], [baseDx, 0]];
@@ -38168,21 +38148,53 @@ ${svg}
38168
38148
  return 1 / Math.tan(Math.PI * headAngle / 180 / 2) / 2;
38169
38149
  }
38170
38150
 
38171
- function getCurvedArrowCoords(stemLen, headLen, curvature, stemDx, headDx, baseDx) {
38151
+ function getFilledArrowCoords(d) {
38152
+ var direction = d.rotation || d.direction || 0,
38153
+ stemTaper = d['stem-taper'] || 0,
38154
+ curvature = d['stem-curve'] || 0,
38155
+ size = calcArrowSize(d);
38156
+ if (!size) return null;
38157
+ var stemLen = size.stemLen,
38158
+ headLen = size.headLen,
38159
+ headDx = size.headWidth / 2,
38160
+ stemDx = size.stemWidth / 2,
38161
+ baseDx = stemDx * (1 - stemTaper),
38162
+ head, stem, coords, dx, dy;
38163
+
38164
+ if (curvature) {
38165
+ if (direction > 0) curvature = -curvature;
38166
+ var theta = Math.abs(curvature) / 180 * Math.PI;
38167
+ var sign = curvature > 0 ? 1 : -1;
38168
+ var ax = baseDx * Math.cos(theta); // rotate arrow base
38169
+ var ay = baseDx * Math.sin(theta) * -sign;
38170
+ dx = stemLen * Math.sin(theta / 2) * sign;
38171
+ dy = stemLen * Math.cos(theta / 2);
38172
+ var leftStem = getCurvedStemCoords(-ax, -ay, -stemDx + dx, dy, theta);
38173
+ var rightStem = getCurvedStemCoords(ax, ay, stemDx + dx, dy, theta);
38174
+ stem = leftStem.concat(rightStem.reverse());
38175
+
38176
+ } else {
38177
+ dx = 0;
38178
+ dy = stemLen;
38179
+ stem = [[-baseDx, 0], [baseDx, 0], [baseDx, 0]];
38180
+ }
38181
+
38172
38182
  // coordinates go counter clockwise, starting from the leftmost head coordinate
38173
- var theta = Math.abs(curvature) / 180 * Math.PI;
38174
- var sign = curvature > 0 ? 1 : -1;
38175
- var dx = stemLen * Math.sin(theta / 2) * sign;
38176
- var dy = stemLen * Math.cos(theta / 2);
38177
- var head = [[stemDx + dx, dy], [headDx + dx, dy],
38178
- [dx, headLen + dy], [-headDx + dx, dy], [-stemDx + dx, dy]];
38179
- var ax = baseDx * Math.cos(theta); // rotate arrow base
38180
- var ay = baseDx * Math.sin(theta) * -sign;
38181
- var leftStem = getCurvedStemCoords(-ax, -ay, -stemDx + dx, dy, theta);
38182
- var rightStem = getCurvedStemCoords(ax, ay, stemDx + dx, dy, theta);
38183
- var stem = leftStem.concat(rightStem.reverse());
38184
- // stem.pop();
38185
- return stem.concat(head);
38183
+ head = [[stemDx + dx, dy], [headDx + dx, dy],
38184
+ [dx, headLen + dy], [-headDx + dx, dy], [-stemDx + dx, dy]];
38185
+
38186
+ coords = stem.concat(head);
38187
+ if (d.anchor == 'end') {
38188
+ scaleAndShiftCoords(coords, 1, [-dx, -dy - headLen]);
38189
+ } else if (d.anchor == 'middle') {
38190
+ scaleAndShiftCoords(coords, 1, [-dx/2, (-dy - headLen)/2]);
38191
+ }
38192
+
38193
+ rotateCoords(coords, direction);
38194
+ if (d.flipped) {
38195
+ flipY(coords);
38196
+ }
38197
+ return [coords];
38186
38198
  }
38187
38199
 
38188
38200
  // ax, ay: point on the base
@@ -38219,6 +38231,30 @@ ${svg}
38219
38231
  return coords;
38220
38232
  }
38221
38233
 
38234
+ function getMinorRadius(points) {
38235
+ var innerAngle = 360 / points;
38236
+ var pointAngle = getDefaultPointAngle(points);
38237
+ var thetaA = Math.PI / 180 * innerAngle / 2;
38238
+ var thetaB = Math.PI / 180 * pointAngle / 2;
38239
+ var a = Math.tan(thetaB) / (Math.tan(thetaB) + Math.tan(thetaA));
38240
+ var c = a / Math.cos(thetaA);
38241
+ return c;
38242
+ }
38243
+
38244
+
38245
+ function getPointAngle(points, skip) {
38246
+ var unitAngle = 360 / points;
38247
+ var centerAngle = unitAngle * (skip + 1);
38248
+ return 180 - centerAngle;
38249
+ }
38250
+
38251
+ function getDefaultPointAngle(points) {
38252
+ var minSkip = 1;
38253
+ var maxSkip = Math.ceil(points / 2) - 2;
38254
+ var skip = Math.floor((maxSkip + minSkip) / 2);
38255
+ return getPointAngle(points, skip);
38256
+ }
38257
+
38222
38258
  // sides: e.g. 5-pointed star has 10 sides
38223
38259
  // radius: distance from center to point
38224
38260
  //
@@ -38228,8 +38264,12 @@ ${svg}
38228
38264
  var type = d.type;
38229
38265
  var sides = +d.sides || getDefaultSides(type);
38230
38266
  var isStar = type == 'star';
38231
- if (isStar && (sides < 6 || sides % 2 !== 0)) {
38232
- stop(`Invalid number of sides for a star (${sides})`);
38267
+ if (isStar && d.points > 0) {
38268
+ sides = d.points * 2;
38269
+ }
38270
+ var starRatio = isStar ? d.star_ratio || getMinorRadius(sides / 2) : 0;
38271
+ if (isStar && (sides < 10 || sides % 2 !== 0)) {
38272
+ stop(`Invalid number of points for a star (${sides / 2})`);
38233
38273
  } else if (sides >= 3 === false) {
38234
38274
  stop(`Invalid number of sides (${sides})`);
38235
38275
  }
@@ -38244,7 +38284,7 @@ ${svg}
38244
38284
  even = i % 2 == 0;
38245
38285
  len = radius;
38246
38286
  if (isStar && even) {
38247
- len *= (d.star_ratio || 0.5);
38287
+ len *= starRatio;
38248
38288
  }
38249
38289
  theta = (i + b) * angle % 360;
38250
38290
  coords.push(getPlanarSegmentEndpoint(0, 0, theta, len));