D3 cho bản đồ --- ở giai đoạn nào để đưa dữ liệu vào địa lý?


12

Tôi muốn lập bản đồ thế giới để hiển thị với D3, a la:

Tôi có một bộ dữ liệu tôi muốn hiển thị khóa đó với các khóa ISO-alpha-3. Vì thế...

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

Vân vân.

Theo hướng dẫn trên topojson, tôi biết tôi có thể làm ...

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

để tạo ra một bản đồ thế giới được ID3 theo ID3.

Câu hỏi của tôi là: tại điểm nào trong quy trình làm việc tôi nên hợp nhất dữ liệu từ risk.csv vào dữ liệu địa lý? Trước đây tôi đã từng làm việc với qGIS như một GUI, nhưng việc hợp nhất / nên / xảy ra ở đâu? Trong .shp? Sau ogr2ogr? Tự động trong trình duyệt sau khi thu nhỏ topojson (như ở đây http://bl.ocks.org/mbostock/4060606 http://bl.ocks.org/mbostock/3306362 )?

Tôi khá giỏi với python, nhưng khá mới với javascript, và thấy mình sao chép và dán các ví dụ Bostock nhiều hơn là thực sự là một lập trình viên thế hệ ở đó.

(Tôi cũng có một phần tiếp theo liên quan, nhưng liên quan nhiều hơn đến Stackoverflow mà có lẽ tôi nên di chuyển ở đây: /programming/18604877/how-to-do-time-data-in-d3-maps )


Tôi chỉ xem xét các ví dụ của @ mbostock và thấy rằng có một ví dụ cụ thể về GeoJoins hoặc "Một tập lệnh đơn giản để tham gia tệp GeoJSON với các thuộc tính bên ngoài trong tệp CSV hoặc TSV; được trích xuất từ ​​TopoJSON" .
RyanKDalton

Câu trả lời:


11

Tự hỏi bản thân hai câu hỏi:

  1. Bạn sẽ sử dụng lại địa lý trên nhiều bộ dữ liệu?

    Nếu bạn sẽ sử dụng cùng một vị trí địa lý với nhiều bộ dữ liệu, thì nên giữ địa lý và dữ liệu riêng biệt và tham gia chúng trong máy khách. Nhiều ví dụ của tôi có các tệp CSV (hoặc TSV) riêng biệt vì lý do này. Bằng cách này, TopoJSON cho các tiểu bang và hạt của Hoa Kỳ hoặc các nước thế giới tương tự có thể được sử dụng lại, thay vì tạo TopoJSON riêng cho mọi ví dụ.

    Mặt khác, nếu bạn chỉ sử dụng địa lý này một lần , thì có lẽ bạn nên nướng bánh dữ liệu vào địa lý dưới dạng các thuộc tính, nếu chỉ để đơn giản hóa mã. Cách tiếp cận này đơn giản hơn vì bạn chỉ cần tải một tệp duy nhất (vì vậy không có queue.js ) và vì dữ liệu được lưu trữ dưới dạng các thuộc tính của từng tính năng, bạn không cần phải tham gia dữ liệu trong máy khách (vì vậy không có d3. bản đồ ).

    Lưu ý bên lề: TSV và CSV thường hiệu quả hơn trong việc lưu trữ các thuộc tính so với GeoJSON và TopoJSON, đơn giản vì sau này phải lặp lại tên thuộc tính trên mọi đối tượng. Kích thước tệp có thể là một lý do khác để lưu trữ dữ liệu của bạn trong một tệp riêng biệt và tham gia dữ liệu đó trong máy khách.

  2. Là dữ liệu của bạn đã được ràng buộc với địa lý (ví dụ, một tài sản của shapefile của bạn)?

    Giả sử bạn đã trả lời câu hỏi không có câu hỏi đầu tiên và muốn đưa dữ liệu vào địa lý (thay vì thực hiện trong ứng dụng khách), cách bạn thực hiện việc này phụ thuộc vào định dạng của dữ liệu.

    Nếu dữ liệu của bạn đã là một thuộc tính của shapefile của bạn, thì hãy sử dụng topojson -pđể kiểm soát các thuộc tính nào được lưu vào tệp TopoJSON được tạo. Bạn cũng có thể sử dụng điều này để đổi tên các thuộc tính và ép buộc chúng thành số. Xem Hãy tạo bản đồ để biết ví dụ.

    Nếu dữ liệu của bạn nằm trong tệp CSV hoặc TSV riêng biệt, thì hãy sử dụng topojson -e (ngoài -p) để chỉ định tệp thuộc tính bên ngoài có thể được nối với các tính năng địa lý của bạn. Lấy ví dụ từ wiki, nếu bạn có tệp TSV như thế này:

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

    Khi sử dụng -e, bạn có thể ánh xạ các thuộc tính này đến một thuộc tính đầu ra số có tên là thất nghiệp.

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

    Một ví dụ về phương pháp này là dân số Kentucky choropleth, bl.ocks.org/5144735 .


2
Và ở đây tôi đã hỏi những câu hỏi ánh xạ D3 khó nhằn của tôi trên stackoverflow thay vì gis.stackexchange vì tôi nghĩ rằng có nhiều chuyên môn hơn ở đó --- và sau đó chính ông chủ trả lời câu hỏi của tôi ở đây. =) Vâng, điều đó làm cho 2 điều tôi học được ngày hôm nay. Cảm ơn!
Găng tay

3

Câu hỏi hay. Một trong những ví dụ bạn cung cấp dường như thực hiện mánh khóe, mặc dù rất khó để làm theo.

Bạn sẽ lưu ý rằng ví dụ này có hai tệp dữ liệu ngoài là us.jsonthất nghiệp.tsv . Bạn có thể nghĩ về thất nghiệp.tsv giống như nguy hiểm của bạn.csv; us.json là các tính năng địa lý mà bạn muốn liên kết các tham số từ risk.csv. Cái sau, thất nghiệp.tsv , có idratecác lĩnh vực idgiống như idtrong us.json.

Trong ứng dụng khách với D3, bạn nên hợp nhất dữ liệu và tính năng của mình , ít nhất là bằng ví dụ này. Trong khách hàng , tỷ lệ thất nghiệp, trong ví dụ này, được kết hợp với các tính năng của quận, sử dụng hàm d3.map () . Đây là nơi nó được khởi tạo:

var rateById = d3.map();

Và đây là nơi rateđược ánh xạ tới 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);

Tôi phải thừa nhận tôi không biết cái gì queue()để làm, nhưng nó không quan trọng đối với cuộc thảo luận này. Điều quan trọng cần lưu ý là các idtrường trong mỗi tính năng quận được thay thế bằng tình trạng thất nghiệp rate. các ratebây giờ đã truy cập bằng nhận dạng chia sẻ id( EDIT: Như @ blord-castillo điểm ra ngoài, điều này thực sự là thế hệ của một mảng mới kết hợp, hoặc băm chìa khóa, nơi rateđược ánh xạ tớiid ). Đây là nơi rateđược gọi cho các mục đích của hệ thống ký hiệu (ở đây, các lớp CSS được xác định trước có sẵn cho mỗi lượng tử):

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

Trường hợp quantize()hàm trả về tên của lớp CSS sẽ được sử dụng để định kiểu tính năng đó (hạt) dựa trên tỷ lệ thất nghiệp của nó, hiện được xác định trong trường của tính năng id.



hàng đợi cho phép tải song song các nguồn dữ liệu thay vì tải nối tiếp.
blord-castillo

1
Điều đang diễn ra trong ví dụ đó là RateById là một hàm băm chính. Không có thay đổi nào được thực hiện đối với các tính năng của quốc gia và dữ liệu us.json không bị ảnh hưởng. Thay vào đó, thất nghiệp.tsv được chuyển đổi thành hàm băm chính gọi là 'RateById'. RateById.set () được lặp qua thất nghiệp.tsv để một khóa được chèn cho mỗi id trong thất nghiệp.tsv (không phải trong us.json) và giá trị của khóa đó được đặt thành trường tỷ lệ cho id đó trong thất nghiệp.tsv . Sau đó, RateById.get () được gọi để sử dụng hàm băm để tra cứu tỷ lệ thất nghiệp theo id; giá trị đó được sử dụng để đặt kiểu trên các tính năng us.json, sau đó loại bỏ.
blord-castillo

Tại sao điều này / thay thế / ID với tỷ lệ thay vì gắn nó như một thuộc tính ở một nơi khác? Điều này dường như làm cho việc lựa chọn sau này trở nên khó khăn hơn.
Găng tay

1
Nó không thay thế id với tỷ lệ. Nó tạo ra một băm tra cứu từ id để đánh giá.
blord-castillo

2

Trước hết, hàng đầu tiên của csv của bạn phải là một danh sách tên cột được phân tách bằng dấu phẩy để sử dụng phương thức này. Nếu điều này là không thể, hãy thêm một nhận xét về điều này và tôi sẽ xem liệu tôi có thể tìm ra cách sử dụng d3.csv.parseRowsthay vì d3.csv.parse. d3.csv.parseđược gọi bởi chức năng đánh giá trên .defer(function, url, assessor).

Tôi sẽ giả sử tập tin của bạn bây giờ trông như thế này:

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

Sử dụng điều này, bạn có thể tạo băm tra cứu từ ISO3 đến mức nguy hiểm.

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)
}

Mã hướng dẫn

var dangerByISO3 = d3.map();

Trước tiên, bạn tạo một đối tượng d3.map () sẽ hoạt động như hàm băm chính của bạn và lưu trữ đối tượng này trong biến nguy hiểmByISO3.

queue()

Sử dụng hàng đợi để tải song song.

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

Tải topojson của bạn làm đối số đầu tiên được chuyển đến hàm await (sau lỗi). Lưu ý kiểu ở đây có chức năng xâu chuỗi trên queue(), nhưng được liệt kê trên một dòng riêng biệt (không có dấu chấm phẩy kết thúc trên queue()).

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

Hai điều đang xảy ra ở đây. Đầu tiên, bạn đang tải risk.csv làm đối số thứ hai của bạn được chuyển đến hàm đang chờ. Như bạn sẽ thấy bên dưới, đối số này không thực sự được sử dụng. Thay vào đó, một đối số đánh giá được cung cấp cho hàm tải, d3.csv. Người đánh giá này sẽ xử lý từng hàng của csv. Trong trường hợp này, chúng tôi gọi hàm set trên riskByISO3 để cho mỗi kết hợp của một isokhóa, chúng tôi đặt levelgiá trị là giá trị đi cùng với khóa đó. Các +d.levelký hiệu sử dụng unary +để ép buộc các giá trị của d.level thành một số.

.await(ready);

Khi cả hai nguồn dữ liệu được tải, chúng được truyền dưới dạng hai đối số riêng biệt cho hàm ready(). Đối số đầu tiên cho cuộc gọi lại luôn là lỗi đầu tiên xảy ra. Nếu không có lỗi xảy ra, thì null sẽ được chuyển làm đối số đầu tiên. Đối số thứ hai là nguồn dữ liệu đầu tiên (kết quả của nhiệm vụ đầu tiên) và đối số thứ ba là nguồn dữ liệu thứ hai (kết quả của nhiệm vụ thứ hai).

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

Đây là chức năng gọi lại ready(). Đầu tiên chúng ta lấy errorđối số là null nếu hai tác vụ tải hoàn thành thành công (bạn thực sự nên thêm ngôn ngữ để bắt và xử lý lỗi). Tiếp theo chúng ta lấy dữ liệu topojson làm đối tượng countries. Dữ liệu này nên được xử lý trong phần thân của hàm với nội dung tương tự .data(topojson.feature(world,world.objects.countries).features). Vì ready()không có đối số thứ ba, kết quả của nhiệm vụ thứ hai, csv của chúng tôi, chỉ đơn giản là bị loại bỏ. Chúng tôi chỉ sử dụng nó để xây dựng hàm băm chính và không cần nó sau đó.


Vâng, bạn nói đúng, csv của tôi thực sự trông giống như một csv được hình thành tốt thay vì bản demo bất cẩn mà tôi đã đăng. =) Xin lỗi, tôi sẽ cập nhật nó.
Găng tay
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.