D3 für Karten --- In welchem ​​Stadium können Daten in das Geo importiert werden?

12

Ich möchte mit D3, a la, ein World Choropleth für die Anzeige abbilden:

Ich möchte einen Datensatz anzeigen, der mit ISO-Alpha-3-Schlüsseln verschlüsselt ist. So...

danger.csv
iso,level
AFG,100
ALB,0
DZA,12

etc.

Wenn ich den Anweisungen auf topojson folge, weiß ich, dass ich ...

wget "http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/50m/cultural/ne_50m_admin_0_countries.zip"
unzip ne_50m_admin_0_countries.zip
ogr2ogr -f "GeoJSON" output_features.json ne_50m_admin_0_countries.shp -select iso_a3
topojson -o topo.json output_features.json --id-property iso_a3

um eine Weltkarte json zu erstellen, die von ISO3 identifiziert wurde.

Meine Frage ist: Zu welchem ​​Zeitpunkt im Workflow sollte ich die Daten von danger.csv mit den Geodaten zusammenführen? Ich hatte zuvor mit qGIS als GUI gearbeitet, aber wo / sollte / passiert die Zusammenführung? In der .shp? Nach dem Ogr2ogr? Dynamisch im Browser nach dem Topojson-Shrink (wie hier http://bl.ocks.org/mbostock/4060606 http://bl.ocks.org/mbostock/3306362 )?

Ich verstehe mich ziemlich gut mit Python, aber JavaScript ist noch ziemlich neu und ich kopiere und füge Bostock-Beispiele mehr ein, als dass ich dort ein generativer Programmierer bin.

(Ich habe auch eine verwandte, aber umfangreichere Nachverfolgung von Stackoverflow, die ich möglicherweise hier migrieren sollte: /programming/18604877/how-to-do-time-data-in-d3-maps )

Mittenchops
quelle
Ich habe mir nur die Beispiele von @ mbostock angesehen und festgestellt , dass es eines gibt, das sich speziell mit GeoJoins befasst .
RyanKDalton

Antworten:

11

Stellen Sie sich zwei Fragen:

  1. Werden Sie die Geografie für mehrere Datensätze wiederverwenden?

    Wenn Sie dieselbe Geografie mit mehreren Datensätzen verwenden, ist es sinnvoll, die Geografie und die Daten getrennt zu halten und sie im Client zusammenzuführen. Viele meiner Beispiele haben aus diesem Grund separate CSV- (oder TSV-) Dateien. Auf diese Weise kann TopoJSON für US-Bundesstaaten und Grafschaften oder auch für Länder der Welt wiederverwendet werden, anstatt für jedes Beispiel ein eigenes TopoJSON zu erstellen.

    Wenn Sie diese Geografie jedoch nur einmal verwenden , sollten Sie die Daten wahrscheinlich als Eigenschaften in die Geografie „backen“, um den Code zu vereinfachen. Dieser Ansatz ist einfacher, da Sie nur eine einzige Datei laden müssen (also keine queue.js ) und da die Daten als Eigenschaften jedes Features gespeichert werden, müssen Sie die Daten nicht im Client verknüpfen (also kein d3). Karte ).

    Randnotiz: TSV und CSV speichern Eigenschaften häufig wesentlich effizienter als GeoJSON und TopoJSON, da letztere die Eigenschaftsnamen für jedes Objekt wiederholen müssen. Die Dateigröße kann ein weiterer Grund sein, Ihre Daten in einer separaten Datei zu speichern und im Client zu verknüpfen.

  2. Sind Ihre Daten bereits an die Geografie gebunden (z. B. eine Eigenschaft Ihres Shapefiles)?

    Angenommen, Sie haben auf die erste Frage mit "Nein" geantwortet und möchten die Daten in die Geografie einbrennen (und nicht im Client). Wie Sie dies tun, hängt vom Format der Daten ab.

    Wenn Ihre Daten bereits eine Eigenschaft Ihres Shapefiles sind, können Sie topojson -psteuern, welche Eigenschaften in der generierten TopoJSON-Datei gespeichert werden. Sie können damit auch Eigenschaften umbenennen und in Zahlen umwandeln. Siehe Machen wir eine Karte für Beispiele.

    Befinden sich Ihre Daten in einer separaten CSV- oder TSV-Datei, geben Sie mit topojson -e (zusätzlich zu -p) eine externe Eigenschaftendatei an , die mit Ihren geografischen Features verknüpft werden kann. Cribbing das Beispiel aus dem Wiki, wenn Sie eine TSV-Datei wie folgt hatten:

    FIPS    rate
    1001    .097
    1003    .091
    1005    .134
    1007    .121
    1009    .099
    1011    .164
    1013    .167
    1015    .108
    1017    .186
    1019    .118
    1021    .099

    Mit -ekönnen Sie diese einer numerischen Ausgabeeigenschaft mit dem Namen "Arbeitslosigkeit" zuordnen:

    topojson \
      -o output.json \
      -e unemployment.tsv \
      --id-property=+FIPS \
      -p unemployment=+rate \
      -- input.shp

    Ein Beispiel für diesen Ansatz ist die Bevölkerung von Kentucky, bl.ocks.org/5144735 .

mbostock
quelle
2
Und hier stellte ich meine harten D3-Mapping-Fragen zum Stackoverflow anstelle von gis.stackexchange, weil ich dachte, dass dort mehr Fachwissen vorhanden ist - und dann beantwortet der Master selbst meine Frage hier. =) Nun, das sind 2 Dinge, die ich heute gelernt habe. Vielen Dank!
Mittenchops
3

Gute Frage. Eines der von Ihnen angegebenen Beispiele scheint den Trick zu tun, obwohl es schwer zu befolgen ist.

Sie werden feststellen, dass das Beispiel zwei externe Datendateien hat, us.json und unemployment.tsv . Sie können sich "unemployment.tsv" als Ihre Gefahr vorstellen. us.json sind die geografischen Merkmale, mit denen Sie Parameter aus danger.csv verknüpfen möchten. Letzterer, unemployment.tsv, hat idund rateFelder, in denen idderselbe ist wie der idin us.json.

Im Client mit D3 sollten Sie Ihre Daten und Funktionen zumindest anhand dieses Beispiels zusammenführen. Im Client wird die Arbeitslosenquote in diesem Beispiel mithilfe der Funktion d3.map () mit den County-Features verknüpft . Hier wird es initialisiert:

var rateById = d3.map();

Und hier ratewird folgendes abgebildet id:

queue()
    .defer(d3.json, "/mbostock/raw/4090846/us.json")
    .defer(d3.tsv, "unemployment.tsv", function(d) { rateById.set(d.id, +d.rate); })
    .await(ready);

Ich muss zugeben, dass ich nicht weiß, wofür es queue()ist, aber es ist nicht wichtig für diese Diskussion. Was wichtig ist, ist, dass das idFeld in jedem County-Feature durch die Arbeitslosigkeit ersetzt wird rate. Auf das kann ratejetzt über die gemeinsam genutzte ID zugegriffen werden id( BEARBEITEN: Wie @ blord-castillo hervorhebt, ist dies tatsächlich die Erzeugung eines neuen assoziativen Arrays oder Schlüssel-Hash, bei dem das dem zugeordnet rateistid ). Hier wird der ratezum Zwecke der Symbologie aufgerufen (hier stehen für jedes Quantil vordefinierte CSS-Klassen zur Verfügung):

...
.enter().append("path")
  .attr("class", function(d) { return quantize(rateById.get(d.id)); })
  .attr("d", path);

Wobei die quantize()Funktion den Namen der CSS-Klasse zurückgibt, die verwendet werden soll, um dieses Feature (Landkreis) basierend auf seiner Arbeitslosenquote zu formatieren, die jetzt im idFeld des Features definiert ist.

Arthur
quelle
1
Zu Ihrer
Information
Die Warteschlange ermöglicht das asynchrone parallele Laden der Datenquellen anstelle des seriellen Ladens.
Blord-Castillo
1
In diesem Beispiel ist rateById ein Schlüssel-Hash. An den Ländereinstellungen werden keine Änderungen vorgenommen, und die us.json-Daten bleiben unberührt. Stattdessen wird unemployment.tsv in einen Schlüssel-Hash namens 'rateById' umgewandelt. rateById.set () führt eine Schleife über unemployment.tsv, sodass für jede ID in unemployment.tsv (nicht in us.json) ein Schlüssel eingefügt wird und der Wert dieses Schlüssels auf das Feld rate für diese ID in unemployment.tsv gesetzt wird . Später wird rateById.get () aufgerufen, um den Hash zu verwenden, um die Arbeitslosenquote nach ID zu ermitteln. Mit diesem Wert wird der Stil für die Features "us.json" festgelegt und anschließend verworfen.
Blord-Castillo
Warum ersetzt / ersetzt / die ID mit der Rate, anstatt sie als Attribut an eine andere Stelle anzuhängen? Dies scheint die spätere Auswahl zu erschweren.
Mittenchops
1
Die ID wird nicht durch die Rate ersetzt. Es wird ein Nachschlage-Hash von ID zu Rate erstellt.
Blord-Castillo
2

Zunächst muss die erste Zeile Ihrer CSV eine durch Kommas getrennte Liste von Spaltennamen sein, um diese Methode verwenden zu können. Wenn dies nicht möglich ist, geben Sie einen Kommentar dazu ein und ich werde sehen, ob ich herausfinden kann, wie ich es d3.csv.parseRowsanstelle von verwenden soll d3.csv.parse. d3.csv.parsewird von der Prüferfunktion aufgerufen .defer(function, url, assessor).

Ich gehe davon aus, dass Ihre Datei jetzt so aussieht:

danger.csv
iso,level
AFG,100
ALB,0
DZA,12
...

Auf diese Weise können Sie einen Nachschlage-Hash von ISO3 bis zur Gefahrenstufe erstellen.

var dangerByISO3 = d3.map();
queue()
    .defer(d3.json, "url to topo.json")
    .defer(d3.csv, "url to danger.csv", function(d) {dangerByISO3.set(d.iso, +d.level);})
    .await(ready);
function ready(error, world) {
    //You now have world as your available topojson
    //And you have dangerByISO3 as your danger level hash
    //You can lookup a danger level by dangerByISO3.get(ISO3 code)
}

Code-Komplettlösung

var dangerByISO3 = d3.map();

Zuerst erstellen Sie ein d3.map () -Objekt, das als Ihr Schlüssel-Hash fungiert, und speichern dieses in der Variablen dangerByISO3.

queue()

Verwenden Sie die Warteschlange zum parallelen Laden.

.defer(d3.json, "url to topo.json")

Laden Sie Ihren Topojson als erstes Argument, das an die Funktion await übergeben wird (nach einem Fehler). Beachten Sie hier den Stil, bei dem es sich um eine verkettete Funktion handelt queue(), die jedoch in einer separaten Zeile aufgeführt ist (kein abschließendes Semikolon queue()).

.defer(d3.csv, "url to danger.csv", function(d) {dangerByISO3.set(d.iso, +d.level);})

Hier passieren zwei Dinge. Zunächst laden Sie danger.csv als zweites Argument, das an die Funktion await übergeben werden soll. Wie Sie weiter unten sehen werden, wird dieses Argument nicht verwendet. Stattdessen wird der Ladefunktion d3.csv ein Prüferargument bereitgestellt. Dieser Prüfer verarbeitet jede Zeile der CSV. In diesem Fall rufen wir die set-Funktion auf dangerByISO3 auf, sodass wir für jede Tastenkombination isoden levelals Wert für diesen Schlüssel festlegen . Die +d.levelNotation verwendet unary +, um den Wert von d.level in eine Zahl umzuwandeln.

.await(ready);

Sobald beide Datenquellen geladen sind, werden sie als zwei separate Argumente an die Funktion übergeben ready(). Das erste Argument für den Rückruf ist immer der erste aufgetretene Fehler. Wenn kein Fehler aufgetreten ist, wird null als erstes Argument übergeben. Das zweite Argument ist die erste Datenquelle (Ergebnis der ersten Aufgabe) und das dritte Argument ist die zweite Datenquelle (Ergebnis der zweiten Aufgabe).

function ready(error, world) {...}

Dies ist die Rückruffunktion ready(). Zuerst nehmen wir das errorArgument, das null sein sollte, wenn die beiden Ladetasks erfolgreich abgeschlossen wurden (Sie sollten tatsächlich eine Sprache hinzufügen, um Fehler abzufangen und zu behandeln). Als nächstes nehmen wir die Topojson-Daten als Objekt countries. Diese Daten sollten im Hauptteil der Funktion mit so etwas wie verarbeitet werden .data(topojson.feature(world,world.objects.countries).features). Da ready()es kein drittes Argument gibt, wird das Ergebnis der zweiten Aufgabe, unser csv, einfach verworfen. Wir haben es nur verwendet, um den Schlüssel-Hash zu erstellen und brauchten ihn danach nicht mehr.

Blord-Castillo
quelle
Ja, du hast recht, mein csv sieht tatsächlich aus wie ein wohlgeformter csv anstelle der unvorsichtigen Demo, die ich gepostet habe. =) Entschuldigung, ich werde das aktualisieren.
Mittenchops