3

I am working on an application that displays GPS tracks on a map. I want to draw the track as a colored path of arbitrary thickness. I found the GeneralPath class which seems to do just what I want. However, I want the colored path to also have a black border. I couldn't find anything about how to add a border to a path, so I came up with a quick hacky solution of drawing a thick black path first, and then drawing a thin colored path on top.

SSCCE:

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.GeneralPath;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class PathBorder {
    private JFrame frame;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    PathBorder window = new PathBorder();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public PathBorder() {
        initialize();
    }

    private void initialize() {
        frame = new JFrame();
        frame.setBounds(100, 100, 450, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel panel = new JPanel() {
            protected void paintComponent(Graphics g) {
                GeneralPath path;
                Graphics2D g2d = (Graphics2D) g;
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
                g2d.setStroke(new BasicStroke(5.5f));
                g2d.setColor(Color.BLACK);
                path = new GeneralPath();
                path.moveTo(0, 0);
                path.lineTo(100, 100);
                path.lineTo(200, 100);
                path.lineTo(100, 80);
                g2d.draw(path);
                g2d.setStroke(new BasicStroke(3));
                g2d.setColor(Color.YELLOW);
                g2d.draw(path);
            }
        };
        frame.setBackground(Color.CYAN);
        frame.add(panel);
    }
}

Here is a pic (taken from the SSCCE) to highlight my problem. See the red circle below, notice how the outside border has a gap in it. I want that gap to be filled in so the border is continuous.

enter image description here

Here are some actual screenshots from my app of a real track:

enter image description here

If you look closely at the sharp hairpin turn in the lower right of the track, you'll see that the border gets lost briefly... the detailed pic below makes it more clear.

enter image description here

I am not sure exactly how to fix this, but I'm open to suggestions, either keeping with the GeneralPath strategy, or using a different idea entirely.

The111
  • 5,757
  • 4
  • 39
  • 55
  • For better help sooner, post an [SSCCE](http://sscce.org/). Hard-code the data with the points. – Andrew Thompson Feb 23 '13 at 09:49
  • BTW - I *think* I understand the problem, but AFAIU there is no solution if you wanted a black line splitting the road(s) on the lower right of the picture. The problem is that the two are so close, and the stroke so thick, that visually they are going to overlap. To split that with a black line would make each road look like a 'lane' or one way street. I think the way to get a better (and still understandable) rendering is to decrease the thickness of the stroke. But if the user zooms out enough, they kind of expect details to merge. – Andrew Thompson Feb 23 '13 at 09:57
  • @AndrewThompson Thanks, I will post a SSCCE momentarily. But to respond to your second comment... my issue is not the lack of a "splitting line"... it is that if you follow the **outside** border around the hairpin corner, you'll see that it has a break in it. – The111 Feb 23 '13 at 10:00
  • Setting some of the rendering hints may help. Try setting KEY_STROKE_CONTROL to VALUE_STROKE_PURE and try setting KEY_RENDERING to VALUE_RENDER_QUALITY. – VGR Feb 23 '13 at 10:06
  • @VGR Thanks for the suggestion, unfortunately neither of those helped. I will try playing with some of the other hints though, maybe one will do it. – The111 Feb 23 '13 at 10:20
  • @AndrewThompson SSCCE is up now, along with another picture to try to be more clear. – The111 Feb 23 '13 at 10:20
  • I am still open to other suggestions, but it seems like setting the Cap/Join styles for my BasicStroke both to "round" may be a viable solution. – The111 Feb 23 '13 at 10:52

1 Answers1

7

Experiment with the cap and join parameters for a better effect. E.G.

PathBorder

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.GeneralPath;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class PathBorder {

    private JFrame frame;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            public void run() {
                try {
                    PathBorder window = new PathBorder();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public PathBorder() {
        initialize();
    }

    private void initialize() {
        frame = new JFrame();
        frame.setBounds(100, 100, 450, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel panel = new JPanel() {

            GeneralPath path;

            protected void paintComponent(Graphics g) {
                Graphics2D g2d = (Graphics2D) g;
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
                BasicStroke s = new BasicStroke(
                        5.5f, 
                        BasicStroke.CAP_ROUND, 
                        BasicStroke.JOIN_ROUND);
                g2d.setStroke(s);
                g2d.setColor(Color.BLACK);
                if (path==null) {
                    path = new GeneralPath();
                    path.moveTo(0, 0);
                    path.lineTo(100, 100);
                    path.lineTo(200, 100);
                    path.lineTo(100, 80);
                }
                g2d.draw(path);
                g2d.setStroke(new BasicStroke(3));
                g2d.setColor(Color.YELLOW);
                g2d.draw(path);
            }
        };
        frame.setBackground(Color.CYAN);
        frame.add(panel);
    }
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • Yeah, I figured if you didn't notice that I'd save you the knowledge that I wasted your time. :-P But I really appreciate the help regardless, and glad you enjoyed playing. – The111 Feb 23 '13 at 11:05