merge.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import {object} from "./feature.js";
  2. import stitch from "./stitch.js";
  3. function planarRingArea(ring) {
  4. var i = -1, n = ring.length, a, b = ring[n - 1], area = 0;
  5. while (++i < n) a = b, b = ring[i], area += a[0] * b[1] - a[1] * b[0];
  6. return Math.abs(area); // Note: doubled area!
  7. }
  8. export default function(topology) {
  9. return object(topology, mergeArcs.apply(this, arguments));
  10. }
  11. export function mergeArcs(topology, objects) {
  12. var polygonsByArc = {},
  13. polygons = [],
  14. groups = [];
  15. objects.forEach(geometry);
  16. function geometry(o) {
  17. switch (o.type) {
  18. case "GeometryCollection": o.geometries.forEach(geometry); break;
  19. case "Polygon": extract(o.arcs); break;
  20. case "MultiPolygon": o.arcs.forEach(extract); break;
  21. }
  22. }
  23. function extract(polygon) {
  24. polygon.forEach(function(ring) {
  25. ring.forEach(function(arc) {
  26. (polygonsByArc[arc = arc < 0 ? ~arc : arc] || (polygonsByArc[arc] = [])).push(polygon);
  27. });
  28. });
  29. polygons.push(polygon);
  30. }
  31. function area(ring) {
  32. return planarRingArea(object(topology, {type: "Polygon", arcs: [ring]}).coordinates[0]);
  33. }
  34. polygons.forEach(function(polygon) {
  35. if (!polygon._) {
  36. var group = [],
  37. neighbors = [polygon];
  38. polygon._ = 1;
  39. groups.push(group);
  40. while (polygon = neighbors.pop()) {
  41. group.push(polygon);
  42. polygon.forEach(function(ring) {
  43. ring.forEach(function(arc) {
  44. polygonsByArc[arc < 0 ? ~arc : arc].forEach(function(polygon) {
  45. if (!polygon._) {
  46. polygon._ = 1;
  47. neighbors.push(polygon);
  48. }
  49. });
  50. });
  51. });
  52. }
  53. }
  54. });
  55. polygons.forEach(function(polygon) {
  56. delete polygon._;
  57. });
  58. return {
  59. type: "MultiPolygon",
  60. arcs: groups.map(function(polygons) {
  61. var arcs = [], n;
  62. // Extract the exterior (unique) arcs.
  63. polygons.forEach(function(polygon) {
  64. polygon.forEach(function(ring) {
  65. ring.forEach(function(arc) {
  66. if (polygonsByArc[arc < 0 ? ~arc : arc].length < 2) {
  67. arcs.push(arc);
  68. }
  69. });
  70. });
  71. });
  72. // Stitch the arcs into one or more rings.
  73. arcs = stitch(topology, arcs);
  74. // If more than one ring is returned,
  75. // at most one of these rings can be the exterior;
  76. // choose the one with the greatest absolute area.
  77. if ((n = arcs.length) > 1) {
  78. for (var i = 1, k = area(arcs[0]), ki, t; i < n; ++i) {
  79. if ((ki = area(arcs[i])) > k) {
  80. t = arcs[0], arcs[0] = arcs[i], arcs[i] = t, k = ki;
  81. }
  82. }
  83. }
  84. return arcs;
  85. }).filter(function(arcs) {
  86. return arcs.length > 0;
  87. })
  88. };
  89. }