Draw route on a map

Hi,

I am doing the routing offline in Java and I’m trying to plot the route obtained by the open source routing engine on a map. Could anyone give me some insights on how to do it ? Or at least is it possible to get the GPXentries of the route ?

Are you wanting to display the map within your java application? or just have it shown in browser?
If you want it shown in your java application I can help and also show you mapsforges markers etc.

Yes, I would like to show it in my java application. I will appreciate your help.

Depending on which map engine want to use, can see the relevant GraphHopper sample:

Thank you for your reply. But these solutions work only with android application or could be applied to the desktop application ? (Sorry if it is a basic question, I am not so familiar with java)

I will share the exact code I use tomorrow.
I’m presuming you have the ability to do the base application of panels etc. So I will skip that.
Also that you can add maven dependencies in your project.

While you wait, download a .map file of your area from here; https://download.mapsforge.org/maps/
Note that this is not for mass download but to host your own copy either local or on your own network.

Their overlay API is common for both Android and Desktop platforms.

Disclaimer; I am also no where near a Java expert and I may have things slightly wrong so you should use this to test and build your own solution.

In your maven dependencies;

<dependency>
        <groupId>org.mapsforge</groupId>
        <artifactId>mapsforge-core</artifactId>
        <version>0.12.0</version>
    </dependency>
    <dependency>
        <groupId>org.mapsforge</groupId>
        <artifactId>mapsforge-map</artifactId>
        <version>0.12.0</version>
    </dependency>
    <dependency>
        <groupId>org.mapsforge</groupId>
        <artifactId>mapsforge-map-awt</artifactId>
        <version>0.12.0</version>
    </dependency>
    <dependency>
        <groupId>org.mapsforge</groupId>
        <artifactId>mapsforge-map-reader</artifactId>
        <version>0.12.0</version>
    </dependency>
    <dependency>
        <groupId>org.mapsforge</groupId>
        <artifactId>mapsforge-themes</artifactId>
        <version>0.12.0</version>
    </dependency>

Imports - You may not require all of these;

import org.mapsforge.core.graphics.Bitmap;
import org.mapsforge.core.graphics.Canvas;
import org.mapsforge.core.graphics.Color;
import org.mapsforge.core.graphics.FontFamily;
import org.mapsforge.core.graphics.FontStyle;
import org.mapsforge.core.graphics.GraphicFactory;
import org.mapsforge.core.graphics.Paint;
import org.mapsforge.core.graphics.Style;
import org.mapsforge.core.model.BoundingBox;
import org.mapsforge.core.model.LatLong;
import org.mapsforge.core.model.MapPosition;
import org.mapsforge.core.model.Point;
import org.mapsforge.core.util.LatLongUtils;
import org.mapsforge.core.util.MercatorProjection;
import org.mapsforge.core.util.Parameters;
import org.mapsforge.map.awt.graphics.AwtBitmap;
import org.mapsforge.map.awt.graphics.AwtGraphicFactory;
import org.mapsforge.map.awt.util.AwtUtil;
import org.mapsforge.map.awt.util.JavaPreferences;
import org.mapsforge.map.awt.view.MapView;
import org.mapsforge.map.datastore.MapDataStore;
import org.mapsforge.map.datastore.MultiMapDataStore;
import org.mapsforge.map.layer.Layers;
import org.mapsforge.map.layer.cache.TileCache;
import org.mapsforge.map.layer.debug.TileCoordinatesLayer;
import org.mapsforge.map.layer.debug.TileGridLayer;
import org.mapsforge.map.layer.download.TileDownloadLayer;
import org.mapsforge.map.layer.download.tilesource.OpenStreetMapMapnik;
import org.mapsforge.map.layer.download.tilesource.TileSource;
import org.mapsforge.map.layer.hills.HillsRenderConfig;
import org.mapsforge.map.layer.overlay.Marker;
import org.mapsforge.map.layer.overlay.Polyline;
import org.mapsforge.map.layer.renderer.TileRendererLayer;
import org.mapsforge.map.model.IMapViewPosition;
import org.mapsforge.map.model.Model;
import org.mapsforge.map.model.common.PreferencesFacade;
import org.mapsforge.map.reader.MapFile;
import org.mapsforge.map.rendertheme.InternalRenderTheme;



public static void RouteTomap(){
    // Multithreaded map rendering
    Parameters.NUMBER_OF_THREADS = 4;
    // Square frame buffer
    Parameters.SQUARE_FRAME_BUFFER = false;
    HillsRenderConfig hillsCfg = null;

List<File> mapFiles = SHOW_RASTER_MAP ? null : getMapFiles(RouteToMap.LocationOfflineMaps); // The location of your .map file
        mapView = createMapView();
        final BoundingBox boundingBox = addLayers(mapView, mapFiles, hillsCfg);
        final PreferencesFacade preferencesFacade = new 
JavaPreferences(Preferences.userNodeForPackage(RouteToMap.class));


//DO YOUR GRAPHHOPPER ROUTE HERE
//Eg. GHResponse route = graphHopper.route(request);
//PathWrapper path = route.getBest();
/////////////////////////////// Trying to add route to map
    polyline = createPolyline(path);

    mapView.getLayerManager().getLayers().add(polyline);
jPanel1.setLayout(new BorderLayout());
        jPanel1.repaint();
        jPanel1.add(new JLabel("Use left mouse button to pan and mouse wheel to zoom"), BorderLayout.NORTH);
    
    final Model model = mapView.getModel();
    model.init(preferencesFacade);
if (model.mapViewPosition.getZoomLevel() == 0 || !boundingBox.contains(model.mapViewPosition.getCenter())) {
    byte zoomLevel = LatLongUtils.zoomForBounds(model.mapViewDimension.getDimension(), boundingBox, model.displayModel.getTileSize());
    model.mapViewPosition.setMapPosition(new MapPosition(boundingBox.getCenterPoint(), zoomLevel));
}

jPanel1.add(mapView, BorderLayout.CENTER);   
    jPanel1.setVisible(true);
    LatLong startLatLong = new LatLong(points.get(0),points.get(1));
    model.mapViewPosition.animateTo(startLatLong); // This is your start location for initial load.
    jPanel1.revalidate();
}

private static BoundingBox addLayers(MapView mapView, List<File> mapFiles, HillsRenderConfig hillsRenderConfig) {
    Layers layers = mapView.getLayerManager().getLayers();

    int tileSize = SHOW_RASTER_MAP ? 256 : 512;

    // Tile cache
    TileCache tileCache = AwtUtil.createTileCache(
            tileSize,
            mapView.getModel().frameBufferModel.getOverdrawFactor(),
            1024,
            new File(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()));

    final BoundingBox boundingBox;
    if (SHOW_RASTER_MAP) {
        // Raster
        mapView.getModel().displayModel.setFixedTileSize(tileSize);
        OpenStreetMapMapnik tileSource = OpenStreetMapMapnik.INSTANCE;
        tileSource.setUserAgent("mapsforge-samples-awt");
        TileDownloadLayer tileDownloadLayer = createTileDownloadLayer(tileCache, mapView.getModel().mapViewPosition, tileSource);
        layers.add(tileDownloadLayer);
        tileDownloadLayer.start();
        mapView.setZoomLevelMin(tileSource.getZoomLevelMin());
        mapView.setZoomLevelMax(tileSource.getZoomLevelMax());
        boundingBox = new BoundingBox(LatLongUtils.LATITUDE_MIN, LatLongUtils.LONGITUDE_MIN, LatLongUtils.LATITUDE_MAX, LatLongUtils.LONGITUDE_MAX);
    } else {
        // Vector
        mapView.getModel().displayModel.setFixedTileSize(tileSize);
        MultiMapDataStore mapDataStore = new MultiMapDataStore(MultiMapDataStore.DataPolicy.RETURN_ALL);
        for (File file : mapFiles) {
            mapDataStore.addMapDataStore(new MapFile(file), false, false);
        }
        TileRendererLayer tileRendererLayer = createTileRendererLayer(tileCache, mapDataStore, mapView.getModel().mapViewPosition, hillsRenderConfig);
        layers.add(tileRendererLayer);
        boundingBox = mapDataStore.boundingBox();
    }

    // Debug
    if (SHOW_DEBUG_LAYERS) {
        layers.add(new TileGridLayer(GRAPHIC_FACTORY, mapView.getModel().displayModel));
        layers.add(new TileCoordinatesLayer(GRAPHIC_FACTORY, mapView.getModel().displayModel));
    }

    return boundingBox;
}

private static MapView createMapView() {
    MapView mapView = new MapView();
    mapView.getMapScaleBar().setVisible(true);
    if (SHOW_DEBUG_LAYERS) {
        mapView.getFpsCounter().setVisible(true);
    }

    return mapView;
}

@SuppressWarnings("unused")
private static TileDownloadLayer createTileDownloadLayer(TileCache tileCache, IMapViewPosition mapViewPosition, TileSource tileSource) {
    return new TileDownloadLayer(tileCache, mapViewPosition, tileSource, GRAPHIC_FACTORY) {
        @Override
        public boolean onTap(LatLong tapLatLong, Point layerXY, Point tapXY) {
            System.out.println("Tap on: " + tapLatLong);
            return true;
        }
    };
}

private static TileRendererLayer createTileRendererLayer(TileCache tileCache, MapDataStore mapDataStore, IMapViewPosition mapViewPosition, HillsRenderConfig hillsRenderConfig) {
    TileRendererLayer tileRendererLayer = new TileRendererLayer(tileCache, mapDataStore, mapViewPosition, false, true, false, GRAPHIC_FACTORY, hillsRenderConfig) {
        @Override
// THIS IS MY OWN onTap EVENT, IT WILL ERROR FOR YOU
        public boolean onTap(LatLong tapLatLong, Point layerXY, Point tapXY) {
            if(RouteToMap.selected_point > 0){
                ReRouteToMap(tapLatLong.latitude,tapLatLong.longitude,selected_point);
                System.out.println("This point has moved (" + selected_point + ")");
                RouteToMap.selected_point = 0;
            }
            System.out.println("Tap on: " + tapLatLong);
            return true;
        }
    };
    tileRendererLayer.setXmlRenderTheme(InternalRenderTheme.OSMARENDER); //DEFAULT
    return tileRendererLayer;
}

private static List<File> getMapFiles(String File) {

    List<File> result = new ArrayList<>();

        File mapFile = new File(File);
        if (!mapFile.exists()) {
            throw new IllegalArgumentException("file does not exist: " + mapFile);
        } else if (!mapFile.isFile()) {
            throw new IllegalArgumentException("not a file: " + mapFile);
        } else if (!mapFile.canRead()) {
            throw new IllegalArgumentException("cannot read file: " + mapFile);
        }
        result.add(mapFile);
    
    return result;
}


private static Polyline createPolyline(PathWrapper path){
    GraphicFactory gf = AwtGraphicFactory.INSTANCE;
    Paint paint = gf.createPaint();
    int R = (int) (Math.random( )*256);
    int G = (int)(Math.random( )*256);
    int B = (int)(Math.random( )*256);
    // createColor(int alpha, int red, int green, int blue)
    paint.setColor(AwtGraphicFactory.INSTANCE.createColor(200, R, G, B));
    paint.setStyle(Style.STROKE);
    
        paint.setStrokeWidth(6);

    Polyline line = new Polyline(paint,AwtGraphicFactory.INSTANCE);

    List<LatLong> geoPoints = line.getLatLongs();

    PointList tmp = path.getPoints();
    
    for (int i = 0; i < tmp.getSize(); i++) {
        geoPoints.add(new LatLong(tmp.getLatitude(i), tmp.getLongitude(i)));
    }

return line;
}

Note that path is your route.getBest(); result
GHResponse route = graphHopper.route(request);
PathWrapper path = route.getBest();

I use a points array for all of my points. LatLong(points.get(0),points.get(1));

If you want a button to do a reroute; make sure you clear the jPanel - jPanel1.removeAll();

Feel free to ask any questions and if working how to apply markers to your GPS locations.

See map matching using graphhopper.

Thank you very much, I just used JFrame instead of jPanel and it works perfectly !

Great, glad to hear it worked for you.

You can now add markers to your points if you want with the following. Essentially loop through each of your points and draw a marker. There is a sequence text number that is put above the marker and only shows zooms > 14.

Notes;
I have a checkbox to enable/disable markers on run.
This references the src/main/resources folder in your project .getResourceAsStream(“waypoint_white_small.png”); .
In the marker override I have it set so when the point is clicked, it shows as a red marker.
This requires a “selected_point” object within your main method.

Place this before you add the mapView to your jFrame.

// Enable / disable markers
        if(jCheckBox1.isSelected()){
            Layers layers = mapView.getLayerManager().getLayers(); 
            Marker marker;
            AwtBitmap image = null;

            try
            {
                InputStream imagepath = RouteToMap.class.getClassLoader().getResourceAsStream("waypoint_white_small.png");
                image = new AwtBitmap(imagepath);
            }
            catch (Exception ex)
            {
                System.out.println("couldn't read waypoint image");
            }

            Bitmap icon = image;
            int count = 0;

            for (int i = 0; i < points.size(); i++) {
                int offset = i + 1;


                if(i == 0){
                    // First marker wont overlap so just do it.
                    count++;
                    LatLong latlong = new LatLong(points.get(i),points.get(offset));
                    marker = new createMarker(latlong, icon, 0, 0, count, address.get(count), mapView);
                    layers.add(marker);
                }else{
                    LatLong latlong = new LatLong(points.get(i),points.get(offset));
                    LatLong prevlatlong = new LatLong(points.get(i-2),points.get(offset-2));

                    //If distance is 0 then don't put a new marker. This removes overlapping
                    if (distance(latlong.latitude, latlong.longitude, prevlatlong.latitude, prevlatlong.longitude) > 0) {
                        count++;
                        marker = new createMarker(latlong, icon, 0, 0, count, address.get(count), mapView);
                        layers.add(marker); 
                    }
                }

                i++;
            }
        }
        // End of markers

The distance checker to see if it’s > 0. Mapsforge for some reason doesnt like markers on exactly the same GPS.

private static double distance(double lat1, double lng1, double lat2, double lng2) {
        double earthRadius = 3958.75; // in miles, change to 6371 for kilometer output

        double dLat = Math.toRadians(lat2-lat1);
        double dLng = Math.toRadians(lng2-lng1);

        double sindLat = Math.sin(dLat / 2);
        double sindLng = Math.sin(dLng / 2);

        double a = Math.pow(sindLat, 2) + Math.pow(sindLng, 2)
            * Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2));

        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

        double dist = earthRadius * c;

        return dist; // output distance, in MILES
    }

Override the default Marker.draw in mapsforge

static class createMarker extends Marker {
        private final Bitmap bitmap;
        private final int horizontalOffset;
        private final LatLong latLong;
        private final int verticalOffset;
        private final String label;
        private final int count;
        private final String address;
        private final MapView mapView;
        private boolean selected;
    
        public createMarker(LatLong latLong, Bitmap bitmap, int horizontalOffset, int verticalOffset, int text, String address, MapView mapView) {
            super(latLong, bitmap, horizontalOffset, verticalOffset);
            this.label = Integer.toString(text);
            this.count = text;
            this.latLong = latLong;
            this.bitmap = bitmap;
            this.horizontalOffset = horizontalOffset;
            this.verticalOffset = verticalOffset;
            this.address = address;
            this.mapView = mapView;
            this.selected = false;
        }
        
        @Override
        public void draw(BoundingBox boundingBox, byte zoomLevel, Canvas canvas, Point topLeftPoint) {
            super.draw(boundingBox, zoomLevel, canvas, topLeftPoint);
            
            // Get the label width
            BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
            Font font = new Font("Tahoma", Font.PLAIN, 12);
            FontMetrics fm = img.getGraphics().getFontMetrics(font);
            int width = fm.stringWidth(label) / 4;
            
            long mapSize = MercatorProjection.getMapSize(zoomLevel, displayModel.getTileSize());
            double pixelX = MercatorProjection.longitudeToPixelX(this.latLong.longitude, mapSize);
            double pixelY = MercatorProjection.latitudeToPixelY(this.latLong.latitude, mapSize);
            int halfBitmapWidth = this.bitmap.getWidth() / 2;
            int halfBitmapHeight = this.bitmap.getHeight() / 2;
            int left = (int) (pixelX - topLeftPoint.x - halfBitmapWidth + horizontalOffset - width);
            int top = (int) (pixelY - topLeftPoint.y - halfBitmapHeight + verticalOffset);
            
            GraphicFactory gf = AwtGraphicFactory.INSTANCE;
            Paint paint = gf.createPaint();
            paint.setColor(gf.createColor(Color.BLUE));
            
            Paint paintScaleText = createTextPaint(Color.BLACK, 0, Style.FILL);
            Paint paintScaleTextStroke = createTextPaint(Color.WHITE, 2, Style.STROKE);
      
            if(zoomLevel > 14){
                canvas.drawText(label, left, top, paintScaleTextStroke);
                canvas.drawText(label, left, top, paintScaleText);
            }
        }
            @Override
		public boolean onTap(LatLong geoPoint, Point viewPosition,
				Point tapPoint) {
			if (contains(viewPosition, tapPoint)) {
                            System.out.println("(" + this.count + ")" + address + " " + this.getLatLong().toString());
                            String fileName;
                            AwtBitmap image = null;
                            Bitmap icon = null;
                            ////////////////////////////////////////////////////////////////
                            if(selected_point > 0){
                            fileName = "waypoint_white_small.png";
                            image = null;
                            try
                            {
                                InputStream imagepath = RouteToMap.class.getClassLoader().getResourceAsStream(fileName);
                                image = new AwtBitmap(imagepath);
                            }
                            catch (Exception ex)
                            {
                                System.out.println("couldn't read waypoint image");
                            }

                            icon = image;        
                            selectedMarker.setBitmap(icon);
       
                            }
                            ////////////////////////////////////////////////////////////////////
                            
                            
                            if(selected_point != this.count){    
                                fileName = "waypoint_red_small.png";
                                this.selected = true;
                                selected_point = this.count;
                                selectedMarker = this;
                            }else{
                                fileName = "waypoint_white_small.png";
                                this.selected = false;
                                selected_point = 0;
                            }
                            
                            image = null;
                            try
                            {
                                InputStream imagepath = RouteToMap.class.getClassLoader().getResourceAsStream(fileName);
                                image = new AwtBitmap(imagepath);
                            }
                            catch (Exception ex)
                            {
                                System.out.println("couldn't read waypoint image");
                            }

                            icon = image;        
                            selectedMarker.setBitmap(icon);
                            
                            this.mapView.getLayerManager().redrawLayers();

                            return true;	
                    }
                    return false;
                }
    }
    
    private static Paint createTextPaint(Color color, float strokeWidth, Style style) {
        Paint paint = AwtGraphicFactory.INSTANCE.createPaint();
        paint.setColor(color);
        paint.setStrokeWidth(strokeWidth * 1);
        paint.setStyle(style);
        paint.setTypeface(FontFamily.DEFAULT, FontStyle.BOLD);
        paint.setTextSize(12 * 1);
        return paint;
    }

Everything should be there for you to fit into your own work, as normal, it likely isn’t a direct copy and paste in, but look through it and use it as a base to learn.

I also have it so when you click on a marker, it draws a short polyline to the next point. This can be added to the onTap event of the marker. I haven’t included this but you can add any functionality you want really.

Powered by Discourse