source: osm/utils/josm/plugins/ywms/src/org/openstreetmap/josm/plugins/ywms/ImageLoader.java@ 2283

Last change on this file since 2283 was 2283, checked in by frsantos, 18 years ago

Keep Yahoo logo in images

File size: 9.1 KB
Line 
1package org.openstreetmap.josm.plugins.ywms;
2
3import java.awt.*;
4import java.awt.image.*;
5import java.io.*;
6import java.net.MalformedURLException;
7import java.net.URL;
8import java.util.ArrayList;
9import java.util.List;
10import java.util.StringTokenizer;
11import java.util.regex.Matcher;
12import java.util.regex.Pattern;
13
14import javax.imageio.ImageIO;
15
16
17/**
18 * Loads a stellite image from Yahoo accordin to a WMS request.
19 * <p>
20 * The image is captured from firefox using one of its features: when the
21 * environment variable MOZ_FORCE_PAINT_AFTER_ONLOAD is set, it causes firefox
22 * to dump, as PPM files, the contents of all the pages loaded. The value of the
23 * variable is used to locate where the files are dumped.
24 * <p>
25 * As the image served by Yahoo is bigger than the one requested, it is cropped
26 * and resized to the desired size.
27 * <p>
28 * Currently, there is no way of knowing whether a zone has imagery or not. If
29 * not, the Yahoo message "We're sorry, the data you have requested is not
30 * available. Please zoom out to see more map information or refresh your
31 * browser to try again".
32 * <p>
33 * <br>
34 * <b>Implementation note:</b> <lu>
35 * <li>Some information is passed from Javascript to Java, so Firefox must be
36 * configured with the method "dump" to work. To allow this method in firefox,
37 * create or modify the option "browser.dom.window.dump.enabled=true" in
38 * "about:config"
39 * <p>
40 * <li>Also, as firefox must be started and killed once and again, it is
41 * recommended to create a profile with the option
42 * "browser.sessionstore.resume_from_crash" to false and set other profile to
43 * default, so no nag screens are shown. </lu>
44 *
45 * @author frsantos
46 *
47 */
48public class ImageLoader
49{
50 /** Firefox writes lines starting with this for each file loadded */
51 public static final String GECKO_DEBUG_LINE = "GECKO: PAINT FORCED AFTER ONLOAD:";
52
53 private URL yahooUrl;
54 double[] orig_bbox = null;
55 int width = -1;
56 int height = -1;
57 int final_width = -1;
58 int final_height = -1;
59 Image image;
60 List<String> firefoxFiles = new ArrayList<String>();
61
62 /** The regular expression used to locate the bounding boxes */
63 private static final Pattern BBOX_RE = Pattern.compile("bbox=([+-]?\\d+\\.\\d+),([+-]?\\d+\\.\\d+),([+-]?\\d+\\.\\d+),([+-]?\\d+\\.\\d+)", Pattern.CASE_INSENSITIVE);
64
65 /**
66 * Constructor.
67 *
68 * @param wmsUrl The WMS request
69 * @param pluginDir The directory of the plugin
70 * @throws ImageLoaderException When error loading the image
71 */
72 public ImageLoader(String wmsUrl) throws ImageLoaderException
73 {
74 System.out.println("YWMS::Requested WMS URL: " + wmsUrl);
75 try {
76 URL request = new URL("file:///page" + wmsUrl);
77 String query = request.getQuery().toLowerCase();
78 yahooUrl = new File(YWMSPlugin.getStaticPluginDir(), "ymap.html").toURI().toURL();
79 yahooUrl = new URL( yahooUrl.toExternalForm() + "?" + query);
80
81 // Parse query to find original bounding box and dimensions
82 StringTokenizer st = new StringTokenizer(query, "&");
83 while( st.hasMoreTokens() )
84 {
85 String param = st.nextToken();
86 if( param.startsWith("width=") )
87 width=Integer.parseInt(param.substring("width=".length()));
88 else if( param.startsWith("height=") )
89 height=Integer.parseInt(param.substring("height=".length()));
90 else if( param.startsWith("bbox=") )
91 {
92 orig_bbox = getBbox(param);
93 }
94 }
95
96 if( width == -1 || height == -1)
97 throw new ImageLoaderException("Can't find dimensions");
98
99 load();
100 }
101 catch (MalformedURLException e) {
102 throw new ImageLoaderException(e);
103 }
104 }
105
106 /**
107 * Does the hard work.
108 * <p>
109 * It spawns a Firefox process with an HTML page that loads Yahoo imagery
110 * using Yahoo's AJAX API. Firefox must be configured to allow the "dump"
111 * method for this to work.
112 * <p>
113 * The image is cropped and reescaled to meet requested dimensions.
114 * @throws ImageLoaderException When error loading the page
115 */
116 private void load() throws ImageLoaderException
117 {
118 Process browser = null;
119 try
120 {
121 browser = GeckoSupport.browse(yahooUrl.toString(), true);
122 // TODO: set focus in main window
123 File imageFilePpm = null;
124
125 // Parse output
126 BufferedReader in = new BufferedReader( new InputStreamReader( browser.getInputStream() ) );
127 String line = in.readLine();
128 while( line != null )
129 {
130 System.out.println("YWMS::" + line);
131 if( line.startsWith("new_width=") )
132 {
133 final_width = (int)Math.round( Double.parseDouble( line.substring(10)) );
134 }
135 else if( line.startsWith("new_height=") )
136 {
137 final_height = (int)Math.round( Double.parseDouble( line.substring(11)) );
138 }
139 else if( line.startsWith(GECKO_DEBUG_LINE))
140 {
141 // Find out the screenshot file
142 StringTokenizer st = new StringTokenizer(line);
143 // Skip header
144 for( int i = 0; i < 5; i++) st.nextToken();
145 String url = st.nextToken();
146 String file = st.nextToken();
147 firefoxFiles.add(file);
148
149 URL browserUrl;
150 try {
151 browserUrl = new URL(url);
152 if( browserUrl.sameFile(yahooUrl))
153 {
154 String status = st.nextToken();
155 if( !"(OK)".equals(status) )
156 throw new ImageLoaderException("Firefox couldn't load image");
157
158 imageFilePpm = new File(file);
159 break;
160 }
161 }
162 catch (MalformedURLException mue)
163 {
164 // Probably a mozilla "chrome://" URL. Do nothing
165 }
166 }
167 else if( line.startsWith("WYMS ERROR:") )
168 {
169 throw new ImageLoaderException("Error in JavaScript page:" + line);
170 }
171 line = in.readLine();
172 }
173
174 if( final_width == -1 && imageFilePpm == null && !firefoxFiles.isEmpty() )
175 {
176 throw new ImageLoaderException("Is there any other firefox window open with same profile?");
177 }
178 if( final_width == -1)
179 {
180 throw new ImageLoaderException("Couldn't find new dimension. Is browser.dom.window.dump.enabled set in Firefox config?");
181 }
182 if( imageFilePpm == null )
183 {
184 throw new ImageLoaderException("Couldn't find dumped image. Is it a modern Gecko browser (i.e., firefox 1.5)?");
185 }
186
187 PPM ppmImage = new PPM(imageFilePpm.getAbsolutePath());
188 image = ppmImage.getImage();
189 cleanImages();
190
191 resizeImage();
192 } catch (IOException e)
193 {
194 throw new ImageLoaderException(e);
195 }
196 finally
197 {
198 if( browser != null )
199 browser.destroy();
200 }
201 }
202
203 /**
204 * Transforms the Image into a BufferedImage
205 * @return The current image as a BufferedImage
206 */
207 public BufferedImage getBufferedImage()
208 {
209 if( image == null )
210 return null;
211
212 BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
213 Graphics g = bufferedImage.createGraphics();
214 g.setColor(Color.white);
215 g.fillRect(0, 0, width, height);
216 g.drawImage(image, 0, 0, null);
217 g.dispose();
218
219 return bufferedImage;
220 }
221
222 /**
223 * Resizes the image to meet the requested dimensions
224 */
225 private void resizeImage()
226 {
227 Toolkit tk = Toolkit.getDefaultToolkit();
228 //save("/tmp/image_orig.png");
229 image = tk.createImage (new FilteredImageSource (image.getSource(), new CropImageFilter(0, 0, final_width, final_height)));
230 //save("/tmp/image_crop.png");
231 image = tk.createImage (new FilteredImageSource (image.getSource(), new ReplicateScaleFilter(width, height)));
232 //save("/tmp/image_scale.png");
233 }
234
235 /**
236 * Parses a line of the form bbox=xmin,ymin,xmax,ymax and extracts the bounding box
237 *
238 * @param line The string to parse
239 * @return The bound box as a double array[4]
240 * @throws ImageLoaderException
241 */
242 private double[] getBbox(String line) throws ImageLoaderException
243 {
244 Matcher matcher = BBOX_RE.matcher(line);
245 if( !matcher.matches() )
246 {
247 throw new ImageLoaderException("Can't find bounding box");
248 }
249
250 double[] bbox = new double[4];
251 for( int i = 0; i < 4; i++)
252 {
253 bbox[i] = Double.parseDouble( matcher.group(i+1) );
254 }
255
256 return bbox;
257 }
258
259 /**
260 * Saves the current image as a PNG file
261 * @param fileName The name of the new file
262 * @throws IOException When error saving the file
263 */
264 public void save(String fileName)
265 {
266 try
267 {
268 FileOutputStream fileStream = new FileOutputStream(fileName);
269 ImageIO.write(getBufferedImage(), "png", fileStream);
270 fileStream.close();
271 }
272 catch(Exception e)
273 {
274 e.printStackTrace();
275 }
276 }
277
278 /**
279 * Delete all images created by firefox when they are not longer used
280 */
281 public void cleanImages()
282 {
283 for(String fileName : firefoxFiles)
284 {
285 try
286 {
287 File file = new File(fileName);
288 file.delete();
289 }
290 catch(Exception e) { }
291 }
292
293 }
294}
Note: See TracBrowser for help on using the repository browser.