mapshaper 0.7.71 → 0.7.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/mapshaper.js CHANGED
@@ -26830,6 +26830,14 @@
26830
26830
  fill: 'color',
26831
26831
  'fill-pattern': 'pattern',
26832
26832
  'fill-effect': null, // todo: validate effect names
26833
+ // soft glows along a polygon's edges -- see svg-glow.mjs. Drawn with a
26834
+ // filter, not written as attributes, so not in propertiesBySymbolType.
26835
+ 'outer-glow-color': 'color',
26836
+ 'outer-glow-opacity': 'number',
26837
+ 'outer-glow-width': 'number',
26838
+ 'inner-glow-color': 'color',
26839
+ 'inner-glow-opacity': 'number',
26840
+ 'inner-glow-width': 'number',
26833
26841
  'font-family': null,
26834
26842
  'font-size': null,
26835
26843
  'font-style': null,
@@ -27236,13 +27244,13 @@
27236
27244
 
27237
27245
  function parseCalloutEnd(str) {
27238
27246
  var end = String(str).trim().toLowerCase();
27239
- return /^(arrow|open-arrow|none)$/.test(end) ? end : null;
27247
+ return /^(arrow|open-arrow|ring|none)$/.test(end) ? end : null;
27240
27248
  }
27241
27249
 
27242
- // A line's ends take a callout's markers, and a dot
27250
+ // A line's ends take a callout's arrowheads, and a dot
27243
27251
  function parseLineEnd(str) {
27244
27252
  var end = String(str).trim().toLowerCase();
27245
- return end == 'dot' ? end : parseCalloutEnd(end);
27253
+ return /^(arrow|open-arrow|dot|none)$/.test(end) ? end : null;
27246
27254
  }
27247
27255
 
27248
27256
  // "x,y" -> [x, y], or null. Stored as a string so that a pair is set and
@@ -28008,12 +28016,21 @@
28008
28016
 
28009
28017
  // An arrowhead's size when callout-end-size does not say, in px: 10 for the
28010
28018
  // default line, and growing with the line, so that a heavier line does not end
28011
- // in a head too small to read.
28019
+ // in a head too small to read. A ring is twice that across, which is room to
28020
+ // circle a point and its symbol.
28012
28021
  function getDefaultCalloutEndSize(end, lineWidth) {
28013
- return getDefaultArrowSize(lineWidth);
28022
+ var size = getDefaultArrowSize(lineWidth);
28023
+ return end == 'ring' ? size * 2 : size;
28024
+ }
28025
+
28026
+ // A ring's size is its diameter, stroke and all, as an icon ring's is; this
28027
+ // is the radius of the circle its stroke is centred on.
28028
+ function getRingRadius(size, lineWidth) {
28029
+ var w = lineWidth > 0 ? lineWidth : DEFAULT_LINE_WIDTH$1;
28030
+ return Math.max(size - w, w) / 2;
28014
28031
  }
28015
28032
 
28016
- // 'arrow', 'open-arrow' or 'none'
28033
+ // 'arrow', 'open-arrow', 'ring' or 'none'
28017
28034
  function getCalloutEndType(rec) {
28018
28035
  return rec && parseCalloutEnd(rec['callout-end'] || '') || 'none';
28019
28036
  }
@@ -28078,7 +28095,8 @@
28078
28095
  // Where a line meets a callout's text and how it gets there from the anchor.
28079
28096
  //
28080
28097
  // o.type 'line', 'elbow' or 'curve'
28081
- // o.end 'arrow' (filled), 'open-arrow' (stroked) or 'none'
28098
+ // o.end 'arrow' (filled), 'open-arrow' (stroked), 'ring' (a hollow circle
28099
+ // around the anchor, in the line's width) or 'none'
28082
28100
  // o.box the text box, from getLabelTextBox()
28083
28101
  // o.via [x, y] the elbow's corner or a point the curve passes through, or null
28084
28102
  // o.attach [fx, fy] where the line meets the text, as fractions of the padded
@@ -28090,21 +28108,22 @@
28090
28108
  // the line width. The size is the length of the head's sides, as
28091
28109
  // drawn, stroke and all: the two styles are different shapes, and
28092
28110
  // what makes one look the size of the other is sides the same
28093
- // length, not the same length along the line.
28111
+ // length, not the same length along the line. A ring's size is its
28112
+ // diameter.
28094
28113
  //
28095
- // Returns {kind, coords, head, openHead, box, attach, via, tip} with coords
28114
+ // Returns {kind, coords, head, openHead, ring, box, attach, via, tip} with coords
28096
28115
  // from the anchor end to the text end -- a polyline's vertices, or a quadratic
28097
28116
  // Bezier's three points -- or null when there is nothing to draw: the anchor
28098
28117
  // inside the padded box, or a gap that leaves no line. head is a filled
28099
28118
  // triangle, openHead the three points of a stroked chevron, wing to tip to
28100
- // wing. The rest is what the shape was worked out from: the padded box, the
28119
+ // wing, ring {center, radius} a stroked circle. The rest is what the shape was worked out from: the padded box, the
28101
28120
  // point where the line meets it, the elbow's corner or the curve's midpoint
28102
28121
  // (null for a straight line), and where the line stops short of the anchor.
28103
28122
  function getCalloutShape(o) {
28104
28123
  var box = padBox$1(o.box, o.padding || 0);
28105
28124
  var a = [0, 0];
28106
28125
  var size = o.endSize > 0 ? o.endSize : getDefaultCalloutEndSize(o.end, o.width);
28107
- var t, v, path, tip, dir, out, lineWidth, headLen, curved;
28126
+ var t, v, path, tip, dir, out, lineWidth, headLen, curved, ringRadius;
28108
28127
  if (boxContains(box, a)) return null;
28109
28128
  t = o.attach ? getBoxPoint(box, o.attach) :
28110
28129
  getAutoAttachment(o.type, box, o.via || null);
@@ -28126,10 +28145,14 @@
28126
28145
  } else {
28127
28146
  path = {kind: 'polyline', coords: [a, t]};
28128
28147
  }
28129
- path = trimPath(path, o.gap || 0);
28148
+ // A ring is centred on the anchor, and the line starts at its stroke, or
28149
+ // at the gap if that is further out -- round a symbol bigger than the ring.
28150
+ ringRadius = o.end == 'ring' ? getRingRadius(size, o.width) : 0;
28151
+ path = trimPath(path, Math.max(o.gap || 0, ringRadius));
28130
28152
  if (!path) return null;
28131
28153
  tip = path.coords[0];
28132
28154
  out = {kind: path.kind, coords: path.coords, head: null, openHead: null,
28155
+ ring: ringRadius > 0 ? {center: a, radius: ringRadius} : null,
28133
28156
  box: box, attach: t, via: v || null, tip: tip};
28134
28157
  if (o.end == 'arrow') {
28135
28158
  headLen = getArrowHeadLength(size, ARROW_ANGLE);
@@ -28196,6 +28219,19 @@
28196
28219
  }
28197
28220
  });
28198
28221
  }
28222
+ if (shape.ring) {
28223
+ children.push({
28224
+ tag: 'circle',
28225
+ properties: {
28226
+ cx: shape.ring.center[0],
28227
+ cy: shape.ring.center[1],
28228
+ r: Math.round(shape.ring.radius * 100) / 100,
28229
+ fill: 'none',
28230
+ stroke: color,
28231
+ 'stroke-width': getLineWidth(rec)
28232
+ }
28233
+ });
28234
+ }
28199
28235
  if (opacity < 1) props.opacity = opacity;
28200
28236
  return {tag: 'g', properties: props, children: children};
28201
28237
  }
@@ -30139,11 +30175,302 @@
30139
30175
  getSphereEffectParams: getSphereEffectParams
30140
30176
  });
30141
30177
 
30178
+ // Soft glows along the edges of polygons: an outer glow outside the shape and
30179
+ // an inner glow inside it, each with a color, an opacity and a width.
30180
+ //
30181
+ // outer-glow-color a glow is drawn when its color is set, and not
30182
+ // inner-glow-color otherwise (or if the color is "none")
30183
+ // outer-glow-width how far the glow reaches from the edge, in px;
30184
+ // inner-glow-width default 10, and 0 draws no glow
30185
+ // outer-glow-opacity 0-1, default 1
30186
+ // inner-glow-opacity
30187
+ //
30188
+ // A glow is a Gaussian blur of the shape's coverage, with a standard deviation
30189
+ // of half the width: it has faded to about 2% of its opacity at that distance.
30190
+ // The canvas renders the same blur as a shadow, whose shadowBlur is defined as
30191
+ // twice the standard deviation -- so shadowBlur is the width itself.
30192
+ //
30193
+ // A glow is computed from everything the shape paints, its fill and its
30194
+ // stroke, faded by its opacity -- which is what an SVG filter on a group sees.
30195
+ //
30196
+ // Inner glows belong to each shape. An outer glow belongs to the layer as a
30197
+ // whole when every shape in the layer has the same one: it then follows the
30198
+ // outline of all of them together, like a drop shadow, instead of spilling
30199
+ // over each neighbor's edge. See getLayerOuterGlow().
30200
+ //
30201
+ // Illustrator drops a filter that is applied to a <path> with more than one
30202
+ // ring (it imports one as a compound path), and keeps one that is applied to a
30203
+ // group. A glowing shape is therefore always wrapped in a <g> that carries the
30204
+ // filter.
30205
+
30206
+ var glowFields = [
30207
+ 'outer-glow-color', 'outer-glow-opacity', 'outer-glow-width',
30208
+ 'inner-glow-color', 'inner-glow-opacity', 'inner-glow-width'
30209
+ ];
30210
+
30211
+ var DEFAULT_GLOW_WIDTH = 10;
30212
+
30213
+ // How far a glow is drawn past the edge, as a multiple of its width: three
30214
+ // standard deviations, where the blur is gone for all practical purposes.
30215
+ var GLOW_REACH = 1.5;
30216
+
30217
+ // Filter regions are given as a percentage of the bounding box of what they
30218
+ // filter, and a small shape needs a larger percentage than a big one to leave
30219
+ // room for the same glow. Rounding up to one of these keeps the number of
30220
+ // distinct filters small; Illustrator lists every one of them in its menu.
30221
+ var regionMargins = [10, 25, 50, 100, 200, 500, 1000, 2500, 5000, 10000];
30222
+
30223
+ // {color, opacity, width} for one of a record's glows, or null if it has none.
30224
+ // @type: 'outer' or 'inner'
30225
+ function getPolygonGlow(rec, type) {
30226
+ var prefix = type + '-glow-';
30227
+ var width, color, opacity;
30228
+ if (!rec) return null;
30229
+ color = rec[prefix + 'color'];
30230
+ if (isBlank(color) || String(color).trim().toLowerCase() == 'none') return null;
30231
+ width = rec[prefix + 'width'];
30232
+ if (isBlank(width)) width = DEFAULT_GLOW_WIDTH;
30233
+ if (!isSvgNumber(width) || !(Number(width) > 0)) return null;
30234
+ opacity = rec[prefix + 'opacity'];
30235
+ opacity = isSvgNumber(opacity) ? Math.min(Math.max(Number(opacity), 0), 1) : 1;
30236
+ if (opacity === 0) return null;
30237
+ return {color: String(color).trim(), opacity: opacity, width: Number(width)};
30238
+ }
30239
+
30240
+ function isBlank(val) {
30241
+ return val === undefined || val === null || String(val).trim() === '';
30242
+ }
30243
+
30244
+ function polygonHasGlow(rec) {
30245
+ return !!(getPolygonGlow(rec, 'outer') || getPolygonGlow(rec, 'inner'));
30246
+ }
30247
+
30248
+ function getGlowKey(glow) {
30249
+ return glow ? glow.color + ' ' + glow.opacity + ' ' + glow.width : '';
30250
+ }
30251
+
30252
+ // The outer glow of a polygon layer as a whole, or null. A layer has one when
30253
+ // every feature with a shape has the same outer glow; features without a shape
30254
+ // are left out, since they draw nothing. Decided from the data rather than
30255
+ // from what is drawn, so that it does not change with the view.
30256
+ function getLayerOuterGlow(lyr) {
30257
+ var records, shapes, glow, key, g, k;
30258
+ if (!lyr || lyr.geometry_type != 'polygon' || !lyr.data || !lyr.shapes) return null;
30259
+ records = lyr.data.getRecords();
30260
+ shapes = lyr.shapes;
30261
+ glow = null;
30262
+ for (var i=0, n=shapes.length; i<n; i++) {
30263
+ if (!shapes[i] || shapes[i].length === 0) continue;
30264
+ g = getPolygonGlow(records[i], 'outer');
30265
+ if (!g) return null;
30266
+ k = getGlowKey(g);
30267
+ if (!glow) {
30268
+ glow = g;
30269
+ key = k;
30270
+ } else if (k != key) {
30271
+ return null;
30272
+ }
30273
+ }
30274
+ return glow;
30275
+ }
30276
+
30277
+ // The widest glow of any feature in a layer, in px, or 0.
30278
+ function getMaxGlowWidth(records) {
30279
+ var max = 0, g;
30280
+ for (var i=0, n=records ? records.length : 0; i<n; i++) {
30281
+ g = getPolygonGlow(records[i], 'outer');
30282
+ if (g && g.width > max) max = g.width;
30283
+ g = getPolygonGlow(records[i], 'inner');
30284
+ if (g && g.width > max) max = g.width;
30285
+ }
30286
+ return max;
30287
+ }
30288
+
30289
+ // What a <g> needs for a glow filter: the glows and the size of what it
30290
+ // wraps, so that the filter region can be made large enough.
30291
+ // @bbox: [xmin, ymin, xmax, ymax] of the wrapped content, in px
30292
+ // @strokeWidth: the widest stroke in it, which reaches past the bbox
30293
+ function getGlowFilterSpec(outer, inner, bbox, strokeWidth) {
30294
+ var width = Math.max(outer ? outer.width : 0, inner ? inner.width : 0);
30295
+ var reach = width * GLOW_REACH + (strokeWidth > 0 ? strokeWidth / 2 : 0) + 1;
30296
+ return {
30297
+ outer: outer || null,
30298
+ inner: inner || null,
30299
+ marginX: getRegionMargin(reach, bbox[2] - bbox[0]),
30300
+ marginY: getRegionMargin(reach, bbox[3] - bbox[1])
30301
+ };
30302
+ }
30303
+
30304
+ function getRegionMargin(reach, size) {
30305
+ var pct = size > 0 ? reach / size * 100 : Infinity;
30306
+ for (var i=0; i<regionMargins.length; i++) {
30307
+ if (regionMargins[i] >= pct) return regionMargins[i];
30308
+ }
30309
+ return regionMargins[regionMargins.length - 1];
30310
+ }
30311
+
30312
+ function getGlowFilterKey(spec) {
30313
+ return getGlowKey(spec.outer) + '|' + getGlowKey(spec.inner) + '|' +
30314
+ spec.marginX + ' ' + spec.marginY;
30315
+ }
30316
+
30317
+ // The <filter> element for @spec. The outer glow is the blurred coverage of
30318
+ // the source outside the source; the inner glow is the blurred coverage of
30319
+ // everything outside the source, inside the source. The inversion is made
30320
+ // by cutting the source out of a flood rather than with feComponentTransfer,
30321
+ // which Safari fades toward the corners of the source's bounding box.
30322
+ function renderGlowFilter(id, spec) {
30323
+ var mx = spec.marginX, my = spec.marginY;
30324
+ var parts = [];
30325
+ var merge = [];
30326
+ if (spec.outer) {
30327
+ parts.push(
30328
+ `<feGaussianBlur in="SourceAlpha" stdDeviation="${getGlowDeviation(spec.outer)}" result="outer-blur"/>`,
30329
+ `<feFlood flood-color="${escapeAttr(spec.outer.color)}" flood-opacity="${spec.outer.opacity}"/>`,
30330
+ '<feComposite in2="outer-blur" operator="in"/>',
30331
+ '<feComposite in2="SourceAlpha" operator="out" result="outer"/>');
30332
+ merge.push('outer');
30333
+ }
30334
+ merge.push('SourceGraphic');
30335
+ if (spec.inner) {
30336
+ parts.push(
30337
+ '<feFlood flood-color="#000000" flood-opacity="1"/>',
30338
+ '<feComposite in2="SourceAlpha" operator="out"/>',
30339
+ `<feGaussianBlur stdDeviation="${getGlowDeviation(spec.inner)}" result="inner-blur"/>`,
30340
+ `<feFlood flood-color="${escapeAttr(spec.inner.color)}" flood-opacity="${spec.inner.opacity}"/>`,
30341
+ '<feComposite in2="inner-blur" operator="in"/>',
30342
+ '<feComposite in2="SourceAlpha" operator="in" result="inner"/>');
30343
+ merge.push('inner');
30344
+ }
30345
+ return `<filter id="${id}" x="${-mx}%" y="${-my}%" width="${100 + 2 * mx}%" height="${100 + 2 * my}%" color-interpolation-filters="sRGB">\n` +
30346
+ parts.join('\n') + '\n<feMerge>' +
30347
+ merge.map(function(name) { return `<feMergeNode in="${name}"/>`; }).join('') +
30348
+ '</feMerge>\n</filter>\n';
30349
+ }
30350
+
30351
+ function getGlowDeviation(glow) {
30352
+ return Math.round(glow.width / 2 * 1000) / 1000;
30353
+ }
30354
+
30355
+ function escapeAttr(str) {
30356
+ return String(str).replace(/[&<>"]/g, '');
30357
+ }
30358
+
30359
+ // Wraps each glowing shape in a <g> that will carry its filter, and marks the
30360
+ // layer's own <g> if the layer has an outer glow of its own. The filters
30361
+ // themselves are added to <defs> later, by convertGlowFilter().
30362
+ // @children: the SVG objects for @features, one each, in order
30363
+ function applyPolygonGlows(layerObj, children, features, lyr) {
30364
+ var layerGlow = getLayerOuterGlow(lyr);
30365
+ var layerBBox = null;
30366
+ var maxStroke = 0;
30367
+ children.forEach(function(child, i) {
30368
+ var feature = features[i];
30369
+ var geom = feature && (feature.type == 'Feature' ? feature.geometry : feature);
30370
+ var rec = feature && feature.properties || {};
30371
+ var bbox, outer, inner, strokeWidth, wrapper;
30372
+ if (!child || !child.properties || !geom || !geom.coordinates) return;
30373
+ bbox = getCoordinatesBBox(geom.coordinates);
30374
+ if (!bbox) return;
30375
+ strokeWidth = getStrokeWidth(rec);
30376
+ if (strokeWidth > maxStroke) maxStroke = strokeWidth;
30377
+ layerBBox = layerBBox ? mergeBBoxes(layerBBox, bbox) : bbox;
30378
+ outer = layerGlow ? null : getPolygonGlow(rec, 'outer');
30379
+ inner = getPolygonGlow(rec, 'inner');
30380
+ if (!outer && !inner) return;
30381
+ wrapper = {tag: 'g', properties: {}, children: [child],
30382
+ glowFilter: getGlowFilterSpec(outer, inner, bbox, strokeWidth)};
30383
+ // The group is the feature now, so its id goes with it (and the data
30384
+ // attributes of svg-data=, which are added to the layer's children).
30385
+ if ('id' in child.properties) {
30386
+ wrapper.properties.id = child.properties.id;
30387
+ delete child.properties.id;
30388
+ }
30389
+ children[i] = wrapper;
30390
+ });
30391
+ if (layerGlow && layerBBox) {
30392
+ layerObj.glowFilter = getGlowFilterSpec(layerGlow, null, layerBBox, maxStroke);
30393
+ }
30394
+ }
30395
+
30396
+ // Replaces the glow filter an object was marked with by a reference to a
30397
+ // <filter> in @defs, which shapes with the same glows share.
30398
+ function convertGlowFilter(obj, defs) {
30399
+ var spec = obj.glowFilter;
30400
+ var key = getGlowFilterKey(spec);
30401
+ var item = defs.find(function(o) { return o.glowFilterKey === key; });
30402
+ var count;
30403
+ if (!item) {
30404
+ count = defs.filter(function(o) { return !!o.glowFilterKey; }).length;
30405
+ item = {glowFilterKey: key, id: 'glow-' + (count + 1)};
30406
+ item.svg = renderGlowFilter(item.id, spec);
30407
+ defs.push(item);
30408
+ }
30409
+ delete obj.glowFilter;
30410
+ if (!obj.properties) obj.properties = {};
30411
+ obj.properties.filter = 'url(#' + item.id + ')';
30412
+ }
30413
+
30414
+ function getStrokeWidth(rec) {
30415
+ var w = rec['stroke-width'];
30416
+ if (!rec.stroke || rec.stroke == 'none') return 0;
30417
+ return isSvgNumber(w) ? Number(w) : 1;
30418
+ }
30419
+
30420
+ function getCoordinatesBBox(coords) {
30421
+ var bbox = [Infinity, Infinity, -Infinity, -Infinity];
30422
+ extendBBox(bbox, coords);
30423
+ return bbox[0] <= bbox[2] ? bbox : null;
30424
+ }
30425
+
30426
+ function extendBBox(bbox, coords) {
30427
+ var x, y;
30428
+ if (!coords || coords.length === 0) return;
30429
+ if (typeof coords[0] == 'number') {
30430
+ x = coords[0];
30431
+ y = coords[1];
30432
+ if (x < bbox[0]) bbox[0] = x;
30433
+ if (y < bbox[1]) bbox[1] = y;
30434
+ if (x > bbox[2]) bbox[2] = x;
30435
+ if (y > bbox[3]) bbox[3] = y;
30436
+ return;
30437
+ }
30438
+ for (var i=0; i<coords.length; i++) {
30439
+ extendBBox(bbox, coords[i]);
30440
+ }
30441
+ }
30442
+
30443
+ function mergeBBoxes(a, b) {
30444
+ return [Math.min(a[0], b[0]), Math.min(a[1], b[1]),
30445
+ Math.max(a[2], b[2]), Math.max(a[3], b[3])];
30446
+ }
30447
+
30448
+ var SvgGlow = /*#__PURE__*/Object.freeze({
30449
+ __proto__: null,
30450
+ DEFAULT_GLOW_WIDTH: DEFAULT_GLOW_WIDTH,
30451
+ GLOW_REACH: GLOW_REACH,
30452
+ applyPolygonGlows: applyPolygonGlows,
30453
+ convertGlowFilter: convertGlowFilter,
30454
+ getGlowDeviation: getGlowDeviation,
30455
+ getGlowFilterKey: getGlowFilterKey,
30456
+ getGlowFilterSpec: getGlowFilterSpec,
30457
+ getGlowKey: getGlowKey,
30458
+ getLayerOuterGlow: getLayerOuterGlow,
30459
+ getMaxGlowWidth: getMaxGlowWidth,
30460
+ getPolygonGlow: getPolygonGlow,
30461
+ glowFields: glowFields,
30462
+ polygonHasGlow: polygonHasGlow,
30463
+ renderGlowFilter: renderGlowFilter
30464
+ });
30465
+
30142
30466
  // convert object properties to definitions for images and hatch fills
30143
30467
  function convertPropertiesToDefinitions(obj, defs) {
30144
30468
  procNode(obj);
30145
30469
 
30146
30470
  function procNode(obj) {
30471
+ if (obj.glowFilter) {
30472
+ convertGlowFilter(obj, defs);
30473
+ }
30147
30474
  if (obj.tag == 'textPath' && obj.properties && obj.properties[LABEL_PATH_PROPERTY]) {
30148
30475
  convertLabelPath(obj.properties, defs);
30149
30476
  }
@@ -30813,7 +31140,7 @@ ${svg}
30813
31140
  lyr = copyLayer(lyr);
30814
31141
  adjustRectangleStyle(lyr);
30815
31142
  }
30816
- layerObj.children = exportSymbolsForSVG(lyr, dataset, opts);
31143
+ layerObj.children = exportSymbolsForSVG(lyr, dataset, opts, layerObj);
30817
31144
  return layerObj;
30818
31145
  }
30819
31146
 
@@ -30991,7 +31318,7 @@ ${svg}
30991
31318
  return dataUrl.split(',')[1];
30992
31319
  }
30993
31320
 
30994
- function exportSymbolsForSVG(lyr, dataset, opts) {
31321
+ function exportSymbolsForSVG(lyr, dataset, opts, layerObj) {
30995
31322
  // TODO: convert geojson features one at a time
30996
31323
  var d = utils.defaults({layers: [lyr]}, dataset);
30997
31324
  var geojson = exportDatasetAsGeoJSON(d, opts);
@@ -31003,6 +31330,9 @@ ${svg}
31003
31330
  var children = importGeoJSONFeatures(features,
31004
31331
  utils.defaults({path_label_report: pathLabelReport}, opts));
31005
31332
  reportPathLabels(pathLabelReport, lyr);
31333
+ if (lyr.geometry_type == 'polygon') {
31334
+ applyPolygonGlows(layerObj, children, features, lyr);
31335
+ }
31006
31336
  // Drop empty placeholder <g/> elements (features whose geometry was null in the
31007
31337
  // source data, collapsed during simplification, or otherwise produced no
31008
31338
  // visible output). Keep the layer's records in lockstep so that data-*
@@ -42289,8 +42619,8 @@ ${svg}
42289
42619
  'halo-opacity': {describe: 'halo opacity, 0-1'},
42290
42620
  'label-width': {describe: 'width of a fixed-width text block in px (lines are wrapped in the web UI)'},
42291
42621
  callout: {describe: 'line from the anchor to the text: line, elbow or curve'},
42292
- 'callout-end': {describe: 'arrowhead at the anchor end: arrow, open-arrow or none'},
42293
- 'callout-end-size': {describe: 'length of an arrowhead\'s sides in px (default is 10)'},
42622
+ 'callout-end': {describe: 'marker at the anchor end: arrow, open-arrow, ring or none'},
42623
+ 'callout-end-size': {describe: 'length of an arrowhead\'s sides, or a ring\'s diameter, in px'},
42294
42624
  'callout-via': {describe: 'x,y of an elbow\'s corner or a point on a curve, in px'},
42295
42625
  'callout-attach': {describe: 'where the callout meets the text, as x,y fractions'},
42296
42626
  'callout-gap': {describe: 'px between the callout and the anchor'},
@@ -44328,6 +44658,24 @@ ${svg}
44328
44658
  .option('fill-hatch', {
44329
44659
  alias_to: 'fill-pattern'
44330
44660
  })
44661
+ .option('outer-glow-color', {
44662
+ describe: 'color of a glow outside each polygon (adds the glow)'
44663
+ })
44664
+ .option('outer-glow-width', {
44665
+ describe: 'how far the outer glow reaches, in px (default is 10)'
44666
+ })
44667
+ .option('outer-glow-opacity', {
44668
+ describe: 'opacity of the outer glow, 0-1'
44669
+ })
44670
+ .option('inner-glow-color', {
44671
+ describe: 'color of a glow inside each polygon (adds the glow)'
44672
+ })
44673
+ .option('inner-glow-width', {
44674
+ describe: 'how far the inner glow reaches, in px (default is 10)'
44675
+ })
44676
+ .option('inner-glow-opacity', {
44677
+ describe: 'opacity of the inner glow, 0-1'
44678
+ })
44331
44679
  .option('stroke', {
44332
44680
  describe: 'stroke color'
44333
44681
  })
@@ -84997,7 +85345,7 @@ ${svg}
84997
85345
  return name == 'rectangle' || name == 'rectangles' || name == 'filter' && opts.cleanup;
84998
85346
  }
84999
85347
 
85000
- var version = "0.7.71";
85348
+ var version = "0.7.72";
85001
85349
 
85002
85350
  // Parse command line args into commands and run them
85003
85351
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
@@ -87865,7 +88213,8 @@ ${svg}
87865
88213
  internal.svg = Object.assign({}, SvgStringify, SvgPathUtils, GeojsonToSvg,
87866
88214
  SvgFeatureUtils,
87867
88215
  SvgLabels, SvgSymbols, SvgLabelPaths, SvgLabelFit, SvgLabelAlign,
87868
- SvgLabelMetrics, SvgLabelHalo, SvgLabelCallout, SvgLabelMarkup, SvgLineArrows);
88216
+ SvgLabelMetrics, SvgLabelHalo, SvgLabelCallout, SvgLabelMarkup, SvgLineArrows,
88217
+ SvgGlow);
87869
88218
 
87870
88219
  // Reached through the bundle rather than imported from source, unlike most of
87871
88220
  // what tests use, because these modules load fs and fontkit through the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.7.71",
3
+ "version": "0.7.72",
4
4
  "description": "A tool for editing geospatial data for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",