OSMap 0.3.0をリリース
@@ -0,0 +1,272 @@ | ||
1 | +import java.awt.BorderLayout; | |
2 | +import java.awt.Graphics; | |
3 | +import java.awt.Graphics2D; | |
4 | +import java.awt.Image; | |
5 | +import java.awt.RenderingHints; | |
6 | +import java.awt.event.MouseAdapter; | |
7 | +import java.awt.event.MouseEvent; | |
8 | +import java.awt.event.MouseMotionListener; | |
9 | +import java.awt.event.MouseWheelEvent; | |
10 | +import java.awt.event.MouseWheelListener; | |
11 | +import java.awt.event.WindowAdapter; | |
12 | +import java.awt.event.WindowEvent; | |
13 | +import java.io.File; | |
14 | +import java.io.FileInputStream; | |
15 | +import java.io.FileNotFoundException; | |
16 | +import java.io.FileOutputStream; | |
17 | +import java.io.FileWriter; | |
18 | +import java.io.IOException; | |
19 | +import java.net.MalformedURLException; | |
20 | +import java.net.URL; | |
21 | +import java.util.Formatter; | |
22 | +import java.util.HashMap; | |
23 | +import java.util.Map; | |
24 | +import java.util.Properties; | |
25 | +import java.util.Timer; | |
26 | +import java.util.TimerTask; | |
27 | + | |
28 | +import javax.imageio.IIOException; | |
29 | +import javax.imageio.ImageIO; | |
30 | +import javax.swing.JFrame; | |
31 | +import javax.swing.JLabel; | |
32 | +import javax.swing.JPanel; | |
33 | +import javax.swing.WindowConstants; | |
34 | + | |
35 | +/** | |
36 | + * OpenStreetMapのビューア | |
37 | + */ | |
38 | +public class Main { | |
39 | + /** | |
40 | + * タイルの大きさ[px] | |
41 | + */ | |
42 | + public static int TILE_SIZE = 256; | |
43 | + /** | |
44 | + * キャッシュファイルを置くディレクトリ | |
45 | + */ | |
46 | + public static String CACHE_DIRECTORY = "cache"; | |
47 | + /** | |
48 | + * 設定ファイルの名前 | |
49 | + */ | |
50 | + public static String CONFIGURATION_FILE = "osmap.ini"; | |
51 | + /** | |
52 | + * 縮尺 | |
53 | + */ | |
54 | + static int zoom; | |
55 | + /** | |
56 | + * 列 | |
57 | + */ | |
58 | + static int col; | |
59 | + /** | |
60 | + * 行 | |
61 | + */ | |
62 | + static int row; | |
63 | + /** | |
64 | + * x座標 | |
65 | + */ | |
66 | + static int offsetX; | |
67 | + /** | |
68 | + * y座標 | |
69 | + */ | |
70 | + static int offsetY; | |
71 | + /** | |
72 | + * マウスドラッグが開始されたx座標 | |
73 | + */ | |
74 | + static int lastX = 0; | |
75 | + /** | |
76 | + * マウスドラッグが開始されたy座標 | |
77 | + */ | |
78 | + static int lastY = 0; | |
79 | + | |
80 | + /** | |
81 | + * メインメソッド | |
82 | + * @param args コマンドライン引数 | |
83 | + * @throws IOException 入出力例外 | |
84 | + */ | |
85 | + public static void main(final String[] args) throws IOException { | |
86 | + final Properties properties = new Properties(); | |
87 | + try { | |
88 | + properties.load(new FileInputStream(CONFIGURATION_FILE)); | |
89 | + } catch (final FileNotFoundException exception) { | |
90 | + // do nothing | |
91 | + } | |
92 | + zoom = Integer.parseInt(properties.getProperty("zoom", "5")); | |
93 | + col = Integer.parseInt(properties.getProperty("col", "27")); | |
94 | + row = Integer.parseInt(properties.getProperty("row", "12")); | |
95 | + offsetX = Integer.parseInt(properties.getProperty("offsetX", "20")); | |
96 | + offsetY = Integer.parseInt(properties.getProperty("offsetY", "150")); | |
97 | + final Map<String, Image> images = new HashMap<String, Image>(); | |
98 | + final JFrame frame = new JFrame("OSMap"); | |
99 | + final Timer timer = new Timer(); | |
100 | + frame.setLayout(new BorderLayout()); | |
101 | + final JLabel label = new JLabel(" "); | |
102 | + final JPanel panel = new JPanel() { | |
103 | + @Override | |
104 | + protected void paintComponent(final Graphics graphics) { | |
105 | + super.paintComponent(graphics); | |
106 | + final Graphics2D g = (Graphics2D) graphics; | |
107 | + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); | |
108 | + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); | |
109 | + for (int i = -1; TILE_SIZE * i + offsetY < this.getHeight(); i++) { | |
110 | + if (row + i < 0 || row + i >= 1 << zoom) { | |
111 | + continue; | |
112 | + } | |
113 | + for (int j = -1; TILE_SIZE * j + offsetX < this.getWidth(); j++) { | |
114 | + if (col + j < 0 || col + j >= 1 << zoom) { | |
115 | + continue; | |
116 | + } | |
117 | + g.drawImage(images.get(zoom + "_" + (col + j) + "_" + (row + i)), TILE_SIZE * j + offsetX, | |
118 | + TILE_SIZE * i + offsetY, this); | |
119 | + g.drawRect(TILE_SIZE * j + offsetX, TILE_SIZE * i + offsetY, TILE_SIZE, TILE_SIZE); | |
120 | + g.drawString(zoom + "_" + (col + j) + "_" + (row + i), TILE_SIZE * j + offsetX, TILE_SIZE * i | |
121 | + + offsetY + TILE_SIZE - 2); | |
122 | + } | |
123 | + } | |
124 | + label.setText(new Formatter().format("zoom = %d, col = %d, row = %d, offsetX = %d, offsetY = %d\n", | |
125 | + zoom, col, row, offsetX, offsetY).toString()); | |
126 | + } | |
127 | + }; | |
128 | + frame.add(panel, BorderLayout.CENTER); | |
129 | + frame.add(label, BorderLayout.SOUTH); | |
130 | + panel.addMouseWheelListener(new MouseWheelListener() { | |
131 | + @Override | |
132 | + public void mouseWheelMoved(final MouseWheelEvent event) { | |
133 | + final int clickedCol = (event.getX() - offsetX + TILE_SIZE) / TILE_SIZE - 1; | |
134 | + final int clickedRow = (event.getY() - offsetY + TILE_SIZE) / TILE_SIZE - 1; | |
135 | + final int diffX = (event.getX() - offsetX + TILE_SIZE) % TILE_SIZE; | |
136 | + final int diffY = (event.getY() - offsetY + TILE_SIZE) % TILE_SIZE; | |
137 | + if (event.getWheelRotation() < 0) { | |
138 | + if (zoom < 18) { | |
139 | + zoom++; | |
140 | + col = col * 2 + clickedCol + (diffX < TILE_SIZE / 2 ? 0 : 1); | |
141 | + row = row * 2 + clickedRow + (diffY < TILE_SIZE / 2 ? 0 : 1); | |
142 | + offsetX = offsetX - diffX % TILE_SIZE + (diffX < TILE_SIZE / 2 ? 0 : TILE_SIZE); | |
143 | + offsetY = offsetY - diffY % TILE_SIZE + (diffY < TILE_SIZE / 2 ? 0 : TILE_SIZE); | |
144 | + } | |
145 | + } else { | |
146 | + if (zoom > 0) { | |
147 | + zoom--; | |
148 | + offsetX = offsetX + diffX / 2 - (col % 2 == 0 ? 0 : TILE_SIZE / 2) + clickedCol * TILE_SIZE / 2; | |
149 | + offsetY = offsetY + diffY / 2 - (row % 2 == 0 ? 0 : TILE_SIZE / 2) + clickedRow * TILE_SIZE / 2; | |
150 | + col /= 2; | |
151 | + row /= 2; | |
152 | + } | |
153 | + } | |
154 | + panel.repaint(); | |
155 | + } | |
156 | + }); | |
157 | + panel.addMouseListener(new MouseAdapter() { | |
158 | + @Override | |
159 | + public void mouseClicked(final MouseEvent event) { | |
160 | + if (event.getButton() == MouseEvent.BUTTON1) { | |
161 | + } else if (event.getButton() == MouseEvent.BUTTON3) { | |
162 | + } | |
163 | + panel.repaint(); | |
164 | + } | |
165 | + | |
166 | + @Override | |
167 | + public void mousePressed(final MouseEvent event) { | |
168 | + lastX = event.getX(); | |
169 | + lastY = event.getY(); | |
170 | + } | |
171 | + }); | |
172 | + panel.addMouseMotionListener(new MouseMotionListener() { | |
173 | + @Override | |
174 | + public void mouseMoved(final MouseEvent event) { | |
175 | + } | |
176 | + | |
177 | + @Override | |
178 | + public void mouseDragged(final MouseEvent event) { | |
179 | + offsetX += event.getX() - lastX; | |
180 | + offsetY += event.getY() - lastY; | |
181 | + lastX = event.getX(); | |
182 | + lastY = event.getY(); | |
183 | + panel.repaint(); | |
184 | + } | |
185 | + }); | |
186 | + frame.setExtendedState(Integer.parseInt(properties.getProperty("frame.extendedState", "0"))); | |
187 | + frame.setSize(Integer.parseInt(properties.getProperty("frame.width", "800")), | |
188 | + Integer.parseInt(properties.getProperty("frame.height", "600"))); | |
189 | + frame.addWindowListener(new WindowAdapter() { | |
190 | + @Override | |
191 | + public void windowClosed(final WindowEvent event) { | |
192 | + timer.cancel(); | |
193 | + try { | |
194 | + properties.put("zoom", Integer.toString(zoom)); | |
195 | + properties.put("col", Integer.toString(col)); | |
196 | + properties.put("row", Integer.toString(row)); | |
197 | + properties.put("offsetX", Integer.toString(offsetX)); | |
198 | + properties.put("offsetY", Integer.toString(offsetY)); | |
199 | + properties.put("frame.extendedState", Integer.toString(frame.getExtendedState())); | |
200 | + if (frame.getExtendedState() == 0) { | |
201 | + properties.put("frame.width", Integer.toString(frame.getWidth())); | |
202 | + properties.put("frame.height", Integer.toString(frame.getHeight())); | |
203 | + } | |
204 | + properties.store(new FileWriter(CONFIGURATION_FILE), "OSMap configuration"); | |
205 | + } catch (final IOException exception) { | |
206 | + exception.printStackTrace(); | |
207 | + } | |
208 | + } | |
209 | + }); | |
210 | + frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); | |
211 | + JFrame.setDefaultLookAndFeelDecorated(true); | |
212 | + frame.setLocationByPlatform(true); | |
213 | + frame.setVisible(true); | |
214 | + timer.scheduleAtFixedRate(new TimerTask() { | |
215 | + @Override | |
216 | + public void run() { | |
217 | + if (offsetX < 0) { | |
218 | + offsetX += TILE_SIZE; | |
219 | + col++; | |
220 | + } else if (offsetX >= TILE_SIZE) { | |
221 | + offsetX -= TILE_SIZE; | |
222 | + col--; | |
223 | + } | |
224 | + if (offsetY < 0) { | |
225 | + offsetY += TILE_SIZE; | |
226 | + row++; | |
227 | + } else if (offsetY >= TILE_SIZE) { | |
228 | + offsetY -= TILE_SIZE; | |
229 | + row--; | |
230 | + } | |
231 | + final int zoom = Main.zoom; | |
232 | + final int col = Main.col; | |
233 | + final int row = Main.row; | |
234 | + final File cacheDirectory = new File(CACHE_DIRECTORY); | |
235 | + if (!cacheDirectory.isDirectory()) { | |
236 | + cacheDirectory.mkdir(); | |
237 | + } | |
238 | + for (int i = -1; TILE_SIZE * i + offsetY < panel.getHeight(); i++) { | |
239 | + if (row + i < 0 || row + i >= 1 << zoom) { | |
240 | + continue; | |
241 | + } | |
242 | + for (int j = -1; TILE_SIZE * j + offsetX < panel.getWidth(); j++) { | |
243 | + if (col + j < 0 || col + j >= 1 << zoom) { | |
244 | + continue; | |
245 | + } | |
246 | + if (!images.containsKey(zoom + "_" + (col + j) + "_" + (row + i))) { | |
247 | + final File file = new File(new Formatter().format("%s%s%d_%d_%d.png", CACHE_DIRECTORY, | |
248 | + File.separator, zoom, col + j, row + i).toString()); | |
249 | + try { | |
250 | + if (!file.isFile()) { | |
251 | + WebUtilities.copy( | |
252 | + new URL(new Formatter().format( | |
253 | + "http://tile.openstreetmap.org/%d/%d/%d.png", zoom, col + j, | |
254 | + row + i).toString()).openStream(), new FileOutputStream(file)); | |
255 | + } | |
256 | + images.put(zoom + "_" + (col + j) + "_" + (row + i), ImageIO.read(file)); | |
257 | + panel.repaint(); | |
258 | + } catch (final IIOException exception) { | |
259 | + file.delete(); | |
260 | + } catch (final MalformedURLException exception) { | |
261 | + exception.printStackTrace(); | |
262 | + } catch (final IOException exception) { | |
263 | + exception.printStackTrace(); | |
264 | + } | |
265 | + } | |
266 | + } | |
267 | + } | |
268 | + panel.repaint(); | |
269 | + } | |
270 | + }, 200, 200); | |
271 | + } | |
272 | +} |
@@ -0,0 +1,23 @@ | ||
1 | +import java.io.IOException; | |
2 | +import java.io.InputStream; | |
3 | +import java.io.OutputStream; | |
4 | + | |
5 | +/** | |
6 | + * ネットワーク関係のユーティリティクラス | |
7 | + */ | |
8 | +public class WebUtilities { | |
9 | + /** | |
10 | + * ストリームを使ってファイルをコピーします。 | |
11 | + * @param in 入力ストリーム | |
12 | + * @param out 出力ストリーム | |
13 | + * @throws IOException 入出力例外 | |
14 | + */ | |
15 | + public static void copy(final InputStream in, final OutputStream out) throws IOException { | |
16 | + final byte buf[] = new byte[1024]; | |
17 | + int size; | |
18 | + while ((size = in.read(buf)) != -1) { | |
19 | + out.write(buf, 0, size); | |
20 | + } | |
21 | + out.close(); | |
22 | + } | |
23 | +} |