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

Last change on this file since 13727 was 13545, checked in by pieren, 16 years ago

Add municipality boundary import from SVG data delivered by cadastre WMS.

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