Flexible Wagner-Projektionen mit d3-geo – Seven Of Nine

by kartenprojektionen.de
Gehe zur englischen Version

Hinweis: In der englischen Version dieser Seite spreche ich vom customizable Wagner.
Im Deutschen habe ihn meistens den anpassbaren Wagner genannt – aber ich fand auch schon immer, dass das ein wenig holprig klingt. Aber was sollte ich sonst schreiben? »Individualisierbar«? »Parametrisierbar«? Maßgeschneidert ist eher das Ergebnis des, ääähm, »maßschneiderbaren« Entwurfs…
Das gefiel mir alles nicht, also hab ich mich entschieden, ihn den flexiblen Wagner zu nennen. Ist auch nicht viel besser, aber in meinen Augen und Ohren noch die angenehmste Variante. Vielleicht fällt mir ja irgendwann noch etwas richtig Gutes ein…

Zwischen 1932 und 1949 hat Karlheinz Wagner neun Kartennetzentwürfe vorgestellt. Jeder davon ist flexibel, d.h. gewisse Eigenschaften der Entwürfe können auf einfache an die Wünsche und Anforderungen des Benutzers angepasst werden. Leider sind die Möglichkeiten – abgesehen von ein paar Ausnahmen – in der kartografischen Literatur weitgehend ignoriert worden.

Die Projektionen, die heutzutage als Wagner I bis IX bekannt sind, sind lediglich Beispiele, ganz bestimmte Ausführungen, die Wagner als besonders vorteilhaft angesehen hat.
Hier zeige ich eine Möglichkeit, unter Verwendung von d3-geo-projection auf alle möglichen Ausführungen zuzugreifen.

Eine nähere Beschreibung der Funktionen und ihrer Parameter sowie die Gründe für die unterschiedliche Handhabung einzelner Parameter gibt es im Blogpost »Mehr Umbeziffern für d3-geo-projections«.

Allgemeine Informationen über Wagners Transformationsmethode gibt in einem Blogpost, im Begleittext zum Wagner Variations Generator (WVG-7) und im Artikel Das Umbeziffern.

Beispiele

Entschuldigung, aber die Beispiel-Seiten sind derzeit nur in Englisch verfügbar.
Ich denke (hoffe), das ist in diesem Fall entschuldbar – wenn Du ernsthaft mit d3-geo-projection arbeiten willst, musst Du dessen Dokumentation lesen können, und auch die gibt es nur in Englisch.
Bei Interesse an einer deutschen Version bitte ich um Rückmeldung (per E-Mail, siehe Impressum). Wenn ich feststelle, dass Bedarf besteht, kann ich mich ja noch an eine Übersetzung machen…

Die examples zeigen jeweils eine einzelne Ausführung der flexiblen Wagner-Projektionen, um einen ersten Eindruck zu vermitteln, was möglich ist. Dazu gibt es den Quellcode, der die Grafik generiert. Dies ist hoffentlich hilfreich für alle, die noch nie mit d3-geo-projection gearbeitet haben.

Im interactive demo kannst Du alle möglichen Ausführungen einer flexiblen Wagner-Projektion generieren, indem Du einfach ein paar Schieberegler betätigst.

Abgesehen vom flexiblen Wagner VII/VIII handelt es sich hierbei im VORSCHLÄGE.
Es könnte noch Fehler geben. Es kann noch Änderungen geben. Sie werden vielleicht nie Teil der offiziellen Distribution von d3-geo-projection werden.

Source code

Der flexible Wagner VII/VIII ist bereits Bestandteil aktueller d3-geo-projection Versionen.

Gesamter Quelltext: d3-geo-projection.umbeziffern.rfc.js anzeigen/runterladen
 
Hier folgt ein Auszug, der nur die neuen Abschnitte zeigt:

        // wagner2 und wagner3 by Tobias Jung
///////////////////////////////////////////////////////////
function wagnerEquallySpacedParallelsRaw(poleline, parallels, ratio, xfactor, parentProjection) {
    // sanitizing the input values
    // poleline and parallels may approximate but never equal 0.
    // making the min value even smaller (e.g. 0.0001 or 1e-10) will render bugs.
    poleline = max(poleline, 0.001);
    parallels = max(parallels, 0.001);
    // poleline must be <= 90; parallels may approximate but never equal 180
    poleline = min(poleline, 90);
    parallels = min(parallels, 179.99);
    // ratio is max. 100, min 1
    ratio = min(ratio, 600);
    ratio = max(ratio, 1);
    
    var p  = ratio / 100,
        ca = xfactor / 100,
        k  = sqrt( (p * poleline) / parallels ),
        cm = poleline/90,
        cn = parallels/180,
        cx = k / (  sqrt( cm * cn ) ),
        cy = 1 / (k*sqrt( cm * cn ) );
    
    
        // console.log("Wagner's notation");
        // console.log("cm  = " + cm);
        // console.log("cn  = " + cn);
        // console.log("cx = " + cx);
        // console.log("cy = " + cy);
        // // console.log("k  = " + k);
        // // console.log("p  = " + 2 * pow(k, 2) * cn/cm);
        // console.log("ca  = " + ca);
        
        console.log('ca = ' + ca + ', cm = ' + cm +', cn = ' + cn +', cx = ' + cx +', cy = ' + cy);
    
    // ready to call the acutal formula:
    return wagnerEquallySpacedParallelsFormula(ca, cm, cn, cx, cy, parentProjection);
}

function wagnerEquallySpacedParallels() {
  var poleline = 70,
      parallels = 50,
      ratio = 200,
      xfactor = 100,
      parentProjection = 9,
      mutate = d3Geo.geoProjectionMutator(wagnerEquallySpacedParallelsRaw),
      projection = mutate(poleline, parallels, ratio, xfactor, parentProjection);

     
    projection.poleline = function(_) {
      return arguments.length ? mutate(poleline = +_, parallels, ratio, xfactor, parentProjection) : poleline;
    };

    projection.parallels = function(_) {
      return arguments.length ? mutate(poleline, parallels = +_, ratio, xfactor, parentProjection) : parallels;
    };
    projection.ratio = function(_) {
      return arguments.length ? mutate(poleline, parallels, ratio = +_, xfactor, parentProjection) : ratio;
    };
    projection.xfactor = function(_) {
      return arguments.length ? mutate(poleline, parallels, ratio, xfactor = +_, parentProjection) : xfactor;
    };
    projection.parentProjection = function(_) {
      return arguments.length ? mutate(poleline, parallels, ratio, xfactor, parentProjection = +_) : parentProjection;
    };
  
return projection
  .scale(112.314);
}


function wagnerEquallySpacedParallelsFormula(ca, cm, cn, cx, cy, parentProjection) {
    if (parentProjection == 3) {
      var pp = d3Geo.geoSinusoidalRaw;
    } else if (parentProjection == 6) {
      var pp = d3Geo.geoApian2Raw;
    } else if (parentProjection == 9) {
      var pp = d3Geo.geoAzimuthalEquidistantRaw;
    } else if (parentProjection == 10) {
        var pp = d3Geo.geoVanDerGrinten4Raw;
    } else if (parentProjection == 11) {
        var pp = d3Geo.geoPolyconicRaw;
    } else if (parentProjection == 12) {
        var pp = d3Geo.geoNicolosiRaw;
    } else if (parentProjection == 13) {
        var pp = d3Geo.geoRectangularPolyconicRaw(0);
    }
    

  function forward(lambda, phi) {
    var xy = pp(cn * lambda, cm * phi),
        x  = ca * cx * xy[0],
        y  = cy * xy[1];
        return [x, y];
  }

  forward.invert = function(x, y) {
    var lambda_phi = pp.invert(x / (ca * cx), y / cy),
        lambda     = (1 / cn) * lambda_phi[0],
        phi        = (1 / cm) * lambda_phi[1];
    return [lambda, phi];
  };

  return forward;
}


// wagner ii
function wagner2Formula(cx, cy, m1, m2) {
    function forward(lambda, phi) {
        phi = asin(m1 * sin(m2 * phi));
        var x = cx * lambda * cos(phi),
        y = (cy * phi);
            
        return [ x, y ];
    }
    
    forward.invert = function(x, y) {
    	y /= cy;
        
        return [
            x / (cx * cos(y)),
            asin(sin(y) / m1) / m2
        ];
    };
    
    return forward;
}

function wagner2Raw(poleline, inflation, ratio) {
    // 60 is always used as reference parallel
    var phi1 = pi / 3;
    // 0 < poleline < 1
    poleline = max(poleline, epsilon);
    poleline = min(poleline, 1 - epsilon);

    ratio = 100/ratio;
    ratio = max(ratio, 0.1);
    // ratio isn't really limited to 3,
    // but this is about where it stops to make any sense...
    ratio = min(ratio, 3);
    inflation = inflation/100 + 1;
    // 1 <= inflation < 2
    inflation = max(inflation, 0);
    inflation = min(inflation, 2 - epsilon);
    
    var m2 = (acos(inflation * cos(phi1))) / phi1,
    m1 = sqrt(1 - pow(poleline, 2)) / sin(m2 * (pi / 2)),
    n = (asin(sqrt(1-pow(poleline, 2)))) / (ratio * pi),
    cx = n / sqrt(n * m1 * m2),
    cy = cx / n;
    
    return wagner2Formula(cx, cy, m1, m2);
}

function wagner2() {
  // default values generate wagner2
  var poleline = 0.5,
      inflation = 20,
      ratio = 200,
    mutate = d3Geo.geoProjectionMutator(wagner2Raw),
    projection = mutate(poleline, inflation, ratio);
    
  
  projection.poleline = function(_) {
    return arguments.length ? mutate(poleline = +_, inflation, ratio) : poleline;
  };

  projection.inflation = function(_) {
    return arguments.length ? mutate(poleline, inflation = +_, ratio) : inflation;
  };
  projection.ratio = function(_) {
    return arguments.length ? mutate(poleline, inflation, ratio = +_) : ratio;
  };

  return projection
    .scale(150);
}

// wagner iii (alternative approach)
function wagner3(poleline, ratio, phi0) {
    // default values render the original wagner iii
    var poleline = 0.5,
    ratio = 200,
    phi0 = 0;
    
    var mutate = d3Geo.geoProjectionMutator(wagner3Raw),
    projection = mutate(poleline, ratio, phi0);
    
    projection.poleline = function(_) {
      return arguments.length ? mutate(poleline = +_, ratio, phi0) : poleline;
    };
    projection.ratio = function(_) {
      return arguments.length ? mutate(poleline, ratio = +_, phi0) : ratio;
    };
    projection.phi0 = function(_) {
      return arguments.length ? mutate(poleline, ratio, phi0 = +_) : phi0;
    };
    
  return projection
    .scale(176.84);
}

function wagner3Raw(poleline, ratio, phi0) {
    ratio = ratio/100;
    var cm = (2/pi) * acos(poleline), 
    cn = (ratio*cm)/2,
    cy = cn / sqrt(cm*cn),
    phi0 = phi0 * radians,
    cosPhi = cos(phi0),
    ca = cos(phi0) / ( cy * cos(cm*phi0)),
    cx = cm / sqrt(cm*cn);
    
    return wagner3Formula(cx, cy, ca, cm);
}

function wagner3Formula(cx, cy, ca, cm) {
    function forward(lambda, phi) {
        var y = cx * phi,
        x = ca * cy * lambda * cos(cm*phi);
        return [x,y];
    }
    
    forward.invert = function(x, y) {
        y /= cx;
        return [
            x / (cy * cos(cm*y) * ca),
            y
        ];
    }
    
    
    return forward;
}

function apian2Raw(lambda, phi) {
  return [lambda * sqrt(1 - pow(phi / halfPi, 2)), phi];
}

apian2Raw.invert = function(x, y) {
  return [x / sqrt(1 - pow(y / halfPi, 2)), y];
};

function apian2() {
  return d3Geo.geoProjection(apian2Raw)
      .scale(112.314);
}


// NOTE: The following function renders Frank Canters' optimization of Wagner IX.
// Actually it is not needed, because you can render this projection with:
// d3.geoWagnerEquallySpacedParallels().parentProjection(9).poleline(67.131).parallels(86.562).ratio(206.7978).xfactor(80.7486)
// However, Canters used a different mathematical approach which is reproduced here.
// If you're interested in the mathematics behind map projections, you might enjoy seeing his approach,
// but I guess it's not necassary to add this to d3-geo-projections.
function cantersW09Raw(cm, cn, k1, k2) {
    var k  = k2,
        ca = k1 / k2,
        p  = 1/(2 * k1 * k2) * cm/cn,
        cx = k / (  sqrt( cm * cn ) ),
        cy = 1 / (k*sqrt( cm * cn ) );
    
    // console.log("Canters's notation");
    // console.log("m  = " + cm);
    // console.log("n  = " + cn);
    // console.log("k1 = " + k1);
    // console.log("k2 = " + k2);
    // console.log("p  = " + p);
    // console.log("");
    // console.log("Wagner's notation");
    // console.log("m  = " + cm);
    // console.log("n  = " + cn);
    // console.log("Cx = " + cx);
    // console.log("Cy = " + cy);
    // console.log("k  = " + k);
    // console.log("p  = " + 2 * pow(k, 2) * cn/cm);
    // console.log("a  = " + ca);
    
    // ready to call the actual formula:
    return wagnerEquallySpacedParallelsFormula(ca, cm, cn, cx, cy, 9);
}

function cantersW09() {
  // default values generate the original Canters W09
  var cm = 0.7459,
      cn = 0.4809,
      k1 = 1.0226,
      k2 = 1.2664,
      mutate = d3Geo.geoProjectionMutator(cantersW09Raw),
      projection = mutate(cm, cn, k1, k2);
  
  projection.cm = function(_) {
    return arguments.length ? mutate(cm = +_, cn, k1, k2) : cm;
  };
  projection.cn = function(_) {
    return arguments.length ? mutate(cm, cn = +_, k1, k2) : cn;
  };
  projection.k1 = function(_) {
    return arguments.length ? mutate(cm, cn, k1 = +_, k2) : k1;
  };
  projection.k2 = function(_) {
    return arguments.length ? mutate(cm, cn, k1, k2 = +_) : k2;
  };        
    

License

Released under the GNU General Public License, version 3.

kartenprojektionen.de: Startseite · Impressum · Datenschutz