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

Last change on this file since 4432 was 4432, checked in by stoecker, 13 years ago

hopefully fix #6662 - selected layer color wrong

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