创建椭圆形信封数据的D3映射 [英] Creating D3 map of ellipse envelope data

查看:127
本文介绍了创建椭圆形信封数据的D3映射的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个包含椭圆的数据集,更具体地说是椭圆信封".我想知道是否有人建议我如何在D3地图上绘制这些图形.我已经有一个带有墨卡托投影的地图设置. 此stackoverflow答案具有​​createEllipse函数,该函数使我很接近,但我想确保自己正确解释了数据.

I have this dataset which has ellipses, more specifically ellipse "envelopes." I was wondering if someone had advice on how I could draw these on a D3 map. I already have a map setup with mercator projection. This stackoverflow answer has a createEllipse function which got me close, but I want to make sure I am interpreting the data correctly.

我从数据中插入了椭圆的长/短轴值,并使用了旋转的方位角,这是正确的吗?我也不太了解信封"部分.每个区域中的几个椭圆如何形成一个连续的形状?

I plugged in the major/minor axis values of the ellipse from the data, and used the azimuth for the rotation, would this be correct? I also don't really understand the "envelope" part. How do several ellipses in each zone create a single contiguous shape?

任何建议将不胜感激.

  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>

推荐答案

似乎您在解释结果几乎是正确的.

It seems that you are interpreting results almost right.

我修复的一个错误是您的代码未考虑方位角.

One error which I fixed is that your code doesn't consider the azimuth.

另一个可能的问题可能与轴有关.在提供的表中,它们被称为轴尺寸",听起来像是椭圆尺寸,而createEllipse函数将半径作为参数. 请查看缩放后的可视化,其中已解决上述问题.添加了悬停工具提示以供参考.

Another possible issue may be related to the axes. In the table provided they are named as "axis dimensions" which sound like an ellipse dimensions, while createEllipse function takes radiuses as params. Please, have a look at the zoomed in visualization with above mentioned issues fixed. Tooltip on hover is added for the reference.

第三个问题是有争议的,取决于表中建立的数据格式.我的意思是x并不总是表示经度,y表示纬度.但是从逻辑上讲,椭圆形的较长值("x"值大于或等于"y"值)应该与水平方向相对应.

Third issue is arguable and depends on the data format established in the table. I mean that x doesn't always mean longitude and y - latitude. But logically it seems that ellipses longer values ("x" values are bigger or equal to "y" values) should correspond to horizontal direction.

作为一个侧面说明:可视化的精度还受到近似地球半径的使用的影响,但这是次要的.

As a side note: precision of visualization is also affected by the usage of approximated Earth radius but that is minor.

这里的包络"可能是指椭圆外接在特定的感兴趣区域内,考虑到给定的面积值远小于椭圆的面积这一事实.

By "envelope" here is probably meant that ellipse circumscibe certain area of interest which lies inside, considering the fact that area values given are much smaller than the area of ellipse.

这篇关于创建椭圆形信封数据的D3映射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆