<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Silnice v číslech</title><link>https://silnicevcislech.cz/posts/</link><description/><generator>Hugo 0.139.3</generator><language>cs</language><managingEditor>Eduard Šubert</managingEditor><lastBuildDate>Sat, 23 May 2026 12:58:28 +0100</lastBuildDate><atom:link href="https://silnicevcislech.cz/posts/feed.xml" rel="self" type="application/rss+xml"/><item><title>Hodnocení Vize nula v České republice za rok 2025</title><link>https://silnicevcislech.cz/posts/vize-nula-hodnoceni/2025/</link><pubDate>Sat, 23 May 2026 12:58:28 +0100</pubDate><guid>https://silnicevcislech.cz/posts/vize-nula-hodnoceni/2025/</guid><description><![CDATA[
<script src="https://d3js.org/d3.v7.js"></script>
<script>
    const progressData = {
        'deaths': [
            { 'year': 2016, 'count': 545 },
            { 'year': 2017, 'count': 502 },
            { 'year': 2018, 'count': 565 },
            { 'year': 2019, 'count': 547 },
            { 'year': 2020, 'count': 460 },
            { 'year': 2021, 'count': 470 },
            { 'year': 2022, 'count': 452 },
            { 'year': 2023, 'count': 455 },
            { 'year': 2024, 'count': 438 },
            { 'year': 2025, 'count': 421 }
        ],
        'severe_injuries': [
            { 'year': 2016, 'count': 2580 },
            { 'year': 2017, 'count': 2340 },
            { 'year': 2018, 'count': 2465 },
            { 'year': 2019, 'count': 2110 },
            { 'year': 2020, 'count': 1807 },
            { 'year': 2021, 'count': 1624 },
            { 'year': 2022, 'count': 1718 },
            { 'year': 2023, 'count': 1751 },
            { 'year': 2024, 'count': 1609 },
            { 'year': 2025, 'count': 1647 }
        ]
    }

    // brewer_lakota: [, , "#931e18", "#da7901", "#247d3f", "#20235b"],
    const progressColors = {
        deaths: "#04a3bd",
        severe_injuries: "#f0be3d",
    }

    function drawProgressChart(data, elementId, color) {
        d3.select(`#${elementId} .chart > *`).remove();

        const width = parseInt(d3.select(`#${elementId} .chart`).style("width"));
        const verticalScreen = (window.screen.width < window.screen.height);

        const height = verticalScreen ? width : Number(width * 1 / 2);
        // Specify the chart’s dimensions.
        const marginTop = verticalScreen ? 30 : 20;
        const marginRight = 10;
        const marginBottom = verticalScreen ? 40 : 20;
        const marginLeft = 50;

        const fontSize = verticalScreen ? "10px" : "14px";

        // Declare the x (horizontal position) scale.
        const x = d3.scaleBand()
            .domain(data.map(d => d.year)) // descending frequency
            .range([marginLeft, width - marginRight])
            .padding(verticalScreen ? 0.2 : 0.3);

        // Declare the y (vertical position) scale.
        const y = d3.scaleLinear()
            .domain([0, d3.max(data, (d) => d.count)])
            .range([height - marginBottom, marginTop]);

        // Create the SVG container.
        const svg = d3.select(`#${elementId} .chart`)
            .attr("style", `height: ${height}px;`)
            .append("svg")
            .attr("width", width)
            .attr("height", height)
            .attr("viewBox", [0, 0, width, height])
            .attr("style", "max-width: 100%; height: auto;");

        // Add a rect for each bar.
        svg.append("g")
            .attr("fill", "steelblue")
            .selectAll()
            .data(data)
            .join("rect")
            .attr("x", (d) => x(d.year))
            .attr("y", (d) => y(d.count))
            .attr("height", (d) => y(0) - y(d.count))
            .attr("width", x.bandwidth())
            .attr("fill", color)
            .attr("rx", 3) // Rounded corners
            .append("title")
            .text((d, i) => {
                return `Rok: ${d.year} \nPočet: ${d.count}`;
            });

        // Add text labels on top of bars
        if (verticalScreen) {
            svg.append("g")
                .selectAll()
                .data(data)
                .join("text")
                .attr("x", d => x(d.year))
                .attr("y", d => y(d.count))
                .attr("font-size", fontSize)
                .attr("transform", d => `translate(${x.bandwidth() / 2},-5)rotate(-45 ${x(d.year)} ${y(d.count)})`)
                .style("text-anchor", "start")
                .text(d => d.count);
        } else {
            svg.append("g")
                .selectAll()
                .data(data)
                .join("text")
                .attr("x", d => x(d.year) + x.bandwidth() / 2)
                .attr("y", d => y(d.count) - 5)  // 5px above the bar
                .attr("text-anchor", "middle")
                .attr("font-size", fontSize)
                .text(d => d.count);
        }

        // Add the x-axis and label.
        if (verticalScreen) {
            svg.append("g")
                .attr("transform", `translate(0,${height - marginBottom})`)
                .call(d3.axisBottom(x).tickSizeOuter(0))
                .call(g => g.select(".domain").remove())
                .call(g => {
                    g.selectAll(".tick text")
                        .attr("transform", `translate(${x.bandwidth() / 2},0)rotate(45)`)
                        .attr("text-anchor", "start")
                        .attr("font-size", fontSize);
                    g.select(".domain").remove();
                });
        } else {
            svg.append("g")
                .attr("transform", `translate(0,${height - marginBottom})`)
                .call(d3.axisBottom(x).tickSizeOuter(0))
                .attr("font-size", fontSize);
        }

        // Add the y-axis and label, and remove the domain line.
        svg.append("g")
            .attr("transform", `translate(${marginLeft},0)`)
            .call(d3.axisLeft(y).tickFormat((y) => y.toFixed()))
            .attr("font-size", fontSize)
            .call(g => g.select(".domain").remove())
            .call(g => g.append("text")
                .attr("x", -marginLeft)
                .attr("y", 10)
                .attr("fill", "currentColor")
                .attr("text-anchor", "start")
                .text("↑ Počet"));
    }
</script>

<script>
    const areaData = {
        'deaths': [
            { 'area': 'Středočeský kraj', 'count': 77 },
            { 'area': 'Jihočeský kraj', 'count': 42 },
            { 'area': 'Jihomoravský kraj', 'count': 38 },
            { 'area': 'Plzeňský kraj', 'count': 35 },
            { 'area': 'Pardubický kraj', 'count': 30 },
            { 'area': 'Ústecký kraj', 'count': 29 },
            { 'area': 'Zlínský kraj', 'count': 28 },
            { 'area': 'Moravskoslezský kraj', 'count': 27 },
            { 'area': 'Kraj Vysočina', 'count': 24 },
            { 'area': 'Olomoucký kraj', 'count': 23 },
            { 'area': 'Královéhradecký kraj', 'count': 20 },
            { 'area': 'Liberecký kraj', 'count': 20 },
            { 'area': 'Hlavní město Praha', 'count': 15 },
            { 'area': 'Karlovarský kraj', 'count': 13 }],
        'severe_injuries': [
            { 'area': 'Středočeský kraj', 'count': 295 },
            { 'area': 'Jihočeský kraj', 'count': 195 },
            { 'area': 'Jihomoravský kraj', 'count': 176 },
            { 'area': 'Plzeňský kraj', 'count': 83 },
            { 'area': 'Pardubický kraj', 'count': 69 },
            { 'area': 'Ústecký kraj', 'count': 137 },
            { 'area': 'Zlínský kraj', 'count': 78 },
            { 'area': 'Moravskoslezský kraj', 'count': 117 },
            { 'area': 'Kraj Vysočina', 'count': 65 },
            { 'area': 'Olomoucký kraj', 'count': 75 },
            { 'area': 'Královéhradecký kraj', 'count': 106 },
            { 'area': 'Liberecký kraj', 'count': 29 },
            { 'area': 'Hlavní město Praha', 'count': 167 },
            { 'area': 'Karlovarský kraj', 'count': 55 }]
    }

    function drawAreaChart(data, elementId, color) {
        d3.select(`#${elementId} .chart > *`).remove();

        const width = parseInt(d3.select(`#${elementId} .chart`).style("width"));
        const verticalScreen = (window.screen.width < window.screen.height);

        const height = verticalScreen ? width : Number(width * 1 / 2);
        // Specify the chart’s dimensions.
        const marginTop = verticalScreen ? 30 : 20;
        const marginRight = 50;
        const marginBottom = 130;
        const marginLeft = 50;

        const fontSize = verticalScreen ? "10px" : "14px";

        // Declare the x (horizontal position) scale.
        const x = d3.scaleBand()
            .domain(data.map(d => d.area)) // descending frequency
            .range([marginLeft, width - marginRight])
            .padding(verticalScreen ? 0.2 : 0.3);

        // Declare the y (vertical position) scale.
        const y = d3.scaleLinear()
            .domain([0, d3.max(data, (d) => d.count)])
            .range([height - marginBottom, marginTop]);

        // Create the SVG container.
        const svg = d3.select(`#${elementId} .chart`)
            .attr("style", `height: ${height}px;`)
            .append("svg")
            .attr("width", width)
            .attr("height", height)
            .attr("viewBox", [0, 0, width, height])
            .attr("style", "max-width: 100%; height: auto;");

        // Add a rect for each bar.
        svg.append("g")
            .selectAll()
            .data(data)
            .join("rect")
            .attr("x", (d) => x(d.area))
            .attr("y", (d) => y(d.count))
            .attr("height", (d) => y(0) - y(d.count))
            .attr("width", x.bandwidth())
            .attr("fill", color)
            .attr("rx", 3) // Rounded corners
            .append("title")
            .text((d, i) => {
                return `Kraj: ${d.area} \nPočet: ${d.count}`;
            });

        // Add text labels on top of bars
        if (verticalScreen) {
            svg.append("g")
                .selectAll()
                .data(data)
                .join("text")
                .attr("x", d => x(d.area))
                .attr("y", d => y(d.count))
                .attr("font-size", fontSize)
                .attr("transform", d => `translate(${x.bandwidth() / 2},-5)rotate(-45 ${x(d.area)} ${y(d.count)})`)
                .style("text-anchor", "start")
                .text(d => d.count);
        } else {
            svg.append("g")
                .selectAll()
                .data(data)
                .join("text")
                .attr("x", d => x(d.area) + x.bandwidth() / 2)
                .attr("y", d => y(d.count) - 5)  // 5px above the bar
                .attr("text-anchor", "middle")
                .attr("font-size", fontSize)
                .text(d => d.count);
        }

        // Add the x-axis and label.
        svg.append("g")
            .attr("transform", `translate(0,${height - marginBottom})`)
            .call(d3.axisBottom(x).tickSizeOuter(0))
            .call(g => g.select(".domain").remove())
            .call(g => {
                g.selectAll(".tick text")
                    .attr("transform", `translate(${x.bandwidth() / (verticalScreen ? 2 : 4)},${verticalScreen ? 0 : 5})rotate(45)`)
                    .attr("text-anchor", "start")
                    .attr("font-size", fontSize);
                g.select(".domain").remove();
            });


        // Add the y-axis and label, and remove the domain line.
        svg.append("g")
            .attr("transform", `translate(${marginLeft},0)`)
            .call(d3.axisLeft(y).tickFormat((y) => y.toFixed()))
            .attr("font-size", fontSize)
            .call(g => g.select(".domain").remove())
            .call(g => g.append("text")
                .attr("x", -marginLeft)
                .attr("y", 10)
                .attr("fill", "currentColor")
                .attr("text-anchor", "start")
                .text("↑ Počet"));
    }
</script>

<script>
    const areaMonthData = {
        'deaths': [{ 'area': 'Hlavní město Praha', 'month': 1, 'count': 1 },
        { 'area': 'Středočeský kraj', 'month': 1, 'count': 7 },
        { 'area': 'Jihočeský kraj', 'month': 1, 'count': 6 },
        { 'area': 'Plzeňský kraj', 'month': 1, 'count': 1 },
        { 'area': 'Ústecký kraj', 'month': 1, 'count': 1 },
        { 'area': 'Královéhradecký kraj', 'month': 1, 'count': 1 },
        { 'area': 'Jihomoravský kraj', 'month': 1, 'count': 0 },
        { 'area': 'Moravskoslezský kraj', 'month': 1, 'count': 1 },
        { 'area': 'Olomoucký kraj', 'month': 1, 'count': 4 },
        { 'area': 'Zlínský kraj', 'month': 1, 'count': 2 },
        { 'area': 'Kraj Vysočina', 'month': 1, 'count': 1 },
        { 'area': 'Pardubický kraj', 'month': 1, 'count': 2 },
        { 'area': 'Liberecký kraj', 'month': 1, 'count': 1 },
        { 'area': 'Karlovarský kraj', 'month': 1, 'count': 0 },
        { 'area': 'Hlavní město Praha', 'month': 2, 'count': 1 },
        { 'area': 'Středočeský kraj', 'month': 2, 'count': 3 },
        { 'area': 'Jihočeský kraj', 'month': 2, 'count': 3 },
        { 'area': 'Plzeňský kraj', 'month': 2, 'count': 5 },
        { 'area': 'Ústecký kraj', 'month': 2, 'count': 1 },
        { 'area': 'Královéhradecký kraj', 'month': 2, 'count': 0 },
        { 'area': 'Jihomoravský kraj', 'month': 2, 'count': 2 },
        { 'area': 'Moravskoslezský kraj', 'month': 2, 'count': 6 },
        { 'area': 'Olomoucký kraj', 'month': 2, 'count': 2 },
        { 'area': 'Zlínský kraj', 'month': 2, 'count': 1 },
        { 'area': 'Kraj Vysočina', 'month': 2, 'count': 1 },
        { 'area': 'Pardubický kraj', 'month': 2, 'count': 0 },
        { 'area': 'Liberecký kraj', 'month': 2, 'count': 0 },
        { 'area': 'Karlovarský kraj', 'month': 2, 'count': 1 },
        { 'area': 'Hlavní město Praha', 'month': 3, 'count': 0 },
        { 'area': 'Středočeský kraj', 'month': 3, 'count': 5 },
        { 'area': 'Jihočeský kraj', 'month': 3, 'count': 4 },
        { 'area': 'Plzeňský kraj', 'month': 3, 'count': 1 },
        { 'area': 'Ústecký kraj', 'month': 3, 'count': 3 },
        { 'area': 'Královéhradecký kraj', 'month': 3, 'count': 0 },
        { 'area': 'Jihomoravský kraj', 'month': 3, 'count': 2 },
        { 'area': 'Moravskoslezský kraj', 'month': 3, 'count': 0 },
        { 'area': 'Olomoucký kraj', 'month': 3, 'count': 0 },
        { 'area': 'Zlínský kraj', 'month': 3, 'count': 1 },
        { 'area': 'Kraj Vysočina', 'month': 3, 'count': 1 },
        { 'area': 'Pardubický kraj', 'month': 3, 'count': 1 },
        { 'area': 'Liberecký kraj', 'month': 3, 'count': 1 },
        { 'area': 'Karlovarský kraj', 'month': 3, 'count': 0 },
        { 'area': 'Hlavní město Praha', 'month': 4, 'count': 4 },
        { 'area': 'Středočeský kraj', 'month': 4, 'count': 7 },
        { 'area': 'Jihočeský kraj', 'month': 4, 'count': 3 },
        { 'area': 'Plzeňský kraj', 'month': 4, 'count': 1 },
        { 'area': 'Ústecký kraj', 'month': 4, 'count': 1 },
        { 'area': 'Královéhradecký kraj', 'month': 4, 'count': 3 },
        { 'area': 'Jihomoravský kraj', 'month': 4, 'count': 6 },
        { 'area': 'Moravskoslezský kraj', 'month': 4, 'count': 3 },
        { 'area': 'Olomoucký kraj', 'month': 4, 'count': 0 },
        { 'area': 'Zlínský kraj', 'month': 4, 'count': 1 },
        { 'area': 'Kraj Vysočina', 'month': 4, 'count': 2 },
        { 'area': 'Pardubický kraj', 'month': 4, 'count': 2 },
        { 'area': 'Liberecký kraj', 'month': 4, 'count': 1 },
        { 'area': 'Karlovarský kraj', 'month': 4, 'count': 4 },
        { 'area': 'Hlavní město Praha', 'month': 5, 'count': 1 },
        { 'area': 'Středočeský kraj', 'month': 5, 'count': 4 },
        { 'area': 'Jihočeský kraj', 'month': 5, 'count': 1 },
        { 'area': 'Plzeňský kraj', 'month': 5, 'count': 3 },
        { 'area': 'Ústecký kraj', 'month': 5, 'count': 0 },
        { 'area': 'Královéhradecký kraj', 'month': 5, 'count': 1 },
        { 'area': 'Jihomoravský kraj', 'month': 5, 'count': 2 },
        { 'area': 'Moravskoslezský kraj', 'month': 5, 'count': 0 },
        { 'area': 'Olomoucký kraj', 'month': 5, 'count': 1 },
        { 'area': 'Zlínský kraj', 'month': 5, 'count': 0 },
        { 'area': 'Kraj Vysočina', 'month': 5, 'count': 2 },
        { 'area': 'Pardubický kraj', 'month': 5, 'count': 3 },
        { 'area': 'Liberecký kraj', 'month': 5, 'count': 1 },
        { 'area': 'Karlovarský kraj', 'month': 5, 'count': 1 },
        { 'area': 'Hlavní město Praha', 'month': 6, 'count': 2 },
        { 'area': 'Středočeský kraj', 'month': 6, 'count': 10 },
        { 'area': 'Jihočeský kraj', 'month': 6, 'count': 2 },
        { 'area': 'Plzeňský kraj', 'month': 6, 'count': 8 },
        { 'area': 'Ústecký kraj', 'month': 6, 'count': 4 },
        { 'area': 'Královéhradecký kraj', 'month': 6, 'count': 5 },
        { 'area': 'Jihomoravský kraj', 'month': 6, 'count': 2 },
        { 'area': 'Moravskoslezský kraj', 'month': 6, 'count': 1 },
        { 'area': 'Olomoucký kraj', 'month': 6, 'count': 4 },
        { 'area': 'Zlínský kraj', 'month': 6, 'count': 3 },
        { 'area': 'Kraj Vysočina', 'month': 6, 'count': 1 },
        { 'area': 'Pardubický kraj', 'month': 6, 'count': 3 },
        { 'area': 'Liberecký kraj', 'month': 6, 'count': 1 },
        { 'area': 'Karlovarský kraj', 'month': 6, 'count': 0 },
        { 'area': 'Hlavní město Praha', 'month': 7, 'count': 0 },
        { 'area': 'Středočeský kraj', 'month': 7, 'count': 9 },
        { 'area': 'Jihočeský kraj', 'month': 7, 'count': 4 },
        { 'area': 'Plzeňský kraj', 'month': 7, 'count': 0 },
        { 'area': 'Ústecký kraj', 'month': 7, 'count': 1 },
        { 'area': 'Královéhradecký kraj', 'month': 7, 'count': 0 },
        { 'area': 'Jihomoravský kraj', 'month': 7, 'count': 3 },
        { 'area': 'Moravskoslezský kraj', 'month': 7, 'count': 1 },
        { 'area': 'Olomoucký kraj', 'month': 7, 'count': 2 },
        { 'area': 'Zlínský kraj', 'month': 7, 'count': 3 },
        { 'area': 'Kraj Vysočina', 'month': 7, 'count': 2 },
        { 'area': 'Pardubický kraj', 'month': 7, 'count': 6 },
        { 'area': 'Liberecký kraj', 'month': 7, 'count': 2 },
        { 'area': 'Karlovarský kraj', 'month': 7, 'count': 2 },
        { 'area': 'Hlavní město Praha', 'month': 8, 'count': 2 },
        { 'area': 'Středočeský kraj', 'month': 8, 'count': 9 },
        { 'area': 'Jihočeský kraj', 'month': 8, 'count': 6 },
        { 'area': 'Plzeňský kraj', 'month': 8, 'count': 6 },
        { 'area': 'Ústecký kraj', 'month': 8, 'count': 5 },
        { 'area': 'Královéhradecký kraj', 'month': 8, 'count': 0 },
        { 'area': 'Jihomoravský kraj', 'month': 8, 'count': 2 },
        { 'area': 'Moravskoslezský kraj', 'month': 8, 'count': 5 },
        { 'area': 'Olomoucký kraj', 'month': 8, 'count': 2 },
        { 'area': 'Zlínský kraj', 'month': 8, 'count': 3 },
        { 'area': 'Kraj Vysočina', 'month': 8, 'count': 5 },
        { 'area': 'Pardubický kraj', 'month': 8, 'count': 3 },
        { 'area': 'Liberecký kraj', 'month': 8, 'count': 4 },
        { 'area': 'Karlovarský kraj', 'month': 8, 'count': 1 },
        { 'area': 'Hlavní město Praha', 'month': 9, 'count': 2 },
        { 'area': 'Středočeský kraj', 'month': 9, 'count': 6 },
        { 'area': 'Jihočeský kraj', 'month': 9, 'count': 7 },
        { 'area': 'Plzeňský kraj', 'month': 9, 'count': 3 },
        { 'area': 'Ústecký kraj', 'month': 9, 'count': 4 },
        { 'area': 'Královéhradecký kraj', 'month': 9, 'count': 7 },
        { 'area': 'Jihomoravský kraj', 'month': 9, 'count': 10 },
        { 'area': 'Moravskoslezský kraj', 'month': 9, 'count': 3 },
        { 'area': 'Olomoucký kraj', 'month': 9, 'count': 2 },
        { 'area': 'Zlínský kraj', 'month': 9, 'count': 4 },
        { 'area': 'Kraj Vysočina', 'month': 9, 'count': 2 },
        { 'area': 'Pardubický kraj', 'month': 9, 'count': 1 },
        { 'area': 'Liberecký kraj', 'month': 9, 'count': 5 },
        { 'area': 'Karlovarský kraj', 'month': 9, 'count': 1 },
        { 'area': 'Hlavní město Praha', 'month': 10, 'count': 1 },
        { 'area': 'Středočeský kraj', 'month': 10, 'count': 10 },
        { 'area': 'Jihočeský kraj', 'month': 10, 'count': 2 },
        { 'area': 'Plzeňský kraj', 'month': 10, 'count': 1 },
        { 'area': 'Ústecký kraj', 'month': 10, 'count': 2 },
        { 'area': 'Královéhradecký kraj', 'month': 10, 'count': 2 },
        { 'area': 'Jihomoravský kraj', 'month': 10, 'count': 1 },
        { 'area': 'Moravskoslezský kraj', 'month': 10, 'count': 2 },
        { 'area': 'Olomoucký kraj', 'month': 10, 'count': 0 },
        { 'area': 'Zlínský kraj', 'month': 10, 'count': 4 },
        { 'area': 'Kraj Vysočina', 'month': 10, 'count': 2 },
        { 'area': 'Pardubický kraj', 'month': 10, 'count': 3 },
        { 'area': 'Liberecký kraj', 'month': 10, 'count': 2 },
        { 'area': 'Karlovarský kraj', 'month': 10, 'count': 2 },
        { 'area': 'Hlavní město Praha', 'month': 11, 'count': 1 },
        { 'area': 'Středočeský kraj', 'month': 11, 'count': 4 },
        { 'area': 'Jihočeský kraj', 'month': 11, 'count': 1 },
        { 'area': 'Plzeňský kraj', 'month': 11, 'count': 5 },
        { 'area': 'Ústecký kraj', 'month': 11, 'count': 3 },
        { 'area': 'Královéhradecký kraj', 'month': 11, 'count': 0 },
        { 'area': 'Jihomoravský kraj', 'month': 11, 'count': 4 },
        { 'area': 'Moravskoslezský kraj', 'month': 11, 'count': 1 },
        { 'area': 'Olomoucký kraj', 'month': 11, 'count': 4 },
        { 'area': 'Zlínský kraj', 'month': 11, 'count': 1 },
        { 'area': 'Kraj Vysočina', 'month': 11, 'count': 4 },
        { 'area': 'Pardubický kraj', 'month': 11, 'count': 3 },
        { 'area': 'Liberecký kraj', 'month': 11, 'count': 2 },
        { 'area': 'Karlovarský kraj', 'month': 11, 'count': 0 },
        { 'area': 'Hlavní město Praha', 'month': 12, 'count': 0 },
        { 'area': 'Středočeský kraj', 'month': 12, 'count': 3 },
        { 'area': 'Jihočeský kraj', 'month': 12, 'count': 3 },
        { 'area': 'Plzeňský kraj', 'month': 12, 'count': 1 },
        { 'area': 'Ústecký kraj', 'month': 12, 'count': 4 },
        { 'area': 'Královéhradecký kraj', 'month': 12, 'count': 1 },
        { 'area': 'Jihomoravský kraj', 'month': 12, 'count': 4 },
        { 'area': 'Moravskoslezský kraj', 'month': 12, 'count': 4 },
        { 'area': 'Olomoucký kraj', 'month': 12, 'count': 2 },
        { 'area': 'Zlínský kraj', 'month': 12, 'count': 5 },
        { 'area': 'Kraj Vysočina', 'month': 12, 'count': 1 },
        { 'area': 'Pardubický kraj', 'month': 12, 'count': 3 },
        { 'area': 'Liberecký kraj', 'month': 12, 'count': 0 },
        { 'area': 'Karlovarský kraj', 'month': 12, 'count': 1 }],
        'severe_injuries': [{ 'area': 'Hlavní město Praha', 'month': 1, 'count': 20 },
        { 'area': 'Středočeský kraj', 'month': 1, 'count': 26 },
        { 'area': 'Jihočeský kraj', 'month': 1, 'count': 14 },
        { 'area': 'Plzeňský kraj', 'month': 1, 'count': 6 },
        { 'area': 'Ústecký kraj', 'month': 1, 'count': 6 },
        { 'area': 'Královéhradecký kraj', 'month': 1, 'count': 12 },
        { 'area': 'Jihomoravský kraj', 'month': 1, 'count': 15 },
        { 'area': 'Moravskoslezský kraj', 'month': 1, 'count': 11 },
        { 'area': 'Olomoucký kraj', 'month': 1, 'count': 6 },
        { 'area': 'Zlínský kraj', 'month': 1, 'count': 5 },
        { 'area': 'Kraj Vysočina', 'month': 1, 'count': 3 },
        { 'area': 'Pardubický kraj', 'month': 1, 'count': 3 },
        { 'area': 'Liberecký kraj', 'month': 1, 'count': 1 },
        { 'area': 'Karlovarský kraj', 'month': 1, 'count': 3 },
        { 'area': 'Hlavní město Praha', 'month': 2, 'count': 8 },
        { 'area': 'Středočeský kraj', 'month': 2, 'count': 12 },
        { 'area': 'Jihočeský kraj', 'month': 2, 'count': 9 },
        { 'area': 'Plzeňský kraj', 'month': 2, 'count': 8 },
        { 'area': 'Ústecký kraj', 'month': 2, 'count': 9 },
        { 'area': 'Královéhradecký kraj', 'month': 2, 'count': 4 },
        { 'area': 'Jihomoravský kraj', 'month': 2, 'count': 10 },
        { 'area': 'Moravskoslezský kraj', 'month': 2, 'count': 8 },
        { 'area': 'Olomoucký kraj', 'month': 2, 'count': 3 },
        { 'area': 'Zlínský kraj', 'month': 2, 'count': 5 },
        { 'area': 'Kraj Vysočina', 'month': 2, 'count': 1 },
        { 'area': 'Pardubický kraj', 'month': 2, 'count': 3 },
        { 'area': 'Liberecký kraj', 'month': 2, 'count': 1 },
        { 'area': 'Karlovarský kraj', 'month': 2, 'count': 3 },
        { 'area': 'Hlavní město Praha', 'month': 3, 'count': 14 },
        { 'area': 'Středočeský kraj', 'month': 3, 'count': 14 },
        { 'area': 'Jihočeský kraj', 'month': 3, 'count': 13 },
        { 'area': 'Plzeňský kraj', 'month': 3, 'count': 9 },
        { 'area': 'Ústecký kraj', 'month': 3, 'count': 13 },
        { 'area': 'Královéhradecký kraj', 'month': 3, 'count': 6 },
        { 'area': 'Jihomoravský kraj', 'month': 3, 'count': 10 },
        { 'area': 'Moravskoslezský kraj', 'month': 3, 'count': 10 },
        { 'area': 'Olomoucký kraj', 'month': 3, 'count': 2 },
        { 'area': 'Zlínský kraj', 'month': 3, 'count': 13 },
        { 'area': 'Kraj Vysočina', 'month': 3, 'count': 0 },
        { 'area': 'Pardubický kraj', 'month': 3, 'count': 7 },
        { 'area': 'Liberecký kraj', 'month': 3, 'count': 3 },
        { 'area': 'Karlovarský kraj', 'month': 3, 'count': 3 },
        { 'area': 'Hlavní město Praha', 'month': 4, 'count': 16 },
        { 'area': 'Středočeský kraj', 'month': 4, 'count': 31 },
        { 'area': 'Jihočeský kraj', 'month': 4, 'count': 16 },
        { 'area': 'Plzeňský kraj', 'month': 4, 'count': 6 },
        { 'area': 'Ústecký kraj', 'month': 4, 'count': 15 },
        { 'area': 'Královéhradecký kraj', 'month': 4, 'count': 7 },
        { 'area': 'Jihomoravský kraj', 'month': 4, 'count': 19 },
        { 'area': 'Moravskoslezský kraj', 'month': 4, 'count': 9 },
        { 'area': 'Olomoucký kraj', 'month': 4, 'count': 10 },
        { 'area': 'Zlínský kraj', 'month': 4, 'count': 6 },
        { 'area': 'Kraj Vysočina', 'month': 4, 'count': 9 },
        { 'area': 'Pardubický kraj', 'month': 4, 'count': 4 },
        { 'area': 'Liberecký kraj', 'month': 4, 'count': 1 },
        { 'area': 'Karlovarský kraj', 'month': 4, 'count': 6 },
        { 'area': 'Hlavní město Praha', 'month': 5, 'count': 17 },
        { 'area': 'Středočeský kraj', 'month': 5, 'count': 25 },
        { 'area': 'Jihočeský kraj', 'month': 5, 'count': 19 },
        { 'area': 'Plzeňský kraj', 'month': 5, 'count': 11 },
        { 'area': 'Ústecký kraj', 'month': 5, 'count': 9 },
        { 'area': 'Královéhradecký kraj', 'month': 5, 'count': 10 },
        { 'area': 'Jihomoravský kraj', 'month': 5, 'count': 19 },
        { 'area': 'Moravskoslezský kraj', 'month': 5, 'count': 8 },
        { 'area': 'Olomoucký kraj', 'month': 5, 'count': 8 },
        { 'area': 'Zlínský kraj', 'month': 5, 'count': 5 },
        { 'area': 'Kraj Vysočina', 'month': 5, 'count': 4 },
        { 'area': 'Pardubický kraj', 'month': 5, 'count': 10 },
        { 'area': 'Liberecký kraj', 'month': 5, 'count': 4 },
        { 'area': 'Karlovarský kraj', 'month': 5, 'count': 2 },
        { 'area': 'Hlavní město Praha', 'month': 6, 'count': 12 },
        { 'area': 'Středočeský kraj', 'month': 6, 'count': 26 },
        { 'area': 'Jihočeský kraj', 'month': 6, 'count': 23 },
        { 'area': 'Plzeňský kraj', 'month': 6, 'count': 11 },
        { 'area': 'Ústecký kraj', 'month': 6, 'count': 16 },
        { 'area': 'Královéhradecký kraj', 'month': 6, 'count': 11 },
        { 'area': 'Jihomoravský kraj', 'month': 6, 'count': 20 },
        { 'area': 'Moravskoslezský kraj', 'month': 6, 'count': 9 },
        { 'area': 'Olomoucký kraj', 'month': 6, 'count': 13 },
        { 'area': 'Zlínský kraj', 'month': 6, 'count': 5 },
        { 'area': 'Kraj Vysočina', 'month': 6, 'count': 7 },
        { 'area': 'Pardubický kraj', 'month': 6, 'count': 6 },
        { 'area': 'Liberecký kraj', 'month': 6, 'count': 2 },
        { 'area': 'Karlovarský kraj', 'month': 6, 'count': 9 },
        { 'area': 'Hlavní město Praha', 'month': 7, 'count': 13 },
        { 'area': 'Středočeský kraj', 'month': 7, 'count': 29 },
        { 'area': 'Jihočeský kraj', 'month': 7, 'count': 30 },
        { 'area': 'Plzeňský kraj', 'month': 7, 'count': 9 },
        { 'area': 'Ústecký kraj', 'month': 7, 'count': 13 },
        { 'area': 'Královéhradecký kraj', 'month': 7, 'count': 7 },
        { 'area': 'Jihomoravský kraj', 'month': 7, 'count': 20 },
        { 'area': 'Moravskoslezský kraj', 'month': 7, 'count': 12 },
        { 'area': 'Olomoucký kraj', 'month': 7, 'count': 4 },
        { 'area': 'Zlínský kraj', 'month': 7, 'count': 7 },
        { 'area': 'Kraj Vysočina', 'month': 7, 'count': 11 },
        { 'area': 'Pardubický kraj', 'month': 7, 'count': 7 },
        { 'area': 'Liberecký kraj', 'month': 7, 'count': 4 },
        { 'area': 'Karlovarský kraj', 'month': 7, 'count': 2 },
        { 'area': 'Hlavní město Praha', 'month': 8, 'count': 12 },
        { 'area': 'Středočeský kraj', 'month': 8, 'count': 36 },
        { 'area': 'Jihočeský kraj', 'month': 8, 'count': 21 },
        { 'area': 'Plzeňský kraj', 'month': 8, 'count': 8 },
        { 'area': 'Ústecký kraj', 'month': 8, 'count': 21 },
        { 'area': 'Královéhradecký kraj', 'month': 8, 'count': 12 },
        { 'area': 'Jihomoravský kraj', 'month': 8, 'count': 17 },
        { 'area': 'Moravskoslezský kraj', 'month': 8, 'count': 19 },
        { 'area': 'Olomoucký kraj', 'month': 8, 'count': 3 },
        { 'area': 'Zlínský kraj', 'month': 8, 'count': 17 },
        { 'area': 'Kraj Vysočina', 'month': 8, 'count': 10 },
        { 'area': 'Pardubický kraj', 'month': 8, 'count': 14 },
        { 'area': 'Liberecký kraj', 'month': 8, 'count': 3 },
        { 'area': 'Karlovarský kraj', 'month': 8, 'count': 8 },
        { 'area': 'Hlavní město Praha', 'month': 9, 'count': 13 },
        { 'area': 'Středočeský kraj', 'month': 9, 'count': 29 },
        { 'area': 'Jihočeský kraj', 'month': 9, 'count': 24 },
        { 'area': 'Plzeňský kraj', 'month': 9, 'count': 5 },
        { 'area': 'Ústecký kraj', 'month': 9, 'count': 11 },
        { 'area': 'Královéhradecký kraj', 'month': 9, 'count': 11 },
        { 'area': 'Jihomoravský kraj', 'month': 9, 'count': 18 },
        { 'area': 'Moravskoslezský kraj', 'month': 9, 'count': 7 },
        { 'area': 'Olomoucký kraj', 'month': 9, 'count': 10 },
        { 'area': 'Zlínský kraj', 'month': 9, 'count': 7 },
        { 'area': 'Kraj Vysočina', 'month': 9, 'count': 7 },
        { 'area': 'Pardubický kraj', 'month': 9, 'count': 7 },
        { 'area': 'Liberecký kraj', 'month': 9, 'count': 2 },
        { 'area': 'Karlovarský kraj', 'month': 9, 'count': 7 },
        { 'area': 'Hlavní město Praha', 'month': 10, 'count': 18 },
        { 'area': 'Středočeský kraj', 'month': 10, 'count': 30 },
        { 'area': 'Jihočeský kraj', 'month': 10, 'count': 12 },
        { 'area': 'Plzeňský kraj', 'month': 10, 'count': 5 },
        { 'area': 'Ústecký kraj', 'month': 10, 'count': 8 },
        { 'area': 'Královéhradecký kraj', 'month': 10, 'count': 11 },
        { 'area': 'Jihomoravský kraj', 'month': 10, 'count': 7 },
        { 'area': 'Moravskoslezský kraj', 'month': 10, 'count': 12 },
        { 'area': 'Olomoucký kraj', 'month': 10, 'count': 5 },
        { 'area': 'Zlínský kraj', 'month': 10, 'count': 5 },
        { 'area': 'Kraj Vysočina', 'month': 10, 'count': 8 },
        { 'area': 'Pardubický kraj', 'month': 10, 'count': 4 },
        { 'area': 'Liberecký kraj', 'month': 10, 'count': 3 },
        { 'area': 'Karlovarský kraj', 'month': 10, 'count': 8 },
        { 'area': 'Hlavní město Praha', 'month': 11, 'count': 13 },
        { 'area': 'Středočeský kraj', 'month': 11, 'count': 15 },
        { 'area': 'Jihočeský kraj', 'month': 11, 'count': 6 },
        { 'area': 'Plzeňský kraj', 'month': 11, 'count': 5 },
        { 'area': 'Ústecký kraj', 'month': 11, 'count': 7 },
        { 'area': 'Královéhradecký kraj', 'month': 11, 'count': 6 },
        { 'area': 'Jihomoravský kraj', 'month': 11, 'count': 9 },
        { 'area': 'Moravskoslezský kraj', 'month': 11, 'count': 5 },
        { 'area': 'Olomoucký kraj', 'month': 11, 'count': 7 },
        { 'area': 'Zlínský kraj', 'month': 11, 'count': 1 },
        { 'area': 'Kraj Vysočina', 'month': 11, 'count': 3 },
        { 'area': 'Pardubický kraj', 'month': 11, 'count': 2 },
        { 'area': 'Liberecký kraj', 'month': 11, 'count': 2 },
        { 'area': 'Karlovarský kraj', 'month': 11, 'count': 2 },
        { 'area': 'Hlavní město Praha', 'month': 12, 'count': 11 },
        { 'area': 'Středočeský kraj', 'month': 12, 'count': 22 },
        { 'area': 'Jihočeský kraj', 'month': 12, 'count': 8 },
        { 'area': 'Plzeňský kraj', 'month': 12, 'count': 0 },
        { 'area': 'Ústecký kraj', 'month': 12, 'count': 9 },
        { 'area': 'Královéhradecký kraj', 'month': 12, 'count': 9 },
        { 'area': 'Jihomoravský kraj', 'month': 12, 'count': 12 },
        { 'area': 'Moravskoslezský kraj', 'month': 12, 'count': 7 },
        { 'area': 'Olomoucký kraj', 'month': 12, 'count': 4 },
        { 'area': 'Zlínský kraj', 'month': 12, 'count': 2 },
        { 'area': 'Kraj Vysočina', 'month': 12, 'count': 2 },
        { 'area': 'Pardubický kraj', 'month': 12, 'count': 2 },
        { 'area': 'Liberecký kraj', 'month': 12, 'count': 3 },
        { 'area': 'Karlovarský kraj', 'month': 12, 'count': 2 }]
    }

    const monthNames = {
        1: "Leden",
        2: "Únor",
        3: "Březen",
        4: "Duben",
        5: "Květen",
        6: "Červen",
        7: "Červenec",
        8: "Srpen",
        9: "Září",
        10: "Říjen",
        11: "Listopad",
        12: "Prosinec"
    };

    const areaNames = [
        "Hlavní město Praha",
        "Středočeský kraj",
        "Jihočeský kraj",
        "Plzeňský kraj",
        "Karlovarský kraj",
        "Ústecký kraj",
        "Liberecký kraj",
        "Královéhradecký kraj",
        "Pardubický kraj",
        "Kraj Vysočina",
        "Jihomoravský kraj",
        "Olomoucký kraj",
        "Moravskoslezský kraj",
        "Zlínský kraj",
    ]
    const rawColors = ['#F3C300', '#875692', '#F38400', '#A1CAF1', '#BE0032', '#C2B280', '#848482', '#008856', '#E68FAC', '#0067A5', '#F99379', '#604E97', '#F6A600', '#B3446C', '#DCD300', '#882D17', '#8DB600', '#654522', '#E25822', '#2B3D26']
    const areaColors = Object.fromEntries(
        areaNames.map((area, i) => [area, rawColors[i % rawColors.length]])
    );

    function drawAreaMonthChart(data, elementId, hiddenAreas) {
        d3.select(`#${elementId} .chart > *`).remove();

        const filteredData = data.filter(d => !hiddenAreas.has(d.area));

        const width = parseInt(d3.select(`#${elementId} .chart`).style("width"));
        const verticalScreen = (window.screen.width < window.screen.height);

        // Determine the series that need to be stacked.
        const series = d3.stack()
            .keys(d3.union(filteredData.map(d => d.area))) // distinct series keys, in input order
            .value(([, D], key) => D.get(key).count) // get value for each series key and stack
            (d3.index(filteredData, d => d.month, d => d.area)); // group by stack then series key

        const areas = new Set(filteredData.map(d => d.area));

        const fontSize = verticalScreen ? 10 : 14;
        const rowHeight = Math.max(areas.size * 7, fontSize * 1.3)

        // Specify the chart’s dimensions.
        const marginTop = (areaNames.length + 1) * fontSize * 1.3 + 10;
        const marginRight = 10;
        const marginBottom = 7;
        const marginLeft = verticalScreen ? 55 : 80;
        const height = series[0].length * rowHeight + marginTop + marginBottom;

        // Prepare the scales for positional and color encodings.
        // Fx encodes the area.
        const fy = d3.scaleBand()
            .domain(new Set(filteredData.map(d => d.month)))
            .rangeRound([marginTop, height - marginBottom])
            .paddingInner(0.05);

        // Create the scales.
        const y = d3.scaleBand()
            .domain(areas)
            .rangeRound([0, fy.bandwidth()])
            .paddingInner(0.2);
        const x = d3.scaleLinear()
            .domain([0, d3.max(data, d => d.count)])
            .rangeRound([marginLeft, width - marginRight]);

        // Create the SVG container.
        const svg = d3.select(`#${elementId} .chart`)
            .attr("style", `height: ${height}px;`)
            .append("svg")
            .attr("width", width)
            .attr("height", height)
            .attr("viewBox", [0, 0, width, height])
            .attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;");

        // Append the horizontal axis multiple times.
        svg.append("g")
            .attr("transform", `translate(0,${marginTop})`)
            .call(d3.axisTop(x).tickFormat(d3.format("d")).tickSizeOuter(0))
            .call(g => g.selectAll(".domain").remove())
            // vertical lines
            .call(g => g.selectAll(".tick line").clone()
                .attr("y2", height - marginTop - marginBottom)
                .attr("stroke-opacity", 0.1))
            .attr("font-size", fontSize);
        
        if (hiddenAreas.size < 10) {
            fy.domain().forEach((month, i) => {
                svg.append("g")
                    .attr("transform", `translate(0,${fy(month) + fy.bandwidth() + fontSize/4*3})`)
                    .call(d3.axisTop(x).tickFormat(d3.format("d")).tickSizeOuter(0).tickSize(0))
                    .call(g => g.selectAll(".domain").remove())
                    .call(g => g.selectAll("text").nodes()[0]?.remove())  // Remove first label
                    .attr("font-size", fontSize)
                    .attr("opacity", 0.4);
            });
        }

        // Append the vertical axis.
        svg.append("g")
            .attr("transform", `translate(${marginLeft},0)`)
            .call(d3.axisLeft(fy).tickSizeOuter(0))
            .selectAll("text")
            .attr("font-size", fontSize)
            .text(d => monthNames[d]);

        // Append a group for each area, and a rect for each class.
        svg.append("g")
            .selectAll()
            .data(d3.group(filteredData, d => d.month))
            .join("g")
            .attr("transform", ([area]) => `translate(0,${fy(area)})`)
            .selectAll()
            .data(([, d]) => d)
            .join("rect")
            .attr("x", d => x(0))
            .attr("y", d => y(d.area))
            .attr("width", d => Math.abs(x(0) - x(d.count)))
            .attr("height", y.bandwidth())
            .attr("fill", d => areaColors[d.area])
            .attr("rx", 3)
            .append("title")
            .text(d => `${d.area}\nMěsíc: ${monthNames[d.month]}\nPočet: ${d.count}`);

        // Create legend
        const legend = svg.append("g")
            .attr("transform", `translate(0, 0)`);
        areaNames.forEach((areaName, i) => {
            const legendRow = legend.append("g")
                .attr("transform", `translate(${marginLeft}, ${i * fontSize * 1.3})`)
                .style("cursor", "pointer")
                .on("click", function () {
                    if (hiddenAreas.has(areaName)) {
                        hiddenAreas.delete(areaName);
                    } else if (hiddenAreas.size < areaNames.length - 1) {
                        hiddenAreas.add(areaName);
                    }
                    drawAreaMonthChart(data, elementId, hiddenAreas);
                });

            // Add colored rectangle
            legendRow.append("rect")
                .attr("width", `${fontSize}px`)
                .attr("height", `${fontSize}px`)
                .attr("fill", areaColors[areaName])
                .attr("stroke", areaColors[areaName])
                .attr("fill-opacity", hiddenAreas.has(areaName) ? 0.4 : 1)
                .attr("rx", 3);

            // Add text label
            legendRow.append("text")
                .attr("x", 25)
                .attr("y", 9)
                .attr("dy", "0.32em")
                .style("font-size", `${fontSize}px`)
                .style("text-decoration", hiddenAreas.has(areaName) ? "line-through" : "none")
                .attr("opacity", hiddenAreas.has(areaName) ? 0.4 : 1)
                .text(areaName);
        });
    }
</script>

<script>
    // brewer_lakota: [, , "#931e18", "#da7901", "#247d3f", "#20235b"],
    const colors = {
        deaths: "#04a3bd",
        severe_injuries: "#f0be3d",
    }
</script>

<p>Co je Vize nula, se můžeme dočíst na webu <a href="https://cdv.gov.cz/vizenula/" target="_blank" rel="noopener">Centra dopravního výzkumu</a>
:</p>
<blockquote>
<p>VIZE NULA je dlouhodobá vize bezpečného silničního provozu, ve kterém nikdo nezemře ani neutrpí vážné zranění při dopravní nehodě.</p>
</blockquote>
<p>Splnění Vize nula by tedy znamenalo, že by počet úmrtí i počet těžkých zranění v silniční dopravě byly rovny nule, odtud patrně název této vize. Chtěl jsem se proto podívat, jak si v tomto ohledu vedeme.</p>
<p>V roce 2025 zemřelo v České republice v silniční dopravě 421 lidí a 1647 bylo těžce zraněno. Obě čísla jsou významně větší než 0. V roce 2025 Vize nula v ČR splněna nebyla. Počet úmrtí alespoň rok od roku klesá, i když zoufale pomalu.</p>

<!-- static chart -->
<div class="chart-box" id="death_progress_chart">
    <div class="chart-header">
        <img class="chart-header-logo light" alt="logo" width="100" height="100" src="/images/logo-light.svg">
        <img class="chart-header-logo dark" alt="logo" width="100" height="100" src="/images/logo-dark.svg">
        <h1>Vývoj počtu úmrtí v silničním provozu od roku 2016 do 2025</h1>
        <p>Každý sloupek znázorňuje svou výškou počet úmrtí v silničním provozu v České republice v daném roce.</p>
    </div>
    <div class="chart"></div>
    <div class="chart-footer">
        <span class="source">Zdroj: <a href="https://policie.gov.cz/clanek/statistika-nehodovosti.aspx?q=Y2hudW09Mg%3d%3d" target="_blank">Policie ČR</a></span>
    </div>
</div>
<script>
    drawProgressChart(progressData.deaths, "death_progress_chart", colors.deaths);
</script>

<p>Počet těžkých zranění bohužel od roku 2021 spíše stagnuje, v roce 2025 dokonce oproti roku 2024 narostl.</p>

<!-- static chart -->
<div class="chart-box" id="severe_injury_progress_chart">
    <div class="chart-header">
        <img class="chart-header-logo light" alt="logo" width="100" height="100" src="/images/logo-light.svg">
        <img class="chart-header-logo dark" alt="logo" width="100" height="100" src="/images/logo-dark.svg">
        <h1>Vývoj počtu těžkých zranění v silničním provozu od roku 2016 do 2025</h1>
        <p>Každý sloupek znázorňuje svou výškou počet těžkých zranění v silničním provozu v České republice v daném roce.</p>
    </div>
    <div class="chart"></div>
    <div class="chart-footer">
        <span class="source">Zdroj: <a href="https://policie.gov.cz/clanek/statistika-nehodovosti.aspx?q=Y2hudW09Mg%3d%3d" target="_blank">Policie ČR</a></span>
    </div>
</div>
<script>
    drawProgressChart(progressData.severe_injuries, "severe_injury_progress_chart", colors.severe_injuries);
</script>

<h2 id="jednotlivé-kraje">Jednotlivé kraje</h2>
<p>Podívejme se na jednotlivé kraje. Najde se alespoň jeden, který Vizi nula v roce 2025 splnil?</p>

<!-- static chart -->
<div class="chart-box" id="deaths_by_area_chart">
    <div class="chart-header">
        <img class="chart-header-logo light" alt="logo" width="100" height="100" src="/images/logo-light.svg">
        <img class="chart-header-logo dark" alt="logo" width="100" height="100" src="/images/logo-dark.svg">
        <h1>Počet úmrtí v silničním provozu v roce 2025 podle krajů</h1>
        <p>Každý sloupek znázorňuje svou výškou počet úmrtí v silničním provozu v roce 2025 v různých krajích.</p>
    </div>
    <div class="chart"></div>
    <div class="chart-footer">
        <span class="source">Zdroj: <a href="https://policie.gov.cz/clanek/statistika-nehodovosti.aspx?q=Y2hudW09Mg%3d%3d" target="_blank">Policie ČR</a></span>
    </div>
</div>
<script>
    drawAreaChart(areaData.deaths, "deaths_by_area_chart", colors.deaths);
</script>

<p>Bohužel také ne. Nutno podotknout, že kraj s nejmenším počtem úmrtí, Karlovarský kraj, je současně nejmenším krajem <a href="https://csu.gov.cz/porovnani-kraju" target="_blank" rel="noopener">podle počtu obyvatel</a>
. Hlavní město Praha, na druhém místě, je ale naopak jedním z největších.</p>
<p>Významně nenulové počty těžkých zranění zanechávám bez komentáře. Pořadí krajů v grafu je stejné jako v tom předchozím.</p>

<!-- static chart -->
<div class="chart-box" id="severe_injuries_by_area_chart">
    <div class="chart-header">
        <img class="chart-header-logo light" alt="logo" width="100" height="100" src="/images/logo-light.svg">
        <img class="chart-header-logo dark" alt="logo" width="100" height="100" src="/images/logo-dark.svg">
        <h1>Počet těžkých zranění v silničním provozu v roce 2025 podle krajů</h1>
        <p>Každý sloupek znázorňuje svou výškou počet těžkých zranění v silničním provozu v roce 2025 v různých krajích.</p>
    </div>
    <div class="chart"></div>
    <div class="chart-footer">
        <span class="source">Zdroj: <a href="https://policie.gov.cz/clanek/statistika-nehodovosti.aspx?q=Y2hudW09Mg%3d%3d" target="_blank">Policie ČR</a></span>
    </div>
</div>
<script>
    drawAreaChart(areaData.severe_injuries, "severe_injuries_by_area_chart", colors.severe_injuries);
</script>

<h2 id="jednotlivé-kraje-v-jednotlivých-měsících">Jednotlivé kraje v jednotlivých měsících</h2>
<p>Podívejme se tedy nejen na kraje, ale i na jednotlivé měsíce. Dokážeme najít kraj a měsíc, ve kterých byla splněna Vize nula?</p>

<!-- static chart -->
<div class="chart-box" id="deaths_by_area_by_month_chart">
    <div class="chart-header">
        <img class="chart-header-logo light" alt="logo" width="100" height="100" src="/images/logo-light.svg">
        <img class="chart-header-logo dark" alt="logo" width="100" height="100" src="/images/logo-dark.svg">
        <h1>Počet úmrtí v silničním provozu v měsících roku 2025 podle krajů</h1>
        <p>Délka řádku znázorňuje počet úmrtí v daném kraji v daném měsíci roku 2025. Klepnutím na kraj v legendě můžete daný kraj z grafu skrýt. </p>
    </div>
    <div class="chart"></div>
    <div class="chart-footer">
        <span class="source">Zdroj: <a href="https://policie.gov.cz/clanek/statistika-nehodovosti.aspx?q=Y2hudW09Mg%3d%3d" target="_blank">Policie ČR</a></span>
    </div>
</div>
<script>
    var hiddenAreasDeaths = new Set();
    drawAreaMonthChart(areaMonthData.deaths, "deaths_by_area_by_month_chart", hiddenAreasDeaths);
</script>


<!-- static chart -->
<div class="chart-box" id="severe_injuries_by_area_by_month_chart">
    <div class="chart-header">
        <img class="chart-header-logo light" alt="logo" width="100" height="100" src="/images/logo-light.svg">
        <img class="chart-header-logo dark" alt="logo" width="100" height="100" src="/images/logo-dark.svg">
        <h1>Počet těžkých zranění v silničním provozu v měsících roku 2025 podle krajů</h1>
        <p>Délka řádku znázorňuje počet těžkých zranění v daném kraji v daném měsíci roku 2025. Klepnutím na kraj v legendě můžete daný kraj z grafu skrýt. </p>
    </div>
    <div class="chart"></div>
    <div class="chart-footer">
        <span class="source">Zdroj: <a href="https://policie.gov.cz/clanek/statistika-nehodovosti.aspx?q=Y2hudW09Mg%3d%3d" target="_blank">Policie ČR</a></span>
    </div>
</div>
<script>
    var hiddenAreasSevereInjury = new Set();
    drawAreaMonthChart(areaMonthData.severe_injuries, "severe_injuries_by_area_by_month_chart", hiddenAreasSevereInjury);
</script>

<p>Opět ne. Nejblíže byl Liberecký kraj, druhý nejmenší kraj, v únoru 2025, což je nejkratší kalendářní měsíc. &ldquo;Pouze&rdquo; jedno těžké zranění a nula úmrtí. Důvod k oslavě? Asi ne.</p>
<h3 id="to-jedno-těžké-zranění-z-libereckého-kraje">To jedno těžké zranění z Libereckého kraje</h3>
<p>Možná vás, jako mě, napadlo, jaká byla ta jedna nehoda v únoru 2025 v Libereckém kraji, která vyústila v těžké zranění. Na všechny veřejné informace se můžete podívat v <a href="https://nehody.cdv.cz/detail.php?p1=180125000246" target="_blank" rel="noopener">aplikaci Centra dopravního výzkumu</a>
.</p>
<h2 id="závěr">Závěr</h2>
<p>Doufám, že v letošním roce budou počty úmrtí i těžkých zranění zase menší. Snad se nám i podaří, aby počty klesaly rychleji. Pevně věřím, že jednoho dne Vize nula dosáhneme (alespoň velmi blízko nule). Původní plán byl dosáhnout Vize nula do roku 2020:</p>
<blockquote>
<p>Základním cílem VIZE NULA je, aby nejpozději do roku 2050 nebyla na pozemních komunikacích usmrcena nebo těžce zraněna žádná osoba. Původní záměr ze Švédska z roku 1997, že se tak v oblasti fatalit mělo stát již v roce 2020, byl nakonec realisticky posunut o tři dekády, tedy na rok 2050. Nástroje, jak se ke společnému cíli přiblížit, definují v jednotlivých dekádách uvedené Strategie.</p>
</blockquote>
]]></description></item><item><title>Kdo umírá na českých silnicích?</title><link>https://silnicevcislech.cz/posts/kdo-umira/</link><pubDate>Tue, 28 Apr 2026 02:58:28 +0100</pubDate><guid>https://silnicevcislech.cz/posts/kdo-umira/</guid><description><![CDATA[
<script src="https://d3js.org/d3.v7.js"></script>
<script>
    const color = {
        "chodec": "#4e79a7",
        "motorová individuální vozidla": "#f28e2c",
        "hromadná doprava": "#e15759",
        "nemotorová vozidla": "#76b7b2",
        "ostatní": "#59a14f",
        "UNKNOWN": "#bab0ab",
    }
</script>
<script>
    const czCS = d3.formatLocale({
        thousands: " ",
        grouping: [3],
        decimal: ","
    });
    var BrowserText = (function () {
        var canvas = document.createElement('canvas'),
            context = canvas.getContext('2d');

        function getWidth(text, fontSize, fontFace) {
            context.font = 'bold ' + fontSize + 'px ' + fontFace;
            return context.measureText(text).width;
        }

        return {
            getWidth: getWidth
        };
    })();
    function format_percentage(number) {
        if (number < 0.1) {
            return czCS.format(".1%")(number)
        }
        return czCS.format(".0%")(number)
    }

    const format = czCS.format(",d");
</script>


<script>
    const parsed = d3.tsvParse(
        "area\tinjury_type\tchodec\třidič osobního automobilu\tspolucestující osobního automobilu\třidič motocyklu\tspolucestující motocyklu\třidič nákladního automobilu\tspolucestující nákladního automobilu\tostatní motorová vozidla\tosoba v autobuse\tosoba v tramvaji\tosoba v trolejbusu\tosoba ve vlaku\třidič jízdního kola\tspolucestující na jízdním kole\tosoba na povozu, jezdec na koni\tjezdec na koloběžce\tostatní nemotorová vozidla\tostatní\nCZ010\t1\t6\t1\t0\t3\t0\t2\t1\t0\t1\t0\t0\t0\t1\t0\t0\t0\t0\t0\nCZ010\t2\t76\t13\t11\t41\t1\t3\t0\t0\t5\t3\t0\t0\t11\t0\t0\t3\t0\t0\nCZ010\t3\t436\t567\t281\t296\t14\t52\t20\t8\t229\t60\t1\t0\t182\t0\t0\t73\t0\t6\nCZ020\t1\t11\t31\t8\t13\t0\t8\t1\t0\t0\t0\t0\t0\t5\t0\t0\t0\t0\t0\nCZ020\t2\t35\t89\t36\t60\t6\t11\t4\t1\t4\t0\t0\t1\t41\t0\t0\t7\t0\t0\nCZ020\t3\t289\t1312\t676\t394\t38\t165\t47\t15\t89\t0\t0\t5\t346\t2\t0\t72\t0\t7\nCZ031\t1\t5\t20\t7\t3\t0\t1\t0\t2\t0\t0\t0\t0\t3\t0\t0\t0\t0\t1\nCZ031\t2\t27\t47\t27\t37\t3\t5\t1\t0\t0\t0\t1\t0\t39\t0\t0\t7\t0\t1\nCZ031\t3\t92\t748\t410\t204\t16\t81\t22\t6\t11\t0\t4\t0\t204\t2\t5\t53\t3\t2\nCZ032\t1\t8\t11\t8\t5\t0\t0\t0\t0\t0\t0\t0\t0\t2\t0\t0\t1\t0\t0\nCZ032\t2\t12\t26\t12\t14\t2\t5\t3\t0\t0\t1\t2\t0\t5\t0\t0\t1\t0\t0\nCZ032\t3\t141\t657\t344\t166\t14\t75\t23\t8\t26\t2\t6\t0\t179\t0\t0\t35\t2\t1\nCZ041\t1\t2\t5\t5\t0\t1\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\nCZ041\t2\t10\t14\t4\t13\t0\t1\t1\t1\t0\t0\t0\t0\t7\t0\t0\t3\t0\t1\nCZ041\t3\t81\t275\t129\t64\t5\t23\t13\t4\t8\t0\t0\t0\t54\t0\t0\t13\t0\t0\nCZ042\t1\t7\t16\t1\t3\t0\t0\t0\t0\t0\t0\t0\t0\t2\t0\t0\t0\t0\t0\nCZ042\t2\t22\t23\t24\t34\t0\t4\t0\t1\t2\t0\t0\t0\t20\t1\t0\t6\t0\t0\nCZ042\t3\t237\t798\t416\t201\t19\t58\t18\t8\t77\t1\t47\t0\t205\t1\t0\t80\t1\t1\nCZ051\t1\t4\t7\t2\t4\t0\t2\t0\t0\t0\t0\t0\t0\t1\t0\t0\t0\t0\t0\nCZ051\t2\t11\t2\t4\t3\t2\t1\t2\t0\t1\t0\t0\t0\t3\t0\t0\t0\t0\t0\nCZ051\t3\t118\t406\t182\t129\t14\t40\t16\t1\t35\t5\t0\t2\t205\t1\t0\t46\t0\t2\nCZ052\t1\t3\t10\t4\t3\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\nCZ052\t2\t19\t27\t12\t18\t0\t2\t0\t1\t0\t0\t0\t0\t20\t0\t0\t7\t0\t0\nCZ052\t3\t100\t448\t206\t139\t10\t45\t10\t3\t2\t0\t1\t0\t170\t1\t0\t26\t0\t6\nCZ053\t1\t4\t13\t4\t4\t1\t1\t0\t0\t0\t0\t0\t0\t3\t0\t0\t0\t0\t0\nCZ053\t2\t7\t12\t11\t10\t1\t5\t0\t2\t1\t0\t0\t0\t15\t0\t0\t4\t0\t1\nCZ053\t3\t81\t474\t242\t107\t5\t40\t9\t7\t28\t0\t12\t2\t142\t0\t0\t35\t2\t1\nCZ063\t1\t2\t8\t2\t7\t0\t2\t0\t0\t0\t0\t0\t0\t3\t0\t0\t0\t0\t0\nCZ063\t2\t6\t19\t9\t13\t0\t3\t1\t0\t1\t0\t0\t0\t9\t0\t0\t1\t0\t3\nCZ063\t3\t89\t663\t278\t167\t15\t80\t27\t6\t7\t0\t1\t0\t169\t1\t0\t19\t0\t7\nCZ064\t1\t3\t15\t7\t3\t0\t3\t1\t0\t0\t0\t0\t0\t4\t0\t0\t2\t0\t0\nCZ064\t2\t45\t34\t22\t33\t5\t5\t1\t0\t1\t0\t1\t0\t24\t0\t1\t3\t0\t1\nCZ064\t3\t275\t966\t453\t322\t20\t111\t23\t7\t50\t10\t14\t1\t426\t1\t2\t112\t0\t11\nCZ071\t1\t4\t8\t5\t3\t0\t2\t0\t0\t0\t0\t0\t0\t1\t0\t0\t0\t0\t0\nCZ071\t2\t12\t12\t10\t11\t1\t3\t1\t1\t1\t0\t0\t0\t18\t0\t0\t4\t0\t1\nCZ071\t3\t118\t453\t223\t125\t4\t30\t6\t8\t78\t5\t0\t1\t293\t2\t0\t52\t1\t4\nCZ072\t1\t3\t8\t4\t2\t1\t0\t0\t1\t0\t0\t0\t0\t7\t0\t0\t1\t1\t0\nCZ072\t2\t15\t13\t10\t20\t1\t1\t0\t0\t0\t0\t0\t0\t16\t0\t0\t2\t0\t0\nCZ072\t3\t124\t479\t194\t132\t8\t41\t11\t5\t21\t0\t10\t0\t275\t1\t1\t33\t1\t5\nCZ080\t1\t8\t8\t3\t4\t0\t1\t0\t0\t0\t0\t0\t0\t2\t0\t0\t1\t0\t0\nCZ080\t2\t27\t14\t21\t19\t0\t8\t3\t1\t0\t1\t0\t1\t19\t0\t0\t3\t0\t0\nCZ080\t3\t260\t778\t314\t200\t22\t60\t23\t8\t127\t8\t8\t0\t417\t1\t0\t84\t0\t9\nall\t1\t70\t161\t60\t57\t3\t22\t3\t3\t1\t0\t0\t0\t34\t0\t0\t5\t1\t1\nall\t2\t324\t345\t213\t326\t22\t57\t17\t8\t16\t5\t4\t2\t247\t1\t1\t51\t0\t8\nall\t3\t2441\t9024\t4348\t2646\t204\t901\t268\t94\t788\t91\t104\t11\t3267\t13\t8\t733\t10\t62\n",
    )
    function select_data(parsed, injury_type, area) {
        for (const row of parsed) {
            if (row["injury_type"] === String(injury_type) && row["area"] === area) {
                return {
                    "name": "dataset", "children": [
                        { "name": "chodec", "value": row["chodec"] },
                        { "name": "motorová individuální vozidla", "children": [{ "name": "řidič osobního automobilu", "value": row["řidič osobního automobilu"] }, { "name": "spolucestující osobního automobilu", "value": row["spolucestující osobního automobilu"] }, { "name": "řidič motocyklu", "value": row["řidič motocyklu"] }, { "name": "spolucestující motocyklu", "value": row["spolucestující motocyklu"] }, { "name": "řidič nákladního automobilu", "value": row["řidič nákladního automobilu"] }, { "name": "spolucestující nákladního automobilu", "value": row["spolucestující nákladního automobilu"] }, { "name": "ostatní motorová vozidla", "value": row["ostatní motorová vozidla"] }] },
                        { "name": "hromadná doprava", "children": [{ "name": "osoba v autobuse", "value": row["osoba v autobuse"] }, { "name": "osoba v tramvaji", "value": row["osoba v tramvaji"] }, { "name": "osoba v trolejbusu", "value": row["osoba v trolejbusu"] }, { "name": "osoba ve vlaku", "value": row["osoba ve vlaku"] }] },
                        { "name": "nemotorová vozidla", "children": [{ "name": "řidič jízdního kola", "value": row["řidič jízdního kola"] }, { "name": "spolucestující na jízdním kole", "value": row["spolucestující na jízdním kole"] }, { "name": "osoba na povozu, jezdec na koni", "value": row["osoba na povozu, jezdec na koni"] }, { "name": "jezdec na koloběžce", "value": row["jezdec na koloběžce"] }, { "name": "ostatní nemotorová vozidla", "value": row["ostatní nemotorová vozidla"] }] },
                        { "name": "ostatní", "value": row["ostatní"] }
                    ]
                }
            }
        }
    }
    function get_font_size(d) {
        const width = d.x1 - d.x0, height = d.y1 - d.y0;
        return Math.max(Math.min(width / 10, height / 6, 26), 14)
    }

    var count = 0;
    function uid(name) {
        return new Id("O-" + (name == null ? "" : name + "-") + ++count);
    }
    function Id(id) {
        this.id = id;
        this.href = new URL(`#${id}`, location) + "";
    }
    Id.prototype.toString = function () {
        return "url(" + this.href + ")";
    };

    function drawChart(parsed, elementId, injury_type, area) {
        // clear chart
        d3.select(`#${elementId} .chart > *`).remove();

        // select data
        var data = select_data(parsed, injury_type, area);

        // set title & description
        const INJURY_TYPES = {
            1: { title: "zemřel", description: "úmrtí" },
            2: { title: "byl těžce zraněn", description: "těžce zraněných osob" },
            3: { title: "byl lehce zraněn", description: "lehce zraněných osob" }
        };

        const AREAS = {
            all: { title: "České republiky", description: "České republice" },
            CZ010: { title: "Hlavního města Prahy", description: "Hlavním městě Praha" },
            CZ020: { title: "Středočeského kraje", description: "Středočeském kraji" },
            CZ031: { title: "Jihočeského kraje", description: "Jihočeském kraji" },
            CZ032: { title: "Plzeňského kraje", description: "Plzeňském kraji" },
            CZ041: { title: "Karlovarského kraje", description: "Karlovarském kraji" },
            CZ042: { title: "Ústeckého kraje", description: "Ústeckém kraji" },
            CZ051: { title: "Libereckého kraje", description: "Libereckém kraji" },
            CZ052: { title: "Královéhradeckého kraje", description: "Královéhradeckém kraji" },
            CZ053: { title: "Pardubického kraje", description: "Pardubickém kraji" },
            CZ063: { title: "Kraje Vysočina", description: "Kraji Vysočina" },
            CZ064: { title: "Jihomoravského kraje", description: "Jihomoravském kraji" },
            CZ071: { title: "Olomouckého kraje", description: "Olomouckém kraji" },
            CZ080: { title: "Moravskoslezského kraje", description: "Moravskoslezském kraji" },
            CZ072: { title: "Zlínského kraje", description: "Zlínském kraji" }
        };

        const injury = INJURY_TYPES[injury_type];
        const areaData = AREAS[area];

        if (!injury || !areaData) {
            throw new Error("Invalid injury type or area");
        }

        d3.select(`#${elementId} .chart-header h1`).html(`Kdo ${injury.title} na silnicích ${areaData.title} v roce 2025?`);
        d3.select(`#${elementId} .chart-header p`).html(`Celá plocha grafu reprezentuje celkový počet ${injury.description} v haváriích zaznamenaných Policií ČR v ${areaData.description} v roce 2025. Každý obdélník uvnitř reprezentuje svou velikostí počet ${injury.description} určité skupiny.`);

        function format_label(d) {
            const width = 0.95 * (d.x1 - d.x0);
            const font_size = get_font_size(d);
            const font_family = window.getComputedStyle(document.getElementById(elementId), null).getPropertyValue("font-family");
            const total = d3.hierarchy(data).sum(d => d.value).value
            const percentage = format_percentage(d.value / total)

            if (width < BrowserText.getWidth(percentage, font_size, font_family)) {
                return []
            }

            const count = `(${format(d.value)})`
            if (width < BrowserText.getWidth(`${percentage} ${count}`, font_size, font_family)) {
                return [percentage]
            }

            const words = d.data.name.split(/(?=[A-Z][a-z])|\s+/g)
            if (width < Math.max(...words.map(function (word) { return BrowserText.getWidth(word, font_size, font_family) }))) {
                return [`${percentage} ${count}`]
            }

            var label = []
            var row = [percentage, count]
            for (const word of words) {
                if (BrowserText.getWidth(row.concat([word]).join(" "), font_size, font_family) < width) {
                    row.push(word)
                }
                else {
                    label.push(row.join(" "))
                    row = [word]
                }
            }
            label.push(row.join(" "))

            return label
        }

        // Specify the chart’s dimensions.
        const width = parseInt(d3.select(`#${elementId} .chart`).style("width"));
        const height = (window.innerWidth > window.innerHeight) ? Number(width * 8 / 16) : Number(width * 3 / 2);
        const verticalScreen = (window.innerWidth < window.innerHeight);
        const treemapMethod = verticalScreen ? d3.treemapSlice : d3.treemapSquarify;

        const total = d3.hierarchy(data).sum(d => d.value).value

        const groupLabelFontSize = 16;

        const treeMapHeight = verticalScreen ? height : height - (groupLabelFontSize * 3);
        const treeMapYOffset = verticalScreen ? 0 : groupLabelFontSize * 1.5;
        // Compute the layout.
        const root = d3.treemap()
            .tile(treemapMethod)
            .size([width, treeMapHeight])
            .paddingInner(2)
            .round(true)
            (d3.hierarchy(data)
                .sum(d => d.value)
                .sort((a, b) => b.value - a.value));

        // Create the SVG container.
        const svg = d3.select(`#${elementId} .chart`)
            .attr("style", `height: ${height}px;`)
            .append("svg")
            .attr("viewBox", [0, 0, width, height])
            .attr("width", width)
            .attr("height", height)
            .attr("style", "max-width: 100%; height: auto;");

        // Add a cell for each leaf of the hierarchy.
        const leaf = svg.selectAll("g")
            .data(root.leaves())
            .join("g")
            .attr("transform", d => `translate(${d.x0},${d.y0 + treeMapYOffset})`);

        // Append a color rectangle. 
        leaf.append("rect")
            .attr("id", d => (d.leafUid = uid("leaf")).id)
            .attr("fill", d => { while (d.depth > 1) d = d.parent; return color[d.data.name]; })
            .attr("fill-opacity", 0.8)
            .attr("width", d => d.x1 - d.x0)
            .attr("height", d => d.y1 - d.y0)
            .attr("rx", 3) // Rounded corners
            .attr("alt", d => `${format_percentage(d.value / total)} (${format(d.value)}) ${d.data.name}`)
            .append("title")
            .text(d => `${format_percentage(d.value / total)} (${format(d.value)}) ${d.data.name}`);

        // Append a clipPath to ensure text does not overflow.
        leaf.append("clipPath")
            .attr("id", d => (d.clipUid = uid("clip")).id)
            .append("use")
            .attr("xlink:href", d => d.leafUid.href);

        // Append multiline text
        leaf.append("text")
            .attr("font-weight", "bold")
            .attr("font-size", d => get_font_size(d))
            .attr("clip-path", d => d.clipUid)
            .selectAll("tspan")
            .data(d => format_label(d))
            .join("tspan")
            .attr("x", 3)
            .attr("y", (d, i, nodes) => `${1.2 + i * 1.1}em`)
            .text(d => d);
        if (!verticalScreen) {
            // Add labels for larger groups the bottom edge
            const parent = svg.selectAll("g.parent")
                .data(root.descendants().filter(d => d.depth === 1 && d.children)) // Only first-level parents
                .join("g")
                .attr("class", "parent");
            // Add colored rectangle behind label
            parent.append("rect")
                .attr("class", "parent-label-bg")
                .attr("x", d => {
                    const regionWidth = d.x1 - d.x0;
                    const font_family = window.getComputedStyle(document.getElementById(elementId), null).getPropertyValue("font-family");
                    const textWidth = BrowserText.getWidth(d.data.name, groupLabelFontSize, font_family);
                    const fullTextWidth = BrowserText.getWidth(`${format_percentage(d.value / total)} (${format(d.value)}) ${d.data.name}`, groupLabelFontSize, font_family);

                    if (d.x0 < width / 2) {
                        return fullTextWidth - textWidth + groupLabelFontSize * 1.2;
                    } else {
                        return width - textWidth - groupLabelFontSize * 1.2;
                    }
                })
                .attr("y", d => {
                    if ((d.y0 + d.y1) > height) {
                        return height - groupLabelFontSize * 1.1
                    } else {
                        return groupLabelFontSize * 0.1
                    }
                })
                .attr("width", d => {
                    const font_family = window.getComputedStyle(document.getElementById(elementId), null).getPropertyValue("font-family");
                    const textWidth = BrowserText.getWidth(d.data.name, groupLabelFontSize, font_family);
                    return textWidth + groupLabelFontSize * 0.2; // Text width plus padding
                })
                .attr("height", d => groupLabelFontSize * 1.1)
                .attr("fill", d => color[d.data.name])
                .attr("fill-opacity", 0.8)
                .attr("rx", 3) // Rounded corners
                .attr("pointer-events", "none")
                .style("display", d => {
                    if (d.value == 0) {
                        return "none"
                    }
                    const minWidth = 100; // Minimum width to show label
                    return (d.x1 - d.x0) > minWidth ? "block" : "none";
                });
            parent.append("text")
                .attr("class", "parent-label")
                .attr("x", d => {
                    const font_family = window.getComputedStyle(document.getElementById(elementId), null).getPropertyValue("font-family");
                    const fullTextWidth = BrowserText.getWidth(`${format_percentage(d.value / total)} (${format(d.value)}) ${d.data.name}`, groupLabelFontSize, font_family);
                    if (d.x0 < width / 2) {
                        return fullTextWidth / 2 + groupLabelFontSize * 1.2
                    } else {
                        return width - fullTextWidth / 2 - groupLabelFontSize * 1.2;
                    }
                })
                .attr("y", d => {
                    if ((d.y0 + d.y1) > height) {
                        return height - groupLabelFontSize * 0.2
                    } else {
                        return groupLabelFontSize
                    }
                })
                .attr("text-anchor", "middle")
                .attr("font-weight", "bold")
                .attr("font-size", d => groupLabelFontSize)
                .attr("pointer-events", "none")
                .text(d => `${format_percentage(d.value / total)} (${format(d.value)}) ${d.data.name}`)
                .style("overflow", "hidden")
                .style("display", d => {
                    if (d.value == 0) {
                        return "none"
                    }
                    const minWidth = 100; // Minimum width to show label
                    return (d.x1 - d.x0) > minWidth ? "block" : "none";
                });
        }
        // Tooltip
        var tool = d3.select(`#${elementId} .tooltip-field`);
        leaf.on("mousemove", function (event, d) {
            tool.html(`${format_percentage(d.value / total)} (${format(d.value)}) ${d.data.name}`);
        }).on("mouseout", function (d) {
            tool.html("<em>(Najeďte na obdélník v grafu.)</em>");
        });
    }
</script>

<script>
    const modalSplitParsed = d3.csvParse(
        'area,řidič osobního-automobilu,spolucestující-osobního-automobilu,jezdec na-motocyklu,chodec,hromadná-doprava,jezdec na-jízdním kole,jiný dopravní-prostředek,nezjištěno\nČeská republika,1895150,338327,9099,604229,1693600,140829,18794,723695\nHlavní město Praha,138135,20148,1488,70366,529502,7549,2815,95167\nStředočeský kraj,301028,58976,1394,66878,196580,13521,2614,99238\nJihočeský kraj,123816,21625,592,37100,54668,11067,1128,43242\nPlzeňský kraj,124393,21077,391,30327,71508,5324,1104,40004\nKarlovarský kraj,52181,9996,145,16056,22750,1330,461,20389\nÚstecký kraj,142884,28779,406,40253,88662,4207,1137,62702\nLiberecký kraj,76706,16506,349,28957,49688,3135,745,32477\nKrálovéhradecký kraj,102094,17969,558,33729,45778,13431,899,35062\nPardubický kraj,97119,17239,556,33460,48262,14980,913,33770\nKraj Vysočina,100439,19187,426,34579,41798,5002,691,30754\nJihomoravský kraj,211019,33071,1136,68492,236148,16395,2321,75776\nOlomoucký kraj,109971,19064,502,41915,69512,17540,1192,39953\nZlínský kraj,107983,18259,403,34822,62472,12466,936,37023\nMoravskoslezský kraj,207382,36431,753,67295,176272,14882,1838,78138\n'
    )
    const columns = modalSplitParsed.columns.slice(1)  // remove "area"
    const pieChartColor = {
        "řidič osobního automobilu": color["motorová individuální vozidla"],
        "spolucestující osobního automobilu": color["motorová individuální vozidla"],
        "jezdec na motocyklu": color["motorová individuální vozidla"],
        "chodec": color["chodec"],
        "hromadná doprava": color["hromadná doprava"],
        "jezdec na jízdním kole": color["nemotorová vozidla"],
        "jiný dopravní prostředek": color["ostatní"],
        "nezjištěno": color["UNKNOWN"],
    }
    const font_size = 16;

    function drawPieChart(parsed, elementId, area) {
        // clear chart
        d3.select(`#${elementId} .chart > *`).remove();

        // set title & description
        const AREAS = {
            0: 'v České republice',
            1: 'v hlavním městě Praze',
            2: 've Středočeském kraji',
            3: 'v Jihočeském kraji',
            4: 'v Plzeňském kraji',
            5: 'v Karlovarském kraji',
            6: 'v Ústeckém kraji',
            7: 'v Libereckém kraji',
            8: 'v Královéhradeckém kraji',
            9: 'v Pardubickém kraji',
            10: 'v kraji Vysočina',
            11: 'v Jihomoravském kraji',
            12: 'v Olomouckém kraji',
            13: 've Zlínském kraji',
            14: 'v Moravskoslezském kraji',
        };

        d3.select(`#${elementId} .chart-header h1`).html(`Jak jezdili lidé do práce a do školy ${AREAS[area]} v roce 2021?`);
        d3.select(`#${elementId} .chart-header p`).html(`Graf ukazuje složení dopravních prostředků, které využívali lidé vyjíždějící do školy nebo do zaměstnání ${AREAS[area]}. Data jsou ze Sčítání 2021.`);

        // select data
        const data = columns.map(column => ({
            name: column,
            value: parsed[area][column]
        }));
        const total = d3.sum(data.map(d => d.value));

        // Specify the chart’s dimensions.
        const verticalScreen = (window.innerWidth < window.innerHeight);

        const width = parseInt(d3.select(`#${elementId} .chart`).style("width"));
        const height = verticalScreen ? Number(width * 2.5) : Number(width * 1 / 2);
        const labelGap = width / 40;
        const outerRadius = verticalScreen ? width * 0.4 : width / 5.1;
        const labelMinAngle = Math.PI * 0.1;
        const labelDistance = outerRadius * 1.1

        // Create the pie layout and arc generator.
        const pie = d3.pie()
                .sort(null)
                .value(d => d.value);
        if (verticalScreen) {
            pie.startAngle(Math.PI / 5)
                .endAngle(Math.PI / 5 + 2 * Math.PI);
        }
        
        const arc = d3.arc()
            .innerRadius(3)
            .outerRadius(outerRadius)
            .cornerRadius(6)
            .padAngle(0.02)
            .padRadius(outerRadius);

        const arcs = pie(data);

        // Create the SVG container.
        const svg = d3.select(`#${elementId} .chart`)
            .attr("style", `height: ${height}px;`)
            .append("svg")
            .attr("width", width)
            .attr("height", height)
            .attr("viewBox", [-width / 2, -height / 2, width, height])
            .attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;");

        // Add a sector path for each value.
        svg.append("g")
            .selectAll()
            .data(arcs)
            .join("path")
            .attr("fill", d => pieChartColor[d.data.name.replaceAll("-", " ")])
            .attr("fill-opacity", 0.8)
            .attr("d", arc)
            .append("title")
            .text(d => `${d.data.name.replaceAll("-", " ")}: (${format_percentage(d.data.value / total)}) ${format(d.data.value)}`);

        
        if (verticalScreen) {
            // sort labels on top and bottom
            function adjustLabelPositions(arcs, labelDistance) {
                // Calculate initial positions
                var positions = arcs.map((d, i) => ({
                    index: i,
                    angle: (d.startAngle + d.endAngle) / 2 - Math.PI / 2,
                    y: Math.sin((d.startAngle + d.endAngle) / 2 - Math.PI / 2) * labelDistance,
                    x: Math.cos((d.startAngle + d.endAngle) / 2 - Math.PI / 2) * labelDistance,
                    d: d,
                }));

                positions.sort((a, b) => a.y - b.y);

                const mid = Math.ceil(positions.length / 2);
                const [bottomHalf, topHalf] = [positions.slice(0, mid), positions.slice(mid)];

                topHalf.sort((a, b) => Math.abs(a.x) - Math.abs(b.x));
                bottomHalf.sort((a, b) => Math.abs(a.x) - Math.abs(b.x));

                var adjusted = []
                var yOffset = labelDistance + font_size * 1.1;
                for (const position of topHalf) {
                    position.y = yOffset;
                    yOffset += (position.d.data.name.split("-").length + 1) * font_size * 1.1 + font_size / 2
                    adjusted.push(position)
                }
                var yOffset = -labelDistance + font_size * 1.1;
                for (const position of bottomHalf) {
                    yOffset -= (position.d.data.name.split("-").length + 1) * font_size * 1.1 + font_size / 2
                    position.y = yOffset;
                    adjusted.push(position)
                }
                return adjusted;
            }
            const adjustedPositions = adjustLabelPositions(arcs, labelDistance);

            // Create a map of index -> adjusted Y position
            const positionMap = new Map(adjustedPositions.map(p => [p.index, p]));

            // labels
            svg.append("g")
                .selectAll()
                .data(arcs)
                .join("g")
                .attr("text-anchor", (d, i) => (positionMap.get(i).x > 0 ? "end" : "start"))
                .attr("transform", (d, i) => {
                    const position = positionMap.get(i);
                    return `translate(${position.x + (position.x > 0 ? -1 : 1)*font_size/2},${position.y})`;
                })
                .attr("font-size", font_size)
                .call(text => text.append("text")
                    .attr("y", 0)
                    .attr("font-weight", "bold")
                    .selectAll("tspan")
                    .data(d => d.data.name.split("-"))
                    .enter()
                    .append("tspan")
                    .attr("x", 0)
                    .attr("dy", (word, i) => i === 0 ? "0em" : "1.1em")
                    .text(word => word))
                .append("text")
                .attr("x", 0)
                .attr("y", d => (`${d.data.name.split("-").length * 1.1}em`))
                .text((d, i) => `(${format_percentage(d.data.value / total)}) ${format(d.data.value)}`);

            // leader lines
            svg.append("g")
                .attr("class", "chart-line")
                .attr("fill", "none")
                .attr("stroke-width", 1.2)
                .selectAll()
                .data(arcs)
                .join("polyline")
                .attr("points", (d, i) => {
                    const pos = arc.centroid(d);
                    const position = positionMap.get(i);
                    const midX = Math.cos(position.angle) * labelDistance;
                    const midY = Math.sin(position.angle) * labelDistance;

                    return `${pos[0]},${pos[1]} ${midX},${midY} ${position.x},${position.y}`;
                });
        } else {
            // Detect and resolve overlapping labels
            function adjustLabelPositions(arcs, labelDistance) {
                // Calculate initial positions
                var positions = arcs.map((d, i) => ({
                    index: i,
                    angle: (d.startAngle + d.endAngle) / 2,
                    d: d,
                }));

                var all_good_flag = false
                const step = Math.PI / 20;

                while (!all_good_flag) {
                    all_good_flag = true
                    positions.sort((a, b) => a.angle - b.angle);

                    for (let i = 1; i < positions.length; i++) {
                        const prev = positions[i - 1];
                        const curr = positions[i];

                        if (
                            (Math.sign(Math.cos(curr.angle - Math.PI / 2)) === Math.sign(Math.cos(prev.angle - Math.PI / 2)))
                            && (Math.abs(Math.sin(curr.angle - Math.PI / 2) * labelDistance - Math.sin(prev.angle - Math.PI / 2) * labelDistance) < ((prev.d.data.name.split("-").length + curr.d.data.name.split("-").length) / 2 + 2) * font_size)
                        ) {
                            curr.angle = Math.min(Math.max(curr.angle + step, labelMinAngle), Math.PI * 2 - labelMinAngle);
                            prev.angle = Math.min(Math.max(prev.angle - step, labelMinAngle), Math.PI * 2 - labelMinAngle);
                            all_good_flag = false;
                        }
                    }
                }

                return positions;
            }
            const adjustedPositions = adjustLabelPositions(arcs, labelDistance);

            // Create a map of index -> adjusted angle
            const angleMap = new Map(adjustedPositions.map(p => [p.index, p.angle]));

            // labels
            svg.append("g")
                .selectAll()
                .data(arcs)
                .join("g")
                .attr("text-anchor", (d, i) => (angleMap.get(i) > Math.PI ? "end" : "start"))
                .attr("transform", (d, i) => {
                    const adjustedAngle = angleMap.get(i);
                    const midX = Math.cos(adjustedAngle - Math.PI / 2) * labelDistance;
                    const midY = Math.sin(adjustedAngle - Math.PI / 2) * labelDistance;
                    const y = midY;
                    const x = adjustedAngle < Math.PI ? midX + labelGap : midX - labelGap;
                    return `translate(${x},${y})`;
                })
                .call(text => text.append("text")
                    .attr("y", "-0.4em")
                    .attr("font-weight", "bold")
                    .attr("font-size", font_size)
                    .selectAll("tspan")
                    .data(d => d.data.name.split("-"))
                    .enter()
                    .append("tspan")
                    .attr("x", 0)
                    .attr("dy", (word, i) => i === 0 ? "0em" : "1.1em")
                    .text(word => word))
                .append("text")
                .attr("x", 0)
                .attr("y", d => (`${0.7 + (d.data.name.split("-").length - 1) * 1.1}em`))
                .attr("font-size", font_size)
                .text((d, i) => `(${format_percentage(d.data.value / total)}) ${format(d.data.value)}`);

            // leader lines
            svg.append("g")
                .attr("class", "chart-line")
                .attr("fill", "none")
                .attr("stroke-width", 1.2)
                .selectAll()
                .data(arcs)
                .join("polyline")
                .attr("points", (d, i) => {
                    const pos = arc.centroid(d);
                    const adjustedAngle = angleMap.get(i);
                    const midX = Math.cos(adjustedAngle - Math.PI / 2) * labelDistance;
                    const midY = Math.sin(adjustedAngle - Math.PI / 2) * labelDistance;

                    const finalX = adjustedAngle < Math.PI ? midX + labelGap * 0.85 : midX - labelGap * 0.85;

                    return `${pos[0]},${pos[1]} ${midX},${midY} ${finalX},${midY}`;
                });
        }
    }
</script>


<p>V roce 2025 bylo na silnicích v České republice těžce zraněno 1647 osob a 421 osob přišlo o život. Když jsem se o tato čísla podělil se svým švagrem, řekl mi něco jako: &ldquo;To jsou ale všechno řidiči, kteří sami zavinili nehodu!&rdquo; Je ale to pravda?</p>
<p>V tomto článku se pokusím odpovědět na první část: &ldquo;To jsou ale všechno řidiči&rdquo;. Druhou část: &ldquo;zavinili nehodu sami&rdquo;, tedy otázku zavinění nehody, řešit nebudeme.</p>
<p>Vyrobil jsem nástroj, který odpovídá na otázku: &ldquo;Kdo byl zraněný na silnicích v České republice v roce 2025?&rdquo; Nejprve si projdeme několik příkladů a na konci článku bude k dispozici interaktivní verze, kde se můžete podívat na všechny kraje i všechny typy zranění.</p>
<h2 id="celá-česká-republika">Celá Česká republika</h2>
<p>Podívejme se nejdříve na úmrtí v celé České republice. Celý graf reprezentuje všech 421 úmrtí v dopravních nehodách v České republice, ke kterým byla přivolána Policie ČR. Každý menší obdélník reprezentuje konkrétní skupinu lidí, kteří zemřeli.</p>
<p>Více než dvě třetiny zemřelých tvoří řidiči a spolucestující v individuálních motorových vozidlech. Jeden ze šesti zemřelých byl chodec.</p>

<!-- static chart -->
<div class="chart-box" id="whole_death_chart">
    <div class="chart-header">
        <img class="chart-header-logo light" alt="logo" width="100" height="100" src="/images/logo-light.svg">
        <img class="chart-header-logo dark" alt="logo" width="100" height="100" src="/images/logo-dark.svg">
        <h1></h1>
        <p></p>
    </div>
    <div class="chart"></div>
    <div class="tooltip">
        <span>Popisek: </span>
        <span class="tooltip-field"><em>(Najeďte na obdélník v grafu.)</em></span>
    </div>
    <div class="chart-footer">
        <span class="source">Zdroj: <a href="https://policie.gov.cz/clanek/statistika-nehodovosti.aspx?q=Y2hudW09Mg%3d%3d" target="_blank">Policie ČR</a></span>
        <!-- <span class="license">CC BY</span> -->
    </div>
</div>
<script>
    // Initial draw
    drawChart(parsed, "whole_death_chart", 1, "all");  // death, all
</script>

<p>Tento typ grafu neznázorňuje absolutní počty zraněných (i když ty jsou uvedeny v závorce), ale poměry počtů mezi různými skupinami.</p>
<p>Nyní se podíváme na jednotlivé kraje České republiky. Nebudeme ale zkoumat, kde je absolutně nejvíce zraněných (to je ve Středočeském kraji), ale kde je poměr mezi zraněnými něčím neobvyklý.</p>
<h2 id="těžce-zranění-chodci-v-praze">Těžce zranění chodci v Praze</h2>
<p>V hlavním městě jsou největší skupinou těžce zraněných i zemřelých chodci.</p>

<!-- static chart -->
<div class="chart-box" id="prague_severe_injury_chart">
    <div class="chart-header">
        <img class="chart-header-logo light" alt="logo" width="100" height="100" src="/images/logo-light.svg">
        <img class="chart-header-logo dark" alt="logo" width="100" height="100" src="/images/logo-dark.svg">
        <h1></h1>
        <p></p>
    </div>
    <div class="chart"></div>
    <div class="tooltip">
        <span>Popisek: </span>
        <span class="tooltip-field"><em>(Najeďte na obdélník v grafu.)</em></span>
    </div>
    <div class="chart-footer">
        <span class="source">Zdroj: <a href="https://policie.gov.cz/clanek/statistika-nehodovosti.aspx?q=Y2hudW09Mg%3d%3d" target="_blank">Policie ČR</a></span>
        <!-- <span class="license">CC BY</span> -->
    </div>
</div>
<script>
    // Initial draw
    drawChart(parsed, "prague_severe_injury_chart", 2, "CZ010");  // death, all
</script>

<p>To je velmi neobvyklé. Řidiči osobních automobilů opravdu bývají největší skupinou zraněných, i když ne většina. Podobný rozdíl byl snad jen v Libereckém kraji pro těžká zranění. Na graf Libereckého kraje se můžete podívat na konci článku.</p>
<p>Možná vás napadlo, že tato neobvyklost musí souviset s tím, že se v Praze méně jezdí osobními automobily a více chodí pěšky, ale je tomu tak? Hledal jsem souhrnná data z celé České republiky o skladbě dopravních prostředků v dopravě v různých krajích a nejlepší jsem našel ze Sčítání 2021, jsou tedy již pár let stará. (Jestli víte o lepších, napište mi!)</p>
<p>V následujícím grafu jsou vyobrazena relativní zastoupení různých dopravních prostředků pro cesty do práce nebo do školy. To není perfektní odpověď na otázku: &ldquo;Jakým dopravním prostředkem se ve daném kraji obvykle jezdí dnes?&rdquo;, ale je snad dostatečně dobrá.</p>
<p>Těch, kteří jdou celou cestu od začátku do konce pouze pěšky, je v Praze relativně k ostatním dopravním prostředkům málo. (Přesto bylo v roce 2021 v Praze 70 366 lidí docházejících pěšky, což je nejvyšší číslo ze všech krajů.)</p>
<p>Obrovské zastoupení má ale hromadná doprava, přes 60%! Všichni cestující hromadnou dopravou, kteří se dostanou do nehody po cestě ze zastávky budou samozřejmě v předchozím grafu započítáni jako chodci. To ostatně platí i pro řidiče jdoucí k zaparkovanému autu. Jednotlivé skupiny v datech o nehodovosti od Policie ČR a v datech ze Sčítání si neodpovídají přesně.</p>

<!-- static chart -->
<div class="chart-box" id="prague_modal_split_chart">
    <div class="chart-header">
        <img class="chart-header-logo light" alt="logo" width="100" height="100" src="/images/logo-light.svg">
        <img class="chart-header-logo dark" alt="logo" width="100" height="100" src="/images/logo-dark.svg">
        <h1></h1>
        <p></p>
    </div>
    <div class="chart"></div>
    <div class="chart-footer">
        <span class="source">Zdroj: <a href="https://scitani.gov.cz/dopravni-prostredek" target="_blank">Sčítání 2021</a></span>
    </div>
</div>
<script>
    // Initial draw
    drawPieChart(modalSplitParsed, "prague_modal_split_chart", 1);  // Hlavní město Praha
</script>

<p>Pro úplnost, pouze v Praze zemřelo více chodců než řidičů osobních aut, v každém jiném kraji platí opačná nerovnost (v Moravskoslezském kraji je počet stejný). U těžkých zranění je ale situace jiná, v následujícím grafu uvádím počty těžce zraněných chodců a řidičů osobních automobilů, kraje jsou seřazené podle rozdílu v těchto počtech.</p>
<p>
<div class="chart-box" id="bar_chart">
    <div class="chart-header">
        <img class="chart-header-logo light" alt="logo" width="100" height="100" src="/images/logo-light.svg">
        <img class="chart-header-logo dark" alt="logo" width="100" height="100" src="/images/logo-dark.svg">
        <h1>Těžká zranění chodců a řidičů osobních automobilů v roce 2025</h1>
        <p>Kolik chodců a kolik řidičů osobních automobilů bylo těžce zraněno na silnicích ve všech krajích České republiky v roce 2025?</p>
    </div>
    <div class="chart"></div>
    <div class="chart-footer">
        <span class="source">Zdroj: <a href="https://policie.gov.cz/clanek/statistika-nehodovosti.aspx?q=Y2hudW09Mg%3d%3d" target="_blank">Policie ČR</a></span>
        <!-- <span class="license">CC BY</span> -->
    </div>
</div>

<script>
    var data = [
        {
            "area": "St\u0159edo\u010desk\u00fd kraj",
            "class": "chodec",
            "size": 35
        },
        {
            "area": "St\u0159edo\u010desk\u00fd kraj",
            "class": "\u0159idi\u010d osobn\u00edho automobilu",
            "size": 89
        },
        {
            "area": "Jiho\u010desk\u00fd kraj",
            "class": "chodec",
            "size": 27
        },
        {
            "area": "Jiho\u010desk\u00fd kraj",
            "class": "\u0159idi\u010d osobn\u00edho automobilu",
            "size": 47
        },
        {
            "area": "Plze\u0148sk\u00fd kraj",
            "class": "chodec",
            "size": 12
        },
        {
            "area": "Plze\u0148sk\u00fd kraj",
            "class": "\u0159idi\u010d osobn\u00edho automobilu",
            "size": 26
        },
        {
            "area": "Kraj Vyso\u010dina",
            "class": "chodec",
            "size": 6
        },
        {
            "area": "Kraj Vyso\u010dina",
            "class": "\u0159idi\u010d osobn\u00edho automobilu",
            "size": 19
        },
        {
            "area": "Kr\u00e1lov\u00e9hradeck\u00fd kraj",
            "class": "chodec",
            "size": 19
        },
        {
            "area": "Kr\u00e1lov\u00e9hradeck\u00fd kraj",
            "class": "\u0159idi\u010d osobn\u00edho automobilu",
            "size": 27
        },
        {
            "area": "Pardubick\u00fd kraj",
            "class": "chodec",
            "size": 7
        },
        {
            "area": "Pardubick\u00fd kraj",
            "class": "\u0159idi\u010d osobn\u00edho automobilu",
            "size": 12
        },
        {
            "area": "Karlovarsk\u00fd kraj",
            "class": "chodec",
            "size": 10
        },
        {
            "area": "Karlovarsk\u00fd kraj",
            "class": "\u0159idi\u010d osobn\u00edho automobilu",
            "size": 14
        },
        {
            "area": "\u00dasteck\u00fd kraj",
            "class": "chodec",
            "size": 22
        },
        {
            "area": "\u00dasteck\u00fd kraj",
            "class": "\u0159idi\u010d osobn\u00edho automobilu",
            "size": 23
        },
        {
            "area": "Olomouck\u00fd kraj",
            "class": "chodec",
            "size": 12
        },
        {
            "area": "Olomouck\u00fd kraj",
            "class": "\u0159idi\u010d osobn\u00edho automobilu",
            "size": 12
        },
        {
            "area": "Zl\u00ednsk\u00fd kraj",
            "class": "chodec",
            "size": 15
        },
        {
            "area": "Zl\u00ednsk\u00fd kraj",
            "class": "\u0159idi\u010d osobn\u00edho automobilu",
            "size": 13
        },
        {
            "area": "Libereck\u00fd kraj",
            "class": "chodec",
            "size": 11
        },
        {
            "area": "Libereck\u00fd kraj",
            "class": "\u0159idi\u010d osobn\u00edho automobilu",
            "size": 2
        },
        {
            "area": "Jihomoravsk\u00fd kraj",
            "class": "chodec",
            "size": 45
        },
        {
            "area": "Jihomoravsk\u00fd kraj",
            "class": "\u0159idi\u010d osobn\u00edho automobilu",
            "size": 34
        },
        {
            "area": "Moravskoslezsk\u00fd kraj",
            "class": "chodec",
            "size": 27
        },
        {
            "area": "Moravskoslezsk\u00fd kraj",
            "class": "\u0159idi\u010d osobn\u00edho automobilu",
            "size": 14
        },
        {
            "area": "Hlavn\u00ed m\u011bsto Praha",
            "class": "chodec",
            "size": 76
        },
        {
            "area": "Hlavn\u00ed m\u011bsto Praha",
            "class": "\u0159idi\u010d osobn\u00edho automobilu",
            "size": 13
        }
    ]

    const barChartColor = {
        "chodec": color["chodec"],
        "\u0159idi\u010d osobn\u00edho automobilu": color["motorová individuální vozidla"]
    }

    const width = parseInt(d3.select("#bar_chart .chart").style("width"));
    const verticalScreen = (window.innerWidth < window.innerHeight);

    function drawLegend(svg, classes, colorMap, transform, fillOpacity) {
        const legend = svg.append("g").attr("transform", transform);
        Array.from(classes).forEach((className, i) => {
            const legendRow = legend.append("g")
                .attr("transform", `translate(0, ${i * 25})`);
            legendRow.append("rect")
                .attr("width", 18)
                .attr("height", 18)
                .attr("fill", colorMap[className])
                .attr("fill-opacity", fillOpacity)
                .attr("rx", 3);
            legendRow.append("text")
                .attr("x", 25)
                .attr("y", 9)
                .attr("dy", "0.32em")
                .style("font-size", "14px")
                .text(className);
        });
    }
    
    if (verticalScreen) {
        const height = Number(width * 3 / 2);
        // Specify the chart’s dimensions, based on a bar’s height.
        const barHeight = 25;
        const marginTop = 80;
        const marginRight = 20;
        const marginBottom = 10;
        const marginLeft = 130;

        // Prepare the scales for positional and color encodings.
        // Fx encodes the area.
        const fy = d3.scaleBand()
            .domain(new Set(data.map(d => d.area)))
            .rangeRound([marginTop, height - marginBottom])
            .paddingInner(0.1);

        // Both x and color encode the age class.
        const classes = new Set(data.map(d => d.class));
        
        // Create the scales.
        const y = d3.scaleBand()
            .domain(classes)
            .rangeRound([0, fy.bandwidth()])
            .padding(0.05);
        const x = d3.scaleLinear()
            .domain([0, d3.max(data, d => d.size)]).nice()
            .rangeRound([marginLeft, width - marginRight]);

        // Create the SVG container.
        const svg = d3.select("#bar_chart .chart")
            .attr("style", `height: ${height}px;`)
            .append("svg")
            .attr("width", width)
            .attr("height", height)
            .attr("viewBox", [0, 0, width, height])
            .attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;");
        
        // Append a group for each area, and a rect for each class.
        svg.append("g")
            .selectAll()
            .data(d3.group(data, d => d.area))
            .join("g")
            .attr("transform", ([area]) => `translate(0,${fy(area)})`)
            .selectAll()
            .data(([, d]) => d)
            .join("rect")
            .attr("x", d => x(0))
            .attr("y", d => y(d.class))
            .attr("width", d => Math.abs(x(0) - x(d.size)))
            .attr("height", y.bandwidth())
            .attr("fill", d => barChartColor[d.class])
            .attr("fill-opacity", 0.6)
            .attr("rx", 3)
            .append("title")
            .text(d => `${d.class} ${d.size}`);

        // Add text labels on top of bars
        svg.append("g")
            .selectAll()
            .data(d3.group(data, d => d.area))
            .join("g")
            .attr("transform", ([state]) => `translate(0,${fy(state)})`)
            .selectAll()
            .data(([, d]) => d)
            .join("text")
            .attr("y", d => y(d.class) + y.bandwidth()/2)
            .attr("x", d => x(d.size))
            .attr("dy", "0.35em")
            .attr("dx", 10)
            .attr("text-anchor", "middle")
            .attr("font-size", "12px")
            .text(d => d.size);

        // Append the horizontal axis.
        svg.append("g")
            .attr("transform", `translate(0,${marginTop})`)
            .call(d3.axisTop(x).ticks(null, "s"))
            .call(g => g.selectAll(".domain").remove());

        // Append the vertical axis.
        svg.append("g")
            .attr("transform", `translate(${marginLeft},0)`)
            .call(d3.axisLeft(fy).tickSizeOuter(0))
            .selectAll("text")
            .text(d => d.replace(/- /g, ""));

        drawLegend(svg, classes, barChartColor, "translate(0, 0)", 0.6);
    } else {
        const height = Number(width * 1 / 2);
        // Specify the chart’s dimensions.
        const marginTop = 15;
        const marginRight = 100;
        const marginBottom = 75;
        const marginLeft = 40;
        // Prepare the scales for positional and color encodings.
        // Fx encodes the state.
        const fx = d3.scaleBand()
            .domain(new Set(data.map(d => d.area)))
            .rangeRound([marginLeft, width - marginRight])
            .paddingInner(0.1);
        // Both x and color encode the age class.
        const classes = new Set(data.map(d => d.class));
        const x = d3.scaleBand()
            .domain(classes)
            .rangeRound([0, fx.bandwidth()])
            .padding(0.05);

        // Y encodes the height of the bar.
        const y = d3.scaleLinear()
            .domain([0, d3.max(data, d => d.size)]).nice()
            .rangeRound([height - marginBottom, marginTop]);
        // Create the SVG container.
        const svg = d3.select("#bar_chart .chart")
            .attr("style", `height: ${height}px;`)
            .append("svg")
            .attr("width", width)
            .attr("height", height)
            .attr("viewBox", [0, 0, width, height])
            .attr("style", "max-width: 100%; height: auto;");
        // Append a group for each area, and a rect for each class.
        svg.append("g")
            .selectAll()
            .data(d3.group(data, d => d.area))
            .join("g")
            .attr("transform", ([area]) => `translate(${fx(area)},0)`)
            .selectAll()
            .data(([, d]) => d)
            .join("rect")
            .attr("x", d => x(d.class))
            .attr("y", d => y(Math.max(d.size, 0)))
            .attr("width", x.bandwidth())
            .attr("height", d => Math.abs(y(0) - y(d.size)))
            .attr("fill", d => barChartColor[d.class])
            .attr("fill-opacity", 0.8)
            .attr("rx", 3)
            .append("title")
            .text(d => `${d.class} ${d.size}`);
        // Add text labels on top of bars
        svg.append("g")
            .selectAll()
            .data(d3.group(data, d => d.area))
            .join("g")
            .attr("transform", ([state]) => `translate(${fx(state)},0)`)
            .selectAll()
            .data(([, d]) => d)
            .join("text")
            .attr("x", d => x(d.class) + x.bandwidth() / 2)
            .attr("y", d => y(d.size) - 5)  // 5px above the bar
            .attr("text-anchor", "middle")
            .attr("font-size", "14px")
            .text(d => d.size);
        // Append the horizontal axis.
        svg.append("g")
            .attr("transform", `translate(0,${height - marginBottom})`)
            .call(d3.axisBottom(fx).tickSizeOuter(0))
            .call(g => g.select(".domain").remove())
            .call(g => {
                g.selectAll(".tick text")
                    .attr("transform", `rotate(20)`)
                    .attr("text-anchor", "start")
                    .style("font-size", "14px");
                g.select(".domain").remove();
            });

        // Append the vertical axis.
        svg.append("g")
            .attr("transform", `translate(${marginLeft},0)`)
            .style("font-size", "14px")
            .call(d3.axisLeft(y).ticks(null, "s"))
            .call(g => g.selectAll(".domain").remove());
        drawLegend(svg, classes, barChartColor, `translate(${width / 2 - 80}, ${marginTop + 20})`, 0.8);
    }
</script>
</p>
<h2 id="nejvíce-úmrtí-cyklistů">Nejvíce úmrtí cyklistů</h2>
<p>Největší podíl cyklistů mezi zemřelými má Zlínský kraj. Shodou okolností je to zároveň kraj, ve kterém zemřelo v roce 2025 i absolutně nejvíce cyklistů ze všech krajů.</p>

<!-- static chart -->
<div class="chart-box" id="zlin_death_chart">
    <div class="chart-header">
        <img class="chart-header-logo light" alt="logo" width="100" height="100" src="/images/logo-light.svg">
        <img class="chart-header-logo dark" alt="logo" width="100" height="100" src="/images/logo-dark.svg">
        <h1></h1>
        <p></p>
    </div>
    <div class="chart"></div>
    <div class="tooltip">
        <span>Popisek: </span>
        <span class="tooltip-field"><em>(Najeďte na obdélník v grafu.)</em></span>
    </div>
    <div class="chart-footer">
        <span class="source">Zdroj: <a href="https://policie.gov.cz/clanek/statistika-nehodovosti.aspx?q=Y2hudW09Mg%3d%3d" target="_blank">Policie ČR</a></span>
        <!-- <span class="license">CC BY</span> -->
    </div>
</div>
<script>
    // Initial draw
    drawChart(parsed, "zlin_death_chart", 1, "CZ072");  // death, all
</script>

<p>Opět se nabízí otázka, jestli se ve Zlínském kraji jezdí více na jízdním kole. Odpověď je ano, ale v tomto případě již ne tak výrazně. Ve Středočeském kraji, Královéhradeckém kraji, Pardubickém kraji i Olomouckém kraji jezdí lidé absolutně i relativně (v poměru k ostatním dopravním prostředkům) na kolech více než v kraji Zlínském.</p>
<p>Přesto patří Zlínský kraj mezi více cyklistické kraje České republiky.</p>

<!-- static chart -->
<div class="chart-box" id="zlin_modal_split_chart">
    <div class="chart-header">
        <img class="chart-header-logo light" alt="logo" width="100" height="100" src="/images/logo-light.svg">
        <img class="chart-header-logo dark" alt="logo" width="100" height="100" src="/images/logo-dark.svg">
        <h1></h1>
        <p></p>
    </div>
    <div class="chart"></div>
    <div class="chart-footer">
        <span class="source">Zdroj: <a href="https://scitani.gov.cz/dopravni-prostredek" target="_blank">Sčítání 2021</a></span>
    </div>
</div>
<script>
    // Initial draw
    drawPieChart(modalSplitParsed, "zlin_modal_split_chart", 13);  // Zlínský kraj
</script>

<h2 id="interaktivní-graf">Interaktivní graf</h2>
<p>Zde je celá interaktivní verze grafu. Ze seznamů vyberte kraj a druh zranění a podívejte se na libovolnou kombinaci. Pod grafem o zranění najdete i graf s poměry dopravních prostředků.</p>
<h3 id="několik-tipů-na-co-se-podívat">Několik tipů, na co se podívat:</h3>
<ul>
<li><strong>Kraj Vysočina, úmrtí:</strong> Poměrně více úmrtí řidičů motocyklů</li>
<li>Těžká zranění jsou pro cyklisty poměrně častější než úmrtí</li>
<li><strong>Moravskoslezský kraj, těžká zranění:</strong> Poměrně méně zranění řidičů osobních automobilů</li>
<li>Lehká zranění mají podobnější distribuci (kromě Hlavního města Prahy) než těžká zranění a úmrtí.</li>
</ul>

<!-- controlable chart -->
<div class="chart-control" id="vehicle_injury_chart_control">
    <form>
        <fieldset class="grid">
        <div>
            <label for="areaChoice">Vyberte kraj:</label>
            <select id="areaChoice" name="area">
                <option value="all" selected>Všechny kraje</option>
                <option value="CZ010">Hlavní město Praha</option>
                <option value="CZ020">Středočeský kraj</option>
                <option value="CZ031">Jihočeský kraj</option>
                <option value="CZ032">Plzeňský kraj</option>
                <option value="CZ041">Karlovarský kraj</option>
                <option value="CZ042">Ústecký kraj</option>
                <option value="CZ051">Liberecký kraj</option>
                <option value="CZ052">Královéhradecký kraj</option>
                <option value="CZ053">Pardubický kraj</option>
                <option value="CZ063">Kraj Vysočina</option>
                <option value="CZ064">Jihomoravský kraj</option>
                <option value="CZ071">Olomoucký kraj</option>
                <option value="CZ080">Moravskoslezský kraj</option>
                <option value="CZ072">Zlínský kraj</option>
            </select>
        </div>
        <div>
            <label for="injuryChoice">Vyberte zranění:</label>
            <select id="injuryChoice" name="injuryChoice">
                <option value="3">Lehké zranění</option>
                <option value="2">Těžké zranění</option>
                <option value="1" selected>Úmrtí</option>
            </select>
        </div>
        </fieldset>
    </form>
</div>

<div class="chart-box" id="vehicle_injury_chart">
    <div class="chart-header">
        <img class="chart-header-logo light" alt="logo" width="100" height="100" src="/images/logo-light.svg">
        <img class="chart-header-logo dark" alt="logo" width="100" height="100" src="/images/logo-dark.svg">
        <h1></h1>
        <p></p>
    </div>
    <div class="chart"></div>
    <div class="tooltip">
        <span>Popisek: </span>
        <span class="tooltip-field"><em>(Najeďte na obdélník v grafu.)</em></span>
    </div>
    <div class="chart-footer">
        <span class="source">Zdroj: <a href="https://policie.gov.cz/clanek/statistika-nehodovosti.aspx?q=Y2hudW09Mg%3d%3d" target="_blank">Policie ČR</a></span>
        <!-- <span class="license">CC BY</span> -->
    </div>
</div>

<div class="chart-box" id="modal_split_chart">
    <div class="chart-header">
        <img class="chart-header-logo light" alt="logo" width="100" height="100" src="/images/logo-light.svg">
        <img class="chart-header-logo dark" alt="logo" width="100" height="100" src="/images/logo-dark.svg">
        <h1></h1>
        <p></p>
    </div>
    <div class="chart"></div>
    <div class="chart-footer">
        <span class="source">Zdroj: <a href="https://scitani.gov.cz/dopravni-prostredek" target="_blank">Sčítání 2021</a></span>
    </div>
</div>

<script>
    // setup vars
    var area = document.getElementById("areaChoice").value;
    var injury_type = document.getElementById("injuryChoice").value

    const pieAreaMapper = {
        'all': 0,
        'CZ010': 1,
        'CZ020': 2,
        'CZ031': 3,
        'CZ032': 4,
        'CZ041': 5,
        'CZ042': 6,
        'CZ051': 7,
        'CZ052': 8,
        'CZ053': 9,
        'CZ063': 10,
        'CZ064': 11,
        'CZ071': 12,
        'CZ072': 13,
        'CZ080': 14,
    }
    // Initial draw
    drawChart(parsed, "vehicle_injury_chart", injury_type, area);
    drawPieChart(modalSplitParsed, "modal_split_chart", pieAreaMapper[area]);
    document.getElementById("areaChoice").addEventListener("change", (event) => {
        area = event.target.value
        drawChart(parsed, "vehicle_injury_chart", injury_type, area);
        drawPieChart(modalSplitParsed, "modal_split_chart", pieAreaMapper[area]);
    });
    document.getElementById("injuryChoice").addEventListener("change", (event) => {
        injury_type = event.target.value
        drawChart(parsed, "vehicle_injury_chart", injury_type, area);
    });
</script>

]]></description></item></channel></rss>