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