Tạo bản đồ D3 của dữ liệu phong bì hình elip


16

Tôi có bộ dữ liệu này có hình elip, cụ thể hơn là "phong bì" hình elip. Tôi đã tự hỏi nếu ai đó có lời khuyên về cách tôi có thể vẽ những thứ này trên bản đồ D3. Tôi đã có một thiết lập bản đồ với phép chiếu Mercator. Câu trả lời stackoverflow này có chức năng createEllipse khiến tôi gần gũi, nhưng tôi muốn chắc chắn rằng tôi đang diễn giải dữ liệu chính xác.

Tôi đã cắm các giá trị trục chính / phụ của hình elip từ dữ liệu và sử dụng góc phương vị cho phép quay, điều này có đúng không? Tôi cũng không thực sự hiểu phần "phong bì". Làm thế nào để một số hình elip trong mỗi khu vực tạo ra một hình dạng tiếp giáp duy nhất?

Lời khuyên nào sẽ được đánh giá cao.

nhập mô tả hình ảnh ở đây

  const margin  = {top:0, right:0, bottom:0, left:0},
        width   = 1000 - margin.left - margin.right,
        height  = 800  - margin.top - margin.bottom;

  const svg = d3.select('body')
      .append('svg')
      .attr('width', '100%')
      .attr('height', '100%')
      .attr('viewBox', `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`);

  const chart = svg.append('g')
      .attr('transform', `translate(${margin.left},${margin.top})`);

  //a/b are ellipse axes, x/y is center
  const createEllipse = function createEllipse(a, b, x = 0, y = 0, rotation = 0) {
    let k = Math.ceil(36 * (Math.max(a/b,b/a))); // sample angles
    let coords = [];
    for (let i = 0; i <= k; i++) {
      let angle = Math.PI*2 / k * i + rotation;
      let r = a * b / Math.sqrt(a*a*Math.sin(angle)*Math.sin(angle) + b*b*Math.cos(angle)*Math.cos(angle));
      coords.push(getLatLong([x,y],angle,r));
    }
    return { 'type':'Polygon', 'coordinates':[coords] };
  }

  const getLatLong = function getLatLong(center,angle,radius) {
    let rEarth = 6371; // kilometers
    x0 = center[0] * Math.PI / 180; // convert to radians.
    y0 = center[1] * Math.PI / 180;
    let y1 = Math.asin( Math.sin(y0)*Math.cos(radius/rEarth) + Math.cos(y0)*Math.sin(radius/rEarth)*Math.cos(angle) );
    let x1 = x0 + Math.atan2(Math.sin(angle)*Math.sin(radius/rEarth)*Math.cos(y0), Math.cos(radius/rEarth)-Math.sin(y0)*Math.sin(y1));
    y1 = y1 * 180 / Math.PI;
    x1 = x1 * 180 / Math.PI;
    return [x1,y1];
  } 


  d3.json('https://media.journalism.berkeley.edu/upload/2019/11/kazakhstan.json').then((data) => {

      const ellipses = [
        {lat: 48.6,    lng: 64.7,     axis_x: 30, axis_y: 16, azimuth: 26.5, area_hectar: 0.0713,  zone: 'U1'},
        {lat: 48.625,  lng: 64.625,   axis_x: 30, axis_y: 16, azimuth: 26.5, area_hectar: 0.0713,  zone: 'U1'},
        {lat: 48.366,  lng: 65.44166, axis_x: 50, axis_y: 30, azimuth: 40,   area_hectar: 0.11775, zone: 'U2'},
        {lat: 48.85,   lng: 65.61666, axis_x: 20, axis_y: 22, azimuth: 29,   area_hectar: 0.17584, zone: 'U3'},
        {lat: 48.9333, lng: 65.8,     axis_x: 22, axis_y: 22, azimuth: 28,   area_hectar: 0.17584, zone: 'U3'},
        {lat: 48.9166, lng: 66.05,    axis_x: 50, axis_y: 20, azimuth: 38,   area_hectar: 0.17584, zone: 'U3'},
        {lat: 48.9166, lng: 65.68333, axis_x: 20, axis_y: 22, azimuth: 29,   area_hectar: 0.17584, zone: 'U3'},
        {lat: 49,      lng: 65.86666, axis_x: 22, axis_y: 22, azimuth: 29,   area_hectar: 0.17584, zone: 'U3'}
      ]

      const projection = d3.geoMercator()
        .fitExtent([[0,0],[width,height]], data)

      const path = d3.geoPath()
        .projection(projection);


      chart.selectAll('path')
        .data(data.features)
        .enter()
        .append('path')
        .attr('d',  path)
        .attr('stroke', 'black')
        .attr('strok-width', '1px')
        .attr('fill', 'none');

      chart.selectAll(".ellipses")
        .data(ellipses.map((d) => createEllipse(d.axis_x, d.axis_y, d.lng, d.lat, d.azimuth)))
        .enter()
        .append('path')
        .attr('d', path)
        .attr('stroke', 'black')
        .attr('stroke-width', '1px')
        .attr('fill', 'orange');

  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="chart"></div>

Câu trả lời:


1

Có vẻ như bạn đang diễn giải kết quả gần như đúng.

Một lỗi mà tôi đã sửa là mã của bạn không xem xét góc phương vị.

Một vấn đề khác có thể liên quan đến các trục. Trong bảng được cung cấp, chúng được đặt tên là "kích thước trục", âm thanh giống như kích thước hình elip, trong khi hàm createdEllipse lấy bán kính làm tham số. Xin vui lòng, có một cái nhìn tại phóng to hình dung với trên các vấn đề đề cập cố định. Tooltip trên hover được thêm vào để tham khảo.

Vấn đề thứ ba là có thể tranh cãi và phụ thuộc vào định dạng dữ liệu được thiết lập trong bảng. Ý tôi là x không phải lúc nào cũng có nghĩa là kinh độ và vĩ độ. Nhưng về mặt logic, dường như các giá trị elip dài hơn (giá trị "x" lớn hơn hoặc bằng giá trị "y") sẽ tương ứng với hướng ngang.

Như một lưu ý phụ: độ chính xác của hình ảnh cũng bị ảnh hưởng bởi việc sử dụng bán kính Trái đất xấp xỉ nhưng đó là chuyện nhỏ.

"Phong bì" ở đây có lẽ có nghĩa là vòng tròn hình elip khu vực quan tâm nhất định nằm bên trong, xem xét thực tế là các giá trị khu vực được đưa ra nhỏ hơn nhiều so với diện tích của hình elip.


Điều này giúp rất nhiều. Cảm ơn bạn đã trả lời và mẫu mã! Tôi đang nhận thêm thông tin về bộ dữ liệu. (Đó là dữ liệu về các mảnh vỡ tên lửa rơi) Vì vậy, tôi tin rằng phong bì là khu vực chứa tất cả các hình elip.
jrue
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.