GeometryDrawingPanel.java

package com.reallifedeveloper.tools.gis;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JPanel;

import org.geotools.geometry.jts.LiteShape;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;

/**
 * A {@code JPanel} that draws {@code com.vividsolutions.jts.geom.Geometry} objects.
 *
 * @author RealLifeDeveloper
 */
public class GeometryDrawingPanel extends JPanel {

    private static final long serialVersionUID = 1L;

    private static final int MARGIN = 5;

    /**
     * The {@code Geometry} objects to draw.
     */
    private final List<Geometry> geometries = new ArrayList<>();

    /**
     * A transform that scales the geometries to fit the current panel size, and adds margins.
     */
    private AffineTransform geomToScreen;

    /**
     * Adds a {@code Geometry} object to be drawn.
     *
     * @param geometry the {@code Geometry} object to add
     */
    public void addGeometry(Geometry geometry) {
        geometries.add(geometry);
    }

    /**
     * Removes all {@code Geometry} objects so that none are drawn.
     */
    public void clearGeometries() {
        geometries.clear();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        if (!geometries.isEmpty()) {
            setTransform();

            Graphics2D g2d = (Graphics2D) g;
            Paint defaultPaint = Color.BLUE;

            for (Geometry geom : geometries) {
                @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
                LiteShape shape = new LiteShape(geom, geomToScreen, false);

                g2d.setPaint(defaultPaint);
                g2d.draw(shape);
            }
        }
    }

    private void setTransform() {
        Envelope env = getGeometryBounds();
        Rectangle visRect = getVisibleRect();
        Rectangle drawingRect = new Rectangle(visRect.x + MARGIN, visRect.y + MARGIN, visRect.width - 2 * MARGIN,
                visRect.height - 2 * MARGIN);

        double scale = Math.min(drawingRect.getWidth() / env.getWidth(), drawingRect.getHeight() / env.getHeight());
        double xoff = MARGIN - scale * env.getMinX();
        double yoff = MARGIN + scale * env.getMaxY();
        geomToScreen = new AffineTransform(scale, 0, 0, -scale, xoff, yoff);
    }

    private Envelope getGeometryBounds() {
        Envelope env = new Envelope();
        for (Geometry geom : geometries) {
            Envelope geomEnv = geom.getEnvelopeInternal();
            env.expandToInclude(geomEnv);
        }

        return env;
    }
}