Membuat peta D3 data amplop elips

16

Saya memiliki dataset ini yang memiliki elips, lebih khusus elips "amplop." Saya bertanya-tanya apakah ada yang punya saran tentang bagaimana saya bisa menggambar ini di peta D3. Saya sudah memiliki pengaturan peta dengan proyeksi mercator. Jawaban stackoverflow ini memiliki fungsi createEllipse yang membuat saya dekat, tetapi saya ingin memastikan saya menafsirkan data dengan benar.

Saya menghubungkan nilai sumbu utama / minor dari elips dari data, dan menggunakan azimuth untuk rotasi, apakah ini benar? Saya juga tidak terlalu mengerti bagian "amplop". Bagaimana beberapa elips di setiap zona membuat bentuk berdekatan yang tunggal?

Nasihat apa pun akan dihargai.

masukkan deskripsi gambar di sini

  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>

jrue
sumber

Jawaban:

1

Tampaknya Anda mengartikan hasil yang hampir tepat.

Satu kesalahan yang saya perbaiki adalah bahwa kode Anda tidak mempertimbangkan azimuth.

Masalah lain yang mungkin terkait dengan sumbu. Dalam tabel yang disediakan, mereka dinamai "dimensi sumbu" yang terdengar seperti dimensi elips, sementara fungsi createEllipse menggunakan radio sebagai params. Silakan, lihat perbesaran dalam visualisasi dengan masalah yang disebutkan di atas diperbaiki. Tooltip pada hover ditambahkan untuk referensi.

Masalah ketiga dapat diperdebatkan dan tergantung pada format data yang ditetapkan dalam tabel. Maksud saya x tidak selalu berarti garis bujur dan y - garis lintang. Namun secara logis tampaknya bahwa nilai elips yang lebih panjang (nilai "x" lebih besar atau sama dengan nilai "y") harus sesuai dengan arah horizontal.

Sebagai catatan: ketepatan visualisasi juga dipengaruhi oleh penggunaan jari-jari Bumi yang diperkirakan tetapi itu kecil.

Dengan "amplop" di sini mungkin dimaksudkan bahwa elips mengitari area tertentu yang menarik di dalamnya, mengingat fakta bahwa nilai area yang diberikan jauh lebih kecil daripada area elips.

Anbu Agarwal
sumber
Ini sangat membantu. Terima kasih atas balasan dan contoh kode! Saya mendapatkan info lebih lanjut tentang dataset. (Ini data tentang puing-puing roket yang jatuh) Jadi, saya percaya amplop itu adalah wilayah di mana semua elips terkandung.
jrue