Creazione della mappa D3 dei dati dell'inviluppo dell'ellisse


16

Ho questo set di dati che ha ellissi, più specificamente "buste" di ellisse. Mi chiedevo se qualcuno avesse dei consigli su come poterli disegnare su una mappa D3. Ho già una configurazione della mappa con la proiezione di mercatore. Questa risposta di StackOverflow ha una funzione createEllipse che mi ha avvicinato, ma voglio essere sicuro di interpretare correttamente i dati.

Ho inserito i valori dell'asse maggiore / minore dell'ellisse dai dati e usato l'azimut per la rotazione, sarebbe corretto? Inoltre non capisco davvero la parte "busta". In che modo diverse ellissi in ciascuna zona creano una singola forma contigua?

Tutto il consiglio sarebbe apprezzato.

inserisci qui la descrizione dell'immagine

  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>

Risposte:


1

Sembra che tu stia interpretando i risultati quasi nel modo giusto.

Un errore che ho corretto è che il tuo codice non considera l'azimut.

Un altro possibile problema potrebbe essere correlato agli assi. Nella tabella fornita sono denominati "dimensioni dell'asse" che suonano come quote di un'ellisse, mentre la funzione createEllipse prende i raggi come parametri. Si prega di dare un'occhiata alla visualizzazione ingrandita con i problemi sopra menzionati risolti. La descrizione comando al passaggio del mouse viene aggiunta come riferimento.

Il terzo problema è discutibile e dipende dal formato dei dati stabilito nella tabella. Voglio dire che x non significa sempre longitudine e y - latitudine. Ma logicamente sembra che i valori più lunghi delle ellissi (i valori "x" siano maggiori o uguali ai valori "y") debbano corrispondere alla direzione orizzontale.

Come nota a margine: la precisione della visualizzazione è influenzata anche dall'uso del raggio terrestre approssimativo, ma questo è minore.

Con "inviluppo" qui si intende probabilmente che l'ellisse circoscrive una certa area di interesse che si trova all'interno, considerando il fatto che i valori dell'area indicati sono molto più piccoli dell'area dell'ellisse.


Questo aiuta immensamente. Grazie per la risposta e l'esempio di codice! Ricevo maggiori informazioni sul set di dati. (Sono i dati sulla caduta di detriti di razzi) Quindi, credo che l'involucro sia la regione in cui sono contenute tutte le ellissi.
jrue,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.