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

Last change on this file since 4506 was 4506, checked in by bastiK, 13 years ago

fixed #6816 - attribution for wmslayer (see [o26806])

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