SVG Koordinaten auslesen


#1

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?


#2

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.


#3

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…)


#4

Wow Marco13,

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


#5

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?


#6

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…!?


#7

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]


#8

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.


#9

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


#10

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?


#11

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?


#12

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.


#13

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?)