lab 5 done
This commit is contained in:
@@ -2,6 +2,7 @@ import { useState } from 'react'
|
||||
import Table from './components/Table.jsx';
|
||||
import buildings from './data.js';
|
||||
import './CSS/App.css'
|
||||
import Chart from './components/Chart.jsx'
|
||||
import Task from './Task.jsx'
|
||||
function App() {
|
||||
const [count, setCount] = useState(0)
|
||||
@@ -9,7 +10,7 @@ function App() {
|
||||
return (
|
||||
<div className="App">
|
||||
<h3>Самые высокие здания и сооружения</h3>
|
||||
<Chart />
|
||||
<Chart data={ buildings }/>
|
||||
<Table data={ buildings } amountRows="15" />
|
||||
{/* <Task/> */}
|
||||
</div>
|
||||
|
||||
@@ -32,3 +32,25 @@ span:hover,.selected {
|
||||
background-color: blue;
|
||||
color:white;
|
||||
}
|
||||
|
||||
|
||||
|
||||
svg {
|
||||
width: 800px;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
svg path, line {
|
||||
fill: none;
|
||||
stroke: black;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
svg text {
|
||||
font: 8px Verdana;
|
||||
}
|
||||
|
||||
.bad-selection{
|
||||
color: red;
|
||||
border: solid thin red;
|
||||
}
|
||||
@@ -1,32 +1,69 @@
|
||||
const Chart = (props) => {
|
||||
import { useState } from "react";
|
||||
import ChartDraw from './ChartDraw.jsx';
|
||||
import * as d3 from "d3";
|
||||
|
||||
return (
|
||||
|
||||
const Chart = (props) => {
|
||||
const [ox, setOx] = useState("Страна");
|
||||
const [oy, setOy] = useState([true, false]);
|
||||
const [graphType, setGraphType] = useState(0);
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
setOx(event.target["ox"].value);
|
||||
setOy([event.target["oy"][0].checked, event.target["oy"][1].checked]);
|
||||
setGraphType(event.target["graphType"].value);
|
||||
}
|
||||
|
||||
const createArrGraph = (data, key) => {
|
||||
const groupObj = d3.group(data, d => d[key]);
|
||||
let arrGraph = [];
|
||||
for (let entry of groupObj) {
|
||||
let minMax = d3.extent(entry[1].map(d => d['Высота']));
|
||||
arrGraph.push({ labelX: entry[0], values: minMax });
|
||||
}
|
||||
return arrGraph;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h4>Визуализация</h4>
|
||||
<form>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<p> Значение по оси OX: </p>
|
||||
<div>
|
||||
<input type="radio" name="ox" value="Страна" />
|
||||
Страна
|
||||
<br/>
|
||||
<input type="radio" name="ox" value="Год" />
|
||||
Год
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" name="ox" value="Страна" defaultChecked={ox === "Страна"} />
|
||||
Страна
|
||||
<br />
|
||||
<input type="radio" name="ox" value="Год" defaultChecked={ox === "Год"} />
|
||||
Год
|
||||
<br />
|
||||
</div>
|
||||
|
||||
<p> Значение по оси OY </p>
|
||||
<div>
|
||||
<input type="checkbox" name="oy" />
|
||||
Максимальная высота <br/>
|
||||
<input type="checkbox" name="oy" />
|
||||
Минимальная высота
|
||||
</div>
|
||||
{(!oy[0]&& !oy[1]) && <span className="bad-selection"> Выберите хотя бы одно</span>}
|
||||
<div>
|
||||
<input type="checkbox" name="oy" defaultChecked={oy[0] === true} />
|
||||
Минимальная высота
|
||||
<br />
|
||||
<input type="checkbox" name="oy" defaultChecked={oy[1] === true} />
|
||||
Максимальная высота
|
||||
|
||||
</div>
|
||||
<p>Тип диаграммы </p>
|
||||
<div>
|
||||
<select name="graphType" defaultValue={graphType}>
|
||||
<option value="0" >Точечная</option>
|
||||
<option value="1">Гистограма</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<p>
|
||||
<button type="submit">Построить </button>
|
||||
</p>
|
||||
</form>
|
||||
</>
|
||||
)
|
||||
</form>
|
||||
<ChartDraw data={createArrGraph(props.data, ox)} ox={ox} minMax={oy} graphType={graphType} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Chart;
|
||||
157
labs/lab5/src/components/ChartDraw.jsx
Normal file
157
labs/lab5/src/components/ChartDraw.jsx
Normal file
@@ -0,0 +1,157 @@
|
||||
import * as d3 from "d3";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
|
||||
function drawGraph(data, keyX, drawMin, drawMax, graphtype) {
|
||||
// значения по оси ОХ
|
||||
|
||||
// создаем массив для построения графика
|
||||
console.log(keyX)
|
||||
if(keyX=="Год"){
|
||||
data = d3.sort(data, (x,y)=>Number(x["labelX"])-Number(y["labelX"]));
|
||||
}
|
||||
const svg = d3.select("svg")
|
||||
svg.selectAll('*').remove();
|
||||
|
||||
// создаем словарь с атрибутами области вывода графика
|
||||
const attr_area = {
|
||||
width: parseFloat(svg.style('width')),
|
||||
height: parseFloat(svg.style('height')),
|
||||
marginX: 50,
|
||||
marginY: 50
|
||||
}
|
||||
|
||||
// создаем шкалы преобразования и выводим оси
|
||||
const [scX, scY] = createAxis(svg, data, attr_area,[drawMin,drawMax]);
|
||||
|
||||
// рисуем график
|
||||
|
||||
const scaleYDomain = d3.extent(data.map(d => d.values[1]));
|
||||
if (drawMin && drawMax){
|
||||
createChart(svg, data, scX, scY, attr_area, "blue", 0,graphtype,0,scaleYDomain)
|
||||
createChart(svg, data, scX, scY, attr_area, "red", 1,graphtype,0,scaleYDomain)
|
||||
}
|
||||
else if (drawMin) {
|
||||
createChart(svg, data, scX, scY, attr_area, "blue", 0,graphtype,1,scaleYDomain)
|
||||
}
|
||||
else if (drawMax) {
|
||||
createChart(svg, data, scX, scY, attr_area, "red", 1,graphtype,1,scaleYDomain)
|
||||
}
|
||||
}
|
||||
|
||||
function createAxis(svg, data, attr_area, selections) {
|
||||
// находим интервал значений, которые нужно отложить по оси OY
|
||||
// максимальное и минимальное значение и максимальных высот по каждой стране
|
||||
const max = d3.max(data,d => d.values[Number(selections[1])]);
|
||||
const min = d3.min(data,d => d.values[Number(selections[0])]);
|
||||
// console.log(max,min,data)
|
||||
// функция интерполяции значений на оси
|
||||
// по оси ОХ текстовые значения
|
||||
const scaleX = d3.scaleBand()
|
||||
.domain(data.map(d => d.labelX))
|
||||
.range([0, attr_area.width - 2 * attr_area.marginX]);
|
||||
|
||||
const scaleY = d3.scaleLinear()
|
||||
.domain([min * 0.85, max * 1.1])
|
||||
.range([attr_area.height - 2 * attr_area.marginY, 0]);
|
||||
|
||||
// создание осей
|
||||
const axisX = d3.axisBottom(scaleX); // горизонтальная
|
||||
const axisY = d3.axisLeft(scaleY); // вертикальная
|
||||
|
||||
// отрисовка осей в SVG-элементе
|
||||
svg.append("g")
|
||||
.attr("transform", `translate(${attr_area.marginX},
|
||||
${attr_area.height - attr_area.marginY})`)
|
||||
.call(axisX)
|
||||
.selectAll("text") // подписи на оси - наклонные
|
||||
.style("text-anchor", "end")
|
||||
.attr("dx", "-.8em")
|
||||
.attr("dy", ".15em")
|
||||
.attr("transform", d => "rotate(-45)");
|
||||
|
||||
svg.append("g")
|
||||
.attr("transform", `translate(${attr_area.marginX}, ${attr_area.marginY})`)
|
||||
.call(axisY);
|
||||
|
||||
return [scaleX, scaleY]
|
||||
}
|
||||
|
||||
function createChart(svg, data, scaleX, scaleY, attr_area, color, valueIdx,isHistogram,horisontalScale,scaleYDomain) {
|
||||
if (isHistogram){
|
||||
svg.selectAll(".dot")
|
||||
.data(data)
|
||||
.enter()
|
||||
.append("rect")
|
||||
.attr("x", d => scaleX(d.labelX)+valueIdx*6)
|
||||
.attr("y", d => scaleY(d.values[valueIdx]))
|
||||
.attr("width",6*(horisontalScale+1))
|
||||
.attr("height",d => scaleY(scaleYDomain[0]*0.85)-scaleY(d.values[valueIdx]))
|
||||
.attr("transform", `translate(${attr_area.marginX}, ${attr_area.marginY})`)
|
||||
.style("fill", color)
|
||||
|
||||
|
||||
}
|
||||
else{
|
||||
const r = 4;
|
||||
|
||||
svg.selectAll(".dot")
|
||||
.data(data)
|
||||
.enter()
|
||||
.append("circle")
|
||||
.attr("r", r)
|
||||
.attr("cx", d => scaleX(d.labelX) + scaleX.bandwidth() / 2+valueIdx*4)
|
||||
.attr("cy", d => scaleY(d.values[valueIdx]))
|
||||
.attr("transform", `translate(${attr_area.marginX}, ${attr_area.marginY})`)
|
||||
.style("fill", color)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const ChartDraw = (props) => {
|
||||
const chartRef = useRef(null);
|
||||
|
||||
const [width, setWidth] = useState(0);
|
||||
const [height, setHeight] = useState(0);
|
||||
|
||||
// заносим в состояния ширину и высоту svg-элемента
|
||||
useEffect(() => {
|
||||
const svg = d3.select(chartRef.current);
|
||||
setWidth(parseFloat(svg.style('width')));
|
||||
setHeight(parseFloat(svg.style('height')));
|
||||
});
|
||||
// задаем отступы в svg-элементе
|
||||
const margin = {
|
||||
top:10,
|
||||
bottom:60,
|
||||
left:40,
|
||||
right:10
|
||||
};
|
||||
|
||||
// вычисляем ширину и высоту области для вывода графиков
|
||||
const boundsWidth = width - margin.left - margin.right;
|
||||
const boundsHeight = height - margin.top - margin.bottom;
|
||||
|
||||
useEffect(() => {
|
||||
if(boundsWidth<0||boundsHeight<0){
|
||||
return;
|
||||
}
|
||||
const svg = d3.select(chartRef.current);
|
||||
// выводим прямоугольник,
|
||||
svg
|
||||
.append("rect")
|
||||
.attr("x", margin.left)
|
||||
.attr("y", margin.top)
|
||||
.attr("width", boundsWidth)
|
||||
.attr("height", boundsHeight)
|
||||
.style("fill", "lightgrey");
|
||||
console.log(props)
|
||||
drawGraph(props.data,props.ox,props.minMax[0],props.minMax[1],Number(props.graphType))
|
||||
});
|
||||
|
||||
return (
|
||||
<svg ref={chartRef} > </svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default ChartDraw;
|
||||
@@ -12,7 +12,7 @@ const TableRow = (props) => {
|
||||
: props.row.map((item, index) => <th key={ index }> {item} </th>);
|
||||
|
||||
return(
|
||||
<> {cells} </>
|
||||
<>{cells}</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user