mapshaper 0.6.100 → 0.6.102

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
@@ -267,6 +267,7 @@
267
267
  });
268
268
  }
269
269
 
270
+
270
271
  function defaults(dest) {
271
272
  for (var i=1, n=arguments.length; i<n; i++) {
272
273
  var src = arguments[i] || {};
@@ -13762,7 +13763,7 @@
13762
13763
  convertIntervalParam(opt[1], crs)];
13763
13764
  }
13764
13765
 
13765
- // Accepts a single value or a list of four values. List order is l,b,t,r
13766
+ // Accepts a single value or a list of four values. List order is l,b,r,t
13766
13767
  function convertFourSides(opt, crs, bounds) {
13767
13768
  var arr = opt.includes(',') ? opt.split(',') : opt.split(' ');
13768
13769
  if (arr.length == 1) {
@@ -13776,7 +13777,7 @@
13776
13777
  tmp = parseFloat(param) / 100 || 0;
13777
13778
  return tmp * (i == 1 || i == 3 ? bounds.height() : bounds.width());
13778
13779
  }
13779
- return convertIntervalParam(opt, crs);
13780
+ return convertIntervalParam(param, crs);
13780
13781
  });
13781
13782
  }
13782
13783
 
@@ -18486,18 +18487,30 @@
18486
18487
  type: 'frame'
18487
18488
  }
18488
18489
  */
18490
+
18491
+
18489
18492
  function getFrameData(dataset, exportOpts) {
18490
18493
  var frameLyr = findFrameLayerInDataset(dataset);
18491
- var frameData;
18494
+ var data;
18492
18495
  if (frameLyr) {
18493
- frameData = getFrameLayerData(frameLyr, dataset.arcs);
18496
+ data = getFrameLayerData(frameLyr, dataset.arcs);
18494
18497
  } else {
18495
- frameData = calcFrameData(dataset, exportOpts);
18498
+ data = calcFrameData(dataset, exportOpts);
18496
18499
  }
18497
- frameData.crs = getDatasetCRS(dataset);
18498
- return frameData;
18500
+ data.invert_y = !!exportOpts.invert_y;
18501
+ data.crs = getDatasetCRS(dataset);
18502
+ return data;
18499
18503
  }
18500
18504
 
18505
+ function fitDatasetToFrame(dataset, frame) {
18506
+ var bounds = new Bounds(frame.bbox);
18507
+ var bounds2 = frame.bbox2 ? new Bounds(frame.bbox2) : new Bounds(0, 0, frame.width, frame.height);
18508
+ bounds.fillOut(bounds2.width() / bounds2.height());
18509
+ var fwd = bounds.getTransform(bounds2, frame.invert_y);
18510
+ transformPoints(dataset, function(x, y) {
18511
+ return fwd.transform(x, y);
18512
+ });
18513
+ }
18501
18514
 
18502
18515
  function getFrameLayerData(lyr, arcs) {
18503
18516
  var bounds = getLayerBounds(lyr, arcs);
@@ -18515,24 +18528,25 @@
18515
18528
 
18516
18529
 
18517
18530
  function calcFrameData(dataset, opts) {
18518
- var bounds;
18531
+ var inputBounds, outputBounds;
18519
18532
  if (opts.svg_bbox) {
18520
- bounds = new Bounds(opts.svg_bbox);
18533
+ inputBounds = new Bounds(opts.svg_bbox);
18521
18534
  opts = Object.assign({margin: 0}, opts); // prevent default pixel margin around content
18522
18535
  } else {
18523
- bounds = getDatasetBounds(dataset);
18536
+ inputBounds = getDatasetBounds(dataset);
18524
18537
  }
18525
- var pixBounds = calcOutputSizeInPixels(bounds, opts);
18538
+ // side effect: inputBounds may be expanded to add margins
18539
+ outputBounds = calcOutputBounds(inputBounds, opts);
18526
18540
  return {
18527
- bbox: bounds.toArray(),
18528
- bbox2: pixBounds.toArray(),
18529
- width: Math.round(pixBounds.width()),
18530
- height: Math.round(pixBounds.height()) || 1,
18541
+ bbox: inputBounds.toArray(),
18542
+ bbox2: outputBounds.toArray(),
18543
+ width: Math.round(outputBounds.width()),
18544
+ height: Math.round(outputBounds.height()) || 1,
18531
18545
  type: 'frame'
18532
18546
  };
18533
18547
  }
18534
18548
 
18535
- // Used by mapshaper-frame and mapshaper-pixel-transform. TODO: refactor
18549
+ // Used by mapshaper-frame TODO: refactor
18536
18550
  function getFrameSize(bounds, opts) {
18537
18551
  var aspectRatio = bounds.width() / bounds.height();
18538
18552
  var height, width;
@@ -18601,9 +18615,9 @@
18601
18615
 
18602
18616
 
18603
18617
  // bounds: Bounds object containing bounds of content in geographic coordinates
18604
- // returns Bounds object containing bounds of pixel output
18618
+ // returns Bounds object containing output bounds
18605
18619
  // side effect: bounds param is modified to match the output frame
18606
- function calcOutputSizeInPixels(bounds, opts) {
18620
+ function calcOutputBounds(bounds, opts) {
18607
18621
  var padX = 0,
18608
18622
  padY = 0,
18609
18623
  offX = 0,
@@ -18616,65 +18630,59 @@
18616
18630
  // TODO: add option to tweak alignment of content when both width and height are given
18617
18631
  wx = 0.5, // how padding is distributed horizontally (0: left aligned, 0.5: centered, 1: right aligned)
18618
18632
  wy = 0.5, // vertical padding distribution
18619
- widthPx, heightPx, size, kx, ky;
18633
+ width2, height2, size2, kx, ky;
18620
18634
 
18621
18635
  if (opts.fit_bbox) {
18622
18636
  // scale + shift content to fit within a bbox
18623
18637
  offX = opts.fit_bbox[0];
18624
18638
  offY = opts.fit_bbox[1];
18625
- widthPx = opts.fit_bbox[2] - offX;
18626
- heightPx = opts.fit_bbox[3] - offY;
18627
- if (width / height > widthPx / heightPx) {
18628
- // data is wider than fit box...
18629
- // scale the data to fit widthwise
18630
- heightPx = 0;
18631
- } else {
18632
- widthPx = 0; // fit the data to the height
18633
- }
18639
+ width2 = opts.fit_bbox[2] - offX;
18640
+ height2 = opts.fit_bbox[3] - offY;
18634
18641
  marginX = marginY = 0; // TODO: support margins
18635
18642
 
18636
18643
  } else if (opts.svg_scale > 0) {
18637
18644
  // alternative to using a fixed width (e.g. when generating multiple files
18638
18645
  // at a consistent geographic scale)
18639
- widthPx = width / opts.svg_scale + marginX;
18640
- heightPx = 0;
18646
+ width2 = width / opts.svg_scale + marginX;
18647
+ height2 = 0;
18641
18648
  } else if (+opts.pixels) {
18642
- size = getFrameSize(bounds, opts);
18643
- widthPx = size[0];
18644
- heightPx = size[1];
18649
+ size2 = getFrameSize(bounds, opts);
18650
+ width2 = size2[0];
18651
+ height2 = size2[1];
18645
18652
  } else {
18646
- heightPx = opts.height || 0;
18647
- widthPx = opts.width || (heightPx > 0 ? 0 : 800); // 800 is default width
18653
+ height2 = opts.height || 0;
18654
+ width2 = opts.width || (height2 > 0 ? 0 : 800); // 800 is default width
18648
18655
  }
18649
18656
 
18650
- if (heightPx > 0) {
18657
+ if (height2 > 0) {
18651
18658
  // vertical meters per pixel to fit height param
18652
- ky = (height || width || 1) / (heightPx - marginY);
18659
+ ky = (height || width || 1) / (height2 - marginY);
18653
18660
  }
18654
- if (widthPx > 0) {
18661
+ if (width2 > 0) {
18655
18662
  // horizontal meters per pixel to fit width param
18656
- kx = (width || height || 1) / (widthPx - marginX);
18663
+ kx = (width || height || 1) / (width2 - marginX);
18657
18664
  }
18658
18665
 
18659
- if (!widthPx) { // heightPx and ky are defined, set width to match
18666
+ if (!width2) { // height2 and ky are defined, set width to match
18660
18667
  kx = ky;
18661
- widthPx = width > 0 ? marginX + width / kx : heightPx; // export square graphic if content has 0 width (reconsider this?)
18662
- } else if (!heightPx) { // widthPx and kx are set, set height to match
18668
+ width2 = width > 0 ? marginX + width / kx : height2; // export square graphic if content has 0 width (reconsider this?)
18669
+ } else if (!height2) { // width2 and kx are set, set height to match
18663
18670
  ky = kx;
18664
- heightPx = height > 0 ? marginY + height / ky : widthPx;
18671
+ height2 = height > 0 ? marginY + height / ky : width2;
18665
18672
  // limit height if max_height is defined
18666
- if (opts.max_height > 0 && heightPx > opts.max_height) {
18667
- ky = kx * heightPx / opts.max_height;
18668
- heightPx = opts.max_height;
18673
+ if (opts.max_height > 0 && height2 > opts.max_height) {
18674
+ ky = kx * height2 / opts.max_height;
18675
+ height2 = opts.max_height;
18669
18676
  }
18670
18677
  }
18671
18678
 
18679
+ // add padding, if needed
18672
18680
  if (kx > ky) { // content is wide -- need to pad vertically
18673
18681
  ky = kx;
18674
- padY = ky * (heightPx - marginY) - height;
18682
+ padY = ky * (height2 - marginY) - height;
18675
18683
  } else if (ky > kx) { // content is tall -- need to pad horizontally
18676
18684
  kx = ky;
18677
- padX = kx * (widthPx - marginX) - width;
18685
+ padX = kx * (width2 - marginX) - width;
18678
18686
  }
18679
18687
 
18680
18688
  bounds.padBounds(
@@ -18683,14 +18691,14 @@
18683
18691
  margins[2] * kx + padX * (1 - wx),
18684
18692
  margins[3] * ky + padY * (1 - wy));
18685
18693
 
18686
- if (!(widthPx > 0 && heightPx > 0)) {
18694
+ if (!(width2 > 0 && height2 > 0)) {
18687
18695
  error("Missing valid height and width parameters");
18688
18696
  }
18689
18697
  if (!(kx === ky && kx > 0)) {
18690
18698
  error("Missing valid margin parameters");
18691
18699
  }
18692
18700
 
18693
- return new Bounds(offX, offY, widthPx + offX, heightPx + offY);
18701
+ return new Bounds(offX, offY, width2 + offX, height2 + offY);
18694
18702
  }
18695
18703
 
18696
18704
  function parseMarginOption(opt) {
@@ -18701,14 +18709,16 @@
18701
18709
  if (margins.length == 3) margins.push(margins[2]);
18702
18710
  return margins.map(function(str) {
18703
18711
  var px = parseFloat(str);
18704
- return isNaN(px) ? 1 : px; // 1 is default
18712
+ return isNaN(px) ? 0 : px; // 0 is default
18705
18713
  });
18706
18714
  }
18707
18715
 
18708
- var FrameData = /*#__PURE__*/Object.freeze({
18716
+ var FrameUtils = /*#__PURE__*/Object.freeze({
18709
18717
  __proto__: null,
18710
18718
  getFrameData: getFrameData,
18719
+ fitDatasetToFrame: fitDatasetToFrame,
18711
18720
  getFrameLayerData: getFrameLayerData,
18721
+ calcFrameData: calcFrameData,
18712
18722
  getFrameSize: getFrameSize,
18713
18723
  isFrameLayer: isFrameLayer,
18714
18724
  findFrameLayerInDataset: findFrameLayerInDataset,
@@ -18717,7 +18727,7 @@
18717
18727
  findFrame: findFrame,
18718
18728
  getFrameLayerBounds: getFrameLayerBounds,
18719
18729
  getMapFrameMetersPerPixel: getMapFrameMetersPerPixel,
18720
- calcOutputSizeInPixels: calcOutputSizeInPixels,
18730
+ calcOutputBounds: calcOutputBounds,
18721
18731
  parseMarginOption: parseMarginOption
18722
18732
  });
18723
18733
 
@@ -18772,6 +18782,9 @@
18772
18782
  geometry_type: 'point', shapes: targetPoints}, {geometry_type: 'polyline',
18773
18783
  shapes: targetShapes}]}, opts);
18774
18784
  transform = getAffineTransform(rotateArg, scaleArg, shiftArg, anchorArg);
18785
+ if (opts.fit_bbox) {
18786
+ transform = getFitBoxTransform(opts.fit_bbox, targetPoints, targetShapes, arcs);
18787
+ }
18775
18788
  if (targetShapes.length > 0) {
18776
18789
  targetFlags = new Uint8Array(arcs.size());
18777
18790
  otherFlags = new Uint8Array(arcs.size());
@@ -18821,6 +18834,33 @@
18821
18834
  };
18822
18835
  }
18823
18836
 
18837
+ function getFitBoxTransform(bbox, points, shapes, arcs) {
18838
+ var dataset = {
18839
+ arcs: arcs,
18840
+ info: {},
18841
+ layers: []
18842
+ };
18843
+ if (points && points.length) {
18844
+ dataset.layers.push({
18845
+ geometry_type: 'point',
18846
+ shapes: points
18847
+ });
18848
+ }
18849
+ if (shapes && shapes.length) {
18850
+ dataset.layers.push({
18851
+ geometry_type: 'polyline',
18852
+ shapes: shapes
18853
+ });
18854
+ }
18855
+ var frame = calcFrameData(dataset, {fit_bbox: bbox});
18856
+ var fromBounds = new Bounds(frame.bbox);
18857
+ var toBounds = new Bounds(frame.bbox2);
18858
+ var fwd = fromBounds.getTransform(toBounds, false);
18859
+ return function(x, y) {
18860
+ return fwd.transform(x, y);
18861
+ };
18862
+ }
18863
+
18824
18864
  function applyArrayMask(destArr, maskArr) {
18825
18865
  for (var i=0, n=destArr.length; i<n; i++) {
18826
18866
  if (maskArr[i] === 0) destArr[i] = 0;
@@ -20120,27 +20160,6 @@
20120
20160
  renderFurnitureLayer: renderFurnitureLayer
20121
20161
  });
20122
20162
 
20123
- function transformDatasetToPixels(dataset, opts) {
20124
- var frame = getFrameData(dataset, opts);
20125
- fitDatasetToFrame(dataset, frame, opts);
20126
- return [frame.width, frame.height];
20127
- }
20128
-
20129
- function fitDatasetToFrame(dataset, frame, opts) {
20130
- var bounds = new Bounds(frame.bbox);
20131
- var bounds2 = frame.bbox2 ? new Bounds(frame.bbox2) : new Bounds(0, 0, frame.width, frame.height);
20132
- var fwd = bounds.getTransform(bounds2, opts.invert_y);
20133
- transformPoints(dataset, function(x, y) {
20134
- return fwd.transform(x, y);
20135
- });
20136
- }
20137
-
20138
- var PixelTransform = /*#__PURE__*/Object.freeze({
20139
- __proto__: null,
20140
- transformDatasetToPixels: transformDatasetToPixels,
20141
- fitDatasetToFrame: fitDatasetToFrame
20142
- });
20143
-
20144
20163
  function stringify(obj) {
20145
20164
  var svg, joinStr;
20146
20165
  if (!obj || !obj.tag) return '';
@@ -20428,10 +20447,11 @@
20428
20447
  dataset = copyDataset(dataset); // Modify a copy of the dataset
20429
20448
  }
20430
20449
 
20431
- // invert_y setting for screen coordinates and geojson polygon generation
20432
- utils.extend(opts, {invert_y: true});
20450
+ // use invert_y: 0 setting for screen coordinates and geojson polygon generation
20451
+ // use 1px default margin so typical strokes don't get cut off on the sides
20452
+ opts = Object.assign({invert_y: true, margin: "1"}, opts);
20433
20453
  frame = getFrameData(dataset, opts);
20434
- fitDatasetToFrame(dataset, frame, opts);
20454
+ fitDatasetToFrame(dataset, frame);
20435
20455
  setCoordinatePrecision(dataset, opts.precision || 0.0001);
20436
20456
 
20437
20457
  // error if one or more svg_data fields are not present in any layers
@@ -22067,10 +22087,14 @@ ${svg}
22067
22087
  }
22068
22088
 
22069
22089
  if (opts.width > 0 || opts.height > 0) {
22090
+ // these options create a TopoJSON with pixel coordinates, including
22091
+ // origin (0,0) in the top left corner of the viewport, generally for
22092
+ // direct conversion to SVG (many online examples using d3 are like this)
22093
+ //
22070
22094
  opts = utils.defaults({invert_y: true}, opts);
22071
- transformDatasetToPixels(dataset, opts);
22095
+ fitDatasetToFrame(dataset, getFrameData(dataset, opts));
22072
22096
  } else if (opts.fit_bbox) {
22073
- transformDatasetToPixels(dataset, {fit_bbox: opts.fit_bbox});
22097
+ fitDatasetToFrame(dataset, getFrameData(dataset, {fit_bbox: opts.fit_bbox}));
22074
22098
  }
22075
22099
 
22076
22100
  if (opts.precision && opts.no_quantization) {
@@ -24279,7 +24303,7 @@ ${svg}
24279
24303
  describe: 'aspect ratio as a number or range (e.g. 2 0.8,1.6 ,2)'
24280
24304
  },
24281
24305
  offsetOpt = {
24282
- describe: 'padding as distance or pct of h/w (single value or list)',
24306
+ describe: 'offset distance or pct of h/w (single value or l,b,r,t list)',
24283
24307
  type: 'distance'
24284
24308
  };
24285
24309
 
@@ -24609,6 +24633,10 @@ ${svg}
24609
24633
  type: 'numbers',
24610
24634
  describe: 'center of rotation/scaling (default is center of selected shapes)'
24611
24635
  })
24636
+ .option('fit-bbox', {
24637
+ type: 'bbox',
24638
+ describe: 'scale and shift coordinates to fit a bbox'
24639
+ })
24612
24640
  .option('where', whereOpt)
24613
24641
  .option('target', targetOpt);
24614
24642
 
@@ -39317,7 +39345,7 @@ ${svg}
39317
39345
  // TODO: consider projections that may or may not be aligned,
39318
39346
  // depending on parameters
39319
39347
  if (inList(P, 'cassini,gnom,bertin1953,chamb,ob_tran,tpeqd,healpix,rhealpix,' +
39320
- 'ocea,omerc,tmerc,etmerc')) {
39348
+ 'ocea,omerc,tmerc,etmerc,nicol')) {
39321
39349
  return false;
39322
39350
  }
39323
39351
  if (isAzimuthal(P)) {
@@ -39892,7 +39920,7 @@ ${svg}
39892
39920
  }
39893
39921
 
39894
39922
  function isCircleClippedProjection(P) {
39895
- return inList(P, 'stere,sterea,ups,ortho,gnom,laea,nsper,tpers,geos');
39923
+ return inList(P, 'stere,sterea,ups,ortho,gnom,laea,nsper,tpers,geos,nicol');
39896
39924
  }
39897
39925
 
39898
39926
  function getPerspectiveClipAngle(P) {
@@ -39917,6 +39945,7 @@ ${svg}
39917
39945
  laea: 179,
39918
39946
  //ortho: 89.9, // projection errors betwen lat +/-35 to 55
39919
39947
  ortho: 89.85, // TODO: investigate
39948
+ nicol: 89.85,
39920
39949
  stere: 142,
39921
39950
  sterea: 142,
39922
39951
  ups: 10.5 // TODO: should be 6.5 deg at north pole
@@ -44521,6 +44550,7 @@ ${svg}
44521
44550
  if (!size) return null;
44522
44551
  var stemLen = size.stemLen,
44523
44552
  headLen = size.headLen,
44553
+ totalLen = stickArrow ? Math.max(stemLen, headLen) : stemLen + headLen,
44524
44554
  headDx = size.headWidth / 2,
44525
44555
  stemDx = size.stemWidth / 2,
44526
44556
  baseDx = stemDx * (1 - stemTaper),
@@ -44573,11 +44603,10 @@ ${svg}
44573
44603
  }
44574
44604
 
44575
44605
  if (d.anchor == 'end') {
44576
- scaleAndShiftCoords(coords, 1, [-dx, -dy - headLen]);
44606
+ scaleAndShiftCoords(coords, 1, [-dx, -totalLen]);
44577
44607
  } else if (d.anchor == 'middle') {
44578
44608
  // shift midpoint away from the head a bit for a more balanced placement
44579
- // scaleAndShiftCoords(coords, 1, [-dx/2, (-dy - headLen)/2]);
44580
- scaleAndShiftCoords(coords, 1, [-dx * 0.5, -dy * 0.5 - headLen * 0.25]);
44609
+ scaleAndShiftCoords(coords, 1, [-dx * 0.5, -dy * 0.5 - totalLen * 0.25]);
44581
44610
  }
44582
44611
 
44583
44612
  rotateCoords(coords, direction);
@@ -44594,15 +44623,16 @@ ${svg}
44594
44623
 
44595
44624
  function calcArrowSize(d, stickArrow) {
44596
44625
  // don't display arrows with negative length
44597
- var totalLen = Math.max(d.radius || d.length || d.r || 0, 0),
44598
- scale = 1,
44599
- o = initArrowSize(d); // calc several parameters
44626
+ var o = initArrowSize(d), // calc several parameters
44627
+ dataLen = Math.max(d.radius || d.length || d.r || 0),
44628
+ totalLen = Math.max(dataLen, o.headLen, 0),
44629
+ scale = 1;
44600
44630
  if (totalLen >= 0) {
44601
44631
  scale = calcScale(totalLen, o.headLen, d);
44602
44632
  o.stemWidth *= scale;
44603
44633
  o.headWidth *= scale;
44604
44634
  o.headLen *= scale;
44605
- o.stemLen = stickArrow ? totalLen : totalLen - o.headLen;
44635
+ o.stemLen = stickArrow ? dataLen : totalLen - o.headLen;
44606
44636
  }
44607
44637
 
44608
44638
  if (o.headWidth < o.stemWidth && o.headWidth > 0) {
@@ -44651,13 +44681,11 @@ ${svg}
44651
44681
  return o;
44652
44682
  }
44653
44683
 
44654
-
44655
44684
  // Returns ratio of head length to head width
44656
44685
  function getHeadSizeRatio(headAngle) {
44657
44686
  return 1 / Math.tan(Math.PI * headAngle / 180 / 2) / 2;
44658
44687
  }
44659
44688
 
44660
-
44661
44689
  // ax, ay: point on the base
44662
44690
  // bx, by: point on the stem
44663
44691
  function getCurvedStemCoords(ax, ay, bx, by) {
@@ -45870,7 +45898,7 @@ ${svg}
45870
45898
  });
45871
45899
  }
45872
45900
 
45873
- var version = "0.6.100";
45901
+ var version = "0.6.102";
45874
45902
 
45875
45903
  // Parse command line args into commands and run them
45876
45904
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
@@ -46563,7 +46591,7 @@ ${svg}
46563
46591
  FileTypes,
46564
46592
  FilterGeom,
46565
46593
  Frame,
46566
- FrameData,
46594
+ FrameUtils,
46567
46595
  Furniture,
46568
46596
  Geodesic,
46569
46597
  GeojsonExport,
@@ -46598,7 +46626,7 @@ ${svg}
46598
46626
  PathImport,
46599
46627
  PathRepair,
46600
46628
  PathUtils,
46601
- PixelTransform,
46629
+ // PixelTransform,
46602
46630
  PointPolygonJoin,
46603
46631
  Points,
46604
46632
  PointToGrid,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.6.100",
3
+ "version": "0.6.102",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -56,7 +56,7 @@
56
56
  "iconv-lite": "^0.6.3",
57
57
  "idb-keyval": "^6.2.0",
58
58
  "kdbush": "^3.0.0",
59
- "mproj": "0.0.39",
59
+ "mproj": "0.0.40",
60
60
  "msgpackr": "^1.10.1",
61
61
  "opn": "^5.3.0",
62
62
  "rw": "~1.3.3",
@@ -6843,6 +6843,7 @@
6843
6843
  });
6844
6844
  }
6845
6845
 
6846
+
6846
6847
  function defaults(dest) {
6847
6848
  for (var i=1, n=arguments.length; i<n; i++) {
6848
6849
  var src = arguments[i] || {};
@@ -12974,7 +12975,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12974
12975
  function calcFullBounds() {
12975
12976
  var b;
12976
12977
  if (isPreviewView()) {
12977
- b = new Bounds(getFrameData().bbox);
12978
+ b = new Bounds(getFrameLayerData().bbox);
12978
12979
  } else {
12979
12980
  b = getContentLayerBounds();
12980
12981
  }
@@ -13014,10 +13015,10 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13014
13015
 
13015
13016
  // Preview view: symbols are scaled based on display size of frame layer
13016
13017
  function isPreviewView() {
13017
- return !isTableView() && !!getFrameData();
13018
+ return !isTableView() && !!getFrameLayerData();
13018
13019
  }
13019
13020
 
13020
- function getFrameData() {
13021
+ function getFrameLayerData() {
13021
13022
  var lyr = findFrameLayer();
13022
13023
  return lyr && internal.getFrameLayerData(lyr, lyr.gui.displayArcs) || null;
13023
13024
  }
@@ -13138,7 +13139,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13138
13139
  }
13139
13140
  if (layersMayHaveChanged) {
13140
13141
  // kludge to handle layer visibility toggling
13141
- _ext.setFrameData(isPreviewView() ? getFrameData() : null);
13142
+ _ext.setFrameData(isPreviewView() ? getFrameLayerData() : null);
13142
13143
  updateFullBounds();
13143
13144
  updateLayerStyles(contentLayers);
13144
13145
  updateLayerStackOrder(model.getLayers());// update menu_order property of all layers
package/www/mapshaper.js CHANGED
@@ -267,6 +267,7 @@
267
267
  });
268
268
  }
269
269
 
270
+
270
271
  function defaults(dest) {
271
272
  for (var i=1, n=arguments.length; i<n; i++) {
272
273
  var src = arguments[i] || {};
@@ -13762,7 +13763,7 @@
13762
13763
  convertIntervalParam(opt[1], crs)];
13763
13764
  }
13764
13765
 
13765
- // Accepts a single value or a list of four values. List order is l,b,t,r
13766
+ // Accepts a single value or a list of four values. List order is l,b,r,t
13766
13767
  function convertFourSides(opt, crs, bounds) {
13767
13768
  var arr = opt.includes(',') ? opt.split(',') : opt.split(' ');
13768
13769
  if (arr.length == 1) {
@@ -13776,7 +13777,7 @@
13776
13777
  tmp = parseFloat(param) / 100 || 0;
13777
13778
  return tmp * (i == 1 || i == 3 ? bounds.height() : bounds.width());
13778
13779
  }
13779
- return convertIntervalParam(opt, crs);
13780
+ return convertIntervalParam(param, crs);
13780
13781
  });
13781
13782
  }
13782
13783
 
@@ -18486,18 +18487,30 @@
18486
18487
  type: 'frame'
18487
18488
  }
18488
18489
  */
18490
+
18491
+
18489
18492
  function getFrameData(dataset, exportOpts) {
18490
18493
  var frameLyr = findFrameLayerInDataset(dataset);
18491
- var frameData;
18494
+ var data;
18492
18495
  if (frameLyr) {
18493
- frameData = getFrameLayerData(frameLyr, dataset.arcs);
18496
+ data = getFrameLayerData(frameLyr, dataset.arcs);
18494
18497
  } else {
18495
- frameData = calcFrameData(dataset, exportOpts);
18498
+ data = calcFrameData(dataset, exportOpts);
18496
18499
  }
18497
- frameData.crs = getDatasetCRS(dataset);
18498
- return frameData;
18500
+ data.invert_y = !!exportOpts.invert_y;
18501
+ data.crs = getDatasetCRS(dataset);
18502
+ return data;
18499
18503
  }
18500
18504
 
18505
+ function fitDatasetToFrame(dataset, frame) {
18506
+ var bounds = new Bounds(frame.bbox);
18507
+ var bounds2 = frame.bbox2 ? new Bounds(frame.bbox2) : new Bounds(0, 0, frame.width, frame.height);
18508
+ bounds.fillOut(bounds2.width() / bounds2.height());
18509
+ var fwd = bounds.getTransform(bounds2, frame.invert_y);
18510
+ transformPoints(dataset, function(x, y) {
18511
+ return fwd.transform(x, y);
18512
+ });
18513
+ }
18501
18514
 
18502
18515
  function getFrameLayerData(lyr, arcs) {
18503
18516
  var bounds = getLayerBounds(lyr, arcs);
@@ -18515,24 +18528,25 @@
18515
18528
 
18516
18529
 
18517
18530
  function calcFrameData(dataset, opts) {
18518
- var bounds;
18531
+ var inputBounds, outputBounds;
18519
18532
  if (opts.svg_bbox) {
18520
- bounds = new Bounds(opts.svg_bbox);
18533
+ inputBounds = new Bounds(opts.svg_bbox);
18521
18534
  opts = Object.assign({margin: 0}, opts); // prevent default pixel margin around content
18522
18535
  } else {
18523
- bounds = getDatasetBounds(dataset);
18536
+ inputBounds = getDatasetBounds(dataset);
18524
18537
  }
18525
- var pixBounds = calcOutputSizeInPixels(bounds, opts);
18538
+ // side effect: inputBounds may be expanded to add margins
18539
+ outputBounds = calcOutputBounds(inputBounds, opts);
18526
18540
  return {
18527
- bbox: bounds.toArray(),
18528
- bbox2: pixBounds.toArray(),
18529
- width: Math.round(pixBounds.width()),
18530
- height: Math.round(pixBounds.height()) || 1,
18541
+ bbox: inputBounds.toArray(),
18542
+ bbox2: outputBounds.toArray(),
18543
+ width: Math.round(outputBounds.width()),
18544
+ height: Math.round(outputBounds.height()) || 1,
18531
18545
  type: 'frame'
18532
18546
  };
18533
18547
  }
18534
18548
 
18535
- // Used by mapshaper-frame and mapshaper-pixel-transform. TODO: refactor
18549
+ // Used by mapshaper-frame TODO: refactor
18536
18550
  function getFrameSize(bounds, opts) {
18537
18551
  var aspectRatio = bounds.width() / bounds.height();
18538
18552
  var height, width;
@@ -18601,9 +18615,9 @@
18601
18615
 
18602
18616
 
18603
18617
  // bounds: Bounds object containing bounds of content in geographic coordinates
18604
- // returns Bounds object containing bounds of pixel output
18618
+ // returns Bounds object containing output bounds
18605
18619
  // side effect: bounds param is modified to match the output frame
18606
- function calcOutputSizeInPixels(bounds, opts) {
18620
+ function calcOutputBounds(bounds, opts) {
18607
18621
  var padX = 0,
18608
18622
  padY = 0,
18609
18623
  offX = 0,
@@ -18616,65 +18630,59 @@
18616
18630
  // TODO: add option to tweak alignment of content when both width and height are given
18617
18631
  wx = 0.5, // how padding is distributed horizontally (0: left aligned, 0.5: centered, 1: right aligned)
18618
18632
  wy = 0.5, // vertical padding distribution
18619
- widthPx, heightPx, size, kx, ky;
18633
+ width2, height2, size2, kx, ky;
18620
18634
 
18621
18635
  if (opts.fit_bbox) {
18622
18636
  // scale + shift content to fit within a bbox
18623
18637
  offX = opts.fit_bbox[0];
18624
18638
  offY = opts.fit_bbox[1];
18625
- widthPx = opts.fit_bbox[2] - offX;
18626
- heightPx = opts.fit_bbox[3] - offY;
18627
- if (width / height > widthPx / heightPx) {
18628
- // data is wider than fit box...
18629
- // scale the data to fit widthwise
18630
- heightPx = 0;
18631
- } else {
18632
- widthPx = 0; // fit the data to the height
18633
- }
18639
+ width2 = opts.fit_bbox[2] - offX;
18640
+ height2 = opts.fit_bbox[3] - offY;
18634
18641
  marginX = marginY = 0; // TODO: support margins
18635
18642
 
18636
18643
  } else if (opts.svg_scale > 0) {
18637
18644
  // alternative to using a fixed width (e.g. when generating multiple files
18638
18645
  // at a consistent geographic scale)
18639
- widthPx = width / opts.svg_scale + marginX;
18640
- heightPx = 0;
18646
+ width2 = width / opts.svg_scale + marginX;
18647
+ height2 = 0;
18641
18648
  } else if (+opts.pixels) {
18642
- size = getFrameSize(bounds, opts);
18643
- widthPx = size[0];
18644
- heightPx = size[1];
18649
+ size2 = getFrameSize(bounds, opts);
18650
+ width2 = size2[0];
18651
+ height2 = size2[1];
18645
18652
  } else {
18646
- heightPx = opts.height || 0;
18647
- widthPx = opts.width || (heightPx > 0 ? 0 : 800); // 800 is default width
18653
+ height2 = opts.height || 0;
18654
+ width2 = opts.width || (height2 > 0 ? 0 : 800); // 800 is default width
18648
18655
  }
18649
18656
 
18650
- if (heightPx > 0) {
18657
+ if (height2 > 0) {
18651
18658
  // vertical meters per pixel to fit height param
18652
- ky = (height || width || 1) / (heightPx - marginY);
18659
+ ky = (height || width || 1) / (height2 - marginY);
18653
18660
  }
18654
- if (widthPx > 0) {
18661
+ if (width2 > 0) {
18655
18662
  // horizontal meters per pixel to fit width param
18656
- kx = (width || height || 1) / (widthPx - marginX);
18663
+ kx = (width || height || 1) / (width2 - marginX);
18657
18664
  }
18658
18665
 
18659
- if (!widthPx) { // heightPx and ky are defined, set width to match
18666
+ if (!width2) { // height2 and ky are defined, set width to match
18660
18667
  kx = ky;
18661
- widthPx = width > 0 ? marginX + width / kx : heightPx; // export square graphic if content has 0 width (reconsider this?)
18662
- } else if (!heightPx) { // widthPx and kx are set, set height to match
18668
+ width2 = width > 0 ? marginX + width / kx : height2; // export square graphic if content has 0 width (reconsider this?)
18669
+ } else if (!height2) { // width2 and kx are set, set height to match
18663
18670
  ky = kx;
18664
- heightPx = height > 0 ? marginY + height / ky : widthPx;
18671
+ height2 = height > 0 ? marginY + height / ky : width2;
18665
18672
  // limit height if max_height is defined
18666
- if (opts.max_height > 0 && heightPx > opts.max_height) {
18667
- ky = kx * heightPx / opts.max_height;
18668
- heightPx = opts.max_height;
18673
+ if (opts.max_height > 0 && height2 > opts.max_height) {
18674
+ ky = kx * height2 / opts.max_height;
18675
+ height2 = opts.max_height;
18669
18676
  }
18670
18677
  }
18671
18678
 
18679
+ // add padding, if needed
18672
18680
  if (kx > ky) { // content is wide -- need to pad vertically
18673
18681
  ky = kx;
18674
- padY = ky * (heightPx - marginY) - height;
18682
+ padY = ky * (height2 - marginY) - height;
18675
18683
  } else if (ky > kx) { // content is tall -- need to pad horizontally
18676
18684
  kx = ky;
18677
- padX = kx * (widthPx - marginX) - width;
18685
+ padX = kx * (width2 - marginX) - width;
18678
18686
  }
18679
18687
 
18680
18688
  bounds.padBounds(
@@ -18683,14 +18691,14 @@
18683
18691
  margins[2] * kx + padX * (1 - wx),
18684
18692
  margins[3] * ky + padY * (1 - wy));
18685
18693
 
18686
- if (!(widthPx > 0 && heightPx > 0)) {
18694
+ if (!(width2 > 0 && height2 > 0)) {
18687
18695
  error("Missing valid height and width parameters");
18688
18696
  }
18689
18697
  if (!(kx === ky && kx > 0)) {
18690
18698
  error("Missing valid margin parameters");
18691
18699
  }
18692
18700
 
18693
- return new Bounds(offX, offY, widthPx + offX, heightPx + offY);
18701
+ return new Bounds(offX, offY, width2 + offX, height2 + offY);
18694
18702
  }
18695
18703
 
18696
18704
  function parseMarginOption(opt) {
@@ -18701,14 +18709,16 @@
18701
18709
  if (margins.length == 3) margins.push(margins[2]);
18702
18710
  return margins.map(function(str) {
18703
18711
  var px = parseFloat(str);
18704
- return isNaN(px) ? 1 : px; // 1 is default
18712
+ return isNaN(px) ? 0 : px; // 0 is default
18705
18713
  });
18706
18714
  }
18707
18715
 
18708
- var FrameData = /*#__PURE__*/Object.freeze({
18716
+ var FrameUtils = /*#__PURE__*/Object.freeze({
18709
18717
  __proto__: null,
18710
18718
  getFrameData: getFrameData,
18719
+ fitDatasetToFrame: fitDatasetToFrame,
18711
18720
  getFrameLayerData: getFrameLayerData,
18721
+ calcFrameData: calcFrameData,
18712
18722
  getFrameSize: getFrameSize,
18713
18723
  isFrameLayer: isFrameLayer,
18714
18724
  findFrameLayerInDataset: findFrameLayerInDataset,
@@ -18717,7 +18727,7 @@
18717
18727
  findFrame: findFrame,
18718
18728
  getFrameLayerBounds: getFrameLayerBounds,
18719
18729
  getMapFrameMetersPerPixel: getMapFrameMetersPerPixel,
18720
- calcOutputSizeInPixels: calcOutputSizeInPixels,
18730
+ calcOutputBounds: calcOutputBounds,
18721
18731
  parseMarginOption: parseMarginOption
18722
18732
  });
18723
18733
 
@@ -18772,6 +18782,9 @@
18772
18782
  geometry_type: 'point', shapes: targetPoints}, {geometry_type: 'polyline',
18773
18783
  shapes: targetShapes}]}, opts);
18774
18784
  transform = getAffineTransform(rotateArg, scaleArg, shiftArg, anchorArg);
18785
+ if (opts.fit_bbox) {
18786
+ transform = getFitBoxTransform(opts.fit_bbox, targetPoints, targetShapes, arcs);
18787
+ }
18775
18788
  if (targetShapes.length > 0) {
18776
18789
  targetFlags = new Uint8Array(arcs.size());
18777
18790
  otherFlags = new Uint8Array(arcs.size());
@@ -18821,6 +18834,33 @@
18821
18834
  };
18822
18835
  }
18823
18836
 
18837
+ function getFitBoxTransform(bbox, points, shapes, arcs) {
18838
+ var dataset = {
18839
+ arcs: arcs,
18840
+ info: {},
18841
+ layers: []
18842
+ };
18843
+ if (points && points.length) {
18844
+ dataset.layers.push({
18845
+ geometry_type: 'point',
18846
+ shapes: points
18847
+ });
18848
+ }
18849
+ if (shapes && shapes.length) {
18850
+ dataset.layers.push({
18851
+ geometry_type: 'polyline',
18852
+ shapes: shapes
18853
+ });
18854
+ }
18855
+ var frame = calcFrameData(dataset, {fit_bbox: bbox});
18856
+ var fromBounds = new Bounds(frame.bbox);
18857
+ var toBounds = new Bounds(frame.bbox2);
18858
+ var fwd = fromBounds.getTransform(toBounds, false);
18859
+ return function(x, y) {
18860
+ return fwd.transform(x, y);
18861
+ };
18862
+ }
18863
+
18824
18864
  function applyArrayMask(destArr, maskArr) {
18825
18865
  for (var i=0, n=destArr.length; i<n; i++) {
18826
18866
  if (maskArr[i] === 0) destArr[i] = 0;
@@ -20120,27 +20160,6 @@
20120
20160
  renderFurnitureLayer: renderFurnitureLayer
20121
20161
  });
20122
20162
 
20123
- function transformDatasetToPixels(dataset, opts) {
20124
- var frame = getFrameData(dataset, opts);
20125
- fitDatasetToFrame(dataset, frame, opts);
20126
- return [frame.width, frame.height];
20127
- }
20128
-
20129
- function fitDatasetToFrame(dataset, frame, opts) {
20130
- var bounds = new Bounds(frame.bbox);
20131
- var bounds2 = frame.bbox2 ? new Bounds(frame.bbox2) : new Bounds(0, 0, frame.width, frame.height);
20132
- var fwd = bounds.getTransform(bounds2, opts.invert_y);
20133
- transformPoints(dataset, function(x, y) {
20134
- return fwd.transform(x, y);
20135
- });
20136
- }
20137
-
20138
- var PixelTransform = /*#__PURE__*/Object.freeze({
20139
- __proto__: null,
20140
- transformDatasetToPixels: transformDatasetToPixels,
20141
- fitDatasetToFrame: fitDatasetToFrame
20142
- });
20143
-
20144
20163
  function stringify(obj) {
20145
20164
  var svg, joinStr;
20146
20165
  if (!obj || !obj.tag) return '';
@@ -20428,10 +20447,11 @@
20428
20447
  dataset = copyDataset(dataset); // Modify a copy of the dataset
20429
20448
  }
20430
20449
 
20431
- // invert_y setting for screen coordinates and geojson polygon generation
20432
- utils.extend(opts, {invert_y: true});
20450
+ // use invert_y: 0 setting for screen coordinates and geojson polygon generation
20451
+ // use 1px default margin so typical strokes don't get cut off on the sides
20452
+ opts = Object.assign({invert_y: true, margin: "1"}, opts);
20433
20453
  frame = getFrameData(dataset, opts);
20434
- fitDatasetToFrame(dataset, frame, opts);
20454
+ fitDatasetToFrame(dataset, frame);
20435
20455
  setCoordinatePrecision(dataset, opts.precision || 0.0001);
20436
20456
 
20437
20457
  // error if one or more svg_data fields are not present in any layers
@@ -22067,10 +22087,14 @@ ${svg}
22067
22087
  }
22068
22088
 
22069
22089
  if (opts.width > 0 || opts.height > 0) {
22090
+ // these options create a TopoJSON with pixel coordinates, including
22091
+ // origin (0,0) in the top left corner of the viewport, generally for
22092
+ // direct conversion to SVG (many online examples using d3 are like this)
22093
+ //
22070
22094
  opts = utils.defaults({invert_y: true}, opts);
22071
- transformDatasetToPixels(dataset, opts);
22095
+ fitDatasetToFrame(dataset, getFrameData(dataset, opts));
22072
22096
  } else if (opts.fit_bbox) {
22073
- transformDatasetToPixels(dataset, {fit_bbox: opts.fit_bbox});
22097
+ fitDatasetToFrame(dataset, getFrameData(dataset, {fit_bbox: opts.fit_bbox}));
22074
22098
  }
22075
22099
 
22076
22100
  if (opts.precision && opts.no_quantization) {
@@ -24279,7 +24303,7 @@ ${svg}
24279
24303
  describe: 'aspect ratio as a number or range (e.g. 2 0.8,1.6 ,2)'
24280
24304
  },
24281
24305
  offsetOpt = {
24282
- describe: 'padding as distance or pct of h/w (single value or list)',
24306
+ describe: 'offset distance or pct of h/w (single value or l,b,r,t list)',
24283
24307
  type: 'distance'
24284
24308
  };
24285
24309
 
@@ -24609,6 +24633,10 @@ ${svg}
24609
24633
  type: 'numbers',
24610
24634
  describe: 'center of rotation/scaling (default is center of selected shapes)'
24611
24635
  })
24636
+ .option('fit-bbox', {
24637
+ type: 'bbox',
24638
+ describe: 'scale and shift coordinates to fit a bbox'
24639
+ })
24612
24640
  .option('where', whereOpt)
24613
24641
  .option('target', targetOpt);
24614
24642
 
@@ -39317,7 +39345,7 @@ ${svg}
39317
39345
  // TODO: consider projections that may or may not be aligned,
39318
39346
  // depending on parameters
39319
39347
  if (inList(P, 'cassini,gnom,bertin1953,chamb,ob_tran,tpeqd,healpix,rhealpix,' +
39320
- 'ocea,omerc,tmerc,etmerc')) {
39348
+ 'ocea,omerc,tmerc,etmerc,nicol')) {
39321
39349
  return false;
39322
39350
  }
39323
39351
  if (isAzimuthal(P)) {
@@ -39892,7 +39920,7 @@ ${svg}
39892
39920
  }
39893
39921
 
39894
39922
  function isCircleClippedProjection(P) {
39895
- return inList(P, 'stere,sterea,ups,ortho,gnom,laea,nsper,tpers,geos');
39923
+ return inList(P, 'stere,sterea,ups,ortho,gnom,laea,nsper,tpers,geos,nicol');
39896
39924
  }
39897
39925
 
39898
39926
  function getPerspectiveClipAngle(P) {
@@ -39917,6 +39945,7 @@ ${svg}
39917
39945
  laea: 179,
39918
39946
  //ortho: 89.9, // projection errors betwen lat +/-35 to 55
39919
39947
  ortho: 89.85, // TODO: investigate
39948
+ nicol: 89.85,
39920
39949
  stere: 142,
39921
39950
  sterea: 142,
39922
39951
  ups: 10.5 // TODO: should be 6.5 deg at north pole
@@ -44521,6 +44550,7 @@ ${svg}
44521
44550
  if (!size) return null;
44522
44551
  var stemLen = size.stemLen,
44523
44552
  headLen = size.headLen,
44553
+ totalLen = stickArrow ? Math.max(stemLen, headLen) : stemLen + headLen,
44524
44554
  headDx = size.headWidth / 2,
44525
44555
  stemDx = size.stemWidth / 2,
44526
44556
  baseDx = stemDx * (1 - stemTaper),
@@ -44573,11 +44603,10 @@ ${svg}
44573
44603
  }
44574
44604
 
44575
44605
  if (d.anchor == 'end') {
44576
- scaleAndShiftCoords(coords, 1, [-dx, -dy - headLen]);
44606
+ scaleAndShiftCoords(coords, 1, [-dx, -totalLen]);
44577
44607
  } else if (d.anchor == 'middle') {
44578
44608
  // shift midpoint away from the head a bit for a more balanced placement
44579
- // scaleAndShiftCoords(coords, 1, [-dx/2, (-dy - headLen)/2]);
44580
- scaleAndShiftCoords(coords, 1, [-dx * 0.5, -dy * 0.5 - headLen * 0.25]);
44609
+ scaleAndShiftCoords(coords, 1, [-dx * 0.5, -dy * 0.5 - totalLen * 0.25]);
44581
44610
  }
44582
44611
 
44583
44612
  rotateCoords(coords, direction);
@@ -44594,15 +44623,16 @@ ${svg}
44594
44623
 
44595
44624
  function calcArrowSize(d, stickArrow) {
44596
44625
  // don't display arrows with negative length
44597
- var totalLen = Math.max(d.radius || d.length || d.r || 0, 0),
44598
- scale = 1,
44599
- o = initArrowSize(d); // calc several parameters
44626
+ var o = initArrowSize(d), // calc several parameters
44627
+ dataLen = Math.max(d.radius || d.length || d.r || 0),
44628
+ totalLen = Math.max(dataLen, o.headLen, 0),
44629
+ scale = 1;
44600
44630
  if (totalLen >= 0) {
44601
44631
  scale = calcScale(totalLen, o.headLen, d);
44602
44632
  o.stemWidth *= scale;
44603
44633
  o.headWidth *= scale;
44604
44634
  o.headLen *= scale;
44605
- o.stemLen = stickArrow ? totalLen : totalLen - o.headLen;
44635
+ o.stemLen = stickArrow ? dataLen : totalLen - o.headLen;
44606
44636
  }
44607
44637
 
44608
44638
  if (o.headWidth < o.stemWidth && o.headWidth > 0) {
@@ -44651,13 +44681,11 @@ ${svg}
44651
44681
  return o;
44652
44682
  }
44653
44683
 
44654
-
44655
44684
  // Returns ratio of head length to head width
44656
44685
  function getHeadSizeRatio(headAngle) {
44657
44686
  return 1 / Math.tan(Math.PI * headAngle / 180 / 2) / 2;
44658
44687
  }
44659
44688
 
44660
-
44661
44689
  // ax, ay: point on the base
44662
44690
  // bx, by: point on the stem
44663
44691
  function getCurvedStemCoords(ax, ay, bx, by) {
@@ -45870,7 +45898,7 @@ ${svg}
45870
45898
  });
45871
45899
  }
45872
45900
 
45873
- var version = "0.6.100";
45901
+ var version = "0.6.102";
45874
45902
 
45875
45903
  // Parse command line args into commands and run them
45876
45904
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
@@ -46563,7 +46591,7 @@ ${svg}
46563
46591
  FileTypes,
46564
46592
  FilterGeom,
46565
46593
  Frame,
46566
- FrameData,
46594
+ FrameUtils,
46567
46595
  Furniture,
46568
46596
  Geodesic,
46569
46597
  GeojsonExport,
@@ -46598,7 +46626,7 @@ ${svg}
46598
46626
  PathImport,
46599
46627
  PathRepair,
46600
46628
  PathUtils,
46601
- PixelTransform,
46629
+ // PixelTransform,
46602
46630
  PointPolygonJoin,
46603
46631
  Points,
46604
46632
  PointToGrid,
package/www/modules.js CHANGED
@@ -20279,6 +20279,7 @@ function pj_phi2(ts, e) {
20279
20279
 
20280
20280
 
20281
20281
  pj_add(pj_merc, 'merc', 'Mercator', 'Cyl, Sph&Ell\nlat_ts=');
20282
+ pj_add(pj_webmerc, 'webmerc', 'Web Mercator / Pseudo Mercator', 'Cyl, Ell');
20282
20283
 
20283
20284
  function pj_merc(P) {
20284
20285
  var EPS10 = 1e-10;
@@ -20333,6 +20334,26 @@ function pj_merc(P) {
20333
20334
  }
20334
20335
  }
20335
20336
 
20337
+ function pj_webmerc(P) {
20338
+ P.k0 = 1;
20339
+ P.inv = s_inv;
20340
+ P.fwd = s_fwd;
20341
+
20342
+ function s_fwd(lp, xy) {
20343
+ if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10) {
20344
+ f_error();
20345
+ }
20346
+ xy.x = P.k0 * lp.lam;
20347
+ xy.y = P.k0 * log(tan(M_FORTPI + 0.5 * lp.phi));
20348
+ }
20349
+
20350
+ function s_inv(xy, lp) {
20351
+ lp.phi = M_HALFPI - 2 * atan(exp(-xy.y / P.k0));
20352
+ lp.lam = xy.x / P.k0;
20353
+ }
20354
+ }
20355
+
20356
+
20336
20357
 
20337
20358
  pj_add(pj_mill, 'mill', 'Miller Cylindrical', 'Cyl, Sph');
20338
20359
 
@@ -20790,6 +20811,45 @@ var NITER = 9,
20790
20811
  }
20791
20812
 
20792
20813
 
20814
+ pj_add(pj_nicol, 'nicol', 'Nicolosi Globular', 'Misc Sph, no inv');
20815
+
20816
+ function pj_nicol(P) {
20817
+ P.es = 0;
20818
+ P.fwd = s_fwd;
20819
+
20820
+ function s_fwd(lp, xy) {
20821
+ var EPS = 1e-10;
20822
+ if (fabs(lp.lam) < EPS) {
20823
+ xy.x = 0;
20824
+ xy.y = lp.phi;
20825
+ } else if (fabs(lp.phi) < EPS) {
20826
+ xy.x = lp.lam;
20827
+ xy.y = 0;
20828
+ } else if (fabs(fabs(lp.lam) - M_HALFPI) < EPS) {
20829
+ xy.x = lp.lam * cos(lp.phi);
20830
+ xy.y = M_HALFPI * sin(lp.phi);
20831
+ } else if (fabs(fabs(lp.phi) - M_HALFPI) < EPS) {
20832
+ xy.x = 0;
20833
+ xy.y = lp.phi;
20834
+ } else {
20835
+ var tb = M_HALFPI / lp.lam - lp.lam / M_HALFPI;
20836
+ var c = lp.phi / M_HALFPI;
20837
+ var sp = sin(lp.phi);
20838
+ var d = (1 - c * c) / (sp - c);
20839
+ var r2 = tb / d;
20840
+ r2 *= r2;
20841
+ var m = (tb * sp / d - 0.5 * tb) / (1 + r2);
20842
+ var n = (sp / r2 + 0.5 * d) / (1 + 1 / r2);
20843
+ xy.x = cos(lp.phi);
20844
+ xy.x = sqrt(m * m + xy.x * xy.x / (1 + r2));
20845
+ xy.x = M_HALFPI * (m + (lp.lam < 0. ? -xy.x : xy.x));
20846
+ xy.y = sqrt(n * n - (sp * sp / r2 + d * sp - 1) / (1 + 1 / r2));
20847
+ xy.y = M_HALFPI * (n + (lp.phi < 0. ? xy.y : -xy.y));
20848
+ }
20849
+ }
20850
+ }
20851
+
20852
+
20793
20853
  pj_add(pj_nsper, 'nsper', 'Near-sided perspective', 'Azi, Sph\nh=');
20794
20854
  pj_add(pj_tpers, 'tpers', 'Tilted perspective', 'Azi, Sph\ntilt= azi= h=');
20795
20855