source: osm/applications/editors/josm/plugins/cadastre-fr/src/cadastre_fr/WMSLayer.java@ 13382

Last change on this file since 13382 was 13382, checked in by pieren, 17 years ago

First commit of the french land registry WMS plugin for JOSM.

File size: 18.1 KB
Line 
1package cadastre_fr;
2
3import static org.openstreetmap.josm.tools.I18n.tr;
4
5import java.awt.Component;
6import java.awt.Graphics;
7import java.awt.Graphics2D;
8import java.awt.Toolkit;
9import java.awt.event.ActionEvent;
10import java.awt.image.BufferedImage;
11import java.io.File;
12import java.io.FileInputStream;
13import java.io.FileOutputStream;
14import java.io.IOException;
15import java.io.ObjectInputStream;
16import java.io.ObjectOutputStream;
17import java.util.ArrayList;
18
19import javax.swing.AbstractAction;
20import javax.swing.Icon;
21import javax.swing.ImageIcon;
22import javax.swing.JFileChooser;
23import javax.swing.JMenuItem;
24import javax.swing.JOptionPane;
25import javax.swing.JSeparator;
26import javax.swing.filechooser.FileFilter;
27
28import org.openstreetmap.josm.Main;
29import org.openstreetmap.josm.actions.ExtensionFileFilter;
30import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
31import org.openstreetmap.josm.data.projection.Lambert;
32import org.openstreetmap.josm.data.Bounds;
33import org.openstreetmap.josm.gui.MapView;
34import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
35import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
36import org.openstreetmap.josm.gui.layer.Layer;
37import org.openstreetmap.josm.tools.ImageProvider;
38import org.openstreetmap.josm.data.coor.EastNorth;
39
40/**
41 * This is a layer that grabs the current screen from the french cadastre WMS
42 * server. The data fetched this way is tiled and managed to the disc to reduce
43 * server load.
44 */
45public class WMSLayer extends Layer {
46
47 public class EastNorthBound {
48 public EastNorth min, max;
49 public EastNorthBound(EastNorth min, EastNorth max) {
50 this.min = min;
51 this.max = max;
52 }
53 @Override public String toString() {
54 return "EastNorthBound[" + min.east() + "," + min.north() + "," + max.east() + "," + max.north() + "]";
55 }
56 }
57
58 Component[] component = null;
59
60 public int lambertZone = -1;
61
62 protected static final Icon icon = new ImageIcon(Toolkit.getDefaultToolkit().createImage(
63 CadastrePlugin.class.getResource("/images/cadastre_small.png")));
64
65 protected ArrayList<GeorefImage> images = new ArrayList<GeorefImage>();
66
67 protected final int serializeFormatVersion = 1;
68
69 private ArrayList<EastNorthBound> dividedBbox = new ArrayList<EastNorthBound>();
70
71 private CacheControl cacheControl = null;
72
73 private String location = "";
74
75 private String codeCommune = "";
76
77 private boolean isRaster = false;
78
79 private EastNorth rasterMin;
80
81 private EastNorth rasterCenter;
82
83 private double rasterRatio;
84
85 double cRasterMaxSizeX = 12286;
86 double cRasterMaxSizeY = 8730;
87
88 public WMSLayer() {
89 this(tr("Blank Layer"), "", -1);
90 }
91
92 public WMSLayer(String location, String codeCommune, int lambertZone) {
93 super(buildName(location, codeCommune));
94 this.location = location;
95 this.codeCommune = codeCommune;
96 this.lambertZone = Lambert.layoutZone;
97 // enable auto-sourcing option
98 CadastrePlugin.pluginUsed = true;
99 }
100
101 private static String buildName(String location, String codeCommune) {
102 String ret = new String(location.toUpperCase());
103 if (codeCommune != null && !codeCommune.equals(""))
104 ret += "(" + codeCommune + ")";
105 return ret;
106 }
107
108 private String rebuildName() {
109 return buildName(this.location.toUpperCase(), this.codeCommune);
110 }
111
112 public void grab(CadastreGrabber grabber, Bounds b) throws IOException {
113 divideBbox(b, Integer.parseInt(Main.pref.get("cadastrewms.scale", Scale.X1.toString())));
114
115 for (EastNorthBound n : dividedBbox) {
116 GeorefImage newImage;
117 try {
118 newImage = grabber.grab(this, n.min, n.max);
119 } catch (IOException e) {
120 System.out.println("Download action cancelled by user or server did not respond");
121 break;
122 }
123 if (grabber.getWmsInterface().downloadCancelled) {
124 System.out.println("Download action cancelled by user");
125 break;
126 }
127 if (CadastrePlugin.backgroundTransparent) {
128 for (GeorefImage img : images) {
129 if (img.overlap(newImage))
130 // mask overlapping zone in already grabbed image
131 img.withdraw(newImage);
132 else
133 // mask overlapping zone in new image only when new
134 // image covers completely the existing image
135 newImage.withdraw(img);
136 }
137 }
138 images.add(newImage);
139 saveToCache(newImage);
140 Main.map.mapView.repaint();
141 /*
142 try { if (dividedBbox.size() > 1) Thread.sleep(1000);
143 } catch (InterruptedException e) {};*/
144 }
145 }
146
147 /**
148 *
149 * @param b the original bbox, usually the current bbox on screen
150 * @param factor 1 = source bbox 1:1
151 * 2 = source bbox divided by 2x2 smaller boxes
152 * 3 = source bbox divided by 3x3 smaller boxes
153 * 4 = hard coded size of boxes (100 meters) rounded allowing
154 * grabbing of next contiguous zone
155 */
156 private void divideBbox(Bounds b, int factor) {
157 EastNorth lambertMin = Main.proj.latlon2eastNorth(b.min);
158 EastNorth lambertMax = Main.proj.latlon2eastNorth(b.max);
159 double minEast = lambertMin.east();
160 double minNorth = lambertMin.north();
161 double dEast = (lambertMax.east() - minEast) / factor;
162 double dNorth = (lambertMax.north() - minNorth) / factor;
163 dividedBbox.clear();
164 if (factor < 4) {
165 for (int xEast = 0; xEast < factor; xEast++)
166 for (int xNorth = 0; xNorth < factor; xNorth++) {
167 dividedBbox.add(new EastNorthBound(new EastNorth(minEast + xEast * dEast, minNorth + xNorth * dNorth),
168 new EastNorth(minEast + (xEast + 1) * dEast, minNorth + (xNorth + 1) * dNorth)));
169 }
170 } else {
171 // divide to fixed size squares
172 int cSquare = 100; // expressed in meters in projection Lambert
173 minEast = minEast - minEast % cSquare;
174 minNorth = minNorth - minNorth % cSquare;
175 for (int xEast = (int)minEast; xEast < lambertMax.east(); xEast+=cSquare)
176 for (int xNorth = (int)minNorth; xNorth < lambertMax.north(); xNorth+=cSquare) {
177 dividedBbox.add(new EastNorthBound(new EastNorth(xEast, xNorth),
178 new EastNorth(xEast + cSquare, xNorth + cSquare)));
179 }
180 }
181 }
182
183 @Override
184 public Icon getIcon() {
185 return icon;
186 }
187
188 @Override
189 public String getToolTipText() {
190 return tr("WMS layer ({0}), {1} tile(s) loaded", name, images.size());
191 }
192
193 @Override
194 public boolean isMergable(Layer other) {
195 return false;
196 }
197
198 @Override
199 public void mergeFrom(Layer from) {
200 }
201
202 @Override
203 public void paint(Graphics g, final MapView mv) {
204 for (GeorefImage img : images)
205 img.paint((Graphics2D) g, mv, CadastrePlugin.backgroundTransparent,
206 CadastrePlugin.transparency, CadastrePlugin.drawBoundaries);
207 }
208
209 @Override
210 public void visitBoundingBox(BoundingXYVisitor v) {
211 for (GeorefImage img : images) {
212 v.visit(img.min);
213 v.visit(img.max);
214 }
215 }
216
217 @Override
218 public Object getInfoComponent() {
219 return getToolTipText();
220 }
221
222 @Override
223 public Component[] getMenuEntries() {
224 /*
225 return new Component[] { new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
226 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)), new JMenuItem(new LoadWmsAction()),
227 new JMenuItem(new SaveWmsAction()), new JSeparator(),
228 new JMenuItem(new LayerListPopup.InfoAction(this)) };
229 */
230 component = new Component[] { new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
231 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)), new JMenuItem(new LoadWmsAction()),
232 new JMenuItem(new SaveWmsAction()), new JSeparator(),
233 new JMenuItem(new LayerListPopup.InfoAction(this)) };
234 return component;
235 }
236
237 public GeorefImage findImage(EastNorth eastNorth) {
238 // Iterate in reverse, so we return the image which is painted last.
239 // (i.e. the topmost one)
240 for (int i = images.size() - 1; i >= 0; i--) {
241 if (images.get(i).contains(eastNorth)) {
242 return images.get(i);
243 }
244 }
245 return null;
246 }
247
248 public boolean isOverlapping(Bounds bounds) {
249 GeorefImage georefImage =
250 new GeorefImage(new BufferedImage(1,1,BufferedImage.TYPE_INT_RGB ), // not really important
251 Main.proj.latlon2eastNorth(bounds.min),
252 Main.proj.latlon2eastNorth(bounds.max));
253 for (GeorefImage img : images) {
254 if (img.overlap(georefImage))
255 return true;
256 }
257 return false;
258 }
259
260 public void saveToCache(GeorefImage image) {
261 if (CacheControl.cacheEnabled) {
262 getCacheControl().saveCache(image);
263 }
264 }
265
266 public void saveNewCache() {
267 if (CacheControl.cacheEnabled) {
268 getCacheControl().deleteCacheFile();
269 for (GeorefImage image : images)
270 getCacheControl().saveCache(image);
271 }
272 }
273
274 public CacheControl getCacheControl() {
275 if (cacheControl == null)
276 cacheControl = new CacheControl(this);
277 return cacheControl;
278 }
279
280 public class SaveWmsAction extends AbstractAction {
281 private static final long serialVersionUID = 1L;
282
283 public SaveWmsAction() {
284 super(tr("Save WMS layer to file"), ImageProvider.get("save"));
285 }
286
287 public void actionPerformed(ActionEvent ev) {
288 File f = openFileDialog(false);
289 try {
290 FileOutputStream fos = new FileOutputStream(f);
291 ObjectOutputStream oos = new ObjectOutputStream(fos);
292 oos.writeInt(serializeFormatVersion);
293 oos.writeInt(lambertZone);
294 oos.writeInt(images.size());
295 for (GeorefImage img : images) {
296 oos.writeObject(img);
297 }
298 oos.close();
299 fos.close();
300 } catch (Exception ex) {
301 ex.printStackTrace(System.out);
302 }
303 }
304 }
305
306 public class LoadWmsAction extends AbstractAction {
307 private static final long serialVersionUID = 1L;
308
309 public LoadWmsAction() {
310 super(tr("Load WMS layer from file"), ImageProvider.get("load"));
311 }
312
313 public void actionPerformed(ActionEvent ev) {
314 File f = openFileDialog(true);
315 if (f == null)
316 return;
317 try {
318 FileInputStream fis = new FileInputStream(f);
319 ObjectInputStream ois = new ObjectInputStream(fis);
320 int sfv = ois.readInt();
321 if (sfv != serializeFormatVersion) {
322 JOptionPane.showMessageDialog(Main.parent, tr(
323 "Unsupported WMS file version; found {0}, expected {1}", sfv, serializeFormatVersion),
324 tr("File Format Error"), JOptionPane.ERROR_MESSAGE);
325 return;
326 }
327 lambertZone = ois.readInt();
328 int numImg = ois.readInt();
329 for (int i = 0; i < numImg; i++) {
330 GeorefImage img = (GeorefImage) ois.readObject();
331 images.add(img);
332 }
333 ois.close();
334 fis.close();
335 } catch (Exception ex) {
336 // FIXME be more specific
337 ex.printStackTrace(System.out);
338 JOptionPane.showMessageDialog(Main.parent, tr("Error loading file"), tr("Error"),
339 JOptionPane.ERROR_MESSAGE);
340 return;
341 }
342 }
343 }
344
345 protected static JFileChooser createAndOpenFileChooser(boolean open, boolean multiple) {
346 String curDir = Main.pref.get("lastDirectory");
347 if (curDir.equals(""))
348 curDir = ".";
349 JFileChooser fc = new JFileChooser(new File(curDir));
350 fc.setMultiSelectionEnabled(multiple);
351 for (int i = 0; i < ExtensionFileFilter.filters.length; ++i)
352 fc.addChoosableFileFilter(ExtensionFileFilter.filters[i]);
353 fc.setAcceptAllFileFilterUsed(true);
354
355 int answer = open ? fc.showOpenDialog(Main.parent) : fc.showSaveDialog(Main.parent);
356 if (answer != JFileChooser.APPROVE_OPTION)
357 return null;
358
359 if (!fc.getCurrentDirectory().getAbsolutePath().equals(curDir))
360 Main.pref.put("lastDirectory", fc.getCurrentDirectory().getAbsolutePath());
361
362 if (!open) {
363 File file = fc.getSelectedFile();
364 if (file == null
365 || (file.exists() && JOptionPane.YES_OPTION != JOptionPane.showConfirmDialog(Main.parent,
366 tr("File exists. Overwrite?"), tr("Overwrite"), JOptionPane.YES_NO_OPTION)))
367 return null;
368 }
369
370 return fc;
371 }
372
373 public static File openFileDialog(boolean open) {
374 JFileChooser fc = createAndOpenFileChooser(open, false);
375 if (fc == null)
376 return null;
377
378 File file = fc.getSelectedFile();
379
380 String fn = file.getPath();
381 if (fn.indexOf('.') == -1) {
382 FileFilter ff = fc.getFileFilter();
383 if (ff instanceof ExtensionFileFilter)
384 fn = "." + ((ExtensionFileFilter) ff).defaultExtension;
385 else
386 fn += ".osm";
387 file = new File(fn);
388 }
389 return file;
390 }
391
392 /**
393 * Convert the eastNorth input coordinates to raster coordinates.
394 * The original raster size is [0,0,12286,8730] where 0,0 is the upper left corner and
395 * 12286,8730 is the approx. raster max size.
396 * @return the raster coordinates for the wms server request URL (minX,minY,maxX,maxY)
397 */
398 public String eastNorth2raster(EastNorth min, EastNorth max) {
399 double minX = (min.east() - rasterMin.east()) / rasterRatio;
400 double minY = (min.north() - rasterMin.north()) / rasterRatio;
401 double maxX = (max.east() - rasterMin.east()) / rasterRatio;
402 double maxY = (max.north() - rasterMin.north()) / rasterRatio;
403 return minX+","+minY+","+maxX+","+maxY;
404 }
405
406
407 public String getLocation() {
408 return location;
409 }
410
411 public void setLocation(String location) {
412 this.location = location;
413 this.name = rebuildName();
414 repaintLayerListDialog();
415 }
416
417 public String getCodeCommune() {
418 return codeCommune;
419 }
420
421 public void setCodeCommune(String codeCommune) {
422 this.codeCommune = codeCommune;
423 this.name = rebuildName();
424 repaintLayerListDialog();
425 }
426
427 public boolean isRaster() {
428 return isRaster;
429 }
430
431 public void setRaster(boolean isRaster) {
432 this.isRaster = isRaster;
433 }
434
435 /**
436 * Set the eastNorth position in rasterMin which is the 0,0 coordinate (bottom left corner).
437 * The bounds width is the raster width and height is calculate on a fixed image ratio.
438 * @param bounds
439 */
440 public void setRasterBounds(Bounds bounds) {
441 rasterMin = new EastNorth(Main.proj.latlon2eastNorth(bounds.min).east(), Main.proj.latlon2eastNorth(bounds.min).north());
442 EastNorth rasterMax = new EastNorth(Main.proj.latlon2eastNorth(bounds.max).east(), Main.proj.latlon2eastNorth(bounds.max).north());
443 // now, resize on same proportion as wms server raster images (bounds center)
444 double rasterHalfHeight = (rasterMax.east() - rasterMin.east())/cRasterMaxSizeX*cRasterMaxSizeY/2;
445 double rasterMid = rasterMin.north() + (rasterMax.north()-rasterMin.north())/2;
446 rasterMin.setLocation(rasterMin.east(), rasterMid - rasterHalfHeight);
447 rasterMax.setLocation(rasterMax.east(), rasterMid + rasterHalfHeight);
448 rasterCenter = new EastNorth(rasterMin.east()+(rasterMax.east()-rasterMin.east())/2,
449 rasterMin.north()+(rasterMax.north()-rasterMin.north())/2);
450 rasterRatio = (rasterMax.east() - rasterMin.east()) / cRasterMaxSizeX;
451 }
452
453 public EastNorth getRasterMin() {
454 return rasterMin;
455 }
456
457 public void setRasterMin(EastNorth rasterMin) {
458 this.rasterMin = rasterMin;
459 }
460
461 public void displace(double dx, double dy) {
462 this.rasterMin = new EastNorth(rasterMin.east() + dx, rasterMin.north() + dy);
463 this.rasterCenter = new EastNorth(rasterCenter.east() + dx, rasterCenter.north() + dy);
464 for (GeorefImage img : images)
465 img.displace(dx, dy);
466 }
467
468 public void resize(double proportion) {
469 this.rasterMin = rasterMin.interpolate(rasterCenter, proportion);
470 for (GeorefImage img : images)
471 img.resize(rasterCenter, proportion);
472 }
473
474 public void rotate(double angle) {
475 this.rasterMin = rasterMin.rotate(rasterCenter, angle);
476 for (GeorefImage img : images)
477 img.rotate(rasterCenter, angle);
478 }
479
480 /**
481 * Repaint the LayerList dialog.
482 * This is the only way I found to refresh the layer name in the layer list when it changes
483 * later (after the construction).
484 */
485 private void repaintLayerListDialog() {
486 if (Main.map != null) {
487 for (Component c : Main.map.toggleDialogs.getComponents()) {
488 if (c instanceof LayerListDialog) {
489 c.repaint();
490 }
491 }
492 }
493 }
494
495 public double getRasterRatio() {
496 return rasterRatio;
497 }
498
499 public void setRasterRatio(double rasterRatio) {
500 this.rasterRatio = rasterRatio;
501 }
502
503 public EastNorth getRasterCenter() {
504 return rasterCenter;
505 }
506
507 public void setRasterCenter(EastNorth rasterCenter) {
508 this.rasterCenter = rasterCenter;
509 }
510
511}
Note: See TracBrowser for help on using the repository browser.