2016-08-09 85 views
2

Ich versuche ein Kartogramm mit cartogram.js und d3.js zu erstellen. Ich habe die Beispiele in der Datei cartogram.js repo und here verwendet, um ein Skript zu erstellen, das eine Weltkarte in einem SVG mit der Projektion d3.geo.mercator() erzeugt. Jetzt versuche ich, die Karte mit der Bibliothek cartogram.js zu verzerren, aber ich sind die folgenden Fehler erhalten:"Fehler: <path> Attribut d: Erwartete Anzahl"

d3.js:8756 Error: <path> attribute d: Expected number, "MNaN,NaNLNaN,NaNL…". 
    (anonymous function) @ d3.js:8756 
    tick @ d3.js:8956 
    (anonymous function) @ d3.js:8936 
    d3_timer_mark @ d3.js:2166 
    d3_timer_step @ d3.js:2147 

Hier ist mein Code, den ich mit der Karte verzerren:

var dataLoaded = new Event("dataLoaded"), 
svg = d3.select("svg"), 
proj = d3.geo.mercator(), 
path = d3.geo.path() 
    .projection(proj), 
countries = svg.append("g") 
    .attr("id", "countries") 
    .selectAll("path"), 
carto = d3.cartogram() 
    .projection(proj) 
    .properties(function(d) { 
     return d.properties 
    }), 
mapData = d3.map(), 
geometries, 
topology 

function init() { 
    d3.csv("data/data.csv", function(data) { 
     data.forEach(function (d) { 
      mapData.set(d.COUNTRY, d.VALUE) 
     }) 
    }) 

    d3.json("data/world.json", function(data) { 
     topology = data 
     geometries = topology.objects.countries 

     var features = carto.features(topology, geometries) 

     countries = countries 
      .data(features) 
      .enter() 
      .append("path") 
      .attr("fill", function (e) { 
       return "#000000" 
      }) 
      .attr("d", path) 

     document.dispatchEvent(dataLoaded) 
    }) 
} 

document.addEventListener("dataLoaded", function() { 
    $("#container").css("visibility", "visible").hide().fadeIn("fast") 
    $("header").css("visibility", "visible").hide().fadeIn("slow") 

    carto.value(function(d) { 
     return +mapData.get(d.properties.name) 
    }) 

    countries.data(carto(topology, geometries).features) 

    countries.transition() 
     .duration(750) 
     .attr("d", carto.path); 
}) 

init() 

und die CSV-Datei verwenden, um die Daten ich mag enthält die Karte zu verzerren:

COUNTRY,VALUE 
Afghanistan,90 
Albania,390 
Algeria,90 
Andora,110 
Angola,10 
Antigua,2400 
Argentina,320 
Armenia,40 
Australia,6600 
Austria,1300 
Axerbaijan,0 
Bahamas,1900 
Bahrain,90 
Bangladesh,50 
Barbados,8100 
Belarus,20 
Belgium,260 
Belize,480 
Benin,0 
Bhutan,170 
Bolivia,90 
Bosnia,70 
Botswana,110 
Brazil,1300 
Brunei,40 
Bulgaria,3600 
Burkina Faso,0 
Burundi,0 
Cabo Verde,0 
Cambodia,720 
Cameroon,10 
Canada,4400 
Central African Republic,0 
Chad,10 
Chile,320 
China,1600 
Combodia,0 
Comoros,10 
Congo,20 
Costa Rica,2900 
Cote d'Ivoire,0 
Croatia,9900 
Cuba,14800 
Cyprus,8100 
Czech Republic,70 
Denmark,320 
Dijbouti,0 
Dominica,0 
Dominican Republic,4400 
Ecuador,90 
Egypt,6600 
El Salvador,10 
Equatorial Guinea,0 
Eritrea,10 
Estonia,110 
Ethiopia,70 
Fiji,1900 
Finland,720 
France,2900 
Gabon,10 
Gambia,2400 
Georgia,70 
Germany,880 
Ghana,210 
Greece,14800 
Grenada,720 
Guatemala,40 
Guinea,0 
Guinea - Bissau,0 
Guyana,50 
Haiti,90 
Honduras,110 
Hungary,170 
Iceland,8100 
India,2900 
Indonesia,390 
Iran,390 
Iraq,140 
Ireland,1900 
Israel,590 
Italy,9900 
Jamaica,6600 
Japan,3600 
Jordan,480 
Kazakhstan,40 
Kenya,1000 
Kiribati,10 
Kosovo,10 
Kuwait,40 
Kyrgyzstan,10 
Laos,70 
Latvia,110 
Lebanon,70 
Lesotho,0 
Liberia,10 
Libya,30 
Liechtenstein,10 
Lithuania,70 
Luxembourg,50 
Macedonia,70 
Madagascar,0 
Malawi,40 
Malaysia,1300 
Maldives,12100 
Mali,40 
Malta,12100 
Marshall Islands,10 
Mauritania,10 
Mauritius,6600 
Mexico,18100 
Micronesia,20 
Moldova,20 
Monaco,590 
Mongolia,110 
Montenegro,880 
Morocco,4400 
Mozambique,90 
Myanmar,90 
Namibia,210 
Nauru,10 
Nepal,0 
Netherlands,50 
New Zealand,1900 
Nicaragua,50 
Niger,10 
Nigeria,90 
North Korea,390 
Norway,1600 
Oman,590 
Pakistan,110 
Palau,50 
Palestine,10 
Panama,210 
Papua New Guinea,40 
Paraguay,10 
Peru,1000 
Philippines,590 
Poland,880 
Portugal,12100 
Qatar,210 
Romania,320 
Russia,480 
Rwanda,20 
Saint Kitts and Nevis,0 
Saint Lucia,90 
Saint Vincent and the Grenadines,0 
Samoa,90 
San Marino,70 
Sao Tome and Principe,10 
Saudi Arabia,110 
Senegal,70 
Serbia,50 
Seychelles,1600 
Sierra Leone,20 
Singapore,880 
Slovakia,70 
Slovenia,390 
Solomon Islands,10 
Somalia,70 
South Africa,1900 
South Korea,140 
South Sudan ,0 
Spain,14800 
Sri Lanka,3600 
Sudan,20 
Suriname,10 
Sweden,720 
Switzerland,1300 
Syria,590 
Taiwan,50 
Tajikistan,10 
Tanzania,260 
Thailand,14800 
Timor-Leste,0 
Togo,10 
Tonga,50 
Trinidad and Tobago,140 
Tunisia,4400 
Turkey,9900 
Turkmenistan,10 
Tuvalu,30 
Uganda,50 
Ukraine,70 
United Arab Emirates,20 
United Kingdom,50 
United States of America,3600 
Uruguay,50 
Uzbekistan,30 
Vanuatu,30 
Vatican City,30 
Venezuela,170 
Vietnam,2400 
Yemen,20 
Zambia,90 
Zimbabwe,70 

Ich habe keine Erfahrung mit d3.js vor diesem Projekt, daher würde ich mich über jede Rückmeldung/Anleitung freuen, die Sie mir geben können.

Ich benutze Version 3.5.17 von d3, fyi.

Danke.


UPDATE - 2016.09.08 15.22 BST

Wie pro @ Mark Vorschlag habe ich d3-queue umgesetzt, obwohl das Problem weiterhin besteht. Wenn ich mit dieser Implementierung etwas falsch gemacht habe, wäre ich dankbar für jede Einsicht, die mir jemand geben kann! :)

var svg = d3.select("svg"), 
proj = d3.geo.mercator(), 
path = d3.geo.path() 
    .projection(proj), 
countries = svg.append("g") 
    .attr("id", "countries") 
    .selectAll("path"), 
carto = d3.cartogram() 
    .projection(proj) 
    .properties(function(d) { 
     return d.properties 
    }), 
queue = d3.queue() 
    .defer(csv) 
    .defer(json) 
    .awaitAll(ready), 
mapData = d3.map(), 
geometries, 
topology 

function json(callback) { 
    d3.json("data/world.json", function(data) { 
     topology = data 
     geometries = topology.objects.countries 

     var features = carto.features(topology, geometries) 

     countries = countries 
      .data(features) 
      .enter() 
      .append("path") 
      .attr("fill", function (e) { 
       return "#000000" 
      }) 
      .attr("d", path) 

     callback() 
    }) 
} 

function csv(callback) { 
    d3.csv("data/data.csv", function(data) { 
     data.forEach(function (d) { 
      mapData.set(d.COUNTRY, +d.VALUE) 
     }) 

     callback() 
    }) 
} 

function ready() { 
    $("#container").css("visibility", "visible").hide().fadeIn("fast") 
    $("header").css("visibility", "visible").hide().fadeIn("slow") 

    carto.value(function(d) { 
     if (mapData.has(d.properties.name)) { 
      return +mapData.get(d.properties.name) 
     } 
    }) 

    countries.data(carto(topology, geometries).features) 

    countries.transition() 
     .duration(750) 
     .attr("d", carto.path); 
} 

UPDATE 2 - 2016.09.08 18.05 BST

Hier ist die neueste Version des Skripts auf Plunker, die zum Testen verwendet werden können, mit freundlicher Genehmigung von @ Marke: http://plnkr.co/edit/iK9EZSIfwIXjIEBHhlep?p=preview

Es scheint, dass mein anfänglicher Fehler behoben wurde, obwohl das resultierende Kartogramm nicht korrekt angezeigt wird.


UPDATE 3 - 2016.10.08 20.45 BST

@ Mark Antwort half eine Menge meiner Fragen zu klären und ich hatte einen teilweise funktionierenden Kartogramm als Ergebnis jedoch das Problem zu beheben here detaillierte, regenerierte ich meine map-Datei mit dem --stitch-poles false Parameter und und dies nachdem ich mich noch einmal erhalte den folgenden Fehler:

d3.js:8756 Error: <path> attribute d: Expected number, "MNaN,NaNLNaN,NaNL…". 

@ Mark anfängliche Korrektur für diesen Fehler ist, Trotzdem bin ich ziemlich verwirrt, warum das wieder aufgetaucht ist. Sie können meinen neuesten Code here und meine neue Karte topojson Datei here sehen. Danke noch einmal.

+0

Wo ist 'carto.path' definiert? –

+1

@GerardoFurtado Innen cartogram.js 'carto.path = d3.geo.path() Vorsprung (null);.' –

+1

Both, 'd3.csv() 'und' d3.json()' sind asynchron . Wenn Sie sie sequentiell aufrufen, wie Sie es in 'init()' tun, werden sie weiterhin parallel ausgeführt, so dass Sie keine Kontrolle über die Reihenfolge haben, in der diese Funktionen zurückkehren. Meine Vermutung ist, dass das 'dataLoaded'-Ereignis ausgelöst wird, bevor' d3.csv() 'zurückkehrt, wodurch' mapData' nur spärlich gefüllt bleibt, wenn versucht wird, auf den Event-Handler zuzugreifen. – altocumulus

Antwort

1

Okay, ich mache Fortschritte. Es stellt sich heraus, dass nach der Festsetzung Ihrer .value Funktion der Grund, warum Sie nicht ein Catrogram bekommen, Ihre Werte sind zu unterschiedlich. Warum das cartogram.js wirft, bin ich nicht sicher, aber das Problem kann leicht durch die Einführung einer Skala gelöst werden.

mit Ihren Daten:

s = d3.scale.linear().range([1,100]).domain(d3.extent(data, function(d){ return +d.VALUE})); 

Und dann in Ihrem .value Accessor:

carto.value(function(d,i) { 
    if (mapData.has(d.properties.name)) { 
    return s(mapData.get(d.properties.name)); 
    } else { 
    return 1; 
    } 
}); 

Ach, aber alle Ihre Probleme sind nicht festgelegt. Es scheint, dass Länder, die die Projektion "umwickeln" (dh Russland und Fidschi), durch die von cartogram.js erzeugten Pfade verzerrt werden. Hier ist ein Fix, ausführlich diskutiert here

Unabhängig davon, here's what we've got so far.

+0

Danke! Ich habe eine neue Topojson-Datei erstellt (gefunden [hier] (https://gist.github.com/brks/e04f62bd9e96d65ca4a1b91913c6a889) basierend auf diesem Thread, obwohl ich wieder da bin, wo ich anfing, mit dem anfänglichen 'NaN'-Fehler, der wieder auftaucht und Ich habe keine Ahnung, warum das einzige, was ich änderte, war die JSON-Datei für die Karte. –

+0

Ich sollte hinzufügen, dass das Kartogramm funktionierte, bevor ich die Map-Datei wechselte. –

+0

Hier ist ein aktueller Plunker zu demonstrieren: http: // plnkr .co/edit/1kFV484rtIdSGLaMsdFb? p = Vorschau –