回転楕円体面上（地球表面上）の測地線

こんにちは。
[geographiclib](http://geographiclib.sourceforge.net) を使うと[回転楕円体面上の測地線](https://en.wikipedia.org/wiki/Geodesics_on_an_ellipsoid)を描けます（緑色の線）。[対蹠点](https://ja.wikipedia.org/wiki/対蹠点)付近を拡大すると真球の場合とは異なる特徴が見られます。始点はドラッグして移動できます。

<img width="753" alt="geodesic.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/54617/e3252d9f-1d50-8671-ec73-0d3d637b7b51.jpeg">

```geodesic.html
<!DOCTYPE html>
<html>
<head>
<title>geodesic</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<style type="text/css">
html, body { height: 100%; margin: 0; padding: 0;}
#map_canvas { height: 100%;}
</style>
<script type="text/javascript" src="http://geographiclib.sourceforge.net/scripts/geographiclib.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?language=ja&key=YOUR_ID"></script>
<script>
var pars = {'lon': 139.731674, 'lat': 35.701917, 'zoom': 2};
var nAng = 32, nDist = 40, KM = 1000.0, dDist = 500*KM;
var map, pointMarker, paths = new google.maps.MVCArray();
var geod = GeographicLib.Geodesic.WGS84;

function init_google_map() {
    var mapOptions = {
        zoom: pars['zoom'],
        center: new google.maps.LatLng(pars['lat'],pars['lon']),
        mapTypeControl: false,
        scaleControl: true,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
    pointMarker = new google.maps.Marker({
        position: new google.maps.LatLng(pars['lat'],pars['lon']),
        draggable: true,
        icon: {
            path: google.maps.SymbolPath.CIRCLE,
            scale: 3
        },
        map: map
    });
    update();
    google.maps.event.addListener(pointMarker, 'dragend', function() {
        update();
    });
}

function update() {
    var mAng = 2, mDist = 32;
    var poly = new Array, myPoint = pointMarker.getPosition();
    var circles = [], lines = [];
    for (var i=0; i< nAng; i++) {
        for (var d = 1,  poly = []; d <= nDist*mDist; d++) {
            poly.push(point(myPoint, i*360/nAng, d*dDist/mDist))
        }
        lines.push(poly);
    }
    for (var i=1; i<= nDist; i++) {
        for (var ang = 0, poly = []; ang <= 360*mAng; ang++) {
            poly.push(point(myPoint, ang/mAng, dDist*i))
        }
        circles.push(poly);
    }
    redraw(circles, lines);
}

function point(center, ang, dist) {
    var vals = geod.Direct(center.lat(), center.lng(), ang, dist);
    return [vals.lat2, vals.lon2];
}

function redraw(circles, lines) {
    paths.forEach(function(p, i){
        p.setMap(null);
    });
    paths.clear();

    circles.forEach(function(data, i){
        var c = new google.maps.Polyline({
            path: makePath(data),
            strokeColor: "#f00080",
            strokeOpacity: 0.3,
            strokeWeight: 1,
            geodesic: true,
            map: map
        });
        paths.push(c);
    });
    
    lines.forEach(function(data, i){
        var c = new google.maps.Polyline({
            path: makePath(data),
            strokeColor: "#00a000",
            strokeOpacity: 0.4,
            strokeWeight: 1,
            geodesic: true,
            map: map
        });
        paths.push(c);
    });
}

function makePath(data) {
    var path = [];
    data.forEach(function(x) {
        path.push({lat: x[0], lng: x[1]});
    });
    return path;
}
</script>
</head>
<body onload="init_google_map()" >
<div id="map_canvas"></div>
</body>
</html>
```