import java.awt.*;
/**
* 一块实现地图绘制的画布。此画布添加到地图显示窗口中。
* @author Fe
*/
public class MapCanvas extends Canvas {
GraphFromFile graph = null;
/**
* 图中结点的数目。
*/
int nodeNum = 0;
/**
* 图的邻接表引用。
*/
GraphAdjList graphAdjList[] = null;
/**
* 图中结点的坐标引用。
*/
int nodePosition[][] = null;
/**
* 图中坐标的名称引用。
*/
String nodeID[] = null;
/**
* 简单的调用父类的构造函数。
*/
public MapCanvas(GraphFromFile g) {
super();
graph = g;
nodeNum = graph.getNodeNum();
graphAdjList = graph.getList();
nodePosition = graph.getNodePosition();
nodeID = graph.getNodeID();
}
/**
* 重载的paint函数用于实现地图网格以及结点和边的具体绘制。
*/
public void paint(Graphics g) {
//地图周围的空白边框。
final int BLANK_BORDER = 15;
//每个结点的直径。
final int POINT_DIAMETER = 10;
//每个网格的宽度。
int cellWidth = (int)((getSize().getWidth() - 2*BLANK_BORDER)/40);
//每个网格的高度。
int cellHeight = (int)((getSize().getHeight() - 2*BLANK_BORDER)/40);
//地图网格区域的总宽度。(网格数为40)
int graphWidthRange = cellWidth * 40;
//地图网格区域的总高度。(网格数为40)
int graphHeightRange = cellHeight * 40;
//绘制灰色的地图网格。
g.setColor(Color.GRAY);
for (int i = BLANK_BORDER; i <= graphHeightRange + BLANK_BORDER; i += cellHeight) {
g.drawLine(BLANK_BORDER, i, graphWidthRange + BLANK_BORDER, i);
}
for (int j = BLANK_BORDER; j <= graphWidthRange + BLANK_BORDER; j += cellWidth) {
g.drawLine(j, BLANK_BORDER, j, graphHeightRange + BLANK_BORDER);
}
//将每个结点根据其坐标信息绘制在相应的网格点上。并在其旁绘制相应的结点名称。
g.setColor(Color.BLACK);
for (int i = 0; i < nodeNum; i++) {
g.fillOval((nodePosition[i][0]*cellWidth - POINT_DIAMETER/2) + BLANK_BORDER, (nodePosition[i][1]*cellHeight - POINT_DIAMETER/2) + BLANK_BORDER, POINT_DIAMETER, POINT_DIAMETER);
g.setColor(Color.BLUE);
g.drawString(nodeID[i], (nodePosition[i][0]*cellWidth + (POINT_DIAMETER/2+2)) + BLANK_BORDER, (nodePosition[i][1]*cellHeight + POINT_DIAMETER/2) + BLANK_BORDER);
g.setColor(Color.BLACK);
}
//根据邻接表的信息,在邻接结点之间连线,并在线中央绘制边权大小。
for (int i = 0; i < nodeNum; i++) {
NextAdjNode temp = null;
//如果该结点有邻接结点,则绘制。
if (graphAdjList[i] != null) {
temp = graphAdjList[i].firstNode;
while (temp != null) {
g.drawLine(nodePosition[i][0]*cellWidth + BLANK_BORDER, nodePosition[i][1]*cellHeight + BLANK_BORDER,
nodePosition[temp.nodeNum][0]*cellWidth + BLANK_BORDER, nodePosition[temp.nodeNum][1]*cellHeight + BLANK_BORDER);
g.setColor(Color.RED);
g.drawString("" + temp.edgeWeight,
(nodePosition[i][0]*cellWidth + nodePosition[temp.nodeNum][0]*cellWidth)/2 + BLANK_BORDER,
(nodePosition[i][1]*cellHeight + nodePosition[temp.nodeNum][1]*cellHeight)/2 + BLANK_BORDER);
g.setColor(Color.BLACK);
//通过链表的遍历,实现所有邻接结点间的连线。
temp = temp.nextNode;
}
}
}
//如果需要对特定路径进行特殊颜色的绘制,则绘制。其中,是否绘制由主程序通过接口控制。
if (drawSpecifiedPath) {
drawPath(g, path, cellWidth, cellHeight, BLANK_BORDER);
}
if (drawSubGraph) {
for (int i = 0; i < subGraph.length; i++) {
drawPath(g, subGraph[i], cellWidth, cellHeight, BLANK_BORDER);
}
}
}
/**
* 控制是否绘制特定路径的布尔变量。由主程序根据需要通过接口设置。
*/
static boolean drawSpecifiedPath = false;
/**
* 需要绘制的特定路径的引用。
*/
static int path[] = null;
/**
* 主程序通过此接口设置是否需要绘制特定的路径信息。
* @param pa
* 需要绘制的特定路径的引用
* @param draw
* 是否需要绘制 true为需要 false为不需要
*/
public static void setDrawSpecifiedPath(int pa[], boolean draw) {
path = pa;
drawSpecifiedPath = draw;
}
static boolean drawSubGraph = false;
static int subGraph[][] = null;
public static void setDrawSubGraph(int gr[][], boolean draw) {
subGraph = gr;
drawSubGraph = draw;
}
/**
* 在原地图的基础上,绘制一条特定的路径信息。在此程序中为两城市间的最短路径。
* @param g
* 由paint传过来的Graphics的引用
* @param path
* 两城市间的最短路径
* @param cellWidth
* 网格宽度
* @param cellHeight
* 网格高度
* @param BLANK_BORDER
* 空白边界宽度
*/
private void drawPath(Graphics g, int path[], int cellWidth, int cellHeight, int BLANK_BORDER) {
g.setColor(Color.GREEN);
for (int i = 0; i < path.length-1; i++) {
g.drawLine(nodePosition[path[i]][0]*cellWidth + BLANK_BORDER, nodePosition[path[i]][1]*cellHeight + BLANK_BORDER,
nodePosition[path[i+1]][0]*cellWidth + BLANK_BORDER, nodePosition[path[i+1]][1]*cellHeight + BLANK_BORDER);
}
}
}