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

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

Add action Zoom to native resolution to wms layer

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