source: josm/trunk/src/org/openstreetmap/josm/gui/layer/WMSLayer.java@ 3749

Last change on this file since 3749 was 3749, checked in by jttt, 13 years ago

Fix loading of wms tiles (when mapview is moved and then moved back then request can stay in processingList and be skipped for ever)

  • Property svn:eol-style set to native
File size: 28.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.layer;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Component;
7import java.awt.Graphics;
8import java.awt.Graphics2D;
9import java.awt.event.ActionEvent;
10import java.io.File;
11import java.io.FileInputStream;
12import java.io.FileOutputStream;
13import java.io.ObjectInputStream;
14import java.io.ObjectOutputStream;
15import java.util.ArrayList;
16import java.util.Collections;
17import java.util.Iterator;
18import java.util.List;
19import java.util.concurrent.locks.Condition;
20import java.util.concurrent.locks.Lock;
21import java.util.concurrent.locks.ReentrantLock;
22
23import javax.swing.AbstractAction;
24import javax.swing.Action;
25import javax.swing.JCheckBoxMenuItem;
26import javax.swing.JFileChooser;
27import javax.swing.JOptionPane;
28
29import org.openstreetmap.josm.Main;
30import org.openstreetmap.josm.actions.DiskAccessAction;
31import org.openstreetmap.josm.actions.SaveActionBase;
32import org.openstreetmap.josm.data.Bounds;
33import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
34import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
35import org.openstreetmap.josm.data.ProjectionBounds;
36import org.openstreetmap.josm.data.coor.EastNorth;
37import org.openstreetmap.josm.data.imagery.GeorefImage;
38import org.openstreetmap.josm.data.imagery.GeorefImage.State;
39import org.openstreetmap.josm.data.imagery.ImageryInfo;
40import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
41import org.openstreetmap.josm.data.imagery.ImageryLayerInfo;
42import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
43import org.openstreetmap.josm.data.preferences.BooleanProperty;
44import org.openstreetmap.josm.data.preferences.IntegerProperty;
45import org.openstreetmap.josm.gui.MapView;
46import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
47import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
48import org.openstreetmap.josm.io.CacheFiles;
49import org.openstreetmap.josm.io.imagery.Grabber;
50import org.openstreetmap.josm.io.imagery.HTMLGrabber;
51import org.openstreetmap.josm.io.imagery.WMSGrabber;
52import org.openstreetmap.josm.io.imagery.WMSRequest;
53import org.openstreetmap.josm.tools.ImageProvider;
54
55/**
56 * This is a layer that grabs the current screen from an WMS server. The data
57 * fetched this way is tiled and managed to the disc to reduce server load.
58 */
59public class WMSLayer extends ImageryLayer implements PreferenceChangedListener {
60 public static final BooleanProperty PROP_ALPHA_CHANNEL = new BooleanProperty("imagery.wms.alpha_channel", true);
61 public static final IntegerProperty PROP_SIMULTANEOUS_CONNECTIONS = new IntegerProperty("imagery.wms.simultaneousConnections", 3);
62 public static final BooleanProperty PROP_OVERLAP = new BooleanProperty("imagery.wms.overlap", false);
63 public static final IntegerProperty PROP_OVERLAP_EAST = new IntegerProperty("imagery.wms.overlapEast", 14);
64 public static final IntegerProperty PROP_OVERLAP_NORTH = new IntegerProperty("imagery.wms.overlapNorth", 4);
65
66 public int messageNum = 5; //limit for messages per layer
67 protected String resolution;
68 protected int imageSize = 500;
69 protected int dax = 10;
70 protected int day = 10;
71 protected int daStep = 5;
72 protected int minZoom = 3;
73
74 protected GeorefImage[][] images;
75 protected final int serializeFormatVersion = 5;
76 protected boolean autoDownloadEnabled = true;
77 protected boolean settingsChanged;
78 protected ImageryInfo info;
79 protected final MapView mv;
80
81 // Image index boundary for current view
82 private volatile int bminx;
83 private volatile int bminy;
84 private volatile int bmaxx;
85 private volatile int bmaxy;
86 private volatile int leftEdge;
87 private volatile int bottomEdge;
88
89 // Request queue
90 private final List<WMSRequest> requestQueue = new ArrayList<WMSRequest>();
91 private final List<WMSRequest> finishedRequests = new ArrayList<WMSRequest>();
92 /**
93 * List of request currently being processed by download threads
94 */
95 private final List<WMSRequest> processingRequests = new ArrayList<WMSRequest>();
96 private final Lock requestQueueLock = new ReentrantLock();
97 private final Condition queueEmpty = requestQueueLock.newCondition();
98 private final List<Grabber> grabbers = new ArrayList<Grabber>();
99 private final List<Thread> grabberThreads = new ArrayList<Thread>();
100 private int threadCount;
101 private int workingThreadCount;
102 private boolean canceled;
103
104
105 /** set to true if this layer uses an invalid base url */
106 private boolean usesInvalidUrl = false;
107 /** set to true if the user confirmed to use an potentially invalid WMS base url */
108 private boolean isInvalidUrlConfirmed = false;
109
110 public WMSLayer() {
111 this(new ImageryInfo(tr("Blank Layer")));
112 }
113
114 public WMSLayer(ImageryInfo info) {
115 super(info);
116 mv = Main.map.mapView;
117 setBackgroundLayer(true); /* set global background variable */
118 initializeImages();
119 this.info = new ImageryInfo(info);
120 if(this.info.getPixelPerDegree() == 0.0) {
121 this.info.setPixelPerDegree(getPPD());
122 }
123 resolution = mv.getDist100PixelText();
124
125 if(info.getURL() != null) {
126 WMSGrabber.getProjection(info.getURL(), true);
127 startGrabberThreads();
128 if(info.getImageryType() == ImageryType.WMS && !ImageryInfo.isUrlWithPatterns(info.getURL())) {
129 if (!(info.getURL().endsWith("&") || info.getURL().endsWith("?"))) {
130 if (!confirmMalformedUrl(info.getURL())) {
131 System.out.println(tr("Warning: WMS layer deactivated because of malformed base url ''{0}''", info.getURL()));
132 usesInvalidUrl = true;
133 setName(getName() + tr("(deactivated)"));
134 return;
135 } else {
136 isInvalidUrlConfirmed = true;
137 }
138 }
139 }
140 }
141
142 Main.pref.addPreferenceChangeListener(this);
143 }
144
145 public void doSetName(String name) {
146 setName(name);
147 info.setName(name);
148 }
149
150 public boolean hasAutoDownload(){
151 return autoDownloadEnabled;
152 }
153
154 @Override
155 public void destroy() {
156 cancelGrabberThreads(false);
157 Main.pref.removePreferenceChangeListener(this);
158 }
159
160 public void initializeImages() {
161 GeorefImage[][] old = images;
162 images = new GeorefImage[dax][day];
163 if (old != null) {
164 for (int i=0; i<old.length; i++) {
165 for (int k=0; k<old[i].length; k++) {
166 GeorefImage o = old[i][k];
167 images[modulo(o.getXIndex(),dax)][modulo(o.getYIndex(),day)] = old[i][k];
168 }
169 }
170 }
171 for(int x = 0; x<dax; ++x) {
172 for(int y = 0; y<day; ++y) {
173 if (images[x][y] == null) {
174 images[x][y]= new GeorefImage(this);
175 }
176 }
177 }
178 }
179
180 @Override public ImageryInfo getInfo() {
181 return info;
182 }
183
184 @Override public String getToolTipText() {
185 if(autoDownloadEnabled)
186 return tr("WMS layer ({0}), automatically downloading in zoom {1}", getName(), resolution);
187 else
188 return tr("WMS layer ({0}), downloading in zoom {1}", getName(), resolution);
189 }
190
191 private int modulo (int a, int b) {
192 return a % b >= 0 ? a%b : a%b+b;
193 }
194
195 private boolean zoomIsTooBig() {
196 //don't download when it's too outzoomed
197 return info.getPixelPerDegree() / getPPD() > minZoom;
198 }
199
200 @Override public void paint(Graphics2D g, final MapView mv, Bounds b) {
201 if(info.getURL() == null || (usesInvalidUrl && !isInvalidUrlConfirmed)) return;
202
203 settingsChanged = false;
204
205 ProjectionBounds bounds = mv.getProjectionBounds();
206 bminx= getImageXIndex(bounds.min.east());
207 bminy= getImageYIndex(bounds.min.north());
208 bmaxx= getImageXIndex(bounds.max.east());
209 bmaxy= getImageYIndex(bounds.max.north());
210
211 leftEdge = (int)(bounds.min.east() * getPPD());
212 bottomEdge = (int)(bounds.min.north() * getPPD());
213
214 if (zoomIsTooBig()) {
215 for(int x = bminx; x<=bmaxx; ++x) {
216 for(int y = bminy; y<=bmaxy; ++y) {
217 images[modulo(x,dax)][modulo(y,day)].paint(g, mv, x, y, leftEdge, bottomEdge);
218 }
219 }
220 } else {
221 downloadAndPaintVisible(g, mv, false);
222 }
223 }
224
225 protected boolean confirmMalformedUrl(String url) {
226 if (isInvalidUrlConfirmed)
227 return true;
228 String msg = tr("<html>The base URL<br>"
229 + "''{0}''<br>"
230 + "for this WMS layer does neither end with a ''&'' nor with a ''?''.<br>"
231 + "This is likely to lead to invalid WMS request. You should check your<br>"
232 + "preference settings.<br>"
233 + "Do you want to fetch WMS tiles anyway?",
234 url);
235 String [] options = new String[] {
236 tr("Yes, fetch images"),
237 tr("No, abort")
238 };
239 int ret = JOptionPane.showOptionDialog(
240 Main.parent,
241 msg,
242 tr("Invalid URL?"),
243 JOptionPane.YES_NO_OPTION,
244 JOptionPane.WARNING_MESSAGE,
245 null,
246 options, options[1]
247 );
248 switch(ret) {
249 case JOptionPane.YES_OPTION: return true;
250 default: return false;
251 }
252 }
253
254 @Override
255 public void setOffset(double dx, double dy) {
256 super.setOffset(dx, dy);
257 settingsChanged = true;
258 }
259
260 public int getImageXIndex(double coord) {
261 return (int)Math.floor( ((coord - dx) * info.getPixelPerDegree()) / imageSize);
262 }
263
264 public int getImageYIndex(double coord) {
265 return (int)Math.floor( ((coord - dy) * info.getPixelPerDegree()) / imageSize);
266 }
267
268 public int getImageX(int imageIndex) {
269 return (int)(imageIndex * imageSize * (getPPD() / info.getPixelPerDegree()) + dx * getPPD());
270 }
271
272 public int getImageY(int imageIndex) {
273 return (int)(imageIndex * imageSize * (getPPD() / info.getPixelPerDegree()) + dy * getPPD());
274 }
275
276 public int getImageWidth(int xIndex) {
277 int overlap = (int)(PROP_OVERLAP.get()?PROP_OVERLAP_EAST.get() * imageSize * getPPD() / info.getPixelPerDegree() / 100:0);
278 return getImageX(xIndex + 1) - getImageX(xIndex) + overlap;
279 }
280
281 public int getImageHeight(int yIndex) {
282 int overlap = (int)(PROP_OVERLAP.get()?PROP_OVERLAP_NORTH.get() * imageSize * getPPD() / info.getPixelPerDegree() / 100:0);
283 return getImageY(yIndex + 1) - getImageY(yIndex) + overlap;
284 }
285
286 /**
287 *
288 * @return Size of image in original zoom
289 */
290 public int getBaseImageWidth() {
291 int overlap = (PROP_OVERLAP.get()?PROP_OVERLAP_EAST.get() * imageSize / 100:0);
292 return imageSize + overlap;
293 }
294
295 /**
296 *
297 * @return Size of image in original zoom
298 */
299 public int getBaseImageHeight() {
300 int overlap = (PROP_OVERLAP.get()?PROP_OVERLAP_NORTH.get() * imageSize / 100:0);
301 return imageSize + overlap;
302 }
303
304
305 /**
306 *
307 * @param xIndex
308 * @param yIndex
309 * @return Real EastNorth of given tile. dx/dy is not counted in
310 */
311 public EastNorth getEastNorth(int xIndex, int yIndex) {
312 return new EastNorth((xIndex * imageSize) / info.getPixelPerDegree(), (yIndex * imageSize) / info.getPixelPerDegree());
313 }
314
315
316 protected void downloadAndPaintVisible(Graphics g, final MapView mv, boolean real){
317
318 int newDax = dax;
319 int newDay = day;
320
321 if (bmaxx - bminx >= dax || bmaxx - bminx < dax - 2 * daStep) {
322 newDax = ((bmaxx - bminx) / daStep + 1) * daStep;
323 }
324
325 if (bmaxy - bminy >= day || bmaxy - bminx < day - 2 * daStep) {
326 newDay = ((bmaxy - bminy) / daStep + 1) * daStep;
327 }
328
329 if (newDax != dax || newDay != day) {
330 dax = newDax;
331 day = newDay;
332 initializeImages();
333 }
334
335 for(int x = bminx; x<=bmaxx; ++x) {
336 for(int y = bminy; y<=bmaxy; ++y){
337 images[modulo(x,dax)][modulo(y,day)].changePosition(x, y);
338 }
339 }
340
341 gatherFinishedRequests();
342
343 for(int x = bminx; x<=bmaxx; ++x) {
344 for(int y = bminy; y<=bmaxy; ++y){
345 GeorefImage img = images[modulo(x,dax)][modulo(y,day)];
346 if (!img.paint(g, mv, x, y, leftEdge, bottomEdge)) {
347 WMSRequest request = new WMSRequest(x, y, info.getPixelPerDegree(), real);
348 addRequest(request);
349 }
350 }
351 }
352 }
353
354 @Override public void visitBoundingBox(BoundingXYVisitor v) {
355 for(int x = 0; x<dax; ++x) {
356 for(int y = 0; y<day; ++y)
357 if(images[x][y].getImage() != null){
358 v.visit(images[x][y].getMin());
359 v.visit(images[x][y].getMax());
360 }
361 }
362 }
363
364 @Override public Action[] getMenuEntries() {
365 return new Action[]{
366 LayerListDialog.getInstance().createActivateLayerAction(this),
367 LayerListDialog.getInstance().createShowHideLayerAction(),
368 LayerListDialog.getInstance().createDeleteLayerAction(),
369 SeparatorLayerAction.INSTANCE,
370 new OffsetAction(),
371 new LoadWmsAction(),
372 new SaveWmsAction(),
373 new BookmarkWmsAction(),
374 SeparatorLayerAction.INSTANCE,
375 new StartStopAction(),
376 new ToggleAlphaAction(),
377 new ChangeResolutionAction(),
378 new ReloadErrorTilesAction(),
379 new DownloadAction(),
380 SeparatorLayerAction.INSTANCE,
381 new LayerListPopup.InfoAction(this)
382 };
383 }
384
385 public GeorefImage findImage(EastNorth eastNorth) {
386 int xIndex = getImageXIndex(eastNorth.east());
387 int yIndex = getImageYIndex(eastNorth.north());
388 GeorefImage result = images[modulo(xIndex, dax)][modulo(yIndex, day)];
389 if (result.getXIndex() == xIndex && result.getYIndex() == yIndex)
390 return result;
391 else
392 return null;
393 }
394
395 /**
396 *
397 * @param request
398 * @return -1 if request is no longer needed, otherwise priority of request (lower number <=> more important request)
399 */
400 private int getRequestPriority(WMSRequest request) {
401 if (request.getPixelPerDegree() != info.getPixelPerDegree())
402 return -1;
403 if (bminx > request.getXIndex()
404 || bmaxx < request.getXIndex()
405 || bminy > request.getYIndex()
406 || bmaxy < request.getYIndex())
407 return -1;
408
409 EastNorth cursorEastNorth = mv.getEastNorth(mv.lastMEvent.getX(), mv.lastMEvent.getY());
410 int mouseX = getImageXIndex(cursorEastNorth.east());
411 int mouseY = getImageYIndex(cursorEastNorth.north());
412 int dx = request.getXIndex() - mouseX;
413 int dy = request.getYIndex() - mouseY;
414
415 return dx * dx + dy * dy;
416 }
417
418 public WMSRequest getRequest() {
419 requestQueueLock.lock();
420 try {
421 workingThreadCount--;
422 Iterator<WMSRequest> it = requestQueue.iterator();
423 while (it.hasNext()) {
424 WMSRequest item = it.next();
425 int priority = getRequestPriority(item);
426 if (priority == -1 || finishedRequests.contains(item) || processingRequests.contains(item)) {
427 it.remove();
428 } else {
429 item.setPriority(priority);
430 }
431 }
432 Collections.sort(requestQueue);
433
434 EastNorth cursorEastNorth = mv.getEastNorth(mv.lastMEvent.getX(), mv.lastMEvent.getY());
435 int mouseX = getImageXIndex(cursorEastNorth.east());
436 int mouseY = getImageYIndex(cursorEastNorth.north());
437 boolean isOnMouse = requestQueue.size() > 0 && requestQueue.get(0).getXIndex() == mouseX && requestQueue.get(0).getYIndex() == mouseY;
438
439 // If there is only one thread left then keep it in case we need to download other tile urgently
440 while (!canceled &&
441 (requestQueue.isEmpty() || (!isOnMouse && threadCount - workingThreadCount == 0 && threadCount > 1))) {
442 try {
443 queueEmpty.await();
444 } catch (InterruptedException e) {
445 // Shouldn't happen
446 }
447 }
448
449 workingThreadCount++;
450 if (canceled)
451 return null;
452 else {
453 WMSRequest request = requestQueue.remove(0);
454 processingRequests.add(request);
455 return request;
456 }
457
458 } finally {
459 requestQueueLock.unlock();
460 }
461 }
462
463 public void finishRequest(WMSRequest request) {
464 requestQueueLock.lock();
465 try {
466 processingRequests.remove(request);
467 if (request.getState() != null) {
468 finishedRequests.add(request);
469 mv.repaint();
470 }
471 } finally {
472 requestQueueLock.unlock();
473 }
474 }
475
476 public void addRequest(WMSRequest request) {
477 requestQueueLock.lock();
478 try {
479 if (!requestQueue.contains(request) && !finishedRequests.contains(request) && !processingRequests.contains(request)) {
480 requestQueue.add(request);
481 queueEmpty.signalAll();
482 }
483 } finally {
484 requestQueueLock.unlock();
485 }
486 }
487
488 public boolean requestIsValid(WMSRequest request) {
489 return bminx <= request.getXIndex() && bmaxx >= request.getXIndex() && bminy <= request.getYIndex() && bmaxy >= request.getYIndex();
490 }
491
492 private void gatherFinishedRequests() {
493 requestQueueLock.lock();
494 try {
495 for (WMSRequest request: finishedRequests) {
496 GeorefImage img = images[modulo(request.getXIndex(),dax)][modulo(request.getYIndex(),day)];
497 if (img.equalPosition(request.getXIndex(), request.getYIndex())) {
498 img.changeImage(request.getState(), request.getImage());
499 }
500 }
501 } finally {
502 requestQueueLock.unlock();
503 finishedRequests.clear();
504 }
505 }
506
507 public class DownloadAction extends AbstractAction {
508 private static final long serialVersionUID = -7183852461015284020L;
509 public DownloadAction() {
510 super(tr("Download visible tiles"));
511 }
512 @Override
513 public void actionPerformed(ActionEvent ev) {
514 if (zoomIsTooBig()) {
515 JOptionPane.showMessageDialog(
516 Main.parent,
517 tr("The requested area is too big. Please zoom in a little, or change resolution"),
518 tr("Error"),
519 JOptionPane.ERROR_MESSAGE
520 );
521 } else {
522 downloadAndPaintVisible(mv.getGraphics(), mv, true);
523 }
524 }
525 }
526
527 public class ChangeResolutionAction extends AbstractAction {
528 public ChangeResolutionAction() {
529 super(tr("Change resolution"));
530 }
531 @Override
532 public void actionPerformed(ActionEvent ev) {
533 initializeImages();
534 resolution = mv.getDist100PixelText();
535 info.setPixelPerDegree(getPPD());
536 settingsChanged = true;
537 mv.repaint();
538 }
539 }
540
541 public class ReloadErrorTilesAction extends AbstractAction {
542 public ReloadErrorTilesAction() {
543 super(tr("Reload erroneous tiles"));
544 }
545 @Override
546 public void actionPerformed(ActionEvent ev) {
547 // Delete small files, because they're probably blank tiles.
548 // See https://josm.openstreetmap.de/ticket/2307
549 Grabber.cache.customCleanUp(CacheFiles.CLEAN_SMALL_FILES, 4096);
550
551 for (int x = 0; x < dax; ++x) {
552 for (int y = 0; y < day; ++y) {
553 GeorefImage img = images[modulo(x,dax)][modulo(y,day)];
554 if(img.getState() == State.FAILED){
555 addRequest(new WMSRequest(img.getXIndex(), img.getYIndex(), info.getPixelPerDegree(), true));
556 mv.repaint();
557 }
558 }
559 }
560 }
561 }
562
563 public class ToggleAlphaAction extends AbstractAction implements LayerAction {
564 public ToggleAlphaAction() {
565 super(tr("Alpha channel"));
566 }
567 @Override
568 public void actionPerformed(ActionEvent ev) {
569 JCheckBoxMenuItem checkbox = (JCheckBoxMenuItem) ev.getSource();
570 boolean alphaChannel = checkbox.isSelected();
571 PROP_ALPHA_CHANNEL.put(alphaChannel);
572
573 // clear all resized cached instances and repaint the layer
574 for (int x = 0; x < dax; ++x) {
575 for (int y = 0; y < day; ++y) {
576 GeorefImage img = images[modulo(x, dax)][modulo(y, day)];
577 img.flushedResizedCachedInstance();
578 }
579 }
580 mv.repaint();
581 }
582 @Override
583 public Component createMenuComponent() {
584 JCheckBoxMenuItem item = new JCheckBoxMenuItem(this);
585 item.setSelected(PROP_ALPHA_CHANNEL.get());
586 return item;
587 }
588 @Override
589 public boolean supportLayers(List<Layer> layers) {
590 return layers.size() == 1 && layers.get(0) instanceof WMSLayer;
591 }
592 }
593
594 public class SaveWmsAction extends AbstractAction {
595 public SaveWmsAction() {
596 super(tr("Save WMS layer to file"), ImageProvider.get("save"));
597 }
598 @Override
599 public void actionPerformed(ActionEvent ev) {
600 File f = SaveActionBase.createAndOpenSaveFileChooser(
601 tr("Save WMS layer"), ".wms");
602 try {
603 if (f != null) {
604 ObjectOutputStream oos = new ObjectOutputStream(
605 new FileOutputStream(f)
606 );
607 oos.writeInt(serializeFormatVersion);
608 oos.writeInt(dax);
609 oos.writeInt(day);
610 oos.writeInt(imageSize);
611 oos.writeDouble(info.getPixelPerDegree());
612 oos.writeObject(info.getName());
613 oos.writeObject(info.getFullURL());
614 oos.writeObject(images);
615 oos.close();
616 }
617 } catch (Exception ex) {
618 ex.printStackTrace(System.out);
619 }
620 }
621 }
622
623 public class LoadWmsAction extends AbstractAction {
624 public LoadWmsAction() {
625 super(tr("Load WMS layer from file"), ImageProvider.get("open"));
626 }
627 @Override
628 public void actionPerformed(ActionEvent ev) {
629 JFileChooser fc = DiskAccessAction.createAndOpenFileChooser(true,
630 false, tr("Load WMS layer"), "wms");
631 if(fc == null) return;
632 File f = fc.getSelectedFile();
633 if (f == null) return;
634 try
635 {
636 FileInputStream fis = new FileInputStream(f);
637 ObjectInputStream ois = new ObjectInputStream(fis);
638 int sfv = ois.readInt();
639 if (sfv != serializeFormatVersion) {
640 JOptionPane.showMessageDialog(Main.parent,
641 tr("Unsupported WMS file version; found {0}, expected {1}", sfv, serializeFormatVersion),
642 tr("File Format Error"),
643 JOptionPane.ERROR_MESSAGE);
644 return;
645 }
646 autoDownloadEnabled = false;
647 dax = ois.readInt();
648 day = ois.readInt();
649 imageSize = ois.readInt();
650 info.setPixelPerDegree(ois.readDouble());
651 doSetName((String)ois.readObject());
652 info.setURL((String) ois.readObject());
653 images = (GeorefImage[][])ois.readObject();
654 ois.close();
655 fis.close();
656 for (GeorefImage[] imgs : images) {
657 for (GeorefImage img : imgs) {
658 if (img != null) {
659 img.setLayer(WMSLayer.this);
660 }
661 }
662 }
663 settingsChanged = true;
664 mv.repaint();
665 if(info.getURL() != null)
666 {
667 startGrabberThreads();
668 }
669 }
670 catch (Exception ex) {
671 // FIXME be more specific
672 ex.printStackTrace(System.out);
673 JOptionPane.showMessageDialog(Main.parent,
674 tr("Error loading file"),
675 tr("Error"),
676 JOptionPane.ERROR_MESSAGE);
677 return;
678 }
679 }
680 }
681 /**
682 * This action will add a WMS layer menu entry with the current WMS layer
683 * URL and name extended by the current resolution.
684 * When using the menu entry again, the WMS cache will be used properly.
685 */
686 public class BookmarkWmsAction extends AbstractAction {
687 public BookmarkWmsAction() {
688 super(tr("Set WMS Bookmark"));
689 }
690 @Override
691 public void actionPerformed(ActionEvent ev) {
692 ImageryLayerInfo.addLayer(new ImageryInfo(info));
693 }
694 }
695
696 private class StartStopAction extends AbstractAction implements LayerAction {
697
698 public StartStopAction() {
699 super(tr("Automatic downloading"));
700 }
701
702 @Override
703 public Component createMenuComponent() {
704 JCheckBoxMenuItem item = new JCheckBoxMenuItem(this);
705 item.setSelected(autoDownloadEnabled);
706 return item;
707 }
708
709 @Override
710 public boolean supportLayers(List<Layer> layers) {
711 return layers.size() == 1 && layers.get(0) instanceof WMSLayer;
712 }
713
714 @Override
715 public void actionPerformed(ActionEvent e) {
716 autoDownloadEnabled = !autoDownloadEnabled;
717 if (autoDownloadEnabled) {
718 mv.repaint();
719 }
720 }
721 }
722
723 private void cancelGrabberThreads(boolean wait) {
724 requestQueueLock.lock();
725 try {
726 canceled = true;
727 for (Grabber grabber: grabbers) {
728 grabber.cancel();
729 }
730 queueEmpty.signalAll();
731 } finally {
732 requestQueueLock.unlock();
733 }
734 if (wait) {
735 for (Thread t: grabberThreads) {
736 try {
737 t.join();
738 } catch (InterruptedException e) {
739 // Shouldn't happen
740 e.printStackTrace();
741 }
742 }
743 }
744 }
745
746 private void startGrabberThreads() {
747 int threadCount = PROP_SIMULTANEOUS_CONNECTIONS.get();
748 requestQueueLock.lock();
749 try {
750 canceled = false;
751 grabbers.clear();
752 grabberThreads.clear();
753 for (int i=0; i<threadCount; i++) {
754 Grabber grabber = getGrabber();
755 grabbers.add(grabber);
756 Thread t = new Thread(grabber, "WMS " + getName() + " " + i);
757 t.setDaemon(true);
758 t.start();
759 grabberThreads.add(t);
760 }
761 this.workingThreadCount = grabbers.size();
762 this.threadCount = grabbers.size();
763 } finally {
764 requestQueueLock.unlock();
765 }
766 }
767
768 @Override
769 public boolean isChanged() {
770 requestQueueLock.lock();
771 try {
772 return !finishedRequests.isEmpty() || settingsChanged;
773 } finally {
774 requestQueueLock.unlock();
775 }
776 }
777
778 @Override
779 public void preferenceChanged(PreferenceChangeEvent event) {
780 if (event.getKey().equals(PROP_SIMULTANEOUS_CONNECTIONS.getKey())) {
781 cancelGrabberThreads(true);
782 startGrabberThreads();
783 } else if (
784 event.getKey().equals(PROP_OVERLAP.getKey())
785 || event.getKey().equals(PROP_OVERLAP_EAST.getKey())
786 || event.getKey().equals(PROP_OVERLAP_NORTH.getKey())) {
787 for (int i=0; i<images.length; i++) {
788 for (int k=0; k<images[i].length; k++) {
789 images[i][k] = new GeorefImage(this);
790 }
791 }
792
793 settingsChanged = true;
794 }
795 }
796
797 protected Grabber getGrabber(){
798 if(getInfo().getImageryType() == ImageryType.HTML)
799 return new HTMLGrabber(mv, this);
800 else if(getInfo().getImageryType() == ImageryType.WMS)
801 return new WMSGrabber(mv, this);
802 else throw new IllegalStateException("getGrabber() called for non-WMS layer type");
803 }
804
805}
Note: See TracBrowser for help on using the repository browser.