Helmut: SVG Grid und Datendarstellung

Hallo,

ich habe da ein Projekt übernommen, in dem mittels SVG zeitliche Datenverläufe dargestellt werden. Das ist aber leider ziemlich verknotet.

Ich würde daher gerne möglichst strukturiert und von vorne anfangen.

Ich brauche ein grobes Raster für jeweils 31 Tage und Werte zwischen Minus 20 und Plus 40 (kWh).

Wie kann ich so ein Raster erstellen? Kann ich dann später mehrere Kurven für Vormonat, Monat, Vorjahr..., oder so ähnlich in verschiedenen Farben reinzeichnen? Die Werte liegen in CSV-Dateien vor und mit PHP kann ich so einigermaßen gut umgehen.

Außerdem will der Eine Kurven und die Andere Balkendiagramme.
Das muss also variabel bleiben.

Wie könnte man das Ganze für SVG also modularisieren?

Liebe Grüße
Helmut

  1. Servus!

    Hallo,

    ich habe da ein Projekt übernommen, in dem mittels SVG zeitliche Datenverläufe dargestellt werden. Das ist aber leider ziemlich verknotet.

    Ich würde daher gerne möglichst strukturiert und von vorne anfangen.

    Das ist eines unserer Zukunftsprojekte:

    Daten aus einem Mikro-Controller (Raspberry, Arduino, etc) auslesen und dann mit HTML ausgeben.

    SVG würde dann die Datenvisualisierung übernehmen.

    Und da liegt unser „ewiges“ ToDo - jedenfalls seit 2016 nicht fertiggestellt. 😕 😟

    Ich brauche ein grobes Raster für jeweils 31 Tage und Werte zwischen Minus 20 und Plus 40 (kWh).

    Das würde ich mit JavaScript dynamisch erstellen: mal 28, mal 30 und mal 31 Tage auf einer SVG-Leinwand.

    Wie kann ich so ein Raster erstellen?

    Ein kartesisches Koodinatensystem eher nicht, aber eben zwei Achsen mit Beschriftung (und entsprechenden role-Attributen).

    Kann ich dann später mehrere Kurven für Vormonat, Monat, Vorjahr..., oder so ähnlich in verschiedenen Farben reinzeichnen? Die Werte liegen in CSV-Dateien vor und mit PHP kann ich so einigermaßen gut umgehen.

    Außerdem will der Eine Kurven und die Andere Balkendiagramme.
    Das muss also variabel bleiben.

    Ja, entweder mit @JürgenB s Funktionsplotter

    oder mit einem Balkendiagramm-Generator - das ist im SELF-Wiki leider eine Baustelle. 😕 😟

    Wie könnte man das Ganze für SVG also modularisieren?

    Ein Objekt mit vielen Methoden

    Herzliche Grüße

    Matthias Scharwies

    --
    Ich habe heute rausgefunden, dass in das Pizzafach meines Rucksacks auch ein Laptop passt!
  2. Hallo Helmut,

    SVG ist dafür eigentlich ein recht unkomplizierter Geselle.

    Eigentlich musst Du nur

    • den SVG Rahmen eröffnen
    • das Koordinatensystem zeichnen
    • wahlweise einen Path entlang der Messpunkte oder die Balken zeichnen
    • den SVG Rahmen wieder schließen.

    Ich nehme an, dass dein Diagramm ein vorgegebenes Breiten/Höhe-Verhältnis haben soll. Ein solches Verhältnis automatisch zu skalieren geht zwar, wenn man dem svg Element preserveAspectRatio="none" gibt, aber dann verzerrt es auch die Linienbreiten und ggf. deine Achsenbeschriftungen. Deshalb würde ich das lassen und beim Default bleiben (xMidYMid meet).

    Wenn Du bspw. ein Verhältnis Breite/Höhe von 4:3 haben willst, setzt Du eine viewBox von "0 0 1000 750". Darin platzierst Du dann deine Elemente, so dass sie ordentlich aussehen.

    Das ist letztlich nur eine blöde Koordinatenumrechnerei, weil die Y-Achse bei SVG entgegengesetzt zur Y-Achse eines mathematischen Koordinatensystems orientiert ist. Und dein Y=0 liegt mitten im SVG. Bei deinem Wertebereich müsste dein "logisches y=0" also bei der SVG-Koordinate 500 liegen. Und weil Du ja vermutlich Platz zum Malen der Y-Achse brauchst, musst Du Dir links im Bild ein paar Punkte dafür freihalten. Z.B. 100 Punkte. Das hängt vom Font ab und dem, was Du da hinschreiben willst. Diese Breite musst Du auf das x draufaddieren.

    Das ist alles machbar, nur nervig. Am besten machst Du Dir eine Funktion, die eine beliebige logische Koordinate in SVG-Koordinaten umrechnet, operierst beim Plotten strikt im mathematischen Koordinatensystem und rechnest jeden Punkt mit dieser Funktion in SVG Koordinaten um.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo @Rolf B,
      hallo @Matthias Scharwies,

      Ich habe das befürchtet, dass es nicht so einfach geht, wie ich gehofft habe. Vielleicht hat ja auch deshalb mein Vordenker so ein Knotenprogramm geschrieben.

      Das hat auch noch Probleme, und zwar bei den Tagesdiagrammen an den Tagen, wenn Schalttage im Spiel sind.

      Die wollte ich dabei auch möglichst gleich beseitigen.

      Meine Frage war daher, kann man bein SVG vielleicht mittels CSS und Positionierung mit Layern arbeiten? Ein Layer wäre dann das Gitterdiagramm und darüber würde man transparent die Layer mit Beschriftungen, Kurve Vortag, Kurve akteueller Tag, Kurve letztes Jahr zum gleichen Tag, usw. legen.

      Und die könnte man dann am Client mittels JavaScript wahlweise ausblenden, so dass kein erneuter Request notwendig wäre und auch keine Neuberechnung im Backend.

      Für JavaScript-Verweigerer müsste man vielleicht einen Fallback bauen, bei dem die Ebenen auch wahlweise ausgeblendet werden könnten, aber dank Caching am Server auch keine Neuberechnung notwendig werden muss.

      Das waren so meine Ideen zu SVG und CSS.
      Sind die Blödsinn, oder sind die realisierbar?

      LG Helmut

      1. Servus!

        Hallo @Rolf B,
        hallo @Matthias Scharwies,

        Ich habe das befürchtet, dass es nicht so einfach geht, wie ich gehofft habe. Vielleicht hat ja auch deshalb mein Vordenker so ein Knotenprogramm geschrieben.

        Das hat auch noch Probleme, und zwar bei den Tagesdiagrammen an den Tagen, wenn Schalttage im Spiel sind.

        Die wollte ich dabei auch möglichst gleich beseitigen.

        Meine Frage war daher, kann man bein SVG vielleicht mittels CSS und Positionierung mit Layern arbeiten?

        Ja!

        Ein Layer wäre dann das Gitterdiagramm und darüber würde man transparent die Layer mit Beschriftungen, Kurve Vortag, Kurve akteueller Tag, Kurve letztes Jahr zum gleichen Tag, usw. legen.

        Geht, einfach drüber schreiben!

        Und die könnte man dann am Client mittels JavaScript wahlweise ausblenden, so dass kein erneuter Request notwendig wäre und auch keine Neuberechnung im Backend.

        oder mit einer Klasse, die per CSS display:none oder hidden kriegt.

        Für JavaScript-Verweigerer müsste man vielleicht einen Fallback bauen, bei dem die Ebenen auch wahlweise ausgeblendet werden könnten, aber dank Caching am Server auch keine Neuberechnung notwendig werden muss.

        Es gibt niemanden mehr, der kein JS hat!

        Das waren so meine Ideen zu SVG und CSS.
        Sind die Blödsinn, oder sind die realisierbar?

        Realisierbar, meist verwendet man eine Bibiothek wie D3.js

        Herzliche Grüße

        Matthias Scharwies

        --
        Ich habe heute rausgefunden, dass in das Pizzafach meines Rucksacks auch ein Laptop passt!
        1. Es gibt niemanden mehr, der kein JS hat!

          Everyone has JavaScript, right?

          1. Servus, du Sockenpuppe!

            Es gibt niemanden mehr, der kein JS hat!

            Everyone has JavaScript, right?

            Have they switched off JavaScript?

            People still do.

            Der Link https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/ ist von 2013.

            Sag mir mal, wie man in Firefox 115 oder Chrome JS deaktivieren kann.

            Herzliche Grüße

            Matthias Scharwies

            --
            Ich habe heute rausgefunden, dass in das Pizzafach meines Rucksacks auch ein Laptop passt!
            1. Hallo Matthias,

              Chrome: Einstellungen, Datenschutz und Sicherheit, Inhalte, Javascript

              Firefox: about:config, nach javascript.enabled suchen.

              Rolf

              --
              sumpsi - posui - obstruxi
              1. Könnte ja auch im Firmen-Proxi gefiltert werden.

            2. @@Matthias Scharwies

              Everyone has JavaScript, right?

              Du bist dem Link gefolgt und hast verstanden, worum es geht?

              Sag mir mal, wie man in Firefox 115 oder Chrome JS deaktivieren kann.

              Hm, vielleicht nicht.

              JavaScript zu deaktivieren ist eine Möglichkeit, kein JavaScript zu haben. Aber nicht die einzige, auch nicht die relevanteste.

              🖖 Живіть довго і процвітайте

              --
              Ad astra per aspera
              1. Servus!

                @@Matthias Scharwies

                Everyone has JavaScript, right?

                Du bist dem Link gefolgt und hast verstanden, worum es geht?

                Ja, schon. Bin am Wochenende mal wieder Bahn gefahren und habe erneut gemerkt, wie wichtig eine gute Internetverbindung ist, damit Bilder geladen (lazy-loading ohne Netz), bzw. JavasCript ausgeführt werden kann.

                mittels SVG zeitliche Datenverläufe dargestellt werden.

                Wie könnte man das Ganze für SVG also modularisieren?

                Wenn ich das

                • planen und dann
                • programmieren

                müsste, würde ich erst mal schauen, was ich brauch:

                1. Daten kommen vom Mikro-Controller und werden als csv-Dateien auf dem Server abgelegt.

                2. Ich habe ein leeres SVG-Diagramm, dass ich

                  • monateweise beschrifte
                  • nach Benutzerwunsch mit Liniendiagramm oder Balken befülle
                  • mit anderen Diagrammen überlagere

                Der Thread-Ersteller kann zwar PHP - ich würde wegen D3.js trotzdem auf JavaScript zurückgreifen.

                Ich persönlich würde hier evtl. mit Node.js auf dem Server arbeiten.

                Herzliche Grüße

                Matthias Scharwies

                --
                Ich habe heute rausgefunden, dass in das Pizzafach meines Rucksacks auch ein Laptop passt!
      2. Hallo Helmut,

        Und die könnte man dann am Client mittels JavaScript wahlweise ausblenden

        Kommt drauf an. Wenn du das SVG separat generierst und als <img> Element einbindest, hast Du aus der Webseite heraus keinen Einfluss. Das SVG-DOM ist dann vom DOM der Webseite getrennt.

        Ich hab voriges jahr mal damit experimentiert - man kann im SVG JavaScript haben, das die URL des SVG abfragt und, dann schaut, ob da URL-Parameter oder Hash-Parameter sind und darauf reagiert. Aber das wäre dann schon extrem speziell, und es ist unnötiges Gefummel.

        Denn wenn Du das SVG inline ins HTML setzt, stehen Dir alle Wege offen. Dann ist das SVG-Bild Teil des HTML-DOM und du kannst mit Klassen, CSS-Regeln und JavaScript dran rumfummeln, wie es Dein Herz erfreut.

        Du könntest auch mit JavaScript statt mit PHP zeichnen. Was es nicht einfacher macht…

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Hallo Rolf,

          Du könntest auch mit JavaScript statt mit PHP zeichnen. Was es nicht einfacher macht…

          Die neuen Daten werden doch ohnehin mittels PHP aus einer Datenbasis (Datenbank oder Dateiensammlung) gewonnen. Da sollte man meiner Meinung nach nicht eine zweite Baustelle am Client aufmachen!?

          Helmut

          1. Hallo Helmut,

            kommt halt drauf an, was man will. Gehen tut beides.

            Man kann die Daten per Ajax-Call beziehen und dann per JS ins SVG plotten. Belastet die Clients etwas mehr.

            Oder man lädt die ganze Seite neu und generiert den Plot am Server. Belastet den Server etwas mehr. Wenn die Parameter, die die geplotteten Daten wiedergeben, alle in der URL stehen, hat das aber den Vorteil, dass die fertige Seite gecached werden kann. Wenn das für PHP-generierte Seiten aktiv ist.

            Rolf

            --
            sumpsi - posui - obstruxi
    2. Moin Rolf,

      Das ist letztlich nur eine blöde Koordinatenumrechnerei, weil die Y-Achse bei SVG entgegengesetzt zur Y-Achse eines mathematischen Koordinatensystems orientiert ist.

      Oder man spiegelt die Zeichenfläche, wie in https://www.robertbienert.de/dokumente/svg/diagramme.html demonstriert.

      Letztlich …

      [ist] SVG […] dafür eigentlich ein recht unkomplizierter Geselle.

      Viele Grüße
      Robert

  3. Hi Helmut,

    Ich brauche ein grobes Raster für jeweils 31 Tage und Werte zwischen Minus 20 und Plus 40 (kWh).

    Wie kann ich so ein Raster erstellen?

    mit ChatGPT erstellt:

    <?php
    // Function to get the number of days in a given month and year
    function getDaysInMonth($month, $year) {
        return cal_days_in_month(CAL_GREGORIAN, $month, $year);
    }
    
    // Current month and year
    $currentMonth = date('n'); // Numeric representation of the current month (1 to 12)
    $currentYear = date('Y');
    
    // Get the number of days in the current month
    $daysInMonth = getDaysInMonth($currentMonth, $currentYear);
    
    // SVG chart dimensions
    $chartWidth = 600;
    $chartHeight = 300;
    $barWidth = $chartWidth / $daysInMonth;
    
    // SVG chart content
    $svgContent = '<?xml version="1.0" encoding="UTF-8"?>
    <svg width="' . $chartWidth . '" height="' . $chartHeight . '" xmlns="http://www.w3.org/2000/svg">';
    
    // Loop through each day and create a rectangle representing the bar
    for ($day = 1; $day <= $daysInMonth; $day++) {
        $x = ($day - 1) * $barWidth;
        $barHeight = mt_rand(50, $chartHeight - 50); // Random height for demonstration purposes
        $svgContent .= '<rect x="' . $x . '" y="' . ($chartHeight - $barHeight) . '" width="' . $barWidth . '" height="' . $barHeight . '" fill="blue" />';
    }
    
    $svgContent .= '</svg>';
    
    // Output the SVG chart
    header('Content-Type: image/svg+xml');
    echo $svgContent;
    ?>
    
    

    Save the above code in a PHP file (e.g., chart.php) and run it on your web server. When you access the PHP file in your browser, it will generate and display an SVG chart where each bar represents the number of days in the current month, and the x-axis will have the days labeled from 1 to the last day of the month. The height of each bar is randomly generated for demonstration purposes, but you can replace it with actual data representing your chart.

    Ich würde jetzt erst mal schauen, wie das SVG ausschaut und es manuell anpassen.

    Dann chatGPT fragen, wie man …

    ... mehrere Kurven für Vormonat, Monat, Vorjahr..., oder so ähnlich in verschiedenen Farben reinzeichne[t]? Die Werte liegen in CSV-Dateien vor

    To create an SVG line diagram with data from a CSV file using PHP, we'll first need to read the data from the CSV file and then use that data to generate the SVG content. In this example, we'll assume the CSV file contains two columns: "Day" and "Value", where "Day" represents the day of the month and "Value" represents the data value for each day.

    Let's proceed with the code:

    Create a CSV file named data.csv with the following sample data (you can replace it with your actual data):

    Day,Value
    1,10
    2,20
    3,15
    4,25
    5,30
    6,18
    7,22
    8,17
    9,29
    10,33
    
    

    Create a PHP script to read the CSV file, generate the SVG content, and display the SVG line diagram:

    <?php
    // Function to read CSV file and return an array of data
    function readCSV($filename) {
        $data = [];
        if (($handle = fopen($filename, "r")) !== false) {
            while (($row = fgetcsv($handle, 1000, ",")) !== false) {
                $data[] = $row;
            }
            fclose($handle);
        }
        return $data;
    }
    
    // Read the CSV data
    $csvData = readCSV('data.csv');
    
    // SVG chart dimensions
    $chartWidth = 600;
    $chartHeight = 300;
    
    // Find the maximum value in the data to scale the chart
    $maxValue = max(array_column($csvData, 1));
    $dataCount = count($csvData);
    
    // Calculate the x and y positions of the points in the chart
    $stepX = $chartWidth / ($dataCount - 1);
    $stepY = $chartHeight / $maxValue;
    
    // SVG chart content
    $svgContent = '<?xml version="1.0" encoding="UTF-8"?>
    <svg width="' . $chartWidth . '" height="' . $chartHeight . '" xmlns="http://www.w3.org/2000/svg">';
    
    // Generate the points for the line
    $linePoints = '';
    foreach ($csvData as $index => $data) {
        $x = $index * $stepX;
        $y = $chartHeight - ($data[1] * $stepY);
        $linePoints .= $x . ',' . $y . ' ';
    }
    
    // Draw the line
    $svgContent .= '<polyline points="' . $linePoints . '" fill="none" stroke="blue" stroke-width="2" />';
    
    // Draw the x and y axis
    $svgContent .= '<line x1="0" y1="0" x2="0" y2="' . $chartHeight . '" stroke="black" stroke-width="1" />';
    $svgContent .= '<line x1="0" y1="' . $chartHeight . '" x2="' . $chartWidth . '" y2="' . $chartHeight . '" stroke="black" stroke-width="1" />';
    
    $svgContent .= '</svg>';
    
    // Output the SVG chart
    header('Content-Type: image/svg+xml');
    echo $svgContent;
    ?>
    
    

    Save the PHP code in a file (e.g., chart.php) and make sure the data.csv file is in the same directory as the PHP script. When you access the PHP file in your browser, it will generate and display an SVG line diagram using the data from the data.csv file. The x-axis represents the days, and the y-axis represents the values from the CSV data. The blue line connects the data points.

    Überprüf das mal und pass das an Deine Bedürfnisse an.

    Liebe Grüße
    Helmut

    LG Tom

    1. Hallo svg-fan,

      danke für die viele Mühe.

      Das ist für mein Projekt allerdings viel zu kompliziert. Ich habe mir jetzt rundherum was zusammengeschaut. Das sieht jetzt sogar ziemlich strukturiert aus und man kann es beim Lesen von oben nach unten sogar verstehen - also ohne Knoten.

      Jetzt muss ich nur noch eine Möglichkeit finden, möglichst ohne JS mit Checkboxen die CSS-Klassen für die Kurven so zu manipulieren, dass die Kurve walweise angezeigt wird, oder nicht.

      LG
      Helmut

      1. Hallo Helmut,

        Jetzt muss ich nur noch eine Möglichkeit finden, möglichst ohne JS mit Checkboxen die CSS-Klassen für die Kurven so zu manipulieren

        Wenn's inline svg ist, könnte das mit dem Checkbox-Hack gehen. Zu jeder Kurve gibt's eine Checkbox und beide kriegen die gleiche Klasse.

        Die Kurven kriegen dazu noch eine graph Klasse, um sie alle zusammen ansprechen zu können.

        svg .graph {
           visibility: hidden;
        }
        input[type="checkbox"].graph_1:checked ~ svg .graph1 {
           visibility: normal;
        }
        

        Setzt im Firefox aber immer noch voraus, dass Checkbox und SVG Geschwister sind. In Chromia und Webkittys kann man das mit :has() verschönern: hier

        Aber GUT ist das nicht. Verhalten soll nicht von CSS gesteuert werden, dafür ist JS da.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Hallo Rolf,

          svg .graph {
             visibility: hidden;
          }
          input[type="checkbox"].graph_1:checked ~ svg .graph1 {
             visibility: normal;
          }
          

          Setzt im Firefox aber immer noch voraus, dass Checkbox und SVG Geschwister sind. In Chromia und Webkittys kann man das mit :has() verschönern: hier

          Aber GUT ist das nicht. Verhalten soll nicht von CSS gesteuert werden, dafür ist JS da.

          Dann lasse ich lieber den Server denken und baue GET-Parameter ein und ein Form mit den Checkboxen und dem Datumswähler für die verfügbaren Monate mit method=GET.

          Sollte man für den Monatswähler select multiple nehmen, oder besser gleich drei Monatswähler? Dann würde ich auch keine Checkboxen mehr brauchen. Angezeigt werden dann nur unterschiedliche Daten, falls vorhanden.

          LG
          Helmut

          1. Hallo Helmut,

            Sollte man für den Monatswähler select multiple nehmen, oder besser gleich drei Monatswähler?

            Eine Liste mit ordentlich beschrifteten Checkboxen. Multiple Select hat - meine ich - Zugängslichkeitsprobleme (sprich: Verwendbarkeit mit Screenreadern und Eingabehilfen).

            Rolf

            --
            sumpsi - posui - obstruxi
            1. Hallo Rolf,

              Sollte man für den Monatswähler select multiple nehmen, oder besser gleich drei Monatswähler?

              Eine Liste mit ordentlich beschrifteten Checkboxen. Multiple Select hat - meine ich - Zugängslichkeitsprobleme (sprich: Verwendbarkeit mit Screenreadern und Eingabehilfen).

              Ich habe dazu hier im Forum inzwischen auch ein paar bissige Kommentare von @Gunnar Bittersmann gefunden. Ok, Select-Multiple fällt flach, genauso aber wie Checkboxen:

              Es können später hunderte von Grafiken angeboten werden, 12 pro Jahr, und davon sollen immer nur drei gleichzeiig zum Vergleich grafisch aufbereitet werden. Also nehme ich drei einzelne Select-Boxen, die alle auf ein gemeinsames Array gemerged werden. Das verhindert dann auch, dass eine Kurve doppelt oder sogar dreifach gerendwert werden muss.

              Fragt sich, ob man da nicht gleich einen Server-Cache vorsehen sollte. (Nun verzettele ich mich wohl gerade.) Aber für jede Grafik müssen alle Tagesdateien des gewünschten Monats gesammelt und konsolidiert werden. Erstaunlich, wie schnell der Raspi das bisher abarbeitet.

              Standardanzeige ist dann: aktueller Tag, Vortag, Vorvorgestern... Und wenn man andere sehen will, kann man sie per Select auswählen, oder gleich in die GET-Parameter schreiben.

              Ich denke, so könnte es gehen :-)

              LG
              Helmut

              1. Hallo Helmut,

                Es können später hunderte von Grafiken angeboten werden, 12 pro Jahr, und davon sollen immer nur drei gleichzeitig zum Vergleich grafisch aufbereitet werden.

                Es gibt Webshops, die bieten hunderte von Produkten an, und von denen kannst Du dann bis zu drei (vier, fünf) für einen Produktvergleich auswählen (der dann nichtssagend ist, weil auch ähnliche Produkte keine übereinstimmenden Kategorien bekommen…).

                In diesen Shops kannst Du filtern, blättern, und bei jedem Produkt ist eine Checkbox, die du für den Vergleich anhaken kannst. Das geht auch ohne JS, man muss sich die selektierten Elemente nur in der Session merken. Sind's dann zu viele, wenn man "Anzeigen" (oder "Vergleichen") drückt, gibt's halt was auffe Fingerchen.

                25 Jahre mit je 12 Monaten - das könnte man als Tabelle mit 25 Zeilen und 12 Spalten realisieren (je Zelle dann aber visuell versteckte, zugängliche Informationen vorsehen, damit der Nutzer einer Vorlesesoftware beim durchnavigieren den Überblick behält). Sind's irgendwann 73 Jahre, macht man die Tabelle in einen scrollbaren Container, in dem nur 10 Jahre sichtbar sind oder so.

                Geht alles.

                Drei einzelne Selectboxen sind – wenn es hunderte von Rows sind – extrem nervig, scheint mir.

                Rolf

                --
                sumpsi - posui - obstruxi