Визуализировать текст с вложенными данными в D3 js версии 5 - PullRequest
3 голосов
/ 06 мая 2020

Я пытаюсь визуализировать концентрическую c p ie диаграмму, разделенную на четыре отдела универмага. Каждый отдел имеет вложенный массив продуктов и объектов продуктов. Есть три круга, внутренний, средний и внешний, которые обозначают высокую маржу, среднюю маржу и низкую маржу. Я могу создать график, см. Ниже, однако я не могу передать названия продуктов в нужный отдел, потому что данные вложены. Ниже приведены данные и метод рендеринга круга с высокой маржой, а также скриншот того, что у меня есть прямо сейчас. Я использую React, который является .ref в методе renderMiddleCircle

this.data = [
  { 
    id: 'department1',
    name: 'Electronics',
    value: 100,
    products: [
      {
        description: 'latptop computers',
        id: 'product1',
        name: 'Fast Laptop computers',
        margin: 'high',
      },
      {
        description: 'Coaxial Cable',
        id: 'product2',
        name: 'Gold Coaxial Cable',
        margin: 'high',
      },
    ]
  }, 
  { 
    id: 'department2', 
    value: 100,
    name: 'Hardware',
    products: [
      {
        description: 'Power Drill',
        id: 'product1',
        name: '24 volt power drill',
        margin: 'high'
      },
      {
        description: 'Air tool Wrench',
        id: 'product2',
        name: 'Air Compressor Wrench',
        margin: 'high',
      },
    ]
  },
  { 
    id: 'department3', 
    value: 100,
    name: 'Lawn and Garden',
    products: [
      {
        description: '24 Volt Weed eater'
        id: 'product1',
        name: 'Ryobi Weed Eater',
        margin: 'high',
      },
    ]
  },
  { 
    id: 'department4', 
    value: 100,
    name: 'Grocery',
    products: [
      {
        description: 'Milk',
        id: 'product1',
        name: 'Whole Milk',
        margin: 'high',
      },
    ]
  },
];

renderCenterCircle = (productData) => {
  const highMarginData = productData.map(({ data }) => {
    return {
      ...data,
      products: data.products.filter(product => product.margin === 'high')
    }
  });
  const prepHighMarginData = this.createPie(highMarginData);

  const svg = d3
    .select(this.departmentsRef.current)
    .append('svg')
    .attr('width', WIDTH * 2)
    .attr('height', HEIGHT * 2)
    .append('g')
    .attr('transform', `translate(${WIDTH}, ${HEIGHT})`);

  const g = svg
    .selectAll('.arc')
    .data(prepHighMarginData)
    .enter();

  g.append('g')
    .append('path')
    .attr('d', this.createArc(1.45, 0))
    .style('fill', (d, i) => BRAND_COLORS.steel)
    .style('border-color', (d, i) => BRAND_COLORS.steel);

  g.append('g')
    .attr('transform', (d) => {
      console.log(d);
      return `translate(${this.topicArc.centroid(d)})`
  })
    .append('g')
    .datum(d => d.data.products)
    .append('text')
    .text((d, i) => {
      console.log(d);
      return 'd.name'
    })
    .style('fill', '#fff');
}

enter image description here

Ответы [ 2 ]

1 голос
/ 06 августа 2020

Для каждого g датум является объектом отдела (после передачи его через d3.p ie), если вы хотите, чтобы каждый дочерний объект text имел данные department.products, вы можете сделать это, как вы :

.datum(data.products)

Однако теперь датум представляет собой массив, поэтому вы не можете получить доступ к d.name, вместо этого вы можете сделать что-то вроде:

.append('g')
.datum(d => d.data.products)
.append('text')
.text((d, i) => {
  return d.filter(function(product) {
    return product.margin == 'high';
  })
  .map(function(product) {
    return product.name;
  })
})

Как показано ниже:

var WIDTH = 200;
var HEIGHT = 200;
var data = [
  { 
    id: 'department1',
    name: 'Electronics',
    value: 100,
    products: [
      {
        description: 'latptop computers',
        id: 'product1',
        name: 'Fast Laptop computers',
        margin: 'high',
      },
      {
        description: 'Coaxial Cable',
        id: 'product2',
        name: 'Gold Coaxial Cable',
        margin: 'high',
      },
    ]
  }, 
  { 
    id: 'department2', 
    value: 100,
    name: 'Hardware',
    products: [
      {
        description: 'Power Drill',
        id: 'product1',
        name: '24 volt power drill',
        margin: 'high'
      },
      {
        description: 'Air tool Wrench',
        id: 'product2',
        name: 'Air Compressor Wrench',
        margin: 'high',
      },
    ]
  },
  { 
    id: 'department3', 
    value: 100,
    name: 'Lawn and Garden',
    products: [
      {
        description: '24 Volt Weed eater',
        id: 'product1',
        name: 'Ryobi Weed Eater',
        margin: 'high',
      },
    ]
  },
  { 
    id: 'department4', 
    value: 100,
    name: 'Grocery',
    products: [
      {
        description: 'Milk',
        id: 'product1',
        name: 'Whole Milk',
        margin: 'high',
      },
    ]
  },
];

  // Set up pie data:
  var prepHighMarginData = d3.pie().value(function(d) { return d.value; })(data);
  
  // Create arc generator:
  var createArc = d3.arc()
    .innerRadius(0)
    .outerRadius(200);

  const svg = d3
    .select("body")
    .append('svg')
    .attr('width', WIDTH * 2)
    .attr('height', HEIGHT * 2)
    .append('g')
    .attr('transform', `translate(${WIDTH}, ${HEIGHT})`);

  const g = svg
    .selectAll('.arc')
    .data(prepHighMarginData)
    .enter();

  g.append('g')
    .append('path')
    .attr('d', createArc)
    .style('fill', "#ccc")
    .style('border-color', "#aaa");
  g.append('g')
    .attr('transform', (d) => {
      return `translate(${createArc.centroid(d)})`
    })
    .append('g')
    .datum(d => d.data.products)
    .append('text')
    .text((d, i) => {
      return d.filter(function(product) {
        return product.margin == 'high';
      })
      .map(function(product) {
        return product.name;
      })
    })
    .style('fill', '#fff');
text {
  text-anchor: middle;
  font-size: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

Однако вы можете просто пропустить установку данных, потому что у отдела уже есть эти данные как часть его:

.append('g')
//.datum(d => d.data.products)
.append('text')
.text((d, i) => {
  // updating to account for the filtered array's location:
  return d.data.products.filter(function(product) { 
    return product.margin == 'high';
  })
  .map(function(product) {
    return product.name;
  })
})

Оба этих подхода работают, если вам нужен только один текстовый элемент на p ie клин. Однако первый вариант немного странен в идиоме d3, поскольку он содержит данные, которые на самом деле являются массивом данных. быть использовать что-то большее, например:

g.append('g')
  .attr('transform', (d) => {
    return `translate(${createArc.centroid(d)})`
  })
  .append('g')
  .selectAll('text')
  .data((d) => d.data.products)
  .enter()
  .append('text')
  .attr("y", (d,i,n) => {
     return (i-(n.length/2))*20;
  })
  .text((d) => d.name)
  .style('fill', '#fff');

, как показано ниже. Это дает немного больше гибкости при размещении нескольких текстовых элементов, поскольку разрывы строк не всегда являются простым решением в SVG. Это также метод idiomati c для добавления (нескольких) дочерних элементов к родителю. Шаблон не нужен, если родитель и ребенок имеют отношения 1: 1.

var WIDTH = 200;
var HEIGHT = 200;
var data = [
  { 
    id: 'department1',
    name: 'Electronics',
    value: 100,
    products: [
      {
        description: 'latptop computers',
        id: 'product1',
        name: 'Fast Laptop computers',
        margin: 'high',
      },
      {
        description: 'Coaxial Cable',
        id: 'product2',
        name: 'Gold Coaxial Cable',
        margin: 'high',
      },
    ]
  }, 
  { 
    id: 'department2', 
    value: 100,
    name: 'Hardware',
    products: [
      {
        description: 'Power Drill',
        id: 'product1',
        name: '24 volt power drill',
        margin: 'high'
      },
      {
        description: 'Air tool Wrench',
        id: 'product2',
        name: 'Air Compressor Wrench',
        margin: 'high',
      },
    ]
  },
  { 
    id: 'department3', 
    value: 100,
    name: 'Lawn and Garden',
    products: [
      {
        description: '24 Volt Weed eater',
        id: 'product1',
        name: 'Ryobi Weed Eater',
        margin: 'high',
      },
    ]
  },
  { 
    id: 'department4', 
    value: 100,
    name: 'Grocery',
    products: [
      {
        description: 'Milk',
        id: 'product1',
        name: 'Whole Milk',
        margin: 'high',
      },
    ]
  },
];

  // Set up pie data:
  var prepHighMarginData = d3.pie().value(function(d) { return d.value; })(data);
  
  // Create arc generator:
  var createArc = d3.arc()
    .innerRadius(0)
    .outerRadius(200);

  const svg = d3
    .select("body")
    .append('svg')
    .attr('width', WIDTH * 2)
    .attr('height', HEIGHT * 2)
    .append('g')
    .attr('transform', `translate(${WIDTH}, ${HEIGHT})`);

  const g = svg
    .selectAll('.arc')
    .data(prepHighMarginData)
    .enter();

  g.append('g')
    .append('path')
    .attr('d', createArc)
    .style('fill', "#ccc")
    .style('border-color', "#aaa");
    
  g.append('g')
    .attr('transform', (d) => {
      return `translate(${createArc.centroid(d)})`
    })
    .append('g')
    .selectAll('text')
    .data((d) => d.data.products)
    .enter()
    .append('text')
    .attr("y", (d,i,n) => {
      return (i-(n.length/2))*20;
    })
    .text((d) => d.name)
    .style('fill', '#fff');
text {
  text-anchor: middle;
  font-size: 14px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
0 голосов
/ 01 августа 2020

похоже, что вы просто возвращаете строку 'd.name'

.text((d, i) => {
  console.log(d);
  return 'd.name'
})

, чтобы вернуть значение, оно должно быть без кавычек?

.text((d, i) => {
  console.log(d);
  return d.name
})

при условии d возвращает объект продукта

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...