diff --git a/jsprit-analysis/src/main/java/analysis/SolutionPlotter.java b/jsprit-analysis/src/main/java/analysis/SolutionPlotter.java index 4ce1b33a..bac3e4c0 100644 --- a/jsprit-analysis/src/main/java/analysis/SolutionPlotter.java +++ b/jsprit-analysis/src/main/java/analysis/SolutionPlotter.java @@ -20,20 +20,21 @@ ******************************************************************************/ package analysis; +import java.awt.Color; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import org.apache.log4j.Logger; -import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartUtilities; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.NumberAxis; -import org.jfree.chart.axis.NumberTickUnit; -import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.data.Range; import org.jfree.data.xy.XYSeries; @@ -41,7 +42,6 @@ import org.jfree.data.xy.XYSeriesCollection; import util.Coordinate; import util.Locations; - import basics.Job; import basics.Service; import basics.VehicleRoutingProblem; @@ -59,10 +59,65 @@ import basics.route.VehicleRoute; */ public class SolutionPlotter { + private static class NoLocationFoundException extends Exception{ + + /** + * + */ + private static final long serialVersionUID = 1L; + + } + private static Logger log = Logger.getLogger(SolutionPlotter.class); + /** - * Plots the solution to pngFile, with title. + * Plots the {@link VehicleRoutingProblem} to png-file. + + * @param vrp + * @param pngFile target path with filename. + * @see VehicleRoutingProblem, VehicleRoutingProblemSolution + */ + public static void plotVrpAsPNG(VehicleRoutingProblem vrp, String pngFile, String title){ + log.info("plot routes to " + pngFile); + XYSeriesCollection problem; + try { + problem = makeVrpSeries(vrp); + } catch (NoLocationFoundException e) { + log.warn("cannot plot vrp, since coord is missing"); + return; + } + XYPlot plot = createPlot(problem); + JFreeChart chart = new JFreeChart(title, plot); + save(chart,pngFile); + } + + /** + * Retrieves the problem from routes, and plots it along with the routes to pngFile. + * + * @param routes + * @param locations indicating the locations for the tour-activities. + * @param pngFile target path with filename. + * @param plotTitle + * @see VehicleRoute + */ + public static void plotRoutesAsPNG(Collection routes, Locations locations, String pngFile, String title) { + log.info("plot routes to " + pngFile); + XYSeriesCollection problem; + try { + problem = makeVrpSeries(routes); + } catch (NoLocationFoundException e) { + log.warn("cannot plot vrp, since coord is missing"); + return; + } + XYSeriesCollection solutionColl = makeSolutionSeries(routes,locations); + XYPlot plot = createPlot(problem, solutionColl); + JFreeChart chart = new JFreeChart(title, plot); + save(chart,pngFile); + } + + /** + * Plots problem and solution to pngFile. * *

This can only plot if vehicles and jobs have locationIds and coordinates (@see Coordinate). Otherwise a warning message is logged * and method returns but does not plot. @@ -72,132 +127,200 @@ public class SolutionPlotter { * @param pngFile target path with filename. * @see VehicleRoutingProblem, VehicleRoutingProblemSolution */ - public static void plotSolutionAsPNG(VehicleRoutingProblem vrp, VehicleRoutingProblemSolution solution, String pngFile, String plotTitle){ - final Map locs = new HashMap(); - boolean locationsRetrieved = retrieveLocations(locs,vrp); - if(!locationsRetrieved) return; - Locations locations = new Locations(){ - - @Override - public Coordinate getCoord(String id) { - return locs.get(id); - } - - }; - plotRoutesAsPNG(solution.getRoutes(), locations, pngFile, plotTitle); + public static void plotSolutionAsPNG(VehicleRoutingProblem vrp, VehicleRoutingProblemSolution solution, String pngFile, String title){ + log.info("plot solution to " + pngFile); + XYSeriesCollection problem; + XYSeriesCollection solutionColl; + try { + problem = makeVrpSeries(vrp); + solutionColl = makeSolutionSeries(vrp, solution); + } catch (NoLocationFoundException e) { + log.warn("cannot plot vrp, since coord is missing"); + return; + } + XYPlot plot = createPlot(problem, solutionColl); + JFreeChart chart = new JFreeChart(title, plot); + save(chart,pngFile); + } - /** - * Plots the a collection of routes to pngFile. - * - * - * @param routes - * @param locations indicating the locations for the tour-activities. - * @param pngFile target path with filename. - * @param plotTitle - * @see VehicleRoute - */ - public static void plotRoutesAsPNG(Collection routes, Locations locations, String pngFile, String plotTitle) { - log.info("plot routes to " + pngFile); + + private static XYPlot createPlot(XYSeriesCollection problem) { + XYPlot plot = new XYPlot(); + plot.setBackgroundPaint(Color.LIGHT_GRAY); + plot.setRangeGridlinePaint(Color.WHITE); + plot.setDomainGridlinePaint(Color.WHITE); + + XYItemRenderer problemRenderer = new XYLineAndShapeRenderer(false, true); // Shapes only + NumberAxis xAxis = new NumberAxis(); + Range xRange = problem.getRangeBounds(false); + Range rangeX = Range.scale(xRange, 1.01); + xAxis.setRange(rangeX); + + NumberAxis yAxis = new NumberAxis(); + Range yRange = problem.getRangeBounds(true); + Range rangeY = Range.scale(yRange, 1.01); + yAxis.setRange(rangeY); + + plot.setDataset(0, problem); + plot.setRenderer(0, problemRenderer); + plot.setDomainAxis(0, xAxis); + plot.setRangeAxis(0, yAxis); + return plot; + } + + private static XYPlot createPlot(XYSeriesCollection problem, XYSeriesCollection solutionColl) { + XYPlot plot = new XYPlot(); + plot.setBackgroundPaint(Color.LIGHT_GRAY); + plot.setRangeGridlinePaint(Color.WHITE); + plot.setDomainGridlinePaint(Color.WHITE); + + XYItemRenderer problemRenderer = new XYLineAndShapeRenderer(false, true); // Shapes only + NumberAxis xAxis = new NumberAxis(); + Range xRange = problem.getRangeBounds(false); + Range rangeX = Range.scale(xRange, 1.01); + xAxis.setRange(rangeX); + + NumberAxis yAxis = new NumberAxis(); + Range yRange = problem.getRangeBounds(true); + Range rangeY = Range.scale(yRange, 1.01); + yAxis.setRange(rangeY); + + plot.setDataset(0, problem); + plot.setRenderer(0, problemRenderer); + plot.setDomainAxis(0, xAxis); + plot.setRangeAxis(0, yAxis); + + XYItemRenderer solutionRenderer = new XYLineAndShapeRenderer(true, false); // Lines only + plot.setDataset(1, solutionColl); + plot.setRenderer(1, solutionRenderer); + plot.setDomainAxis(1, xAxis); + plot.setRangeAxis(1, yAxis); + return plot; + } + + private static void save(JFreeChart chart, String pngFile) { + try { + ChartUtilities.saveChartAsPNG(new File(pngFile), chart, 1000, 600); + } catch (IOException e) { + log.error("cannot plot"); + log.error(e); + e.printStackTrace(); + } + } + + private static XYSeriesCollection makeSolutionSeries(VehicleRoutingProblem vrp, VehicleRoutingProblemSolution solution) throws NoLocationFoundException{ + Locations locations = retrieveLocations(vrp); + XYSeriesCollection coll = new XYSeriesCollection(); + int counter = 1; + for(VehicleRoute route : solution.getRoutes()){ + if(route.isEmpty()) continue; + XYSeries series = new XYSeries(counter, false, true); + + Coordinate startCoord = locations.getCoord(route.getStart().getLocationId()); + series.add(startCoord.getX(), startCoord.getY()); + + for(TourActivity act : route.getTourActivities().getActivities()){ + Coordinate coord = locations.getCoord(act.getLocationId()); + series.add(coord.getX(), coord.getY()); + } + + Coordinate endCoord = locations.getCoord(route.getEnd().getLocationId()); + series.add(endCoord.getX(), endCoord.getY()); + + coll.addSeries(series); + counter++; + } + return coll; + } + + private static XYSeriesCollection makeSolutionSeries(Collection routes, Locations locations){ XYSeriesCollection coll = new XYSeriesCollection(); int counter = 1; - double maxX = 0; - double minX = Double.MAX_VALUE; - double maxY = 0; - double minY = Double.MAX_VALUE; for(VehicleRoute route : routes){ if(route.isEmpty()) continue; XYSeries series = new XYSeries(counter, false, true); Coordinate startCoord = locations.getCoord(route.getStart().getLocationId()); series.add(startCoord.getX(), startCoord.getY()); - if(startCoord.getX() > maxX) maxX = startCoord.getX(); - if(startCoord.getY() > maxY) maxY = startCoord.getY(); - if(startCoord.getX() < minX) minX = startCoord.getX(); - if(startCoord.getY() < minY) minY = startCoord.getY(); for(TourActivity act : route.getTourActivities().getActivities()){ Coordinate coord = locations.getCoord(act.getLocationId()); series.add(coord.getX(), coord.getY()); - if(coord.getX() > maxX) maxX = coord.getX(); - if(coord.getY() > maxY) maxY = coord.getY(); - if(coord.getX() < minX) minX = coord.getX(); - if(coord.getY() < minY) minY = coord.getY(); } Coordinate endCoord = locations.getCoord(route.getEnd().getLocationId()); series.add(endCoord.getX(), endCoord.getY()); - if(endCoord.getX() > maxX) maxX = endCoord.getX(); - if(endCoord.getY() > maxY) maxY = endCoord.getY(); - if(endCoord.getX() < minX) minX = endCoord.getX(); - if(endCoord.getY() < minY) minY = endCoord.getY(); coll.addSeries(series); counter++; } - JFreeChart chart = ChartFactory.createXYLineChart(plotTitle,"x-coordinate", "y-coordinate",coll, PlotOrientation.VERTICAL,true,true,false); - - XYPlot plot = chart.getXYPlot(); - - XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer(); - renderer.setBaseShapesVisible(true); - - NumberAxis xAxis = (NumberAxis) plot.getDomainAxis(); -// xAxis.setTickUnit(new NumberTickUnit(10)); - Range rangeX = new Range(minX - 0.001*minX, maxX+0.001*maxX); -// Range rangeX = new Range(minX, maxX); - xAxis.setRange(rangeX); - - NumberAxis yAxis = (NumberAxis) plot.getRangeAxis(); -// yAxis.setTickUnit(new NumberTickUnit(10)); - Range rangeY = new Range(minY-0.001*minY,maxY + 0.001*maxY); -// Range rangeY = new Range(minY,maxY); - yAxis.setRange(rangeY); - try { - ChartUtilities.saveChartAsPNG(new File(pngFile), chart, 1000, 600); - } catch (IOException e) { - log.error("cannot plot"); - log.error(e); - e.printStackTrace(); - - } + return coll; } - - private static boolean retrieveLocations(Map locs, VehicleRoutingProblem vrp) { + + private static XYSeriesCollection makeVrpSeries(Collection vehicles, Collection services) throws NoLocationFoundException{ + XYSeriesCollection coll = new XYSeriesCollection(); + XYSeries vehicleSeries = new XYSeries("depot", false, true); + for(Vehicle v : vehicles){ + Coordinate coord = v.getCoord(); + if(coord == null) throw new NoLocationFoundException(); + vehicleSeries.add(coord.getX(),coord.getY()); + } + coll.addSeries(vehicleSeries); + + XYSeries jobSeries = new XYSeries("service", false, true); + for(Job job : services){ + Service service = (Service)job; + Coordinate coord = service.getCoord(); + jobSeries.add(coord.getX(), coord.getY()); + } + coll.addSeries(jobSeries); + return coll; + } + + private static XYSeriesCollection makeVrpSeries(Collection routes) throws NoLocationFoundException{ + Set vehicles = new HashSet(); + Set jobs = new HashSet(); + for(VehicleRoute route : routes){ + vehicles.add(route.getVehicle()); + jobs.addAll(route.getTourActivities().getJobs()); + } + return makeVrpSeries(vehicles, jobs); + } + + private static XYSeriesCollection makeVrpSeries(VehicleRoutingProblem vrp) throws NoLocationFoundException{ + return makeVrpSeries(vrp.getVehicles(), vrp.getJobs().values()); + } + + private static Locations retrieveLocations(VehicleRoutingProblem vrp) throws NoLocationFoundException { + final Map locs = new HashMap(); for(Vehicle v : vrp.getVehicles()){ String locationId = v.getLocationId(); - if(locationId == null){ - log.warn("cannot plot solution, since vehicle " + v + " has no locationId."); - return false; - } + if(locationId == null) throw new NoLocationFoundException(); Coordinate coord = v.getCoord(); - if(coord == null){ - log.warn("cannot plot solution, since vehicle " + v + " has no location-coordinate."); - return false; - } + if(coord == null) throw new NoLocationFoundException(); locs.put(locationId, coord); } for(Job j : vrp.getJobs().values()){ if(j instanceof Service){ String locationId = ((Service) j).getLocationId(); - if(locationId == null){ - log.warn("cannot plot solution, since job " + j + " has no locationId."); - return false; - } + if(locationId == null) throw new NoLocationFoundException(); Coordinate coord = ((Service) j).getCoord(); - if(coord == null){ - log.warn("cannot plot solution, since job " + j + " has no location-coordinate."); - return false; - } + if(coord == null) throw new NoLocationFoundException(); locs.put(locationId, coord); } else{ throw new IllegalStateException("job is not a service. this is not supported yet."); } } - return true; + return new Locations() { + + @Override + public Coordinate getCoord(String id) { + return locs.get(id); + } + }; } - - + }