{"id":"e2cd77a0e9a30840","slug":"cordiform-map-projections","trashed":false,"description":"","likes":9,"publish_level":"public","forks":0,"fork_of":null,"has_importers":false,"update_time":"2019-02-18T22:21:18.921Z","first_public_version":null,"paused_version":null,"publish_time":"2019-02-14T16:08:22.469Z","publish_version":1943,"latest_version":1943,"thumbnail":"6fd91ae171c62d9d8e7cc8beafeefdb9cfccddb3244b346f7d90d9501676bc8b","default_thumbnail":"6fd91ae171c62d9d8e7cc8beafeefdb9cfccddb3244b346f7d90d9501676bc8b","roles":[],"sharing":null,"owner":{"id":"da2a32154638c457","avatar_url":"https://avatars.observableusercontent.com/avatar/9cf371db2e1a2b4ed5f4a9783b6954cf7aeb2a88c56d4054c96ef360cf85ef89","login":"fil","name":"Fil","bio":"Vocateur.","home_url":"https://visionscarto.net/","type":"team","tier":"starter_2024"},"creator":{"id":"45a379fcfcb14253","avatar_url":"https://avatars.observableusercontent.com/avatar/9cf371db2e1a2b4ed5f4a9783b6954cf7aeb2a88c56d4054c96ef360cf85ef89","login":"fil","name":"Fil","bio":"Vocateur.","home_url":"https://visionscarto.net/","tier":"pro"},"authors":[{"id":"45a379fcfcb14253","avatar_url":"https://avatars.observableusercontent.com/avatar/9cf371db2e1a2b4ed5f4a9783b6954cf7aeb2a88c56d4054c96ef360cf85ef89","name":"Fil","login":"fil","bio":"Vocateur.","home_url":"https://visionscarto.net/","tier":"pro","approved":true,"description":""}],"collections":[{"id":"8d7a994f5bc56ea2","type":"public","slug":"projections","title":"Projections","description":"flattening the earth","update_time":"2018-09-13T15:56:48.538Z","pinned":true,"ordered":false,"custom_thumbnail":null,"default_thumbnail":"813b390d11e566b160a43bec3a7363fb25faa754e72379c62875ba580674d487","thumbnail":"813b390d11e566b160a43bec3a7363fb25faa754e72379c62875ba580674d487","listing_count":137,"parent_collection_count":0,"owner":{"id":"da2a32154638c457","avatar_url":"https://avatars.observableusercontent.com/avatar/9cf371db2e1a2b4ed5f4a9783b6954cf7aeb2a88c56d4054c96ef360cf85ef89","login":"fil","name":"Fil","bio":"Vocateur.","home_url":"https://visionscarto.net/","type":"team","tier":"starter_2024"}}],"files":[],"comments":[],"commenting_lock":null,"suggestion_from":null,"suggestions_to":[],"version":1943,"title":"Cordiform map projections 💛💗💖","license":null,"copyright":"","nodes":[{"id":0,"value":"md`# Cordiform map projections 💛💗💖`","pinned":false,"mode":"js","data":null,"name":null},{"id":216,"value":"md`Cordiform means “in the shape of a heart”.\n\nOronce Finé made this [gracious map](#orontius) in 1534/36. The same projection was reused by John Bartholomew & sons in this [very beautiful page of their 1913 atlas](#bartholomew).\n\nThe projection was invented circa 1500 by Johann Stab (or Stabius, ~1460–1522†) from a map designed by [Albrecht Dürer](https://fr.wikipedia.org/wiki/Albrecht_D%C3%BCrer), then popularized by Johannes Werner (1468–1522†), and is now known as the Werner projection. However, as John Snyder details (p.34 of “Flattening the earth”), there are _three_ different cordiform Stab–Werner projections. The Wikipedia page about the [Werner projection](https://en.wikipedia.org/wiki/Werner_projection) is rather confusing, so let’s try to get this right.\n\nHappy Valentine's day!`","pinned":false,"mode":"js","data":null,"name":null},{"id":1907,"value":"md`${third([-10, 0])}${third([-160, 0])}`","pinned":false,"mode":"js","data":null,"name":null},{"id":1520,"value":"md`## The first Stab–Werner projection\n\nThe _first_ Stab–Werner projection shows just one hemisphere. Each parallel is mapped on a circle centered on the North pole, with a scale that varies with latitude in order to keep the lengths proportional. The half-equator is mapped to a half-circle.`","pinned":false,"mode":"js","data":null,"name":null},{"id":1388,"value":"first = r =>\n  map(\n    d3\n      .geoProjection(\n        stabWernerRaw(halfPi /* map half-equator to a half-circle */, 90)\n      )\n      .rotate(r)\n      .preclip(\n        d3.geoClipPolygon({\n          type: \"Polygon\",\n          coordinates: [\n            [[-90, -90], [-90, 90], [90 - 1e-4, 90], [90 - 1e-4, 0], [-90, -90]]\n          ]\n        })\n      )\n      .precision(0.1)\n  )","pinned":false,"mode":"js","data":null,"name":null},{"id":1528,"value":"md`${first([180 - 70.01, 0])} ${first([0 - 70.01, 0])}`","pinned":false,"mode":"js","data":null,"name":null},{"id":1518,"value":"md`## The second Stab–Werner projection\n\nIn D3, the Werner projection is described as “[a limiting form of the Bonne projection, having its standard parallel at ±90°](https://bl.ocks.org/mbostock/a7ae83252305ed4d54d4).” This is in fact the _second_ Stab–Werner projection.\n`","pinned":false,"mode":"js","data":null,"name":null},{"id":1535,"value":"second = r =>\n  map(\n    d3\n      .geoProjection(stabWernerRaw(1, 90)) // identical to d3.geoBonneRaw(halfPi)\n      .rotate(r)\n      .precision(0.1)\n  )","pinned":false,"mode":"js","data":null,"name":null},{"id":572,"value":"md`${second([-10, 0])}`","pinned":false,"mode":"js","data":null,"name":null},{"id":1410,"value":"md`## The third Stab–Werner projection\n\nThe _third_ Stab–Werner projection expands the equator more, but that expansion has to stop at 60°N, to avoid overlaps — the polar region is then drawn with an azimuthal projection. A specific [clipPolygon](https://github.com/d3/d3-geo-polygon) is necessary to avoid an ugly line going to the pole when drawing the Sphere. This one is my favorite.`","pinned":false,"mode":"js","data":null,"name":null},{"id":218,"value":"third = r =>\n  map(\n    d3\n      .geoProjection(stabWernerRaw(1 + 1 / 21, 60))\n      .rotate(r)\n      .preclip(\n        d3.geoClipPolygon({\n          type: \"Polygon\",\n          coordinates: [\n            [\n              [0, -90],\n              [-180 + 1e-4, 0],\n              [-180 + 1e-4, 60],\n              [-180, 60],\n              [180 - 1e-4, 60],\n              [180 - 1e-4, 0],\n              [0, -90]\n            ]\n          ]\n        })\n      )\n      .precision(0.1)\n  )","pinned":false,"mode":"js","data":null,"name":null},{"id":1872,"value":"md`${third([-10.01, 0])}`","pinned":false,"mode":"js","data":null,"name":null},{"id":1878,"value":"md`Finally, here are the second and third projections, displayed with the historical [Ferro Meridian](https://en.wikipedia.org/wiki/Ferro_Meridian) so we can compare with Oronce Finé’s map below:`","pinned":false,"mode":"js","data":null,"name":null},{"id":1876,"value":"md`${second([17 + 40 / 60, 0])} ${third([17 + 40 / 60, 0])}`","pinned":false,"mode":"js","data":null,"name":null},{"id":1856,"value":"md`----\n\n_ define the Stab-Werner raw projection_`","pinned":false,"mode":"js","data":null,"name":null},{"id":1583,"value":"function stabWernerRaw(ratio, max) {\n  /*\n   * simplified Bonne, with phi0 = halfPi ;\n   * https://github.com/d3/d3-geo-projection/blob/master/src/bonne.js\n   */\n  function r(lambda, phi) {\n    const rho = halfPi - phi,\n      e = rho ? (lambda * cos(phi)) / rho : rho;\n    return [rho * sin(e), -rho * cos(e)];\n  }\n  r.invert = function(x, y) {\n    var rho = sqrt(x ** 2 + y ** 2),\n      phi = halfPi - rho;\n    return [(rho / cos(phi)) * atan2(x, -y), phi];\n  };\n\n  /* azimuthal equidistant for the pole part */\n  const az = d3\n    .geoAzimuthalEquidistant()\n    .rotate([0, -90])\n    .translate([0, 0])\n    .scale(1);\n\n  /* mix */\n  function forward(lambda, phi) {\n    if (phi < max * radians) {\n      lambda *= ratio;\n      return r(lambda, phi);\n    } else {\n      const p = az([lambda * degrees, phi * degrees]);\n      return [p[0], -p[1]];\n    }\n  }\n\n  function invert(x, y) {\n    let i = az.invert([x, -y]);\n    if (i[1] < max) {\n      i = r.invert(x, y);\n      i[0] /= ratio;\n    } else {\n      i[0] *= radians;\n      i[1] *= radians;\n    }\n    return [i[0], i[1]];\n  }\n\n  forward.invert = invert;\n\n  return forward;\n}","pinned":true,"mode":"js","data":null,"name":null},{"id":1866,"value":"orontius = md`<figure>\n  <img src=\"https://visionscarto.neocities.org/cordiforms/orontius.jpg\">\n  <figcaption>Source: https://fr.wikipedia.org/wiki/Carte_d%27Oronce_Fine</figcaption>\n</figure>`","pinned":false,"mode":"js","data":null,"name":null},{"id":1896,"value":"bartholomew = md`<figure>\n  <img src=\"https://visionscarto.neocities.org/cordiforms/bartholomew.jpg\">\n  <figcaption>Source: https://mdl.library.utoronto.ca/collections/scanned-maps/atlas-world ([PDF](http://maps.library.utoronto.ca/datapub/digital/G_1019_1913/G_1019_1913_large.pdf)).</figcaption>\n</figure>`","pinned":false,"mode":"js","data":null,"name":null},{"id":1754,"value":"md`----\n_boring zone_`","pinned":false,"mode":"js","data":null,"name":null},{"id":6,"value":"import {abs, epsilon, sign, degrees, radians, atan2, cos, halfPi, pi, sin, sqrt} from \"@fil/math\"","pinned":false,"mode":"js","data":null,"name":null},{"id":1408,"value":"w = Math.max(400, width / 2 - 10)","pinned":false,"mode":"js","data":null,"name":null},{"id":22,"value":"import {map} with {w as width} from \"@fil/base-map\"","pinned":false,"mode":"js","data":null,"name":null},{"id":20,"value":"d3 = require(\"d3-array\", \"d3-geo\", \"d3-geo-projection\", \"d3-fetch\", \"d3-geo-polygon\")","pinned":false,"mode":"js","data":null,"name":null},{"id":1939,"value":"import {signature} from \"@fil/signature\"","pinned":false,"mode":"js","data":null,"name":null},{"id":1942,"value":"signature","pinned":false,"mode":"js","data":null,"name":null}],"resolutions":[],"schedule":null,"last_view_time":null}