東京シティサイクリング2009動画で使用した地図、時刻、速度の作成に使ったものです。実行には国土数値情報の公共施設、鉄道、道路データが別途必要です。
@@ -0,0 +1,45 @@ | ||
1 | +import java.awt.Color; | |
2 | +import java.awt.Font; | |
3 | +import java.awt.Graphics; | |
4 | +import java.awt.Graphics2D; | |
5 | +import java.awt.RenderingHints; | |
6 | + | |
7 | +import javax.swing.JPanel; | |
8 | + | |
9 | +/** | |
10 | + * 速度を表示するパネルです。 | |
11 | + */ | |
12 | +public class SpeedPanel extends JPanel { | |
13 | + | |
14 | + /** | |
15 | + * 時刻 | |
16 | + */ | |
17 | + public double speed; | |
18 | + | |
19 | + @Override | |
20 | + protected void paintComponent(Graphics g) { | |
21 | + super.paintComponent(g); | |
22 | + draw((Graphics2D) g); | |
23 | + } | |
24 | + | |
25 | + /** | |
26 | + * 時刻を描画します。 | |
27 | + * @param g 描画対象 | |
28 | + */ | |
29 | + public void draw(Graphics2D g) { | |
30 | + g.setColor(Color.BLACK); | |
31 | + g.fillRect(0, 0, getWidth(), getHeight()); | |
32 | + g.setFont(new Font("Seoge", Font.BOLD, getHeight())); | |
33 | + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); | |
34 | + g.setColor(Color.WHITE); | |
35 | + int x = g.getFontMetrics().stringWidth("00"); | |
36 | + int y = getHeight() - g.getFontMetrics().getDescent() / 2; | |
37 | + String integer = String.valueOf((int) this.speed); | |
38 | + g.drawString(integer, x - g.getFontMetrics().stringWidth(integer), y); | |
39 | + g.setFont(new Font("Seoge", Font.BOLD, getHeight() / 2)); | |
40 | + g.drawString(String.valueOf((int) (this.speed * 10) % 10), x + getHeight() / 15, g.getFontMetrics().getHeight() | |
41 | + - g.getFontMetrics().getDescent()); | |
42 | + g.setFont(new Font("Seoge", Font.PLAIN, getHeight() / 3)); | |
43 | + g.drawString("km/h", x + getHeight() / 20, y); | |
44 | + } | |
45 | +} |
@@ -1,5 +1,5 @@ | ||
1 | +import java.util.ArrayList; | |
1 | 2 | import java.util.Collection; |
2 | -import java.util.concurrent.CopyOnWriteArrayList; | |
3 | 3 | |
4 | 4 | /** |
5 | 5 | * 線データに対応するクラスです。 |
@@ -38,15 +38,20 @@ | ||
38 | 38 | public String stationName; |
39 | 39 | |
40 | 40 | /** |
41 | + * 道路種別コード | |
42 | + */ | |
43 | + public Const.RoadTypeCode roadTypeCode; | |
44 | + | |
45 | + /** | |
41 | 46 | * 線データを初期化します。 |
42 | 47 | */ |
43 | 48 | public LineData() { |
44 | - this.points = new CopyOnWriteArrayList<PointData>(); | |
49 | + this.points = new ArrayList<PointData>(); | |
45 | 50 | } |
46 | 51 | |
47 | 52 | @Override |
48 | 53 | public String toString() { |
49 | 54 | return "(" + this.railwayClassCode + ", " + this.institutionTypeCode + ", " + this.lineName + ", " |
50 | - + this.company + ", " + this.stationName + ")" + this.points.toString(); | |
55 | + + this.company + ", " + this.stationName + ", " + this.label + ", " + this.roadTypeCode + ")" + this.points.toString(); | |
51 | 56 | } |
52 | 57 | } |
@@ -1,8 +1,8 @@ | ||
1 | 1 | import java.io.InputStream; |
2 | +import java.util.ArrayList; | |
2 | 3 | import java.util.Collection; |
3 | 4 | import java.util.HashMap; |
4 | 5 | import java.util.Map; |
5 | -import java.util.concurrent.CopyOnWriteArrayList; | |
6 | 6 | import java.util.regex.Matcher; |
7 | 7 | import java.util.regex.Pattern; |
8 | 8 |
@@ -44,7 +44,7 @@ | ||
44 | 44 | |
45 | 45 | @Override |
46 | 46 | public Collection<LineData> read(InputStream in) throws XMLStreamException { |
47 | - Collection<LineData> ret = new CopyOnWriteArrayList<LineData>(); | |
47 | + Collection<LineData> ret = new ArrayList<LineData>(); | |
48 | 48 | XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(in); |
49 | 49 | Mode mode = Mode.POINT; |
50 | 50 | String id = null; |
@@ -0,0 +1,77 @@ | ||
1 | +import java.io.InputStream; | |
2 | +import java.io.Reader; | |
3 | +import java.util.ArrayList; | |
4 | +import java.util.Collection; | |
5 | +import java.util.HashMap; | |
6 | +import java.util.Map; | |
7 | +import java.util.Scanner; | |
8 | + | |
9 | +/** | |
10 | + * 国土数値情報の道路データを読み込むクラス | |
11 | + */ | |
12 | +public class RoadLineDataReader implements LineDataReader { | |
13 | + /** | |
14 | + * 道路の一覧 | |
15 | + */ | |
16 | + Map<Integer, LineData> lines; | |
17 | + | |
18 | + /** | |
19 | + * コンストラクタ | |
20 | + * @param in リンク台帳ファイルの入力ストリームリーダ | |
21 | + */ | |
22 | + public RoadLineDataReader(Reader in) { | |
23 | + this.lines = new HashMap<Integer, LineData>(); | |
24 | + final Scanner scanner = new Scanner(in); | |
25 | + while (scanner.hasNextLine()) { | |
26 | + String line = scanner.nextLine(); | |
27 | + if (!line.startsWith("DL")) { | |
28 | + continue; | |
29 | + } | |
30 | + int code = Integer.parseInt(line.substring(3, 13).trim()); | |
31 | + int type = Integer.parseInt(line.substring(16, 18).trim()); | |
32 | + String label = line.substring(18).trim(); | |
33 | + LineData lineData = new LineData(); | |
34 | + lineData.label = label; | |
35 | + lineData.roadTypeCode = Const.RoadTypeCode.get(type); | |
36 | + this.lines.put(code, lineData); | |
37 | + } | |
38 | + } | |
39 | + | |
40 | + @Override | |
41 | + public Collection<LineData> read(InputStream in) { | |
42 | + Collection<LineData> ret = new ArrayList<LineData>(); | |
43 | + final Scanner scanner = new Scanner(in); | |
44 | + LineData currentLineData = null; | |
45 | + while (scanner.hasNextLine()) { | |
46 | + String line = scanner.nextLine(); | |
47 | + if (line.startsWith("L")) { | |
48 | + if (currentLineData != null) { | |
49 | + ret.add(currentLineData); | |
50 | + } | |
51 | + int code = Integer.parseInt(line.substring(35, 45).trim()); | |
52 | + if (this.lines.containsKey(code)) { | |
53 | + currentLineData = new LineData(); | |
54 | + currentLineData.label = this.lines.get(code).label; | |
55 | + currentLineData.roadTypeCode = this.lines.get(code).roadTypeCode; | |
56 | + } else { | |
57 | + currentLineData = null; | |
58 | + } | |
59 | + } else { | |
60 | + if (currentLineData != null) { | |
61 | + final Scanner scanner2 = new Scanner(line); | |
62 | + while (scanner2.hasNextInt()) { | |
63 | + int x = scanner2.nextInt(); | |
64 | + if (scanner2.hasNextInt()) { | |
65 | + int y = scanner2.nextInt(); | |
66 | + currentLineData.points.add(new PointData(y / 36000.0, x / 36000.0, null)); | |
67 | + } | |
68 | + } | |
69 | + } | |
70 | + } | |
71 | + } | |
72 | + if (currentLineData != null) { | |
73 | + ret.add(currentLineData); | |
74 | + } | |
75 | + return ret; | |
76 | + } | |
77 | +} |
@@ -5,6 +5,7 @@ | ||
5 | 5 | import java.io.File; |
6 | 6 | import java.io.FileInputStream; |
7 | 7 | import java.io.IOException; |
8 | +import java.io.InputStreamReader; | |
8 | 9 | import java.text.DateFormat; |
9 | 10 | import java.text.ParseException; |
10 | 11 | import java.util.Collection; |
@@ -38,6 +39,9 @@ | ||
38 | 39 | final ClockPanel clockPanel = new ClockPanel(); |
39 | 40 | frame.add(clockPanel, BorderLayout.BEFORE_FIRST_LINE); |
40 | 41 | clockPanel.setPreferredSize(new Dimension(200, 30)); |
42 | + final SpeedPanel speedPanel = new SpeedPanel(); | |
43 | + frame.add(speedPanel, BorderLayout.AFTER_LAST_LINE); | |
44 | + speedPanel.setPreferredSize(new Dimension(200, 40)); | |
41 | 45 | frame.pack(); |
42 | 46 | frame.setLocationByPlatform(true); |
43 | 47 | frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); |
@@ -51,83 +55,37 @@ | ||
51 | 55 | max.latitude = Math.max(max.latitude, point.latitude); |
52 | 56 | max.longitude = Math.max(max.longitude, point.longitude); |
53 | 57 | } |
54 | - mapPanel.lines.addAll(new RailwayLineDataReader(min, max).read(new FileInputStream(new File("N02-08.xml")))); | |
55 | - Collection<PointData> points = new GPXPointDataReader().read(new FileInputStream(new File( | |
56 | - "20090913-yamanote.gpx"))); | |
58 | + mapPanel.railways.addAll(new RailwayLineDataReader(min, max).read(new FileInputStream(new File("N02-08.xml")))); | |
59 | + mapPanel.roads.addAll(new RoadLineDataReader(new InputStreamReader(new FileInputStream( | |
60 | + new File("N01_07L_台.txt")), "SJIS")).read(new FileInputStream(new File("N01-07L-2K-13.txt")))); | |
61 | + Collection<PointData> points = new GPXPointDataReader() | |
62 | + .read(new FileInputStream(new File("20090920-tokyo.gpx"))); | |
57 | 63 | Interpolator interpolator = new Interpolator(points); |
58 | - mapPanel.zoom = 0.3; | |
59 | - int division = 1; | |
60 | - int offsetSec = 0; // -16 | |
61 | - DateFormat format = DateFormat.getDateTimeInstance(); | |
64 | + mapPanel.zoom = 0.4; | |
65 | + int offsetSec = -15; | |
66 | + int division = 30; | |
67 | + int previewSpeed = 4; | |
62 | 68 | Mode mode = Mode.RENDER; |
63 | - // 品川→田町 | |
64 | - Setting map379 = new Setting("map379", format.parse("2009/9/13 12:02:50"), 6 * 60 + 1, 0); | |
65 | - // 田町→浜松町 | |
66 | - Setting map380 = new Setting("map380", format.parse("2009/9/13 12:14:36"), 4 * 60 + 52, 0); | |
67 | - // 浜松町→新橋 | |
68 | - Setting map381 = new Setting("map381", format.parse("2009/9/13 13:02:34"), 4 * 60 + 16, 0); | |
69 | - // 新橋→有楽町 | |
70 | - Setting map383 = new Setting("map383", format.parse("2009/9/13 13:13:42"), 4 * 60 + 1, 0); | |
71 | - // 有楽町→東京 | |
72 | - Setting map384 = new Setting("map384", format.parse("2009/9/13 13:21:20"), 3 * 60 + 4, 0); | |
73 | - // 東京→途中(淡路町) | |
74 | - Setting map385 = new Setting("map385", format.parse("2009/9/13 13:31:00"), 5 * 60 + 51, 0); | |
75 | - // 途中(淡路町)→神田、ちょっとカメラ右に傾いた | |
76 | - Setting map386 = new Setting("map386", format.parse("2009/9/13 13:41:44"), 4 * 60 + 18, 600); | |
77 | - // 神田→秋葉原 | |
78 | - Setting map387 = new Setting("map387", format.parse("2009/9/13 13:50:42"), 2 * 60 + 39, 0); | |
79 | - // 秋葉原→御徒町 | |
80 | - Setting map388 = new Setting("map388", format.parse("2009/9/13 14:00:26"), 4 * 60 + 40, 0); | |
81 | - // 御徒町→上野、アメ横は人が多いのでカットしよう。 | |
82 | - Setting map389 = new Setting("map389", format.parse("2009/9/13 14:07:28"), 4 * 60 + 12, 0); | |
83 | - // 上野→途中(上野中) | |
84 | - Setting map390 = new Setting("map390", format.parse("2009/9/13 14:15:50"), 7 * 60 + 14, 0); | |
85 | - // 途中(上野中)→鶯谷 | |
86 | - Setting map391 = new Setting("map391", format.parse("2009/9/13 14:24:16"), 1 * 60 + 51, 600); | |
87 | - // 鶯谷→日暮里 | |
88 | - Setting map392 = new Setting("map392", format.parse("2009/9/13 14:29:02"), 6 * 60 + 42, 0); | |
89 | - // 日暮里→途中(東日暮里5) | |
90 | - Setting map393 = new Setting("map393", format.parse("2009/9/13 14:41:14"), 1 * 60 + 18, 0); | |
91 | - // 途中(東日暮里5)→途中(根岸小) | |
92 | - Setting map394 = new Setting("map394", format.parse("2009/9/13 14:43:48"), 1 * 60 + 28, 2 * 60 + 34); | |
93 | - // 途中(根岸小)→西日暮里 | |
94 | - Setting map395 = new Setting("map395", format.parse("2009/9/13 14:49:52"), 7 * 60 + 9, 8 * 60 + 38); | |
95 | - // 西日暮里→途中 | |
96 | - Setting map396 = new Setting("map396", format.parse("2009/9/13 15:01:40"), 2 * 60 + 6, 0); | |
97 | - // 途中→田端 | |
98 | - Setting map397 = new Setting("map397", format.parse("2009/9/13 15:04:42"), 4 * 60 + 51, 182); | |
99 | - for (Setting setting : new Setting[] { new Setting("01-gotanda", format.parse("2009/9/13 11:24:00"), 1, 0), | |
100 | - new Setting("02-osaki", format.parse("2009/9/13 11:35:00"), 1, 0), | |
101 | - new Setting("03-shinagawa", format.parse("2009/9/13 11:50:00"), 1, 0), | |
102 | - new Setting("04-tamachi", format.parse("2009/9/13 12:10:00"), 1, 0), | |
103 | - new Setting("05-hamamatsucho", format.parse("2009/9/13 12:24:00"), 1, 0), | |
104 | - new Setting("06-shinbashi", format.parse("2009/9/13 13:09:00"), 1, 0), | |
105 | - new Setting("07-yurakucho", format.parse("2009/9/13 13:19:00"), 1, 0), | |
106 | - new Setting("08-tokyo", format.parse("2009/9/13 13:27:00"), 1, 0), | |
107 | - new Setting("09-kanda", format.parse("2009/9/13 13:48:00"), 1, 0), | |
108 | - new Setting("10-akihabara", format.parse("2009/9/13 13:57:00"), 1, 0), | |
109 | - new Setting("11-okachimachi", format.parse("2009/9/13 14:06:00"), 1, 0), | |
110 | - new Setting("12-ueno", format.parse("2009/9/13 14:13:00"), 1, 0), | |
111 | - new Setting("13-uguisudani", format.parse("2009/9/13 14:27:00"), 1, 0), | |
112 | - new Setting("14-nippori", format.parse("2009/9/13 14:38:00"), 1, 0), | |
113 | - new Setting("15-nishinippori", format.parse("2009/9/13 14:59:00"), 1, 0), | |
114 | - new Setting("16-tabashi", format.parse("2009/9/13 15:10:00"), 1, 0), | |
115 | - new Setting("17-komagome", format.parse("2009/9/13 15:37:00"), 1, 0), | |
116 | - new Setting("18-sugamo", format.parse("2009/9/13 15:48:00"), 1, 0), | |
117 | - new Setting("19-otsuka", format.parse("2009/9/13 15:59:00"), 1, 0), | |
118 | - new Setting("20-ikebukuro", format.parse("2009/9/13 16:14:00"), 1, 0), | |
119 | - new Setting("21-mejiro", format.parse("2009/9/13 16:32:00"), 1, 0), | |
120 | - new Setting("22-takadanobaba", format.parse("2009/9/13 16:42:00"), 1, 0), | |
121 | - new Setting("23-shinokubo", format.parse("2009/9/13 16:54:00"), 1, 0), | |
122 | - new Setting("24-shinjuku", format.parse("2009/9/13 17:05:00"), 1, 0), | |
123 | - new Setting("25-yoyogi", format.parse("2009/9/13 17:15:00"), 1, 0), | |
124 | - new Setting("26-harajuku", format.parse("2009/9/13 17:30:00"), 1, 0), | |
125 | - new Setting("27-shibuya", format.parse("2009/9/13 17:44:00"), 1, 0), | |
126 | - new Setting("28-ebisu", format.parse("2009/9/13 18:25:00"), 1, 0), | |
127 | - new Setting("29-meguro", format.parse("2009/9/13 18:42:00"), 1, 0), | |
128 | - new Setting("30-gotanda", format.parse("2009/9/13 18:51:00"), 1, 0), }) { | |
129 | - process(mode, setting, offsetSec, division, frame, mapPanel, interpolator, clockPanel); | |
130 | - } | |
69 | +// process(mode, new Setting("../410", DateFormat.getDateTimeInstance().parse("2009/9/20 9:27:58"), 60 * 13 + 18, 0), | |
70 | +// offsetSec, division, previewSpeed, frame, mapPanel, interpolator, clockPanel, speedPanel); | |
71 | +// process(mode, new Setting("../411", DateFormat.getDateTimeInstance().parse("2009/9/20 9:42:52"), 60 * 1 + 15, 60), | |
72 | +// offsetSec, division, previewSpeed, frame, mapPanel, interpolator, clockPanel, speedPanel); | |
73 | +// process(mode, new Setting("../412", DateFormat.getDateTimeInstance().parse("2009/9/20 9:44:24"), 60 * 10 + 12, 120), | |
74 | +// offsetSec, division, previewSpeed, frame, mapPanel, interpolator, clockPanel, speedPanel); | |
75 | +// process(mode, new Setting("../413", DateFormat.getDateTimeInstance().parse("2009/9/20 9:54:56"), 60 * 11 + 56, 300), | |
76 | +// offsetSec, division, previewSpeed, frame, mapPanel, interpolator, clockPanel, speedPanel); | |
77 | +// process(mode, new Setting("../414", DateFormat.getDateTimeInstance().parse("2009/9/20 10:07:14"), 60 * 28 + 5, 120), | |
78 | +// offsetSec, division, previewSpeed, frame, mapPanel, interpolator, clockPanel, speedPanel); | |
79 | +// process(mode, new Setting("../415", DateFormat.getDateTimeInstance().parse("2009/9/20 10:48:16"), 60 * 5 + 43, 600), | |
80 | +// offsetSec, division, previewSpeed, frame, mapPanel, interpolator, clockPanel, speedPanel); | |
81 | + process(mode, new Setting("../416", DateFormat.getDateTimeInstance().parse("2009/9/20 10:58:32"), 60 * 46 + 34, 600), | |
82 | + offsetSec, division, previewSpeed, frame, mapPanel, interpolator, clockPanel, speedPanel); | |
83 | + process(mode, new Setting("../417", DateFormat.getDateTimeInstance().parse("2009/9/20 12:12:14"), 60 * 2 + 30, 120), | |
84 | + offsetSec, division, previewSpeed, frame, mapPanel, interpolator, clockPanel, speedPanel); | |
85 | + process(mode, new Setting("../418", DateFormat.getDateTimeInstance().parse("2009/9/20 13:09:04"), 60 * 3 + 24, 120), | |
86 | + offsetSec, division, previewSpeed, frame, mapPanel, interpolator, clockPanel, speedPanel); | |
87 | + process(mode, new Setting("../419", DateFormat.getDateTimeInstance().parse("2009/9/20 13:37:40"), 60 * 1 + 35, 120), | |
88 | + offsetSec, division, previewSpeed, frame, mapPanel, interpolator, clockPanel, speedPanel); | |
131 | 89 | if (mode == Mode.RENDER) { |
132 | 90 | frame.dispose(); |
133 | 91 | } |
@@ -139,15 +97,18 @@ | ||
139 | 97 | * @param setting 設定 |
140 | 98 | * @param offsetSec 時刻の補正[秒]。動画のタイムスタンプが遅れている場合、負の値を指定する。 |
141 | 99 | * @param division 1秒あたりのフレーム数 |
100 | + * @param previewSpeed プレビュー時の速度 | |
142 | 101 | * @param frame フレーム |
143 | 102 | * @param panel パネル |
144 | 103 | * @param interpolator 補間を行うオブジェクト |
145 | 104 | * @param clockPanel 時計を表示するパネル |
105 | + * @param speedPanel 速度を表示するパネル | |
146 | 106 | * @throws InterruptedException 割り込み例外 |
147 | 107 | * @throws IOException 入出力例外 |
148 | 108 | */ |
149 | - private static void process(Mode mode, Setting setting, int offsetSec, int division, JFrame frame, MapPanel panel, | |
150 | - Interpolator interpolator, ClockPanel clockPanel) throws InterruptedException, IOException { | |
109 | + private static void process(Mode mode, Setting setting, int offsetSec, int division, int previewSpeed, | |
110 | + JFrame frame, MapPanel panel, Interpolator interpolator, ClockPanel clockPanel, SpeedPanel speedPanel) | |
111 | + throws InterruptedException, IOException { | |
151 | 112 | panel.gpsPoints.clear(); |
152 | 113 | for (int i = -setting.preSeconds * division; i < setting.seconds * division; i++) { |
153 | 114 | Date date = new Date(setting.date.getTime() + 1000 * i / division + offsetSec * 1000); |
@@ -167,6 +128,8 @@ | ||
167 | 128 | panel.repaint(); |
168 | 129 | clockPanel.date = date; |
169 | 130 | clockPanel.repaint(); |
131 | + speedPanel.speed = 0; | |
132 | + speedPanel.repaint(); | |
170 | 133 | } |
171 | 134 | continue; |
172 | 135 | case PREVIEW: |
@@ -177,7 +140,16 @@ | ||
177 | 140 | clockPanel.date = date; |
178 | 141 | clockPanel.repaint(); |
179 | 142 | } |
180 | - Thread.sleep(1000 / division); | |
143 | + { | |
144 | + int msec = 1000; | |
145 | + PointData p1 = interpolator.get(new Date(date.getTime() - msec / 2)); | |
146 | + PointData p2 = interpolator.get(new Date(date.getTime() + msec / 2)); | |
147 | + speedPanel.speed = UTMUtil.toUTM(p1.longitude, p1.latitude).distance( | |
148 | + UTMUtil.toUTM(p2.longitude, p2.latitude)) | |
149 | + * 3600 / 1000 * 1000 / msec; | |
150 | + } | |
151 | + speedPanel.repaint(); | |
152 | + Thread.sleep(1000 / division / previewSpeed); | |
181 | 153 | break; |
182 | 154 | case RENDER: |
183 | 155 | panel.centerX = point2.getX(); |
@@ -194,10 +166,23 @@ | ||
194 | 166 | BufferedImage clockImage = new BufferedImage(clockPanel.getWidth(), clockPanel.getHeight(), |
195 | 167 | BufferedImage.TYPE_INT_BGR); |
196 | 168 | clockPanel.draw(clockImage.createGraphics()); |
197 | - new File(setting.directory).mkdir(); | |
198 | 169 | ImageIO.write(clockImage, "PNG", new File(new Formatter().format("%s/clock-%04d.png", |
199 | 170 | setting.directory, i / division + 1).toString())); |
200 | 171 | } |
172 | + { | |
173 | + int msec = 1000; | |
174 | + PointData p1 = interpolator.get(new Date(date.getTime() - msec / 2)); | |
175 | + PointData p2 = interpolator.get(new Date(date.getTime() + msec / 2)); | |
176 | + speedPanel.speed = UTMUtil.toUTM(p1.longitude, p1.latitude).distance( | |
177 | + UTMUtil.toUTM(p2.longitude, p2.latitude)) | |
178 | + * 3600 / 1000 * 1000 / msec; | |
179 | + } | |
180 | + speedPanel.repaint(); | |
181 | + BufferedImage speedImage = new BufferedImage(speedPanel.getWidth(), speedPanel.getHeight(), | |
182 | + BufferedImage.TYPE_INT_BGR); | |
183 | + speedPanel.draw(speedImage.createGraphics()); | |
184 | + ImageIO.write(speedImage, "PNG", new File(new Formatter().format("%s/speed-%06d.png", | |
185 | + setting.directory, i + 1).toString())); | |
201 | 186 | break; |
202 | 187 | } |
203 | 188 | } |
@@ -490,4 +490,39 @@ | ||
490 | 490 | */ |
491 | 491 | THIRD_SECTOR, |
492 | 492 | } |
493 | + | |
494 | + /** | |
495 | + * 道路種別コード | |
496 | + */ | |
497 | + public static enum RoadTypeCode { | |
498 | + /** | |
499 | + * 高速道路 | |
500 | + */ | |
501 | + HIGHWAY, | |
502 | + /** | |
503 | + * 国道 | |
504 | + */ | |
505 | + NATIONAL_ROAD, | |
506 | + /** | |
507 | + * 主要地方道 | |
508 | + */ | |
509 | + PRINCIPAL_PREFECTUAL_ROAD; | |
510 | + | |
511 | + /** | |
512 | + * @param type 道路種別コード | |
513 | + * @return 道路種別コードの列挙型 | |
514 | + */ | |
515 | + public static RoadTypeCode get(int type) { | |
516 | + switch (type) { | |
517 | + case 1: | |
518 | + return HIGHWAY; | |
519 | + case 2: | |
520 | + return NATIONAL_ROAD; | |
521 | + case 3: | |
522 | + return PRINCIPAL_PREFECTUAL_ROAD; | |
523 | + default: | |
524 | + return null; | |
525 | + } | |
526 | + } | |
527 | + } | |
493 | 528 | } |
@@ -37,11 +37,16 @@ | ||
37 | 37 | public List<PointData> points; |
38 | 38 | |
39 | 39 | /** |
40 | - * 線データの一覧 | |
40 | + * 鉄道データの一覧 | |
41 | 41 | */ |
42 | - public List<LineData> lines; | |
42 | + public List<LineData> railways; | |
43 | 43 | |
44 | 44 | /** |
45 | + * 道路データの一覧 | |
46 | + */ | |
47 | + public List<LineData> roads; | |
48 | + | |
49 | + /** | |
45 | 50 | * GPSログに含まれる点データの一覧 |
46 | 51 | */ |
47 | 52 | public List<PointData> gpsPoints; |
@@ -71,7 +76,8 @@ | ||
71 | 76 | */ |
72 | 77 | public MapPanel() { |
73 | 78 | this.points = new CopyOnWriteArrayList<PointData>(); |
74 | - this.lines = new CopyOnWriteArrayList<LineData>(); | |
79 | + this.railways = new CopyOnWriteArrayList<LineData>(); | |
80 | + this.roads = new CopyOnWriteArrayList<LineData>(); | |
75 | 81 | this.gpsPoints = new CopyOnWriteArrayList<PointData>(); |
76 | 82 | this.addMouseListener(new MouseAdapter() { |
77 | 83 | @Override |
@@ -129,15 +135,41 @@ | ||
129 | 135 | g.setFont(new Font("メイリオ", Font.PLAIN, 16)); |
130 | 136 | double radius = 3; |
131 | 137 | TreeMap<Double, Point2D> fixedPoints = new TreeMap<Double, Point2D>(); |
138 | + // 一般道路 | |
139 | + int principalPrefectualRoadWidth = (int) Math.max(1, 4 * this.zoom); | |
140 | + int nationalRoadWidth = (int) Math.max(2, 6 * this.zoom); | |
141 | + for (LineData line : this.roads) { | |
142 | + Path2D path = toPath(line); | |
143 | + if (path == null | |
144 | + || !path.intersects(-principalPrefectualRoadWidth, -principalPrefectualRoadWidth, getWidth() | |
145 | + + principalPrefectualRoadWidth * 2, getHeight() + principalPrefectualRoadWidth * 2)) { | |
146 | + continue; | |
147 | + } | |
148 | + switch (line.roadTypeCode) { | |
149 | + case NATIONAL_ROAD: | |
150 | + g.setStroke(new BasicStroke(nationalRoadWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); | |
151 | + g.setColor(Color.ORANGE); | |
152 | + g.draw(path); | |
153 | + break; | |
154 | + case PRINCIPAL_PREFECTUAL_ROAD: | |
155 | + g | |
156 | + .setStroke(new BasicStroke(principalPrefectualRoadWidth, BasicStroke.CAP_ROUND, | |
157 | + BasicStroke.JOIN_ROUND)); | |
158 | + g.setColor(Color.YELLOW); | |
159 | + g.draw(path); | |
160 | + break; | |
161 | + default: | |
162 | + } | |
163 | + } | |
132 | 164 | // 鉄道 |
133 | 165 | g.setColor(Color.WHITE); |
134 | 166 | Collection<LineData> stations = new ArrayList<LineData>(); |
135 | - int stationWidth = (int) Math.max(4, 30 * this.zoom); | |
136 | - int jrWidth = (int) Math.max(2, 10 * this.zoom); | |
167 | + int stationWidth = (int) Math.max(4, 20 * this.zoom); | |
168 | + int jrWidth = (int) Math.max(2, 8 * this.zoom); | |
137 | 169 | int privateRailwayWidth = (int) Math.max(2, 6 * this.zoom); |
138 | 170 | int lineWidthTwice = (int) Math.max(3, 3 * this.zoom); |
139 | 171 | float jrDash = (float) Math.max(6, 50 * this.zoom); |
140 | - for (LineData line : this.lines) { | |
172 | + for (LineData line : this.railways) { | |
141 | 173 | Path2D path = toPath(line); |
142 | 174 | if (path == null |
143 | 175 | || !path.intersects(-stationWidth, -stationWidth, getWidth() + stationWidth * 2, getHeight() |
@@ -178,6 +210,21 @@ | ||
178 | 210 | g.setStroke(new BasicStroke(stationWidth, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND)); |
179 | 211 | g.draw(path); |
180 | 212 | } |
213 | + // 高速道路 | |
214 | + int highwayWidth = (int) Math.max(3, 8 * this.zoom); | |
215 | + g.setStroke(new BasicStroke(highwayWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); | |
216 | + for (LineData line : this.roads) { | |
217 | + if (line.roadTypeCode == Const.RoadTypeCode.HIGHWAY) { | |
218 | + Path2D path = toPath(line); | |
219 | + if (path == null | |
220 | + || !path.intersects(-principalPrefectualRoadWidth, -principalPrefectualRoadWidth, getWidth() | |
221 | + + principalPrefectualRoadWidth * 2, getHeight() + principalPrefectualRoadWidth * 2)) { | |
222 | + continue; | |
223 | + } | |
224 | + g.setColor(Color.GREEN); | |
225 | + g.draw(path); | |
226 | + } | |
227 | + } | |
181 | 228 | // 駅名 |
182 | 229 | for (LineData line : stations) { |
183 | 230 | Path2D path = toPath(line); |