SVG Koordinaten auslesen

Hallo Zusammen,

ich möchte in einer GUI gerne eine Vektorgrafik, d. h. eine SVG anzeigen. Das klappt mittels Batik (https://xmlgraphics.apache.org/batik/) auch soweit ganz gut.

Allerdings reicht für mein Vorhaben das reine anzeigen nicht aus, ich würde auch gerne die X- und Y-Koordinate der SVG an einer bestimmten Mausposition auslesen.

Ich schaffe es allerdings nur entweder die Mausposition auszulesen, diese ändert sich allerdings je nachdem ob man die Größe der GUI ändert oder die SVG zoomt.

karte.setLayout( new BoxLayout(karte, BoxLayout.Y_AXIS ) );

JSVGCanvas canvas = new JSVGCanvas();  
canvas.setURI(new File("/home/arbeit/home/arbeit/europa.svg").toURI().toString());
canvas.setDocumentState(JSVGCanvas.ALWAYS_DYNAMIC);

canvas.addMouseListener(new MouseAdapter(){
    @Override
    public void mousePressed(MouseEvent e){
        xwert =  e.getX();
        ywert =  e.getY();
        lagebestimmung();
        main.setVisible(true);
    }
});

Kann mir jemand helfen?

Muss das über Batik sein? Du könntest die SVG doch auch über JDom oder ähnliches laden und die Daten in einen GeneralPath parsen. Vllt. gibt es da ja in Batik auch was für.

Von Canvas kann man sich mit getRenderingTransform die Rendering-Transformation holen. Das inverse davon ist das, was du brauchst, um die Maus/Bildschirmkoordinaten in Weltkoordinaten umzurechnen.

import java.awt.BorderLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.nio.file.Paths;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

import org.apache.batik.swing.JSVGCanvas;

public class SvgCoordinatesTest
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JSVGCanvas canvas = new JSVGCanvas();
        canvas.setURI(Paths.get("europe.svg").toUri().toString());
        canvas.setDocumentState(JSVGCanvas.ALWAYS_DYNAMIC);

        canvas.addMouseListener(new MouseAdapter()
        {
            @Override
            public void mousePressed(MouseEvent e)
            {
                System.out.println("Screen coordinates: " + e.getPoint());

                AffineTransform at = canvas.getRenderingTransform();
                Point2D worldCoordinates = null;
                try
                {
                    worldCoordinates = at.inverseTransform(e.getPoint(), null);
                }
                catch (NoninvertibleTransformException ex)
                {
                    ex.printStackTrace();
                }
                System.out.println("World coordinates: " + worldCoordinates);
            }
        });

        f.getContentPane().setLayout(new BorderLayout());
        f.getContentPane().add(new JLabel(
            "<html>" + 
            "Arrow keys to translate<br>" + 
            "CTRL+I and CTRL+O to zoom" + 
            "</html>"), BorderLayout.NORTH);
        f.getContentPane().add(canvas, BorderLayout.CENTER);

        f.setSize(1000, 1000);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

(BTW: In solchen Fällen kann es hilfreich sein, Code zu posten, der möglichst nahe an einem compilierbaren Beispiel ist)

EDIT:

Das main.setVisible(true); sieht jedenfalls seeeehr suspekt aus. Vermutlich (!) sollte das nicht dort stehen (aber vielleicht ist es OK…)

Wow Marco13,

vielen, vielen Dank für die schnelle und kompetente Hilfe.

Hallo,

mittlerweile habe ich doch noch ein Problem bezüglich der Koordinaten.

Meine SVG stellt einen Plan dar. Mit Klick auf einen beliebigen Punkt, sollen die Koordinaten ausgelesen werden. Zum entsprechenden Punkt werden Daten eingetragen (die sich immer wieder ändern). Diese sollen in eine Datenbank geschrieben werden. Dies klappt dank der Hilfe von Marco13, auch grundsätzlich alles.

Doch bei “Resize” ändern sich die Koordinaten. Bei der Verwendung unterschiedlicher Bildschirm dürfte das ebenfalls ein Problem sein.

Was kann man da tun?

Hm…!? Die Weltkoordinaten sollten sich doch nicht ändern. Ein Punkt, der bei Weltkoordinate (123,456) (z.B. die “Position von Berlin”) liegt, wird auf dem Bildschirm vielleicht mal bei (100,200) oder bei (400,600) angezeigt, aber die Weltkoordinaten sollten immer gleich bleiben…!?

Hallo,

genau da liegt bei Deinem Code wohl das Problem. Die Koordinaten werden nicht umgerechnet. Ist mir gerade erst aufgefallen.

Terminalausgabe:
Screen coordinates: java.awt.Point[x=307,y=46]
World coordinates: Point2D.Float[307.0, 46.0]
Screen coordinates: java.awt.Point[x=449,y=50]
World coordinates: Point2D.Float[449.0, 50.0]

Ich hab das Problem gefunden. Die AffineTransform ändert sich bei der Veränderung der Größe nicht automatisch mit. Ich weiß leider nur nicht wie man das ändern kann.

Poste ggf. mal etwas code, d.h. wo du die Transform wie änderst, und wo du sie wieder abholst. (“Im Beispiel funktioniert’s ja…”)

Hallo Marco13,

zunächst dachte ich auch, dass ich irgendwas verändert habe. Daher habe ich Dein Beispiel noch mal hineingeladen. Dies hat allerdings nichts geändert. Ich verwende 1:1 Dein Beispiel, und dennoch sind die Koordinaten gleich.

Kann das an Netbeans liegen?

Standardmäßig wird das ganze einfach so groß angezeigt, “wie es ist”. D.h. am Anfang sind die Weltkoordinaten und die Pixelkoordinaten gleich.

Wenn du mit den Pfeiltasten in der Ansicht rumscrollst oder mit STRG+I oder STRG+O zoomst, sollten danach die Koordinaten nicht mehr gleich sein. Sind sie das bei dir?

Hi,

erstmal vielen Dank für Deine Hilfe.

Nein, dann sind die Koordinaten nicht gleich. Auf die Idee das zu testen bin ich nicht gekommen.

Das Problem ist allerdings, dass sich die Welt-Koordinaten ändern, wenn ich die Größe des JFrames ändere, d.h. der eigentlich selbe Punkt in der Welt ist nicht mehr an der Selben Koordinatenstelle.

Also wenn man das Beispiel startet, kann man die Fenstergröße ändern und die Karte verschieben und zoomen, wie man will: Wenn man auf den gleichen (Welt-) Punkt klickt, kommen auch immer die gleichen Weltkoordinaten raus…

(Kann es sein, dass du dir die RenderingTransform irgendwo abholst und das nach einer Änderung dann eben die alte ist?)

Hallo Marco13,

hoffentlich ist es in Ordnung diesen alten Thread wieder auszubuddeln. Es ist ein bisschen her, dass ich mich zuletzt mit dem Programmieren beschäftigen konnte - mein Problem bleibt aber leider bestehen.

Was Du schreibst ist alles vollkommen korrekt. Ich starte Dein Beispiel, ändere die Größe des JFrames und habe dann beim Zoomen und Bewegen mit den Maustasten die gleichen Koordinaten.

Mein Problem beginnt danach. Wenn ich jetzt die Größe des JFrames ändere, ändern sich auch die Weltkoordinaten. Es sind nun andere Weltkoordinaten als vor der Änderung der Framegröße.

Hast Du mir einen Tipp?

Nochmal:

  • Du startest das Programm.
  • Du klickst auf “Berlin”. Die Ausgabe der Weltkoordinaten ist dann sowas wie (123.456, 567.890)
  • Du änderst die Fenstergröße, verschiebst die Karte, oder machst irgendwas anderes.
  • Du klickst auf “Berlin”. Die Ausgabe der Weltkoordinaten ist dann … das gleiche wie vorher, oder was anderes?

Hallo und danke für Deine Antwort.

Genau. Hier mal eine ausführliche Beschreibung meines Problems:

Ich starte das Programm und klicke auf einen Punkt, sagen wir mal Berlin. Nun sagt mir das Programm: Weltkoordinaten: 485.0, 219.0 Screenkoordinaten: 485.0, 219.0. Bis hierhin ist alles wie es soll.

Ich verschiebe die Grafik mit den Pfeiltasten und nun sagt mir das Programm : Weltkoordinaten: 483.0, 219.0 Screenkoordinaten: 393.0, 219.0. Bis hierhin ist immer noch alles wie es soll, die leichten Änderungen der Weltkoordinaten liegen daran, dass ich nicht exakt klicke.

Ich zoome die Grafik mit den STRG + I und nun sagt mir das Programm : Weltkoordinaten: 485.0, 220.0 Screenkoordinaten: 290.0, 100.0. Bis hierhin ist immer noch alles wie es soll.

Nun Ändere ich die Größe des JFrame indem ich das Fenster Maximiere. Nun sagt mir das Programm : Weltkoordinaten: 635.0, 219,5 Screenkoordinaten: 440.0, 98.0. Die Weltkoordinaten haben sich also durch die Maximierung des Fensters geändert.

Könntest du das gleiche auch mal mit Zwei Koordinaten machen. Also München und Berlin zum Beispiel.

Und wie veränderst du das Fenster, wohin verschiebt sich der Nullpunkt des Fensters beim maximieren. Welche Fenstergrössen sind es vor und nach der Grössenänderung.

Wird beim maximieren die Zoomstufe verändert oder bleibt diese gleich.

Das wären zumindest noch Fragen die der Lösung des Problems weiterhelfen könnten.

Hallo und vielen Dank für Deine Nachricht.

Ich verwende wieder das Beispiel das Marco13 hier im November gepostet hat.

Wenn ich zwei Koordinaten habe, gelten die Beobachtungen entsprechend für beide. Beim Ändern der Framegröße ändern sich auch deren Weltkoordinaten.

Ich maximiere indem ich oben auf der Fensterleiste auf Maximieren klicke. Und Minimiere indem ich entweder wieder darauf klicke oder einen der Ränder mit der Maus einfach kleiner ziehe.

Ich haben das Programm mit Testweise mit f.setSize(700, 400) gestartet. Die Weltkoordinaten sind beginnen von ca. 0.0, 0.0 oben links bis 700, 340 unten rechts (60 für den Texterbreich könnte hinkommen). Nach dem Maximieren ist die Weltkoordinate unten Rechts bei ca. 1298, 682. Dieser Wert entspricht dann auch wieder den Mauskoordinaten.

Wenn ich das Fenster maximiere wird das SVG vergrößert. Wenn ich es das Frame kleiner ziehe wird das SVG entsprechend kleiner. D. h. maximieren und minimieren verändert die Zoomstufe.

Mir wäre es wichtig, dass ich die Punkte an mehreren Computern abfragen kann und der selbe Punkt am jedem PC am selben Ort ist. Ist das mit den Weltkoordinaten sicher machbar?

Hab’s gerade nochmal probiert: Egal, wie man zoomt oder verschiebt oder die Fenstergröße ändert: Die Weltkoordinaten für den gleichen Punkt (etwa Berlin) bleiben gleich…

Hast du genau das gepostete Beispiel verwendet?

Hallo Marco13,

mittlerweile habe ich denke ich das Problem lokalisiert. Es kommt aus irgendeinem Grund auf die SVG Datei an. Manche SVGs skalieren bei Änderung des JFrames nicht, wie das von Dir verwendete. Manche schon. Und sobald dies passiert ändern sich auch die Weltkoordinaten.

Probiere das Programm bitte noch mal mit dieser Vectorgrafik:
https://commons.wikimedia.org/wiki/File:Palais_Lanckoro%C5%84ski_ground_floor_plan.svg Dann wirst Du das gleiche Problem haben.