Lógica do desenho de eixos utilizando a biblioteca D3.js
Tags: javascript, d3, webdev, portugues
Published: 2023-06-15 21:10:58

Hoje é dia de registrar a maneira que eu projetei mentalmente, durante meus estudos, a identificação da disposição da área durante o desenho de eixos (x e y) em gráficos svg utilizando a biblioteca JavaScript D3.js.

Geralmente, em alguns tutoriais, se define alguns passos básicos, não obrigatórios, ao se desenhar o início de um gráfico. Dentre eles:

1. Definir constantes dimensionais relativas ao gráfico
2. Append uma _group tag_ à _svg tag_, para conter os dados plotados no gráfico
3. Append outra _group tag_ à _svg tag_, para conter os dois eixos do gráfico
      -  à essa _group tag_ append uma _group tag_ para plotar separadamente apenas eixo x
      -  à essa _group tag_ append outra _group tag_ para plotar separadamente apenas eixo y
4. Criação dos scales do gráfico
5. Mais alguns passos adicionais
6. Plotar o restante do gráfico

## Identificação visual

Durante a declaração dos tutoriais, notei que normalmente encontro dificuldades em correlacionar as medidas declaradas no código com dimensionamento no desenho do gráfico. Por isso que decidi criar uma definição visual de como entender o relacionamento do gráfico com esses números.

### Vamos lá

Inicialmente, vamos realizar o primeiro passo, que é definir as contantes dimensionais do gráfico:

```javascript
const height = 600
const width = 800
const margins = {
    top: 10,
    bottom: 40,
    left: 40,
    right: 150
};
```
Tendo isso declarado, imagine que dentro de seu código HTML exista um elemento HTML (_div_, talvez) que seja encarregado de possuir a _svg tag_ com o qual desenharemos o gráfico. Essa _svg tag_ teria as medidas _width_ e _height_ declaradas acima e seria imaginada como a seguinte imagem:

![Image1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3hyrukywhls1g1zetf6p.png)

Esse retângulo de cor roxa acima seria relacionado à nossa _svg tag_. A partir de agora precisamos definir a àrea na qual serão plotados os dados dentro do gráfico.

Nesse ponto podem ser executados o 2º e o 3º passo, mencionados acima, com a criação dos groups relacionados aos eixos do gráfico, além do 4º com os scales.

Note que as medidas de _margin_ acima definem as coordenadas _top_, _left_, _right_ e _bottom_. Elas serão utilizadas tanto pelos groups quanto pelos scales durante suas definições.

Para que nossos eixos possam ser visualizados de uma maneira visualmente mais completa, é necessário que exista um espaçamento mínimo sobrando para eles.

![Image2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j05k66dto0qgm2ig00ls.png)

Conforme a imagem acima e se baseando nos valores constantes de margin declarados, visualmente teriamos esse espaçamento à left da borda da _svg tag_, reservada especialmente para o eixo y (coordenada vertical).

![Image3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hxn3izofwd9tnbp1p7va.png)

Conforme a imagem acima, o mesmo segue para o espaçamento abaixo da borda superior da _svg tag_ e acima do eixo y (coordenada vertical). Vamos então colocar agora algo que simbolize a _group tag_ reservada para o eixo y:

![Image4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/50g2ng1pznuryel1a7kg.png)

Isso fica evidencialmente definido quando se declara o seguinte código:

```javascript
let axisGroup = svg.append('g').attr('class', 'axisGroup')
let yAxisGroup = axisGroup.append('g').attr('class', 'yAxisGroup').attr(
    'transform',
    `translate(${margins.left}, ${margins.top})`
);
```
O eixo x (coordenada horizontal) deve ser colocado na parte inferior da _svg tag_. Logo deve também haver uma área com um espaçamento "sobrando" e reservado para o desenho do eixo x:

![Image5](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k23cxbqbzemdxg9lr1dg.png)

Vamos também colocar aqui algo que simbolize a _group tag_ reservada para o eixo x:

![Image6](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9ixeux8nrbmgztbjzpgm.png)

A representação visual acima pode ser encontrada no seguinte código, durante a criação da _group tag_ relacionada ao eixo x:

```javascript
let xAxisGroup = axisGroup.append('g').attr('class', 'xAxisGroup').attr(
    'transform',
    `translate(${margins.left}, ${height - margins.bottom})`
);
```

## Definição dos scales

Para que os dados possam ser plotados no gráfico, os scales são criados pela biblioteca D3 e eles utilizam tanto o **range** quanto o **domain**.

Bem resumidamente, _domain_ seria a definição dos limites dos dados reais em seu valor bruto e _range_ seria a demarcação dos limites dos dados inseridos e já convertidos dentro da proporção das medidas do desenho gráfico. Em outras palavras seria como desenhar a "Torre Eiffel", de tamanho gigantesco, em uma folha de papel, de tamanho A4.

Conversões de tamanho devem ser feitas nesse caso, tranformando um metro de altura da torre em centímetros do papel (em nosso caso, em píxeis).

Durante a declaração dos scales usando D3.js, definimos a limitação da área no qual o desenho será plotado utilizando os scales.

Por exemplo, utilizando o template das imagens anteriores, digamos que a área no qual desejamos plotar nos dados seja definida conforme o próximo desenho (área em azul fraco):

![Image7](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jseaj569o4l6hvfbi69g.png)

Se você notou, as margins left e top dessa área (uma _group tag_ também) já começam a partir do mesmo posicionamento definido para os groups dos eixos.

Por exemplo:

```javascript
yGroupAxis.call(yAxis).call((group) => {
    const { width } = group.node().getBoundingClientRect();
    // a variável "spacing" é o espaço ocupado pelo eixo y,
    // que cresce conforme o tamanho da maior label
    // presente no eixo
    const spacing = width - margin.left;
    groupAxis.attr('transform', `translate(${spacing}, 0)`);

    // group tag utilizada para plotar os dados no gráfico
    const groupData = svg
        .append('g')
        .attr(
            'transform',
            `translate(${spacing}, ${margin.top})`
        );

    // o restante do código
    // ...
});
```

Aí você se pergunta: "Mas e as margins bottom e right? Como que elas são definidas?"

Ninguém define elas. Elas são deduzidas através do range estabelecido por você.

Ao declarar o range em um scale, você define os limites mínimos e máximos que podem ser plotados dentro de uma group tag.

```javascript
const xScale = d3.scalePoint().range([0, width]);
```
O código acima diz que o starting point é zero e o finish point tem o valor de _width_, gerando a seguinte visualização:

![Image8](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qwsinr0clnym3epm7rjp.png)

A barra laranja, na imagem acima, refere-se ao espaço imaginário no qual a plotagem ocorrerá quando utilizar a _scale function_ do sentido horizontal (do eixo x).

Assim o plotagem pode ficar fora do gráfico em alguns casos. Precisamos remover a quantidade de área reservada que fica à left do eixo y:

```javascript
const xScale = d3.scalePoint().range([
    0,
    width - margin.left
]);
```
Os retângulos verdes presentes nas visualizações, a partir de agora, simbolizarão a quantidade de margin que foi removida da área horizontal disponível para plotar o gráfico. Confira a seguinte visualização:

![Image9](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sbs5eyjq2esjmy9phu0n.png)

Bom... Ainda está grande demais. Falta remover a margin right também.

```javascript
const xScale = d3.scalePoint().range([
    0,
    width - margin.left - margin.right
]);
```
Ficando da seguinte forma:

![Image10](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l7e6hn81jhxkn09sgfna.png)

Horizontalmente está OK. Vejamos agora a porção vertical da área do gráfico:

![Image11](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d359wpfvq8ltgg13q2cg.png)

A barra laranja, na imagem acima, refere-se ao espaço imaginário no qual a plotagem ocorrerá quando utilizar a _scale function_ do sentido vertical (do eixo y).

Os retângulos verdes simbolizam novamente a quantidade de margin que foi removida da área vertical disponível e as flexas vermelhas simboliam as margins top e left adicionadas à _group tag_ reservada para plotar os dados no gráfico.

Abaixo segue o código referente à criação da scale function referente ao sentido vertical:

```javascript
const yScale = d3
    .scaleLinear()
    .range([height - margin.bottom - margin.top, margin.top]);
```
 
> **Nota:** A biblioteca D3.js define seu gráfico considerando sua origin (no retângulo azul da imagem) abaixo da origin da própria _svg tag_ (no retângulo verde da imagem). Isso faz com que, além da conversão entre os dados de domain e os dados de range, tenha que ocorrer também uma conversão entre o posicionamento entre essas origins existentes.

> ![Image11.1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uwsg1kiqx6o6lm54suom.png)

> Quando se declara pontos no gráfico que devam descer no eixo y, na verdade deve-se declarar ele com valores cada vez maiores. Isso não é um problema, pois D3.js cuida disso para nós, desde que os valores de range, na _scale function_ do eixo y, sejam declarados de maneira invertida, ou seja, primeiro o valor considerando a localização inferior e depois o valor considerando a localização superior. O restante segue sendo declarado normalmente.

Abaixo segue a imagem com o resumo de tudo o que já foi mencionado.

![Image12](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g0cpxn5bih6ve8e2m7rp.png)

Espero que dessa forma tenha ficado visualmente um pouco mais claro como que a biblioteca D3.js plota gráficos que utilizem dos eixos x e y.

Imagino que essa forma de pensar visualmente seja a mais próxima da realidade (e foi como eu entendi 😅 kkk). Porém pode ser que este enunciado possua algo incoerente que tenha passado despercebido ou que eu tenha esquecido de mencionar algo.

Fico à espera de sugestões 👍...

Valeu!