lab 5 done

This commit is contained in:
2026-04-10 11:34:16 +10:00
parent 88721a25cd
commit 084d3ed9ca
5 changed files with 239 additions and 22 deletions

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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;

View 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;

View File

@@ -12,7 +12,7 @@ const TableRow = (props) => {
: props.row.map((item, index) => <th key={ index }> {item} </th>);
return(
<> {cells} </>
<>{cells}</>
)
}