main
František Špaček 2 years ago
parent 23a92f9660
commit a0c52e4cdb

@ -358,7 +358,6 @@ class ChartLoader {
*/ */
async addInteractivity() { async addInteractivity() {
setTimeout(() => { setTimeout(() => {
console.time("2")
// Set dimensions of effect canvas // Set dimensions of effect canvas
this.effectCanvas.width = this.canvas.width this.effectCanvas.width = this.canvas.width
this.effectCanvas.height = this.canvas.height this.effectCanvas.height = this.canvas.height
@ -367,7 +366,6 @@ class ChartLoader {
this.detectionCanvas.height = this.canvas.height this.detectionCanvas.height = this.canvas.height
// Draw detection map on the detection canvas // Draw detection map on the detection canvas
this.chart.drawDetectionMap(this.detectionCanvas.getContext("2d")) this.chart.drawDetectionMap(this.detectionCanvas.getContext("2d"))
console.timeEnd("2")
}, 0) }, 0)
} }
@ -377,7 +375,6 @@ class ChartLoader {
* @param {Array} data - The data to visualize on the chart. * @param {Array} data - The data to visualize on the chart.
*/ */
drawChart(chartSettings, data) { drawChart(chartSettings, data) {
console.time("1")
let zoomManager = new ZoomManager(chartSettings.horizontalZoom, chartSettings.verticalZoom) let zoomManager = new ZoomManager(chartSettings.horizontalZoom, chartSettings.verticalZoom)
// Choose the correct type of chart // Choose the correct type of chart
@ -419,8 +416,6 @@ class ChartLoader {
this.chart.draw() this.chart.draw()
console.timeEnd("1")
this.addInteractivity() this.addInteractivity()
this.addListeners(this.chart) this.addListeners(this.chart)
} }

@ -17,12 +17,17 @@ class AreaChart extends PointChart {
/** /**
* Draws the area chart on the canvas. * Draws the area chart on the canvas.
* @param {Boolean} async - Says if the chart should be drawn synchronously or asynchronously
*/ */
draw() { draw(async = true) {
// Clear the canvas and draw the axis // Clear the canvas and draw the axis
this.clear() this.clear()
this.drawAxis() this.drawAxis()
this.ctx.lineWidth = this.settings.lineWidth
// Define function for async
let fn = () => {
// Iterate over each category in the data // Iterate over each category in the data
this.data.forEach(categ => { this.data.forEach(categ => {
// Begin a new path for drawing lines // Begin a new path for drawing lines
@ -60,4 +65,8 @@ class AreaChart extends PointChart {
if (this.settings.displayPoints) if (this.settings.displayPoints)
this.data.forEach((categ, colId) => { this.drawPoints(categ.values, colId, categ.color) }) this.data.forEach((categ, colId) => { this.drawPoints(categ.values, colId, categ.color) })
} }
// Draw the chart
async ? setTimeout(() => fn(), 0) : fn()
}
} }

@ -17,12 +17,17 @@ class LineChart extends PointChart {
/** /**
* Draws the line chart on the canvas. * Draws the line chart on the canvas.
* @param {Boolean} async - Says if the chart should be drawn synchronously or asynchronously
*/ */
draw() { draw(async = true) {
// Clear the canvas and draw the axis // Clear the canvas and draw the axis
this.clear() this.clear()
this.drawAxis() this.drawAxis()
this.ctx.lineWidth = this.settings.lineWidth
// Define function for async
let fn = () => {
// Iterate over each category in the data // Iterate over each category in the data
this.data.forEach(categ => { this.data.forEach(categ => {
// Begin a new path for drawing lines // Begin a new path for drawing lines
@ -47,4 +52,8 @@ class LineChart extends PointChart {
if (this.settings.displayPoints) if (this.settings.displayPoints)
this.data.forEach((categ, colId) => { this.drawPoints(categ.values, colId, categ.color) }) this.data.forEach((categ, colId) => { this.drawPoints(categ.values, colId, categ.color) })
} }
// Draw the chart
async ? setTimeout(() => fn(), 0) : fn()
}
} }

@ -17,12 +17,18 @@ class SmoothAreaChart extends PointChart {
/** /**
* Draws the smooth area chart on the canvas. * Draws the smooth area chart on the canvas.
* @param {Boolean} async - Says if the chart should be drawn synchronously or asynchronously
*/ */
draw() { draw(async = true) {
// Clear the canvas and draw the axis // Clear the canvas and draw the axis
this.clear() this.clear()
this.drawAxis() this.drawAxis()
this.ctx.lineWidth = this.settings.lineWidth
// Define function for async
let fn = () => {
// Iterate over each category in the data // Iterate over each category in the data
this.data.forEach(categ => { this.data.forEach(categ => {
// Begin a new path for drawing lines // Begin a new path for drawing lines
@ -65,6 +71,7 @@ class SmoothAreaChart extends PointChart {
this.ctx.fillStyle = categ.color this.ctx.fillStyle = categ.color
this.ctx.closePath() this.ctx.closePath()
this.ctx.fill() this.ctx.fill()
this.ctx.stroke()
// Reset transparency // Reset transparency
this.ctx.globalAlpha = 1 this.ctx.globalAlpha = 1
@ -74,4 +81,8 @@ class SmoothAreaChart extends PointChart {
if (this.settings.displayPoints) if (this.settings.displayPoints)
this.data.forEach((categ, colId) => { this.drawPoints(categ.values, colId, categ.color) }) this.data.forEach((categ, colId) => { this.drawPoints(categ.values, colId, categ.color) })
} }
// Draw the chart
async ? setTimeout(() => fn(), 0) : fn()
}
} }

@ -64,7 +64,6 @@ class SmoothLineChart extends PointChart {
} }
// Draw the chart // Draw the chart
if (this.settings.displayPoints)
async ? setTimeout(() => fn(), 0) : fn() async ? setTimeout(() => fn(), 0) : fn()
} }
} }

@ -13,6 +13,9 @@ class Table {
this.tableHead = tableElement.querySelector("thead") this.tableHead = tableElement.querySelector("thead")
this.rcMenu = rcMenu this.rcMenu = rcMenu
this.importElement = document.getElementById("import")
this.exportElement = document.getElementById("export")
// Initialize right-click menu options // Initialize right-click menu options
this.rcAddRow = rcMenu.querySelector("#rcAddRow") this.rcAddRow = rcMenu.querySelector("#rcAddRow")
this.rcDelRow = rcMenu.querySelector("#rcDelRow") this.rcDelRow = rcMenu.querySelector("#rcDelRow")
@ -52,7 +55,89 @@ class Table {
this.rcAddCol.addEventListener("mousedown", (e) => { this.addCol() }) this.rcAddCol.addEventListener("mousedown", (e) => { this.addCol() })
this.rcDelCol.addEventListener("mousedown", (e) => { this.delCol() }) this.rcDelCol.addEventListener("mousedown", (e) => { this.delCol() })
this.get this.importElement.addEventListener("input", (event) => {
let file = this.importElement.files[0]
let reader = new FileReader()
reader.onload = (e) => {
let result = e.target.result
let rows = result.split("\n")
let header = rows[0].split(",")
rows.shift()
this.selectedCell = this.tableBody.querySelector("#chart_table_0_values_0")
while (this.tableBody.rows.length > 1)
this.delRow()
while (this.tableHead.lastElementChild.childElementCount > header.length)
this.delCol()
while (this.tableHead.lastElementChild.childElementCount < header.length)
this.addCol()
for (let i = 0; i < header.length; i++) {
this.tableHead.querySelector("#chart_table_" + i +"_col_name").value = header[i]
}
for (let i = 1; i < rows.length - 1; i++)
this.addRow()
for (let i = 0; i < rows.length - 1; i++) {
let cells = rows[i].split(",")
for (let j = 0; j < header.length; j++) {
console.log("#chart_table_" + j + "_values_" + i, +cells[j])
this.tableBody.querySelector("#chart_table_" + j + "_values_" + i).value = +cells[j]
}
}
}
reader.readAsText(file)
})
this.exportElement.addEventListener("input", (e) => {
let newName = ""
let fileURL = ""
let choice = this.exportElement.value
if (choice === "png") {
let iframe = document.getElementById("chartDiv")
let canvas = iframe.contentWindow.document.getElementById("chartCanvas")
fileURL = canvas.toDataURL("image/png")
newName = 'chart.png'
}
else if (choice === "csv" || choice === "txt") {
// Variable to store the final csv data
let csv_data = []
let rows = document.getElementsByTagName('tr');
for (let i = 0; i < rows.length; i++) {
// Get each column data
let cols = rows[i].querySelectorAll('td,th');
// Stores each csv row data
let csvrow = [];
for (let j = 0; j < cols.length; j++) {
// Get the text data of each cell of
// a row and push it to csvrow
csvrow.push(cols[j].querySelector("input").value);
}
// Combine each column value with comma
csv_data.push(csvrow.join(","));
}
// Combine each row data with new line character
csv_data = csv_data.join('\n');
let CSVFile = new Blob([csv_data], { type: "text/csv" })
fileURL = window.URL.createObjectURL(CSVFile)
newName = 'data.' + choice
}
let downloadLink = document.createElement("a")
downloadLink.download = newName
downloadLink.href = fileURL
downloadLink.click()
})
} }
handleContextMenu(rcMenu, tableElement, pos) { handleContextMenu(rcMenu, tableElement, pos) {

@ -96,6 +96,10 @@ div[id^="chart_metadata_group"] {
padding: 0.5rem; padding: 0.5rem;
} }
#fileDiv label {
margin-top: 1rem;
}
/* Styles for secondary div */ /* Styles for secondary div */
#secondaryDiv { #secondaryDiv {
flex-basis: 40%; flex-basis: 40%;

@ -167,23 +167,24 @@ h2 {
color: darkred; color: darkred;
} }
#myCharts { #myCharts, #indexDiv {
height: 100%; height: 100%;
width: 100%; width: 100%;
margin: 1rem; margin: 1rem;
text-align: center; text-align: center;
background-color: var(--main); background-color: var(--main);
display: block; display: block;
overflow: auto;
} }
#myCharts #createChart { #createChart {
color: var(--dark); color: var(--dark);
background-color: green; background-color: green;
padding: 1rem; padding: 1rem;
transition: 300ms; transition: 300ms;
} }
#myCharts #createChart:hover { #createChart:hover {
background-color: darkgreen; background-color: darkgreen;
} }
@ -231,6 +232,25 @@ h2 {
transition: 300ms; transition: 300ms;
} }
#indexDiv {
display: block;
overflow: auto;
}
#indexDiv section{
margin-top: 4rem;
}
#indexDiv iframe {
width: 60%;
height: 22rem;
}
#indexDiv li {
list-style-type: none;
margin: 0.5rem;
}
/* Media query for smaller screens */ /* Media query for smaller screens */
@media (max-width: 600px) { @media (max-width: 600px) {
header { header {

@ -39,13 +39,14 @@ class Chart
"yStep" => 1, "yStep" => 1,
"displaySupportLines" => true, "displaySupportLines" => true,
"displayAxisValues" => true, "displayAxisValues" => true,
"pointSize" => 15, "pointSize" => 7,
"titleFont" => ["font" => 'Courier New', "size" => 25], "titleFont" => ["font" => 'Courier New', "size" => 25],
"labelFont" => ["font" => 'Georgia', "size" => 15], "labelFont" => ["font" => 'Georgia', "size" => 15],
"legendFont" => ["font" => 'Arial', "size" => 10], "legendFont" => ["font" => 'Arial', "size" => 10],
"pointBorderSize" => 2, "pointBorderSize" => 2,
"pointBorderColor" => "#241f31", "pointBorderColor" => "#241f31",
"horizontalZoom" => true "horizontalZoom" => true,
"lineWidth" => 3
]; ];
#[MongoDB\Field(type: 'hash')] #[MongoDB\Field(type: 'hash')]

@ -154,9 +154,21 @@ class MetadataType extends AbstractType
'required' => false, 'required' => false,
]) ])
) )
// Zoom settings // Line
->add( ->add(
$builder->create('group5', FormType::class, [ $builder->create('group5', FormType::class, [
'inherit_data' => true,
'label' => 'Nastavení čar',
'label_attr' => ['class' => 'submenuLabel arrow-down']
])
->add('lineWidth', NumberType::class, [
'label' => 'Tloušťka čáry',
'required' => false,
])
)
// Zoom settings
->add(
$builder->create('group6', FormType::class, [
'inherit_data' => true, 'inherit_data' => true,
'label' => 'Nastavení přiblížení', 'label' => 'Nastavení přiblížení',
'label_attr' => ['class' => 'submenuLabel arrow-down'] 'label_attr' => ['class' => 'submenuLabel arrow-down']
@ -172,7 +184,7 @@ class MetadataType extends AbstractType
) )
// Background settings // Background settings
->add( ->add(
$builder->create('group6', FormType::class, [ $builder->create('group7', FormType::class, [
'inherit_data' => true, 'inherit_data' => true,
'label' => 'Nastavení pozadí', 'label' => 'Nastavení pozadí',
'label_attr' => ['class' => 'submenuLabel arrow-down'] 'label_attr' => ['class' => 'submenuLabel arrow-down']

@ -43,14 +43,14 @@ let table = new Table(tableElement, rcMenu)
</div> </div>
<div id="secondaryDiv"> <div id="secondaryDiv">
<div id="fileDiv"> <div id="fileDiv">
<h2>Import - Export</h2>
<label for="import">Nahrát soubor</label> <label for="import">Nahrát soubor</label>
<input accept=".csv,.txt" id="import" type="file"> <input accept=".csv,.txt" id="import" type="file">
<label for="export">Exportovat</label> <label for="export">Exportovat</label>
<select id="export"> <select id="export">
<option value="txt">txt</option> <option value="txt">txt</option>
<option value="csv">csv</option> <option value="csv">csv</option>
<option value="txt">xml</option> <option value="png">png</option>
<option value="txt">png</option>
</select> </select>
</div> </div>
<div id="tableDiv"> <div id="tableDiv">

@ -6,11 +6,81 @@
{% block body %} {% block body %}
{{ parent() }} {{ parent() }}
<h1>Interaktivní grafy</h1> <main>
<div id="indexDiv">
<h1>Dokumentace aplikace</h1>
<p>V této dokumentaci se nacházejí ukázky grafů, které je možné pomocí této aplikace vyrobit. Lze je exportovat ve statické formě, nebo je vkládat na webové stránky pomocí:</p>
<p>
<strong>&lt iframe src="example.com/charts/6645cec13703bac5a30e6a41"&gt &lt/iframe &gt
</strong>
</p>
<section>
<iframe src={{ path('charts_display', {'id': "6645cec13703bac5a30e6a41"} ) }}></iframe>
<p>
Bodové grafy jsou základním nástrojem pro vizualizaci datových bodů na rovině. Každý bod v grafu reprezentuje jednu konkrétní hodnotu nebo pozorování. Tyto datové body mohou být v závislosti na datech rovnoměrně nebo nerovnoměrně rozloženy napříč vodorovnou osou. Tento typ grafu je ideální pro zobrazování vztahů mezi dvěma proměnnými a umožňuje identifikovat vzory, korelace a odlehlé hodnoty. Někdy se jim proto říká korelační diagramy. V bodových grafech jsou jednotlivé body umístěny na kartézské soustavě souřadnic. Mohou být v některých případech i trojrozměrné a každý bod tedy popisují tři souřadnice, ale v praxi se tato verze příliš nepoužívá kvůli špatné přehlednosti. Bodové grafy jsou často využívány k analýze dat ve vědeckém výzkumu, ekonomii, sociologii a dalších disciplínách, kde je potřeba vizualizovat vztahy mezi proměnnými.
</p>
</section>
<section>
<iframe src={{ path('charts_display', {'id': "664657fdf6b92077ce0bd5a3"} ) }}></iframe>
<p>
Spojnicové grafy se používají k vizualizaci vztahů mezi body v čase nebo v závislosti na jiné proměnné. Tento typ grafu je vhodný pro zobrazování trendů, vývoje a dynamiky dat. Ve spojnicových grafech jsou body spojeny čarami, které zobrazují vztah mezi nimi. Tyto čáry obvykle představují vývoj dat v čase nebo vztah mezi dvěma proměnnými. Na rozdíl od bodového grafu jsou data téměř vždy rovnoměrně rozprostřena po celé délce vodorovné osy. Spojnicové grafy jsou často využívány k vizualizaci dat jako je vývoj cen, teplot nebo akcií.
</p>
</section>
<section>
<iframe src={{ path('charts_display', {'id': "664659bc3703bac5a30e6a42"} ) }}></iframe>
</section>
<section>
<iframe src={{ path('charts_display', {'id': "66465ab0f6b92077ce0bd5a4"} ) }}></iframe>
<p>
Plošné grafy vizualizují data jako plné oblasti, kde každá oblast reprezentuje jednu datovou kategorii. Tyto grafy jsou ideální pro srovnávání hodnot mezi různými kategoriemi a pro zjištění jejich relativního podílu. Jednou z hlavních výhod plošných grafů jednoduchost interpretace, protože plné plochy poskytují přehledný obraz o datech a umožňují snadno vizuálně porovnávat velikosti jednotlivých kategorií. Plošné grafy mohou nést i některé nevýhody. Mezi ně patří možná ztráta podrobností, pokud je potřeba zobrazit detailnější informace nebo individuální hodnoty. Mohou být často viděny v oblastech jako marketing, ekonomie nebo třeba ve zdravotnictví, pro srovnání prevalence různých onemocnění.
</p>
</section>
<section>
<iframe src={{ path('charts_display', {'id': "66465b9ff6b92077ce0bd5a5"} ) }}></iframe>
<p>
Koláčové grafy jsou kruhové diagramy, které vizualizují data jako procentuální části celku. Každý segment v grafu odpovídá určitému procentu celkového množství. Koláčové grafy jsou často využívány pro prezentaci procentuálního rozložení různých položek, jako jsou například náklady, zisky nebo populace. Jsou také vhodné pro zobrazení struktury portfolia, podílu tržního segmentu a demografických trendů. Mohou však být méně efektivní při zobrazení velkého počtu kategorií a mohou být matoucí, pokud jsou některé segmenty příliš malé na to, aby byly přehledné.
</p>
</section>
<section> <section>
<iframe src={{ path('charts_display', {'id': "66465be2f6b92077ce0bd5a6"} ) }}></iframe>
</section>
<section>
<iframe src={{ path('charts_display', {'id': "66465c073703bac5a30e6a43"} ) }}></iframe>
<p> <p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Accusamus odio neque est voluptatum Sloupcové grafy jsou vynikajícím nástrojem pro vizualizaci dat, které mají jasně definované kategorie a hodnoty. Data reprezentují pomocí svislých sloupců, kde výška sloupce odpovídá hodnotě datového bodu. Jsou ideální pro srovnávání hodnot mezi různými kategoriemi a rychlé pochopení relativních hodnot. Tento typ grafu je často využíván v různých oblastech, jako jsou ekonomie, marketing, nebo vědecký výzkum, kde je potřeba srovnávat hodnoty mezi různými skupinami nebo kategoriemi dat. Díky své jednoduché interpretaci a přehlednému zobrazení je sloupcový graf oblíbeným nástrojem nejen pro analyzování dat, ale i pro prezentaci výsledků a komunikaci významných trendů nebo rozdílů.
repellat animi dicta aliquam ipsa culpa commodi.
</p> </p>
</section> </section>
<section>
<iframe src={{ path('charts_display', {'id': "66465c4c3703bac5a30e6a44"} ) }}></iframe>
<p>
Skládané grafy představují pokročilejší formu sloupcových grafů, kde každý sloupec zobrazuje kumulativní hodnoty všech kategorií namísto jednotlivých hodnot. Skládané grafy jsou užitečné pro porovnání celkové velikosti a struktury různých skupin dat a používají se proto při vizualizaci trendů, jako jsou například příjmy, náklady nebo produkční výstupy.
</p>
</section>
<h2>API</h2>
<section>
<p>API pro správu grafů poskytuje několik koncových bodů pro získání seznamu grafů, zobrazení detailů konkrétního grafu, vkládání nových grafů, aktualizaci stávajících grafů a mazání grafů.</p>
<ul>
<li><strong>GET /api/charts</strong>: Vrací seznam ID grafů, jež patří přihlášenému uživateli. Odpověď obsahuje seznam ID grafů a HTTP kód 200 OK.</li>
<li><strong>GET /api/charts/{id}</strong>: Endpoint vrací detailní informace o grafu na základě jeho ID. Odpověď obsahuje detailní informace o grafu a kód 200.</li>
<li><strong>POST /api/charts/insert</strong>: Umožňuje uživateli vytvořit nový graf. Data grafu se posílají v těle zprávy ve formátu JSON. Backend následně vytvoří novou entitu a přiřadí jí unikátní ID. Odpověď obsahuje název vytvořeného grafu s HTTP kódem 200. Uživatel musí být přihlášen, jinak se vrátí chyba přístupu s HTTP kódem 403.</li>
<li><strong>POST /api/charts/{id}/update</strong>: Umožňuje uživateli aktualizovat jeho grafy. Data se posílají v těle zprávy jako JSON. Obsahem odpovědi je název aktualizovaného grafu s HTTP kódem 200. Pokud graf patří jinému uživateli, vrátí se chyba přístupu s HTTP kódem 403.</li>
<li><strong>DELETE /api/charts/{id}/remove</strong>: Umožňuje smazat existující graf. Odpověď obsahuje název smazaného grafu s HTTP kódem 200. Uživatel může mazat pouze své vlastní grafy, jinak se vrátí chyba přístupu s kódem 403</li>
</ul>
</section>
</div>
</main>
{% endblock %} {% endblock %}

@ -6,13 +6,21 @@
{% block body %} {% block body %}
{{ parent() }} {{ parent() }}
<main>
<div id="indexDiv"> <div id="indexDiv">
<h1>Interaktivní grafy</h1> <h1>Interaktivní grafy</h1>
<section> <section>
<iframe src={{ path('charts_display', {'id': "66465c073703bac5a30e6a43"} ) }}></iframe>
<p> <p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Accusamus odio neque est voluptatum Vítejte v aplikaci pro tvorbu interaktivních grafů. Klikněte na tlačítko níže a vytvořte si svůj graf.
repellat animi dicta aliquam ipsa culpa commodi.
</p> </p>
{% if app.user %}
<a href="{{ path('charts_create') }}" id="createChart">Vytvořit nový graf!</a>
{% else %}
<a href="{{ path('users_login') }}" id="createChart">Začít!</a>
{% endif %}
</section> </section>
</div> </div>
</main>
{% endblock %} {% endblock %}

Loading…
Cancel
Save

Powered by TurnKey Linux.