001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.layer; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.event.ActionEvent; 007import java.util.ArrayList; 008import java.util.Arrays; 009import java.util.Collection; 010import java.util.List; 011import java.util.Objects; 012 013import javax.swing.AbstractAction; 014import javax.swing.Action; 015 016import org.apache.commons.jcs.access.CacheAccess; 017import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader; 018import org.openstreetmap.josm.Main; 019import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry; 020import org.openstreetmap.josm.data.imagery.AbstractWMSTileSource; 021import org.openstreetmap.josm.data.imagery.ImageryInfo; 022import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType; 023import org.openstreetmap.josm.data.imagery.ImageryLayerInfo; 024import org.openstreetmap.josm.data.imagery.TemplatedWMSTileSource; 025import org.openstreetmap.josm.data.imagery.WMSCachedTileLoader; 026import org.openstreetmap.josm.data.preferences.BooleanProperty; 027import org.openstreetmap.josm.data.preferences.IntegerProperty; 028import org.openstreetmap.josm.data.projection.Projection; 029import org.openstreetmap.josm.data.projection.Projections; 030import org.openstreetmap.josm.gui.layer.imagery.TileSourceDisplaySettings; 031import org.openstreetmap.josm.tools.CheckParameterUtil; 032import org.openstreetmap.josm.tools.Logging; 033import org.openstreetmap.josm.tools.Utils; 034 035/** 036 * This is a layer that grabs the current screen from an WMS server. The data 037 * fetched this way is tiled and managed to the disc to reduce server load. 038 * 039 */ 040public class WMSLayer extends AbstractCachedTileSourceLayer<AbstractWMSTileSource> { 041 private static final String PREFERENCE_PREFIX = "imagery.wms"; 042 /** 043 * Registers all setting properties 044 */ 045 static { 046 new TileSourceDisplaySettings(PREFERENCE_PREFIX); 047 } 048 049 /** default tile size for WMS Layer */ 050 public static final IntegerProperty PROP_IMAGE_SIZE = new IntegerProperty(PREFERENCE_PREFIX + ".imageSize", 512); 051 052 /** should WMS layer autozoom in default mode */ 053 public static final BooleanProperty PROP_DEFAULT_AUTOZOOM = new BooleanProperty(PREFERENCE_PREFIX + ".default_autozoom", true); 054 055 private static final String CACHE_REGION_NAME = "WMS"; 056 057 private final List<String> serverProjections; 058 059 /** 060 * Constructs a new {@code WMSLayer}. 061 * @param info ImageryInfo description of the layer 062 */ 063 public WMSLayer(ImageryInfo info) { 064 super(info); 065 CheckParameterUtil.ensureThat(info.getImageryType() == ImageryType.WMS, "ImageryType is WMS"); 066 CheckParameterUtil.ensureParameterNotNull(info.getUrl(), "info.url"); 067 TemplatedWMSTileSource.checkUrl(info.getUrl()); 068 this.serverProjections = new ArrayList<>(info.getServerProjections()); 069 } 070 071 @Override 072 protected TileSourceDisplaySettings createDisplaySettings() { 073 return new TileSourceDisplaySettings(PREFERENCE_PREFIX); 074 } 075 076 @Override 077 public Action[] getMenuEntries() { 078 List<Action> ret = new ArrayList<>(); 079 ret.addAll(Arrays.asList(super.getMenuEntries())); 080 ret.add(SeparatorLayerAction.INSTANCE); 081 ret.add(new LayerSaveAction(this)); 082 ret.add(new LayerSaveAsAction(this)); 083 ret.add(new BookmarkWmsAction()); 084 return ret.toArray(new Action[0]); 085 } 086 087 @Override 088 protected AbstractWMSTileSource getTileSource() { 089 AbstractWMSTileSource tileSource = new TemplatedWMSTileSource( 090 info, chooseProjection(Main.getProjection())); 091 info.setAttribution(tileSource); 092 return tileSource; 093 } 094 095 /** 096 * This action will add a WMS layer menu entry with the current WMS layer 097 * URL and name extended by the current resolution. 098 * When using the menu entry again, the WMS cache will be used properly. 099 */ 100 public class BookmarkWmsAction extends AbstractAction { 101 /** 102 * Constructs a new {@code BookmarkWmsAction}. 103 */ 104 public BookmarkWmsAction() { 105 super(tr("Set WMS Bookmark")); 106 } 107 108 @Override 109 public void actionPerformed(ActionEvent ev) { 110 ImageryLayerInfo.addLayer(new ImageryInfo(info)); 111 } 112 } 113 114 @Override 115 public Collection<String> getNativeProjections() { 116 return serverProjections; 117 } 118 119 @Override 120 public void projectionChanged(Projection oldValue, Projection newValue) { 121 super.projectionChanged(oldValue, newValue); 122 Projection tileProjection = chooseProjection(newValue); 123 if (!Objects.equals(tileSource.getTileProjection(), tileProjection)) { 124 tileSource.setTileProjection(tileProjection); 125 } 126 } 127 128 private Projection chooseProjection(Projection requested) { 129 if (serverProjections.contains(requested.toCode())) { 130 return requested; 131 } else { 132 for (String code : serverProjections) { 133 Projection proj = Projections.getProjectionByCode(code); 134 if (proj != null) { 135 Logging.info(tr("Reprojecting layer {0} from {1} to {2}. For best image quality and performance," 136 + " switch to one of the supported projections: {3}", 137 getName(), proj.toCode(), Main.getProjection().toCode(), Utils.join(", ", getNativeProjections()))); 138 return proj; 139 } 140 } 141 Logging.warn(tr("Unable to find supported projection for layer {0}. Using {1}.", getName(), requested.toCode())); 142 return requested; 143 } 144 } 145 146 @Override 147 protected Class<? extends TileLoader> getTileLoaderClass() { 148 return WMSCachedTileLoader.class; 149 } 150 151 @Override 152 protected String getCacheName() { 153 return CACHE_REGION_NAME; 154 } 155 156 /** 157 * @return cache region for WMS layer 158 */ 159 public static CacheAccess<String, BufferedImageCacheEntry> getCache() { 160 return AbstractCachedTileSourceLayer.getCache(CACHE_REGION_NAME); 161 } 162}