mirror of
https://github.com/graphhopper/jsprit.git
synced 2020-01-24 07:45:05 +01:00
461 lines
15 KiB
Java
461 lines
15 KiB
Java
/*******************************************************************************
|
|
* Copyright (C) 2014 Stefan Schroeder
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 3.0 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
******************************************************************************/
|
|
package jsprit.analysis.toolbox;
|
|
|
|
|
|
import jsprit.core.problem.VehicleRoutingProblem;
|
|
import jsprit.core.problem.job.Job;
|
|
import jsprit.core.problem.job.Service;
|
|
import jsprit.core.problem.job.Shipment;
|
|
import jsprit.core.problem.solution.VehicleRoutingProblemSolution;
|
|
import jsprit.core.problem.solution.route.VehicleRoute;
|
|
import jsprit.core.problem.solution.route.activity.DeliveryActivity;
|
|
import jsprit.core.problem.solution.route.activity.PickupActivity;
|
|
import jsprit.core.problem.solution.route.activity.TourActivity;
|
|
import jsprit.core.problem.solution.route.activity.TourActivity.JobActivity;
|
|
import jsprit.core.problem.vehicle.PenaltyVehicleType;
|
|
import jsprit.core.problem.vehicle.Vehicle;
|
|
import jsprit.core.util.Time;
|
|
import org.graphstream.graph.Edge;
|
|
import org.graphstream.graph.Graph;
|
|
import org.graphstream.graph.Node;
|
|
import org.graphstream.graph.implementations.MultiGraph;
|
|
import org.graphstream.ui.swingViewer.View;
|
|
import org.graphstream.ui.swingViewer.Viewer;
|
|
|
|
import javax.swing.*;
|
|
import java.awt.*;
|
|
|
|
|
|
|
|
public class GraphStreamViewer {
|
|
|
|
protected static String styleSheet =
|
|
"node {" +
|
|
" size: 10px, 10px;" +
|
|
" fill-color: #6CC644;" +
|
|
" text-alignment: at-right;" +
|
|
" stroke-mode: plain;" +
|
|
" stroke-color: #999;" +
|
|
" stroke-width: 1.0;" +
|
|
" text-font: couriernew;" +
|
|
" text-offset: 2,-5;" +
|
|
" text-size: 8;" +
|
|
"}" +
|
|
"node.pickup {" +
|
|
" fill-color: #6CC644;" +
|
|
"}" +
|
|
"node.delivery {" +
|
|
" fill-color: #f93;" +
|
|
"}" +
|
|
"node.pickupInRoute {" +
|
|
" fill-color: #6CC644;" +
|
|
" stroke-mode: plain;" +
|
|
" stroke-color: #333;" +
|
|
" stroke-width: 2.0;" +
|
|
"}" +
|
|
"node.deliveryInRoute {" +
|
|
" fill-color: #f93;" +
|
|
" stroke-mode: plain;" +
|
|
" stroke-color: #333;" +
|
|
" stroke-width: 2.0;" +
|
|
"}" +
|
|
"node.depot {" +
|
|
" fill-color: #BD2C00;" +
|
|
" size: 10px, 10px;" +
|
|
" shape: box;" +
|
|
"}" +
|
|
|
|
"edge {" +
|
|
" fill-color: #333;" +
|
|
" arrow-size: 6px,3px;" +
|
|
"}" +
|
|
"edge.shipment {" +
|
|
" fill-color: #999;" +
|
|
" arrow-size: 6px,3px;" +
|
|
"}" ;
|
|
|
|
public static enum Label {
|
|
NO_LABEL, ID, JOB_NAME, ARRIVAL_TIME, DEPARTURE_TIME, ACTIVITY
|
|
}
|
|
|
|
private static class Center {
|
|
final double x;
|
|
final double y;
|
|
|
|
public Center(double x, double y) {
|
|
super();
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
|
|
}
|
|
|
|
private Label label = Label.NO_LABEL;
|
|
|
|
private long renderDelay_in_ms = 0;
|
|
|
|
private boolean renderShipments = false;
|
|
|
|
private Center center;
|
|
|
|
private VehicleRoutingProblem vrp;
|
|
|
|
private VehicleRoutingProblemSolution solution;
|
|
|
|
private double zoomFactor;
|
|
|
|
private double scaling = 1.0;
|
|
|
|
public GraphStreamViewer(VehicleRoutingProblem vrp) {
|
|
super();
|
|
this.vrp = vrp;
|
|
}
|
|
|
|
public GraphStreamViewer(VehicleRoutingProblem vrp, VehicleRoutingProblemSolution solution) {
|
|
super();
|
|
this.vrp = vrp;
|
|
this.solution = solution;
|
|
}
|
|
|
|
public GraphStreamViewer labelWith(Label label){
|
|
this.label=label;
|
|
return this;
|
|
}
|
|
|
|
public GraphStreamViewer setRenderDelay(long ms){
|
|
this.renderDelay_in_ms=ms;
|
|
return this;
|
|
}
|
|
|
|
public GraphStreamViewer setEnableAutoLayout(boolean enableAutoLayout) {
|
|
return this;
|
|
}
|
|
|
|
public GraphStreamViewer setRenderShipments(boolean renderShipments){
|
|
this.renderShipments = renderShipments;
|
|
return this;
|
|
}
|
|
|
|
public GraphStreamViewer setGraphStreamFrameScalingFactor(double factor){
|
|
this.scaling=factor;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the camera-view. Center describes the center-focus of the camera and zoomFactor its
|
|
* zoomFactor.
|
|
*
|
|
* <p>a zoomFactor < 1 zooms in and > 1 out.
|
|
*
|
|
* @param centerX x coordinate of center
|
|
* @param centerY y coordinate of center
|
|
* @param zoomFactor zoom factor
|
|
* @return the viewer
|
|
*/
|
|
public GraphStreamViewer setCameraView(double centerX, double centerY, double zoomFactor){
|
|
center = new Center(centerX,centerY);
|
|
this.zoomFactor = zoomFactor;
|
|
return this;
|
|
}
|
|
|
|
public void display(){
|
|
System.setProperty("org.graphstream.ui.renderer", "org.graphstream.ui.j2dviewer.J2DGraphRenderer");
|
|
|
|
JFrame jframe = new JFrame();
|
|
|
|
JPanel basicPanel = new JPanel();
|
|
basicPanel.setLayout(new BoxLayout(basicPanel, BoxLayout.Y_AXIS));
|
|
|
|
//result-panel
|
|
JPanel resultPanel = createResultPanel();
|
|
//graphstream-panel
|
|
Graph g = new MultiGraph("g");
|
|
g.addAttribute("ui.quality");
|
|
g.addAttribute("ui.antialias");
|
|
g.addAttribute("ui.stylesheet", styleSheet);
|
|
|
|
JPanel graphStreamPanel = new JPanel();
|
|
graphStreamPanel.setPreferredSize(new Dimension((int)(800*scaling),(int)(460*scaling)));
|
|
graphStreamPanel.setBackground(Color.WHITE);
|
|
|
|
JPanel graphStreamBackPanel = new JPanel();
|
|
graphStreamBackPanel.setPreferredSize(new Dimension((int)(700*scaling),(int)(450*scaling)));
|
|
graphStreamBackPanel.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1));
|
|
graphStreamBackPanel.setBackground(Color.WHITE);
|
|
|
|
Viewer viewer = new Viewer(g,Viewer.ThreadingModel.GRAPH_IN_ANOTHER_THREAD);
|
|
View view = viewer.addDefaultView(false);
|
|
view.setPreferredSize(new Dimension((int)(698*scaling),(int)(440*scaling)));
|
|
|
|
graphStreamBackPanel.add(view);
|
|
graphStreamPanel.add(graphStreamBackPanel);
|
|
|
|
//setup basicPanel
|
|
basicPanel.add(resultPanel);
|
|
basicPanel.add(graphStreamPanel);
|
|
// basicPanel.add(legendPanel);
|
|
|
|
//put it together
|
|
jframe.add(basicPanel);
|
|
|
|
//conf jframe
|
|
jframe.setSize((int)(800*scaling),(int)(580*scaling));
|
|
jframe.setLocationRelativeTo(null);
|
|
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
|
jframe.setVisible(true);
|
|
jframe.pack();
|
|
jframe.setTitle("jsprit - GraphStream");
|
|
|
|
//start rendering graph
|
|
render(g,view);
|
|
}
|
|
|
|
private void render(Graph g, View view) {
|
|
if(center != null){
|
|
view.resizeFrame(view.getWidth(), view.getHeight());
|
|
view.getCamera().setViewCenter(center.x, center.y, 0);
|
|
view.getCamera().setViewPercent(zoomFactor);
|
|
}
|
|
|
|
for(Vehicle vehicle : vrp.getVehicles()){
|
|
renderVehicle(g,vehicle,label);
|
|
sleep(renderDelay_in_ms);
|
|
}
|
|
|
|
for(Job j : vrp.getJobs().values()){
|
|
if(j instanceof Service){
|
|
renderService(g,(Service)j,label);
|
|
}
|
|
else if(j instanceof Shipment){
|
|
renderShipment(g,(Shipment)j,label,renderShipments);
|
|
}
|
|
sleep(renderDelay_in_ms);
|
|
}
|
|
|
|
if(solution != null){
|
|
int routeId = 1;
|
|
for(VehicleRoute route : solution.getRoutes()){
|
|
renderRoute(g,route,routeId,renderDelay_in_ms,label);
|
|
sleep(renderDelay_in_ms);
|
|
routeId++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private JLabel createEmptyLabel() {
|
|
JLabel emptyLabel1 = new JLabel();
|
|
emptyLabel1.setPreferredSize(new Dimension((int)(40*scaling),(int)(25*scaling)));
|
|
return emptyLabel1;
|
|
}
|
|
|
|
private JPanel createResultPanel() {
|
|
int width = 800;
|
|
int height = 50;
|
|
|
|
JPanel panel = new JPanel();
|
|
panel.setPreferredSize(new Dimension((int)(width*scaling),(int)(height*scaling)));
|
|
panel.setBackground(Color.WHITE);
|
|
|
|
JPanel subpanel = new JPanel();
|
|
subpanel.setLayout(new FlowLayout());
|
|
subpanel.setPreferredSize(new Dimension((int)(700*scaling),(int)(40*scaling)));
|
|
subpanel.setBackground(Color.WHITE);
|
|
subpanel.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY,1));
|
|
|
|
Font font = Font.decode("couriernew");
|
|
|
|
JLabel jobs = new JLabel("jobs");
|
|
jobs.setFont(font);
|
|
jobs.setPreferredSize(new Dimension((int)(40*scaling),(int)(25*scaling)));
|
|
|
|
JFormattedTextField nJobs = new JFormattedTextField(this.vrp.getJobs().values().size());
|
|
nJobs.setFont(font);
|
|
nJobs.setEditable(false);
|
|
nJobs.setBorder(BorderFactory.createEmptyBorder());
|
|
nJobs.setBackground(new Color(230,230,230));
|
|
|
|
JLabel costs = new JLabel("costs");
|
|
costs.setFont(font);
|
|
costs.setPreferredSize(new Dimension((int)(40*scaling),(int)(25*scaling)));
|
|
|
|
JFormattedTextField costsVal = new JFormattedTextField(getSolutionCosts());
|
|
costsVal.setFont(font);
|
|
costsVal.setEditable(false);
|
|
costsVal.setBorder(BorderFactory.createEmptyBorder());
|
|
costsVal.setBackground(new Color(230,230,230));
|
|
|
|
JLabel vehicles = new JLabel("routes");
|
|
vehicles.setFont(font);
|
|
vehicles.setPreferredSize(new Dimension((int)(40*scaling),(int)(25*scaling)));
|
|
// vehicles.setForeground(Color.DARK_GRAY);
|
|
|
|
JFormattedTextField vehVal = new JFormattedTextField(getNoRoutes());
|
|
vehVal.setFont(font);
|
|
vehVal.setEditable(false);
|
|
vehVal.setBorder(BorderFactory.createEmptyBorder());
|
|
// vehVal.setForeground(Color.DARK_GRAY);
|
|
vehVal.setBackground(new Color(230,230,230));
|
|
|
|
//platzhalter
|
|
JLabel placeholder1 = new JLabel();
|
|
placeholder1.setPreferredSize(new Dimension((int)(60*scaling),(int)(25*scaling)));
|
|
|
|
JLabel emptyLabel1 = createEmptyLabel();
|
|
|
|
subpanel.add(jobs);
|
|
subpanel.add(nJobs);
|
|
|
|
subpanel.add(emptyLabel1);
|
|
|
|
subpanel.add(costs);
|
|
subpanel.add(costsVal);
|
|
|
|
JLabel emptyLabel2 = createEmptyLabel();
|
|
subpanel.add(emptyLabel2);
|
|
|
|
subpanel.add(vehicles);
|
|
subpanel.add(vehVal);
|
|
|
|
panel.add(subpanel);
|
|
|
|
return panel;
|
|
}
|
|
|
|
private Integer getNoRoutes() {
|
|
if(solution!=null) return solution.getRoutes().size();
|
|
return 0;
|
|
}
|
|
|
|
private Double getSolutionCosts() {
|
|
if(solution!=null) return solution.getCost();
|
|
return 0.0;
|
|
}
|
|
|
|
private void renderShipment(Graph g, Shipment shipment, Label label, boolean renderShipments) {
|
|
|
|
Node n1 = g.addNode(makeId(shipment.getId(),shipment.getPickupLocationId()));
|
|
if(label.equals(Label.ID)) n1.addAttribute("ui.label", shipment.getId());
|
|
n1.addAttribute("x", shipment.getPickupCoord().getX());
|
|
n1.addAttribute("y", shipment.getPickupCoord().getY());
|
|
n1.setAttribute("ui.class", "pickup");
|
|
|
|
Node n2 = g.addNode(makeId(shipment.getId(),shipment.getDeliveryLocationId()));
|
|
if(label.equals(Label.ID)) n2.addAttribute("ui.label", shipment.getId());
|
|
n2.addAttribute("x", shipment.getDeliveryCoord().getX());
|
|
n2.addAttribute("y", shipment.getDeliveryCoord().getY());
|
|
n2.setAttribute("ui.class", "delivery");
|
|
|
|
if(renderShipments){
|
|
Edge s = g.addEdge(shipment.getId(), makeId(shipment.getId(),shipment.getPickupLocationId()),
|
|
makeId(shipment.getId(),shipment.getDeliveryLocationId()), true);
|
|
s.addAttribute("ui.class", "shipment");
|
|
}
|
|
|
|
}
|
|
|
|
private void sleep(long renderDelay_in_ms2) {
|
|
try {
|
|
Thread.sleep(renderDelay_in_ms2);
|
|
} catch (InterruptedException e) {
|
|
// TODO Auto-generated catch block
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
private void renderService(Graph g, Service service, Label label) {
|
|
Node n = g.addNode(makeId(service.getId(),service.getLocationId()));
|
|
if(label.equals(Label.ID)) n.addAttribute("ui.label", service.getId());
|
|
n.addAttribute("x", service.getCoord().getX());
|
|
n.addAttribute("y", service.getCoord().getY());
|
|
if(service.getType().equals("pickup")) n.setAttribute("ui.class", "pickup");
|
|
if(service.getType().equals("delivery")) n.setAttribute("ui.class", "delivery");
|
|
}
|
|
|
|
private String makeId(String id, String locationId) {
|
|
return id + "_" + locationId;
|
|
}
|
|
|
|
private void renderVehicle(Graph g, Vehicle vehicle, Label label) {
|
|
String nodeId = makeId(vehicle.getId(),vehicle.getStartLocationId());
|
|
if(vehicle.getType() instanceof PenaltyVehicleType) nodeId = makeId("pen_"+vehicle.getId(),vehicle.getStartLocationId());
|
|
Node vehicleStart = g.addNode(nodeId);
|
|
if(label.equals(Label.ID)) vehicleStart.addAttribute("ui.label", "depot");
|
|
// if(label.equals(Label.ACTIVITY)) n.addAttribute("ui.label", "start");
|
|
vehicleStart.addAttribute("x", vehicle.getStartLocationCoordinate().getX());
|
|
vehicleStart.addAttribute("y", vehicle.getStartLocationCoordinate().getY());
|
|
vehicleStart.setAttribute("ui.class", "depot");
|
|
|
|
if(!vehicle.getStartLocationId().equals(vehicle.getEndLocationId())){
|
|
Node vehicleEnd = g.addNode(makeId(vehicle.getId(),vehicle.getEndLocationId()));
|
|
if(label.equals(Label.ID)) vehicleEnd.addAttribute("ui.label", "depot");
|
|
// if(label.equals(Label.ACTIVITY)) n.addAttribute("ui.label", "start");
|
|
vehicleEnd.addAttribute("x", vehicle.getEndLocationCoordinate().getX());
|
|
vehicleEnd.addAttribute("y", vehicle.getEndLocationCoordinate().getY());
|
|
vehicleEnd.setAttribute("ui.class", "depot");
|
|
|
|
}
|
|
}
|
|
|
|
private void renderRoute(Graph g, VehicleRoute route, int routeId, long renderDelay_in_ms, Label label) {
|
|
int vehicle_edgeId = 1;
|
|
String prevIdentifier = makeId(route.getVehicle().getId(),route.getVehicle().getStartLocationId());
|
|
if(label.equals(Label.ACTIVITY) || label.equals(Label.JOB_NAME)){
|
|
Node n = g.getNode(prevIdentifier);
|
|
n.addAttribute("ui.label", "start");
|
|
}
|
|
for(TourActivity act : route.getActivities()){
|
|
Job job = ((JobActivity) act).getJob();
|
|
String currIdentifier = makeId(job.getId(),act.getLocationId());
|
|
if(label.equals(Label.ACTIVITY)){
|
|
Node actNode = g.getNode(currIdentifier);
|
|
actNode.addAttribute("ui.label", act.getName());
|
|
}
|
|
else if(label.equals(Label.JOB_NAME)){
|
|
Node actNode = g.getNode(currIdentifier);
|
|
actNode.addAttribute("ui.label", job.getName());
|
|
}
|
|
else if(label.equals(Label.ARRIVAL_TIME)){
|
|
Node actNode = g.getNode(currIdentifier);
|
|
actNode.addAttribute("ui.label", Time.parseSecondsToTime(act.getArrTime()));
|
|
}
|
|
else if(label.equals(Label.DEPARTURE_TIME)){
|
|
Node actNode = g.getNode(currIdentifier);
|
|
actNode.addAttribute("ui.label", Time.parseSecondsToTime(act.getEndTime()));
|
|
}
|
|
g.addEdge(makeEdgeId(routeId,vehicle_edgeId), prevIdentifier, currIdentifier, true);
|
|
if(act instanceof PickupActivity) g.getNode(currIdentifier).addAttribute("ui.class", "pickupInRoute");
|
|
else if (act instanceof DeliveryActivity) g.getNode(currIdentifier).addAttribute("ui.class", "deliveryInRoute");
|
|
prevIdentifier = currIdentifier;
|
|
vehicle_edgeId++;
|
|
sleep(renderDelay_in_ms);
|
|
}
|
|
if(route.getVehicle().isReturnToDepot()){
|
|
String lastIdentifier = makeId(route.getVehicle().getId(),route.getVehicle().getEndLocationId());
|
|
g.addEdge(makeEdgeId(routeId,vehicle_edgeId), prevIdentifier, lastIdentifier, true);
|
|
}
|
|
}
|
|
|
|
private String makeEdgeId(int routeId, int vehicle_edgeId) {
|
|
return Integer.valueOf(routeId).toString() + "." + Integer.valueOf(vehicle_edgeId).toString();
|
|
}
|
|
|
|
// public void saveAsPNG(String filename){
|
|
//
|
|
// }
|
|
}
|