Changeset 17277 in osm for applications/editors/josm/plugins/wmsplugin/src
- Timestamp:
- 2009-08-26T13:51:23+02:00 (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSLayer.java
r16860 r17277 26 26 import org.openstreetmap.josm.Main; 27 27 import org.openstreetmap.josm.actions.DiskAccessAction; 28 import org.openstreetmap.josm.actions.SaveActionBase; 28 29 import org.openstreetmap.josm.data.ProjectionBounds; 29 30 import org.openstreetmap.josm.data.coor.EastNorth; 30 import org.openstreetmap.josm.data.coor.LatLon;31 31 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 32 32 import org.openstreetmap.josm.gui.MapView; … … 42 42 */ 43 43 public class WMSLayer extends Layer { 44 protected static final Icon icon = 45 new ImageIcon(Toolkit.getDefaultToolkit().createImage(WMSPlugin.class.getResource("/images/wms_small.png"))); 46 47 public int messageNum = 5; //limit for messages per layer 48 protected MapView mv; 49 protected String resolution; 50 protected boolean stopAfterPaint = false; 51 protected int ImageSize = 500; 52 protected int dax = 10; 53 protected int day = 10; 54 protected int minZoom = 3; 55 protected double dx = 0.0; 56 protected double dy = 0.0; 57 protected double pixelPerDegree; 58 protected GeorefImage[][] images = new GeorefImage[dax][day]; 59 JCheckBoxMenuItem startstop = new JCheckBoxMenuItem(tr("Automatic downloading"), true); 60 protected JCheckBoxMenuItem alphaChannel = new JCheckBoxMenuItem(new ToggleAlphaAction()); 61 protected String baseURL; 62 protected String cookies; 63 protected final int serializeFormatVersion = 4; 64 65 private ExecutorService executor = null; 66 67 public WMSLayer() { 68 this(tr("Blank Layer"), null, null); 69 initializeImages(); 70 mv = Main.map.mapView; 71 } 72 73 public WMSLayer(String name, String baseURL, String cookies) { 74 super(name); 75 alphaChannel.setSelected(Main.pref.getBoolean("wmsplugin.alpha_channel")); 76 background = true; /* set global background variable */ 77 initializeImages(); 78 this.baseURL = baseURL; 79 this.cookies = cookies; 80 WMSGrabber.getProjection(baseURL, true); 81 mv = Main.map.mapView; 82 resolution = mv.getDist100PixelText(); 83 pixelPerDegree = getPPD(); 84 85 executor = Executors.newFixedThreadPool(3); 86 } 87 88 @Override 89 public void destroy() { 90 try { 91 executor.shutdown(); 92 // Might not be initalized, so catch NullPointer as well 93 } catch(Exception x) {} 94 } 95 96 public double getPPD(){ 97 ProjectionBounds bounds = mv.getProjectionBounds(); 98 return mv.getWidth() / (bounds.max.east() - bounds.min.east()); 99 } 100 101 public void initializeImages() { 102 images = new GeorefImage[dax][day]; 103 for(int x = 0; x<dax; ++x) 104 for(int y = 0; y<day; ++y) 105 images[x][y]= new GeorefImage(false); 106 } 107 108 @Override public Icon getIcon() { 109 return icon; 110 } 111 112 @Override public String getToolTipText() { 113 if(startstop.isSelected()) 114 return tr("WMS layer ({0}), automatically downloading in zoom {1}", getName(), resolution); 115 else 116 return tr("WMS layer ({0}), downloading in zoom {1}", getName(), resolution); 117 } 118 119 @Override public boolean isMergable(Layer other) { 120 return false; 121 } 122 123 @Override public void mergeFrom(Layer from) { 124 } 125 126 private ProjectionBounds XYtoBounds (int x, int y) { 127 return new ProjectionBounds( 128 new EastNorth( x * ImageSize / pixelPerDegree, y * ImageSize / pixelPerDegree), 129 new EastNorth((x + 1) * ImageSize / pixelPerDegree, (y + 1) * ImageSize / pixelPerDegree)); 130 } 131 132 private int modulo (int a, int b) { 133 return a % b >= 0 ? a%b : a%b+b; 134 } 135 136 @Override public void paint(Graphics g, final MapView mv) { 137 if(baseURL == null) return; 138 139 if( !startstop.isSelected() || (pixelPerDegree / getPPD() > minZoom) ){ //don't download when it's too outzoomed 140 for(int x = 0; x<dax; ++x) 141 for(int y = 0; y<day; ++y) 142 images[modulo(x,dax)][modulo(y,day)].paint(g, mv, dx, dy); 143 } else 144 downloadAndPaintVisible(g, mv); 145 } 146 147 public void displace(double dx, double dy) { 148 this.dx += dx; 149 this.dy += dy; 150 } 151 152 protected void downloadAndPaintVisible(Graphics g, final MapView mv){ 153 ProjectionBounds bounds = mv.getProjectionBounds(); 154 int bminx= (int)Math.floor ((bounds.min.east() * pixelPerDegree ) / ImageSize ); 155 int bminy= (int)Math.floor ((bounds.min.north() * pixelPerDegree ) / ImageSize ); 156 int bmaxx= (int)Math.ceil ((bounds.max.east() * pixelPerDegree ) / ImageSize ); 157 int bmaxy= (int)Math.ceil ((bounds.max.north() * pixelPerDegree ) / ImageSize ); 158 159 if((bmaxx - bminx > dax) || (bmaxy - bminy > day)){ 160 OptionPaneUtil.showMessageDialog( 161 Main.parent, 162 tr("The requested area is too big. Please zoom in a little, or change resolution"), 163 tr("Error"), 164 JOptionPane.ERROR_MESSAGE 165 ); 166 return; 167 } 168 169 for(int x = bminx; x<bmaxx; ++x) 170 for(int y = bminy; y<bmaxy; ++y){ 171 GeorefImage img = images[modulo(x,dax)][modulo(y,day)]; 172 g.drawRect(x, y, dax, bminy); 173 if(!img.paint(g, mv, dx, dy) && !img.downloadingStarted){ 174 img.downloadingStarted = true; 175 img.image = null; 176 img.flushedResizedCachedInstance(); 177 Grabber gr = WMSPlugin.getGrabber(XYtoBounds(x,y), img, mv, this); 178 executor.submit(gr); 179 } 180 } 181 } 182 183 @Override public void visitBoundingBox(BoundingXYVisitor v) { 184 for(int x = 0; x<dax; ++x) 185 for(int y = 0; y<day; ++y) 186 if(images[x][y].image!=null){ 187 v.visit(images[x][y].min); 188 v.visit(images[x][y].max); 189 } 190 } 191 192 @Override public Object getInfoComponent() { 193 return getToolTipText(); 194 } 195 196 @Override public Component[] getMenuEntries() { 197 return new Component[]{ 198 new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)), 199 new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)), 200 new JSeparator(), 201 new JMenuItem(new LoadWmsAction()), 202 new JMenuItem(new SaveWmsAction()), 203 new JSeparator(), 204 startstop, 205 alphaChannel, 206 new JMenuItem(new changeResolutionAction()), 207 new JMenuItem(new reloadErrorTilesAction()), 208 new JMenuItem(new downloadAction()), 209 new JSeparator(), 210 new JMenuItem(new LayerListPopup.InfoAction(this)) 211 }; 212 } 213 214 public GeorefImage findImage(EastNorth eastNorth) { 215 for(int x = 0; x<dax; ++x) 216 for(int y = 0; y<day; ++y) 217 if(images[x][y].image!=null && images[x][y].min!=null && images[x][y].max!=null) 218 if(images[x][y].contains(eastNorth, dx, dy)) 219 return images[x][y]; 220 return null; 221 } 222 223 public class downloadAction extends AbstractAction { 224 public downloadAction() { 225 super(tr("Download visible tiles")); 226 } 227 public void actionPerformed(ActionEvent ev) { 228 downloadAndPaintVisible(mv.getGraphics(), mv); 229 } 230 } 231 232 public class changeResolutionAction extends AbstractAction { 233 public changeResolutionAction() { 234 super(tr("Change resolution")); 235 } 236 public void actionPerformed(ActionEvent ev) { 237 initializeImages(); 238 resolution = mv.getDist100PixelText(); 239 pixelPerDegree = getPPD(); 240 mv.repaint(); 241 } 242 } 243 244 public class reloadErrorTilesAction extends AbstractAction { 245 public reloadErrorTilesAction() { 246 super(tr("Reload erroneous tiles")); 247 } 248 public void actionPerformed(ActionEvent ev) { 249 // Delete small files, because they're probably blank tiles. 250 // See https://josm.openstreetmap.de/ticket/2307 251 WMSPlugin.cache.customCleanUp(WMSPlugin.cache.CLEAN_SMALL_FILES, 2048); 252 253 for (int x = 0; x < dax; ++x) { 254 for (int y = 0; y < day; ++y) { 255 GeorefImage img = images[modulo(x,dax)][modulo(y,day)]; 256 img.image = null; 257 img.flushedResizedCachedInstance(); 258 img.downloadingStarted = false; 259 img.failed = false; 260 mv.repaint(); 261 } 262 } 263 } 264 } 265 266 public class ToggleAlphaAction extends AbstractAction { 267 public ToggleAlphaAction() { 268 super(tr("Alpha channel")); 269 } 270 public void actionPerformed(ActionEvent ev) { 271 JCheckBoxMenuItem checkbox = (JCheckBoxMenuItem) ev.getSource(); 272 boolean alphaChannel = checkbox.isSelected(); 273 Main.pref.put("wmsplugin.alpha_channel", alphaChannel); 274 275 // clear all resized cached instances and repaint the layer 276 for (int x = 0; x < dax; ++x) { 277 for (int y = 0; y < day; ++y) { 278 GeorefImage img = images[modulo(x, dax)][modulo(y, day)]; 279 img.flushedResizedCachedInstance(); 280 } 281 } 282 mv.repaint(); 283 } 284 } 285 286 public class SaveWmsAction extends AbstractAction { 287 public SaveWmsAction() { 288 super(tr("Save WMS layer to file"), ImageProvider.get("save")); 289 } 290 public void actionPerformed(ActionEvent ev) { 291 File f = DiskAccessAction.createAndOpenSaveFileChooser(tr("Save WMS layer"), ".wms"); 292 try 293 { 294 FileOutputStream fos = new FileOutputStream(f); 295 ObjectOutputStream oos = new ObjectOutputStream(fos); 296 oos.writeInt(serializeFormatVersion); 297 oos.writeInt(dax); 298 oos.writeInt(day); 299 oos.writeInt(ImageSize); 300 oos.writeDouble(pixelPerDegree); 301 oos.writeObject(getName()); 302 oos.writeObject(baseURL); 303 oos.writeObject(images); 304 oos.close(); 305 fos.close(); 306 } 307 catch (Exception ex) { 308 ex.printStackTrace(System.out); 309 } 310 } 311 } 312 313 public class LoadWmsAction extends AbstractAction { 314 public LoadWmsAction() { 315 super(tr("Load WMS layer from file"), ImageProvider.get("load")); 316 } 317 public void actionPerformed(ActionEvent ev) { 318 JFileChooser fc = DiskAccessAction.createAndOpenFileChooser(true, 319 false, tr("Load WMS layer")); 320 if(fc == null) return; 321 File f = fc.getSelectedFile(); 322 if (f == null) return; 323 try 324 { 325 FileInputStream fis = new FileInputStream(f); 326 ObjectInputStream ois = new ObjectInputStream(fis); 327 int sfv = ois.readInt(); 328 if (sfv != serializeFormatVersion) { 329 OptionPaneUtil.showMessageDialog(Main.parent, 330 tr("Unsupported WMS file version; found {0}, expected {1}", sfv, serializeFormatVersion), 331 tr("File Format Error"), 332 JOptionPane.ERROR_MESSAGE); 333 return; 334 } 335 startstop.setSelected(false); 336 dax = ois.readInt(); 337 day = ois.readInt(); 338 ImageSize = ois.readInt(); 339 pixelPerDegree = ois.readDouble(); 340 setName((String)ois.readObject()); 341 baseURL = (String) ois.readObject(); 342 images = (GeorefImage[][])ois.readObject(); 343 ois.close(); 344 fis.close(); 345 mv.repaint(); 346 } 347 catch (Exception ex) { 348 // FIXME be more specific 349 ex.printStackTrace(System.out); 350 OptionPaneUtil.showMessageDialog(Main.parent, 351 tr("Error loading file"), 352 tr("Error"), 353 JOptionPane.ERROR_MESSAGE); 354 return; 355 } 356 } 357 } 44 protected static final Icon icon = 45 new ImageIcon(Toolkit.getDefaultToolkit().createImage(WMSPlugin.class.getResource("/images/wms_small.png"))); 46 47 public int messageNum = 5; //limit for messages per layer 48 protected MapView mv; 49 protected String resolution; 50 protected boolean stopAfterPaint = false; 51 protected int ImageSize = 500; 52 protected int dax = 10; 53 protected int day = 10; 54 protected int minZoom = 3; 55 protected double dx = 0.0; 56 protected double dy = 0.0; 57 protected double pixelPerDegree; 58 protected GeorefImage[][] images = new GeorefImage[dax][day]; 59 JCheckBoxMenuItem startstop = new JCheckBoxMenuItem(tr("Automatic downloading"), true); 60 protected JCheckBoxMenuItem alphaChannel = new JCheckBoxMenuItem(new ToggleAlphaAction()); 61 protected String baseURL; 62 protected String cookies; 63 protected final int serializeFormatVersion = 4; 64 65 private ExecutorService executor = null; 66 67 public WMSLayer() { 68 this(tr("Blank Layer"), null, null); 69 initializeImages(); 70 mv = Main.map.mapView; 71 } 72 73 public WMSLayer(String name, String baseURL, String cookies) { 74 super(name); 75 alphaChannel.setSelected(Main.pref.getBoolean("wmsplugin.alpha_channel")); 76 background = true; /* set global background variable */ 77 initializeImages(); 78 this.baseURL = baseURL; 79 this.cookies = cookies; 80 WMSGrabber.getProjection(baseURL, true); 81 mv = Main.map.mapView; 82 resolution = mv.getDist100PixelText(); 83 pixelPerDegree = getPPD(); 84 85 executor = Executors.newFixedThreadPool(3); 86 } 87 88 @Override 89 public void destroy() { 90 try { 91 executor.shutdown(); 92 // Might not be initalized, so catch NullPointer as well 93 } catch(Exception x) {} 94 } 95 96 public double getPPD(){ 97 ProjectionBounds bounds = mv.getProjectionBounds(); 98 return mv.getWidth() / (bounds.max.east() - bounds.min.east()); 99 } 100 101 public void initializeImages() { 102 images = new GeorefImage[dax][day]; 103 for(int x = 0; x<dax; ++x) { 104 for(int y = 0; y<day; ++y) { 105 images[x][y]= new GeorefImage(false); 106 } 107 } 108 } 109 110 @Override public Icon getIcon() { 111 return icon; 112 } 113 114 @Override public String getToolTipText() { 115 if(startstop.isSelected()) 116 return tr("WMS layer ({0}), automatically downloading in zoom {1}", getName(), resolution); 117 else 118 return tr("WMS layer ({0}), downloading in zoom {1}", getName(), resolution); 119 } 120 121 @Override public boolean isMergable(Layer other) { 122 return false; 123 } 124 125 @Override public void mergeFrom(Layer from) { 126 } 127 128 private ProjectionBounds XYtoBounds (int x, int y) { 129 return new ProjectionBounds( 130 new EastNorth( x * ImageSize / pixelPerDegree, y * ImageSize / pixelPerDegree), 131 new EastNorth((x + 1) * ImageSize / pixelPerDegree, (y + 1) * ImageSize / pixelPerDegree)); 132 } 133 134 private int modulo (int a, int b) { 135 return a % b >= 0 ? a%b : a%b+b; 136 } 137 138 @Override public void paint(Graphics g, final MapView mv) { 139 if(baseURL == null) return; 140 141 if( !startstop.isSelected() || (pixelPerDegree / getPPD() > minZoom) ){ //don't download when it's too outzoomed 142 for(int x = 0; x<dax; ++x) { 143 for(int y = 0; y<day; ++y) { 144 images[modulo(x,dax)][modulo(y,day)].paint(g, mv, dx, dy); 145 } 146 } 147 } else { 148 downloadAndPaintVisible(g, mv); 149 } 150 } 151 152 public void displace(double dx, double dy) { 153 this.dx += dx; 154 this.dy += dy; 155 } 156 157 protected void downloadAndPaintVisible(Graphics g, final MapView mv){ 158 ProjectionBounds bounds = mv.getProjectionBounds(); 159 int bminx= (int)Math.floor ((bounds.min.east() * pixelPerDegree ) / ImageSize ); 160 int bminy= (int)Math.floor ((bounds.min.north() * pixelPerDegree ) / ImageSize ); 161 int bmaxx= (int)Math.ceil ((bounds.max.east() * pixelPerDegree ) / ImageSize ); 162 int bmaxy= (int)Math.ceil ((bounds.max.north() * pixelPerDegree ) / ImageSize ); 163 164 if((bmaxx - bminx > dax) || (bmaxy - bminy > day)){ 165 OptionPaneUtil.showMessageDialog( 166 Main.parent, 167 tr("The requested area is too big. Please zoom in a little, or change resolution"), 168 tr("Error"), 169 JOptionPane.ERROR_MESSAGE 170 ); 171 return; 172 } 173 174 for(int x = bminx; x<bmaxx; ++x) { 175 for(int y = bminy; y<bmaxy; ++y){ 176 GeorefImage img = images[modulo(x,dax)][modulo(y,day)]; 177 g.drawRect(x, y, dax, bminy); 178 if(!img.paint(g, mv, dx, dy) && !img.downloadingStarted){ 179 img.downloadingStarted = true; 180 img.image = null; 181 img.flushedResizedCachedInstance(); 182 Grabber gr = WMSPlugin.getGrabber(XYtoBounds(x,y), img, mv, this); 183 executor.submit(gr); 184 } 185 } 186 } 187 } 188 189 @Override public void visitBoundingBox(BoundingXYVisitor v) { 190 for(int x = 0; x<dax; ++x) { 191 for(int y = 0; y<day; ++y) 192 if(images[x][y].image!=null){ 193 v.visit(images[x][y].min); 194 v.visit(images[x][y].max); 195 } 196 } 197 } 198 199 @Override public Object getInfoComponent() { 200 return getToolTipText(); 201 } 202 203 @Override public Component[] getMenuEntries() { 204 return new Component[]{ 205 new JMenuItem(LayerListDialog.getInstance().createShowHideLayerAction(this)), 206 new JMenuItem(LayerListDialog.getInstance().createDeleteLayerAction(this)), 207 new JSeparator(), 208 new JMenuItem(new LoadWmsAction()), 209 new JMenuItem(new SaveWmsAction()), 210 new JSeparator(), 211 startstop, 212 alphaChannel, 213 new JMenuItem(new changeResolutionAction()), 214 new JMenuItem(new reloadErrorTilesAction()), 215 new JMenuItem(new downloadAction()), 216 new JSeparator(), 217 new JMenuItem(new LayerListPopup.InfoAction(this)) 218 }; 219 } 220 221 public GeorefImage findImage(EastNorth eastNorth) { 222 for(int x = 0; x<dax; ++x) { 223 for(int y = 0; y<day; ++y) 224 if(images[x][y].image!=null && images[x][y].min!=null && images[x][y].max!=null) 225 if(images[x][y].contains(eastNorth, dx, dy)) 226 return images[x][y]; 227 } 228 return null; 229 } 230 231 public class downloadAction extends AbstractAction { 232 public downloadAction() { 233 super(tr("Download visible tiles")); 234 } 235 public void actionPerformed(ActionEvent ev) { 236 downloadAndPaintVisible(mv.getGraphics(), mv); 237 } 238 } 239 240 public class changeResolutionAction extends AbstractAction { 241 public changeResolutionAction() { 242 super(tr("Change resolution")); 243 } 244 public void actionPerformed(ActionEvent ev) { 245 initializeImages(); 246 resolution = mv.getDist100PixelText(); 247 pixelPerDegree = getPPD(); 248 mv.repaint(); 249 } 250 } 251 252 public class reloadErrorTilesAction extends AbstractAction { 253 public reloadErrorTilesAction() { 254 super(tr("Reload erroneous tiles")); 255 } 256 public void actionPerformed(ActionEvent ev) { 257 // Delete small files, because they're probably blank tiles. 258 // See https://josm.openstreetmap.de/ticket/2307 259 WMSPlugin.cache.customCleanUp(WMSPlugin.cache.CLEAN_SMALL_FILES, 2048); 260 261 for (int x = 0; x < dax; ++x) { 262 for (int y = 0; y < day; ++y) { 263 GeorefImage img = images[modulo(x,dax)][modulo(y,day)]; 264 img.image = null; 265 img.flushedResizedCachedInstance(); 266 img.downloadingStarted = false; 267 img.failed = false; 268 mv.repaint(); 269 } 270 } 271 } 272 } 273 274 public class ToggleAlphaAction extends AbstractAction { 275 public ToggleAlphaAction() { 276 super(tr("Alpha channel")); 277 } 278 public void actionPerformed(ActionEvent ev) { 279 JCheckBoxMenuItem checkbox = (JCheckBoxMenuItem) ev.getSource(); 280 boolean alphaChannel = checkbox.isSelected(); 281 Main.pref.put("wmsplugin.alpha_channel", alphaChannel); 282 283 // clear all resized cached instances and repaint the layer 284 for (int x = 0; x < dax; ++x) { 285 for (int y = 0; y < day; ++y) { 286 GeorefImage img = images[modulo(x, dax)][modulo(y, day)]; 287 img.flushedResizedCachedInstance(); 288 } 289 } 290 mv.repaint(); 291 } 292 } 293 294 public class SaveWmsAction extends AbstractAction { 295 public SaveWmsAction() { 296 super(tr("Save WMS layer to file"), ImageProvider.get("save")); 297 } 298 public void actionPerformed(ActionEvent ev) { 299 File f = SaveActionBase.createAndOpenSaveFileChooser( 300 tr("Save WMS layer"), ".wms"); 301 try 302 { 303 FileOutputStream fos = new FileOutputStream(f); 304 ObjectOutputStream oos = new ObjectOutputStream(fos); 305 oos.writeInt(serializeFormatVersion); 306 oos.writeInt(dax); 307 oos.writeInt(day); 308 oos.writeInt(ImageSize); 309 oos.writeDouble(pixelPerDegree); 310 oos.writeObject(getName()); 311 oos.writeObject(baseURL); 312 oos.writeObject(images); 313 oos.close(); 314 fos.close(); 315 } 316 catch (Exception ex) { 317 ex.printStackTrace(System.out); 318 } 319 } 320 } 321 322 public class LoadWmsAction extends AbstractAction { 323 public LoadWmsAction() { 324 super(tr("Load WMS layer from file"), ImageProvider.get("load")); 325 } 326 public void actionPerformed(ActionEvent ev) { 327 JFileChooser fc = DiskAccessAction.createAndOpenFileChooser(true, 328 false, tr("Load WMS layer")); 329 if(fc == null) return; 330 File f = fc.getSelectedFile(); 331 if (f == null) return; 332 try 333 { 334 FileInputStream fis = new FileInputStream(f); 335 ObjectInputStream ois = new ObjectInputStream(fis); 336 int sfv = ois.readInt(); 337 if (sfv != serializeFormatVersion) { 338 OptionPaneUtil.showMessageDialog(Main.parent, 339 tr("Unsupported WMS file version; found {0}, expected {1}", sfv, serializeFormatVersion), 340 tr("File Format Error"), 341 JOptionPane.ERROR_MESSAGE); 342 return; 343 } 344 startstop.setSelected(false); 345 dax = ois.readInt(); 346 day = ois.readInt(); 347 ImageSize = ois.readInt(); 348 pixelPerDegree = ois.readDouble(); 349 setName((String)ois.readObject()); 350 baseURL = (String) ois.readObject(); 351 images = (GeorefImage[][])ois.readObject(); 352 ois.close(); 353 fis.close(); 354 mv.repaint(); 355 } 356 catch (Exception ex) { 357 // FIXME be more specific 358 ex.printStackTrace(System.out); 359 OptionPaneUtil.showMessageDialog(Main.parent, 360 tr("Error loading file"), 361 tr("Error"), 362 JOptionPane.ERROR_MESSAGE); 363 return; 364 } 365 } 366 } 358 367 }
Note:
See TracChangeset
for help on using the changeset viewer.