Changeset 23207 in osm for applications/editors/josm/plugins/wmsplugin
- Timestamp:
- 2010-09-16T11:32:35+02:00 (14 years ago)
- Location:
- applications/editors/josm/plugins/wmsplugin
- Files:
-
- 1 added
- 16 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/wmsplugin/build.xml
r22856 r23207 29 29 30 30 <property name="commit.message" value="fixed josm bug 4671 - wms url for sicily has changed" /> 31 <property name="plugin.main.version" value="3451" /> 32 31 <property name="plugin.main.version" value="3530" /> 33 32 34 33 <property name="josm" location="../../core/dist/josm-custom.jar" /> -
applications/editors/josm/plugins/wmsplugin/src/wmsplugin/GeorefImage.java
r22929 r23207 21 21 22 22 public class GeorefImage implements Serializable { 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 23 private static final long serialVersionUID = 1L; 24 25 public enum State { IMAGE, NOT_IN_CACHE, FAILED}; 26 27 private WMSLayer layer; 28 private State state; 29 30 private BufferedImage image; 31 private SoftReference<BufferedImage> reImg; 32 private int xIndex; 33 private int yIndex; 34 35 36 public EastNorth getMin() { 37 return layer.getEastNorth(xIndex, yIndex); 38 } 39 40 public EastNorth getMax() { 41 return layer.getEastNorth(xIndex+1, yIndex+1); 42 } 43 44 45 public GeorefImage(WMSLayer layer) { 46 this.layer = layer; 47 } 48 49 public void changePosition(int xIndex, int yIndex) { 50 if (!equalPosition(xIndex, yIndex)) { 51 this.xIndex = xIndex; 52 this.yIndex = yIndex; 53 this.image = null; 54 flushedResizedCachedInstance(); 55 } 56 } 57 58 public boolean equalPosition(int xIndex, int yIndex) { 59 return this.xIndex == xIndex && this.yIndex == yIndex; 60 } 61 62 public void changeImage(State state, BufferedImage image) { 63 flushedResizedCachedInstance(); 64 this.image = image; 65 this.state = state; 66 67 switch (state) { 68 case FAILED: 69 { 70 BufferedImage img = createImage(); 71 Graphics g = img.getGraphics(); 72 g.setColor(Color.RED); 73 g.fillRect(0, 0, img.getWidth(), img.getHeight()); 74 g.setFont(g.getFont().deriveFont(Font.PLAIN).deriveFont(36.0f)); 75 g.setColor(Color.BLACK); 76 g.drawString(tr("Exception occurred"), 10, img.getHeight()/2); 77 this.image = img; 78 break; 79 } 80 case NOT_IN_CACHE: 81 { 82 BufferedImage img = createImage(); 83 Graphics g = img.getGraphics(); 84 g.setColor(Color.GRAY); 85 g.fillRect(0, 0, img.getWidth(), img.getHeight()); 86 Font font = g.getFont(); 87 Font tempFont = font.deriveFont(Font.PLAIN).deriveFont(36.0f); 88 g.setFont(tempFont); 89 g.setColor(Color.BLACK); 90 g.drawString(tr("Not in cache"), 10, img.getHeight()/2); 91 g.setFont(font); 92 this.image = img; 93 break; 94 } 95 default: 96 break; 97 } 98 } 99 100 private BufferedImage createImage() { 101 return new BufferedImage(layer.getBaseImageWidth(), layer.getBaseImageHeight(), BufferedImage.TYPE_INT_RGB); 102 } 103 104 public boolean paint(Graphics g, NavigatableComponent nc, int xIndex, int yIndex, int leftEdge, int bottomEdge) { 105 if (image == null) 106 return false; 107 108 if(!(this.xIndex == xIndex && this.yIndex == yIndex)){ 109 return false; 110 } 111 112 int left = layer.getImageX(xIndex); 113 int bottom = layer.getImageY(yIndex); 114 int width = layer.getImageWidth(xIndex); 115 int height = layer.getImageHeight(yIndex); 116 117 int x = left - leftEdge; 118 int y = nc.getHeight() - (bottom - bottomEdge) - height; 119 120 // This happens if you zoom outside the world 121 if(width == 0 || height == 0) 122 return false; 123 124 BufferedImage img = reImg == null?null:reImg.get(); 125 if(img != null && img.getWidth() == width && img.getHeight() == height) { 126 g.drawImage(img, x, y, null); 127 return true; 128 } 129 130 boolean alphaChannel = WMSLayer.PROP_ALPHA_CHANNEL.get() && getImage().getTransparency() != Transparency.OPAQUE; 131 132 try { 133 if(img != null) img.flush(); 134 long freeMem = Runtime.getRuntime().maxMemory() - Runtime.getRuntime().totalMemory(); 135 //System.out.println("Free Memory: "+ (freeMem/1024/1024) +" MB"); 136 // Notice that this value can get negative due to integer overflows 137 //System.out.println("Img Size: "+ (width*height*3/1024/1024) +" MB"); 138 139 int multipl = alphaChannel ? 4 : 3; 140 // This happens when requesting images while zoomed out and then zooming in 141 // Storing images this large in memory will certainly hang up JOSM. Luckily 142 // traditional rendering is as fast at these zoom levels, so it's no loss. 143 // Also prevent caching if we're out of memory soon 144 if(width > 2000 || height > 2000 || width*height*multipl > freeMem) { 145 fallbackDraw(g, getImage(), x, y, width, height); 146 } else { 147 // We haven't got a saved resized copy, so resize and cache it 148 img = new BufferedImage(width, height, alphaChannel?BufferedImage.TYPE_INT_ARGB:BufferedImage.TYPE_3BYTE_BGR); 149 img.getGraphics().drawImage(getImage(), 150 0, 0, width, height, // dest 151 0, 0, getImage().getWidth(null), getImage().getHeight(null), // src 152 null); 153 img.getGraphics().dispose(); 154 g.drawImage(img, x, y, null); 155 reImg = new SoftReference<BufferedImage>(img); 156 } 157 } catch(Exception e) { 158 fallbackDraw(g, getImage(), x, y, width, height); 159 } 160 return true; 161 } 162 163 private void fallbackDraw(Graphics g, Image img, int x, int y, int width, int height) { 164 flushedResizedCachedInstance(); 165 g.drawImage( 166 img, x, y, x + width, y + height, 167 0, 0, img.getWidth(null), img.getHeight(null), 168 null); 169 } 170 171 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 172 state = (State) in.readObject(); 173 boolean hasImage = in.readBoolean(); 174 if (hasImage) 175 image = (ImageIO.read(ImageIO.createImageInputStream(in))); 176 else { 177 in.readObject(); // read null from input stream 178 image = null; 179 } 180 } 181 182 private void writeObject(ObjectOutputStream out) throws IOException { 183 out.writeObject(state); 184 if(getImage() == null) { 185 out.writeBoolean(false); 186 out.writeObject(null); 187 } else { 188 out.writeBoolean(true); 189 ImageIO.write(getImage(), "png", ImageIO.createImageOutputStream(out)); 190 } 191 } 192 193 public void flushedResizedCachedInstance() { 194 if (reImg != null) { 195 BufferedImage img = reImg.get(); 196 if (img != null) { 197 img.flush(); 198 } 199 } 200 reImg = null; 201 } 202 203 204 public BufferedImage getImage() { 205 return image; 206 } 207 208 public State getState() { 209 return state; 210 } 211 212 public int getXIndex() { 213 return xIndex; 214 } 215 216 public int getYIndex() { 217 return yIndex; 218 } 219 220 public void setLayer(WMSLayer layer) { 221 this.layer = layer; 222 } 223 223 } -
applications/editors/josm/plugins/wmsplugin/src/wmsplugin/Grabber.java
r22929 r23207 11 11 12 12 abstract public class Grabber implements Runnable { 13 14 15 13 protected final MapView mv; 14 protected final WMSLayer layer; 15 protected final CacheFiles cache; 16 16 17 18 19 20 21 17 protected ProjectionBounds b; 18 protected Projection proj; 19 protected double pixelPerDegree; 20 protected WMSRequest request; 21 protected volatile boolean canceled; 22 22 23 24 25 26 27 23 Grabber(MapView mv, WMSLayer layer, CacheFiles cache) { 24 this.mv = mv; 25 this.layer = layer; 26 this.cache = cache; 27 } 28 28 29 30 31 32 33 if (b.min != null && b.max != null && WMSPlugin.PROP_OVERLAP.get()) {34 35 29 private void updateState(WMSRequest request) { 30 b = new ProjectionBounds( 31 layer.getEastNorth(request.getXIndex(), request.getYIndex()), 32 layer.getEastNorth(request.getXIndex() + 1, request.getYIndex() + 1)); 33 if (b.min != null && b.max != null && WMSPlugin.instance.PROP_OVERLAP.get()) { 34 double eastSize = b.max.east() - b.min.east(); 35 double northSize = b.max.north() - b.min.north(); 36 36 37 double eastCoef = WMSPlugin.PROP_OVERLAP_EAST.get() / 100.0;38 double northCoef = WMSPlugin.PROP_OVERLAP_NORTH.get() / 100.0;37 double eastCoef = WMSPlugin.instance.PROP_OVERLAP_EAST.get() / 100.0; 38 double northCoef = WMSPlugin.instance.PROP_OVERLAP_NORTH.get() / 100.0; 39 39 40 41 42 43 44 40 this.b = new ProjectionBounds( new EastNorth(b.min.east(), 41 b.min.north()), 42 new EastNorth(b.max.east() + eastCoef * eastSize, 43 b.max.north() + northCoef * northSize)); 44 } 45 45 46 47 48 49 46 this.proj = Main.proj; 47 this.pixelPerDegree = request.getPixelPerDegree(); 48 this.request = request; 49 } 50 50 51 51 abstract void fetch(WMSRequest request) throws Exception; // the image fetch code 52 52 53 54 55 56 57 58 53 int width(){ 54 return layer.getBaseImageWidth(); 55 } 56 int height(){ 57 return layer.getBaseImageHeight(); 58 } 59 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 60 @Override 61 public void run() { 62 while (true) { 63 if (canceled) { 64 return; 65 } 66 WMSRequest request = layer.getRequest(); 67 if (request == null) { 68 return; 69 } 70 updateState(request); 71 if(!loadFromCache(request)){ 72 attempt(request); 73 } 74 if (request.getState() != null) { 75 layer.finishRequest(request); 76 mv.repaint(); 77 } 78 } 79 } 80 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 81 protected void attempt(WMSRequest request){ // try to fetch the image 82 int maxTries = 5; // n tries for every image 83 for (int i = 1; i <= maxTries; i++) { 84 if (canceled) { 85 return; 86 } 87 try { 88 if (!layer.requestIsValid(request)) { 89 return; 90 } 91 fetch(request); 92 break; // break out of the retry loop 93 } catch (Exception e) { 94 try { // sleep some time and then ask the server again 95 Thread.sleep(random(1000, 2000)); 96 } catch (InterruptedException e1) {} 97 97 98 99 100 101 102 103 104 98 if(i == maxTries) { 99 e.printStackTrace(); 100 request.finish(State.FAILED, null); 101 } 102 } 103 } 104 } 105 105 106 107 108 106 public static int random(int min, int max) { 107 return (int)(Math.random() * ((max+1)-min) ) + min; 108 } 109 109 110 110 abstract public boolean loadFromCache(WMSRequest request); 111 111 112 113 114 112 public void cancel() { 113 canceled = true; 114 } 115 115 } -
applications/editors/josm/plugins/wmsplugin/src/wmsplugin/HTMLGrabber.java
r22712 r23207 15 15 16 16 public class HTMLGrabber extends WMSGrabber { 17 HTMLGrabber(MapView mv, WMSLayer layer, CacheFiles cache) { 18 super(mv, layer, cache); 19 this.baseURL = layer.baseURL.replaceFirst("html:", ""); 20 } 17 HTMLGrabber(MapView mv, WMSLayer layer, CacheFiles cache) { 18 super(mv, layer, cache); 19 } 21 20 22 23 24 21 @Override 22 protected BufferedImage grab(URL url) throws IOException { 23 String urlstring = url.toExternalForm(); 25 24 26 25 System.out.println("Grabbing HTML " + url); 27 26 28 29 30 31 32 27 ArrayList<String> cmdParams = new ArrayList<String>(); 28 StringTokenizer st = new StringTokenizer(MessageFormat.format( 29 Main.pref.get("wmsplugin.browser", "webkit-image {0}"), urlstring)); 30 while( st.hasMoreTokens() ) 31 cmdParams.add(st.nextToken()); 33 32 34 33 ProcessBuilder builder = new ProcessBuilder( cmdParams); 35 34 36 37 38 39 40 41 35 Process browser; 36 try { 37 browser = builder.start(); 38 } catch(IOException ioe) { 39 throw new IOException( "Could not start browser. Please check that the executable path is correct.\n" + ioe.getMessage() ); 40 } 42 41 43 44 45 46 42 BufferedImage img = ImageIO.read(browser.getInputStream()); 43 cache.saveImg(urlstring, img); 44 return img; 45 } 47 46 } -
applications/editors/josm/plugins/wmsplugin/src/wmsplugin/Map_Rectifier_WMSmenuAction.java
r22677 r23207 29 29 30 30 public class Map_Rectifier_WMSmenuAction extends JosmAction { 31 /** 32 * Class that bundles all required information of a rectifier service 33 */ 34 public static class RectifierService { 35 private final String name; 36 private final String url; 37 private final String wmsUrl; 38 private final Pattern urlRegEx; 39 private final Pattern idValidator; 40 public JRadioButton btn; 41 /** 42 * @param name: Name of the rectifing service 43 * @param url: URL to the service where users can register, upload, etc. 44 * @param wmsUrl: URL to the WMS server where JOSM will grab the images. Insert __s__ where the ID should be placed 45 * @param urlRegEx: a regular expression that determines if a given URL is one of the service and returns the WMS id if so 46 * @param idValidator: regular expression that checks if a given ID is syntactically valid 47 */ 48 public RectifierService(String name, String url, String wmsUrl, String urlRegEx, String idValidator) { 49 this.name = name; 50 this.url = url; 51 this.wmsUrl = wmsUrl; 52 this.urlRegEx = Pattern.compile(urlRegEx); 53 this.idValidator = Pattern.compile(idValidator); 54 } 55 56 public boolean isSelected() { 57 return btn.isSelected(); 58 } 59 } 60 61 /** 62 * List of available rectifier services. May be extended from the outside 63 */ 64 public ArrayList<RectifierService> services = new ArrayList<RectifierService>(); 65 66 public Map_Rectifier_WMSmenuAction() { 67 super(tr("Rectified Image..."), 68 "OLmarker", 69 tr("Download Rectified Images From Various Services"), 70 Shortcut.registerShortcut("wms:rectimg", 71 tr("WMS: {0}", tr("Rectified Image...")), 72 KeyEvent.VK_R, 73 Shortcut.GROUP_NONE), 74 true 75 ); 76 77 // Add default services 78 services.add( 79 new RectifierService("Metacarta Map Rectifier", 80 "http://labs.metacarta.com/rectifier/", 81 "http://labs.metacarta.com/rectifier/wms.cgi?id=__s__&srs=EPSG:4326" 82 + "&Service=WMS&Version=1.1.0&Request=GetMap&format=image/png&", 83 // This matches more than the "classic" WMS link, so users can pretty much 84 // copy any link as long as it includes the ID 85 "labs\\.metacarta\\.com/(?:.*?)(?:/|=)([0-9]+)(?:\\?|/|\\.|$)", 86 "^[0-9]+$") 87 ); 88 services.add( 89 // TODO: Change all links to mapwarper.net once the project has moved. 90 // The RegEx already matches the new URL and old URLs will be forwarded 91 // to make the transition as smooth as possible for the users 92 new RectifierService("Geothings Map Warper", 93 "http://warper.geothings.net/", 94 "http://warper.geothings.net/maps/wms/__s__?request=GetMap&version=1.1.1" 95 + "&styles=&format=image/png&srs=epsg:4326&exceptions=application/vnd.ogc.se_inimage&", 96 // This matches more than the "classic" WMS link, so users can pretty much 97 // copy any link as long as it includes the ID 98 "(?:mapwarper\\.net|warper\\.geothings\\.net)/(?:.*?)/([0-9]+)(?:\\?|/|\\.|$)", 99 "^[0-9]+$") 100 ); 101 102 // This service serves the purpose of "just this once" without forcing the user 103 // to commit the link to the preferences 104 105 // Clipboard content gets trimmed, so matching whitespace only ensures that this 106 // service will never be selected automatically. 107 services.add(new RectifierService(tr("Custom WMS Link"), "", "", "^\\s+$", "")); 108 } 109 110 public void actionPerformed(ActionEvent e) { 111 JPanel panel = new JPanel(new GridBagLayout()); 112 panel.add(new JLabel(tr("Supported Rectifier Services:")), GBC.eol()); 113 114 JTextField tfWmsUrl = new JTextField(30); 115 116 String clip = getClipboardContents(); 117 ButtonGroup group = new ButtonGroup(); 118 119 JRadioButton firstBtn = null; 120 for(RectifierService s : services) { 121 JRadioButton serviceBtn = new JRadioButton(s.name); 122 if(firstBtn == null) 123 firstBtn = serviceBtn; 124 // Checks clipboard contents against current service if no match has been found yet. 125 // If the contents match, they will be inserted into the text field and the corresponding 126 // service will be pre-selected. 127 if(!clip.equals("") && tfWmsUrl.getText().equals("") 128 && (s.urlRegEx.matcher(clip).find() || s.idValidator.matcher(clip).matches())) { 129 serviceBtn.setSelected(true); 130 tfWmsUrl.setText(clip); 131 } 132 s.btn = serviceBtn; 133 group.add(serviceBtn); 134 if(!s.url.equals("")) { 135 panel.add(serviceBtn, GBC.std()); 136 panel.add(new UrlLabel(s.url, tr("Visit Homepage")), GBC.eol().anchor(GridBagConstraints.EAST)); 137 } else 138 panel.add(serviceBtn, GBC.eol().anchor(GridBagConstraints.WEST)); 139 } 140 141 // Fallback in case no match was found 142 if(tfWmsUrl.getText().equals("") && firstBtn != null) 143 firstBtn.setSelected(true); 144 145 panel.add(new JLabel(tr("WMS URL or Image ID:")), GBC.eol()); 146 panel.add(tfWmsUrl, GBC.eol().fill(GridBagConstraints.HORIZONTAL)); 147 148 ExtendedDialog diag = new ExtendedDialog(Main.parent, 149 tr("Add Rectified Image"), 150 151 new String[] {tr("Add Rectified Image"), tr("Cancel")}); 152 diag.setContent(panel); 153 diag.setButtonIcons(new String[] {"OLmarker.png", "cancel.png"}); 154 155 // This repeatedly shows the dialog in case there has been an error. 156 // The loop is break;-ed if the users cancels 157 outer: while(true) { 158 diag.showDialog(); 159 int answer = diag.getValue(); 160 // Break loop when the user cancels 161 if(answer != 1) 162 break; 163 164 String text = tfWmsUrl.getText().trim(); 165 // Loop all services until we find the selected one 166 for(RectifierService s : services) { 167 if(!s.isSelected()) 168 continue; 169 170 // We've reached the custom WMS URL service 171 // Just set the URL and hope everything works out 172 if(s.wmsUrl.equals("")) { 173 addWMSLayer(s.name + " (" + text + ")", text); 174 break outer; 175 } 176 177 // First try to match if the entered string as an URL 178 Matcher m = s.urlRegEx.matcher(text); 179 if(m.find()) { 180 String id = m.group(1); 181 String newURL = s.wmsUrl.replaceAll("__s__", id); 182 String title = s.name + " (" + id + ")"; 183 addWMSLayer(title, newURL); 184 break outer; 185 } 186 // If not, look if it's a valid ID for the selected service 187 if(s.idValidator.matcher(text).matches()) { 188 String newURL = s.wmsUrl.replaceAll("__s__", text); 189 String title = s.name + " (" + text + ")"; 190 addWMSLayer(title, newURL); 191 break outer; 192 } 193 194 // We've found the selected service, but the entered string isn't suitable for 195 // it. So quit checking the other radio buttons 196 break; 197 } 198 199 // and display an error message. The while(true) ensures that the dialog pops up again 200 JOptionPane.showMessageDialog(Main.parent, 201 tr("Couldn't match the entered link or id to the selected service. Please try again."), 202 tr("No valid WMS URL or id"), 203 JOptionPane.ERROR_MESSAGE); 204 diag.setVisible(true); 205 } 206 } 207 208 /** 209 * Adds a WMS Layer with given title and URL 210 * @param title: Name of the layer as it will shop up in the layer manager 211 * @param url: URL to the WMS server 212 * @param cookies: Cookies to send with each image request (Format: josm=is; very=cool) 213 */ 214 private void addWMSLayer(String title, String url, String cookies) { 215 WMSLayer wmsLayer = new WMSLayer(title, url, cookies); 216 Main.main.addLayer(wmsLayer); 217 } 218 219 /** 220 * Adds a WMS Layer with given title and URL 221 * @param title: Name of the layer as it will shop up in the layer manager 222 * @param url: URL to the WMS server 223 */ 224 private void addWMSLayer(String title, String url) { 225 addWMSLayer(title, url, ""); 226 } 227 228 /** 229 * Helper function that extracts a String from the Clipboard if available. 230 * Returns an empty String otherwise 231 * @return String Clipboard contents if available 232 */ 233 private String getClipboardContents() { 234 String result = ""; 235 Transferable contents = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null); 236 237 if(contents == null || !contents.isDataFlavorSupported(DataFlavor.stringFlavor)) 238 return ""; 239 240 try { 241 result = (String)contents.getTransferData(DataFlavor.stringFlavor); 242 } catch(Exception ex) { 243 return ""; 244 } 245 return result.trim(); 246 } 31 /** 32 * Class that bundles all required information of a rectifier service 33 */ 34 public static class RectifierService { 35 private final String name; 36 private final String url; 37 private final String wmsUrl; 38 private final Pattern urlRegEx; 39 private final Pattern idValidator; 40 public JRadioButton btn; 41 /** 42 * @param name: Name of the rectifing service 43 * @param url: URL to the service where users can register, upload, etc. 44 * @param wmsUrl: URL to the WMS server where JOSM will grab the images. Insert __s__ where the ID should be placed 45 * @param urlRegEx: a regular expression that determines if a given URL is one of the service and returns the WMS id if so 46 * @param idValidator: regular expression that checks if a given ID is syntactically valid 47 */ 48 public RectifierService(String name, String url, String wmsUrl, String urlRegEx, String idValidator) { 49 this.name = name; 50 this.url = url; 51 this.wmsUrl = wmsUrl; 52 this.urlRegEx = Pattern.compile(urlRegEx); 53 this.idValidator = Pattern.compile(idValidator); 54 } 55 56 public boolean isSelected() { 57 return btn.isSelected(); 58 } 59 } 60 61 /** 62 * List of available rectifier services. May be extended from the outside 63 */ 64 public ArrayList<RectifierService> services = new ArrayList<RectifierService>(); 65 66 public Map_Rectifier_WMSmenuAction() { 67 super(tr("Rectified Image..."), 68 "OLmarker", 69 tr("Download Rectified Images From Various Services"), 70 Shortcut.registerShortcut("wms:rectimg", 71 tr("WMS: {0}", tr("Rectified Image...")), 72 KeyEvent.VK_R, 73 Shortcut.GROUP_NONE), 74 true 75 ); 76 77 // Add default services 78 services.add( 79 new RectifierService("Metacarta Map Rectifier", 80 "http://labs.metacarta.com/rectifier/", 81 "http://labs.metacarta.com/rectifier/wms.cgi?id=__s__&srs=EPSG:4326" 82 + "&Service=WMS&Version=1.1.0&Request=GetMap&format=image/png&", 83 // This matches more than the "classic" WMS link, so users can pretty much 84 // copy any link as long as it includes the ID 85 "labs\\.metacarta\\.com/(?:.*?)(?:/|=)([0-9]+)(?:\\?|/|\\.|$)", 86 "^[0-9]+$") 87 ); 88 services.add( 89 // TODO: Change all links to mapwarper.net once the project has moved. 90 // The RegEx already matches the new URL and old URLs will be forwarded 91 // to make the transition as smooth as possible for the users 92 new RectifierService("Geothings Map Warper", 93 "http://warper.geothings.net/", 94 "http://warper.geothings.net/maps/wms/__s__?request=GetMap&version=1.1.1" 95 + "&styles=&format=image/png&srs=epsg:4326&exceptions=application/vnd.ogc.se_inimage&", 96 // This matches more than the "classic" WMS link, so users can pretty much 97 // copy any link as long as it includes the ID 98 "(?:mapwarper\\.net|warper\\.geothings\\.net)/(?:.*?)/([0-9]+)(?:\\?|/|\\.|$)", 99 "^[0-9]+$") 100 ); 101 102 // This service serves the purpose of "just this once" without forcing the user 103 // to commit the link to the preferences 104 105 // Clipboard content gets trimmed, so matching whitespace only ensures that this 106 // service will never be selected automatically. 107 services.add(new RectifierService(tr("Custom WMS Link"), "", "", "^\\s+$", "")); 108 } 109 110 public void actionPerformed(ActionEvent e) { 111 JPanel panel = new JPanel(new GridBagLayout()); 112 panel.add(new JLabel(tr("Supported Rectifier Services:")), GBC.eol()); 113 114 JTextField tfWmsUrl = new JTextField(30); 115 116 String clip = getClipboardContents(); 117 ButtonGroup group = new ButtonGroup(); 118 119 JRadioButton firstBtn = null; 120 for(RectifierService s : services) { 121 JRadioButton serviceBtn = new JRadioButton(s.name); 122 if(firstBtn == null) 123 firstBtn = serviceBtn; 124 // Checks clipboard contents against current service if no match has been found yet. 125 // If the contents match, they will be inserted into the text field and the corresponding 126 // service will be pre-selected. 127 if(!clip.equals("") && tfWmsUrl.getText().equals("") 128 && (s.urlRegEx.matcher(clip).find() || s.idValidator.matcher(clip).matches())) { 129 serviceBtn.setSelected(true); 130 tfWmsUrl.setText(clip); 131 } 132 s.btn = serviceBtn; 133 group.add(serviceBtn); 134 if(!s.url.equals("")) { 135 panel.add(serviceBtn, GBC.std()); 136 panel.add(new UrlLabel(s.url, tr("Visit Homepage")), GBC.eol().anchor(GridBagConstraints.EAST)); 137 } else 138 panel.add(serviceBtn, GBC.eol().anchor(GridBagConstraints.WEST)); 139 } 140 141 // Fallback in case no match was found 142 if(tfWmsUrl.getText().equals("") && firstBtn != null) 143 firstBtn.setSelected(true); 144 145 panel.add(new JLabel(tr("WMS URL or Image ID:")), GBC.eol()); 146 panel.add(tfWmsUrl, GBC.eol().fill(GridBagConstraints.HORIZONTAL)); 147 148 ExtendedDialog diag = new ExtendedDialog(Main.parent, 149 tr("Add Rectified Image"), 150 151 new String[] {tr("Add Rectified Image"), tr("Cancel")}); 152 diag.setContent(panel); 153 diag.setButtonIcons(new String[] {"OLmarker.png", "cancel.png"}); 154 155 // This repeatedly shows the dialog in case there has been an error. 156 // The loop is break;-ed if the users cancels 157 outer: while(true) { 158 diag.showDialog(); 159 int answer = diag.getValue(); 160 // Break loop when the user cancels 161 if(answer != 1) 162 break; 163 164 String text = tfWmsUrl.getText().trim(); 165 // Loop all services until we find the selected one 166 for(RectifierService s : services) { 167 if(!s.isSelected()) 168 continue; 169 170 // We've reached the custom WMS URL service 171 // Just set the URL and hope everything works out 172 if(s.wmsUrl.equals("")) { 173 addWMSLayer(s.name + " (" + text + ")", text); 174 break outer; 175 } 176 177 // First try to match if the entered string as an URL 178 Matcher m = s.urlRegEx.matcher(text); 179 if(m.find()) { 180 String id = m.group(1); 181 String newURL = s.wmsUrl.replaceAll("__s__", id); 182 String title = s.name + " (" + id + ")"; 183 addWMSLayer(title, newURL); 184 break outer; 185 } 186 // If not, look if it's a valid ID for the selected service 187 if(s.idValidator.matcher(text).matches()) { 188 String newURL = s.wmsUrl.replaceAll("__s__", text); 189 String title = s.name + " (" + text + ")"; 190 addWMSLayer(title, newURL); 191 break outer; 192 } 193 194 // We've found the selected service, but the entered string isn't suitable for 195 // it. So quit checking the other radio buttons 196 break; 197 } 198 199 // and display an error message. The while(true) ensures that the dialog pops up again 200 JOptionPane.showMessageDialog(Main.parent, 201 tr("Couldn't match the entered link or id to the selected service. Please try again."), 202 tr("No valid WMS URL or id"), 203 JOptionPane.ERROR_MESSAGE); 204 diag.setVisible(true); 205 } 206 } 207 208 /** 209 * Adds a WMS Layer with given title and URL 210 * @param title: Name of the layer as it will shop up in the layer manager 211 * @param url: URL to the WMS server 212 */ 213 private void addWMSLayer(String title, String url) { 214 Main.main.addLayer(new WMSLayer(new WMSInfo(title, url))); 215 } 216 217 /** 218 * Helper function that extracts a String from the Clipboard if available. 219 * Returns an empty String otherwise 220 * @return String Clipboard contents if available 221 */ 222 private String getClipboardContents() { 223 String result = ""; 224 Transferable contents = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null); 225 226 if(contents == null || !contents.isDataFlavorSupported(DataFlavor.stringFlavor)) 227 return ""; 228 229 try { 230 result = (String)contents.getTransferData(DataFlavor.stringFlavor); 231 } catch(Exception ex) { 232 return ""; 233 } 234 return result.trim(); 235 } 247 236 } -
applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSAdjustAction.java
r22761 r23207 31 31 32 32 public class WMSAdjustAction extends MapMode implements MouseListener, MouseMotionListener{ 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 33 //static private final Logger logger = Logger.getLogger(WMSAdjustAction.class.getName()); 34 35 GeorefImage selectedImage; 36 boolean mouseDown; 37 EastNorth prevEastNorth; 38 private WMSLayer adjustingLayer; 39 40 public WMSAdjustAction(MapFrame mapFrame) { 41 super(tr("Adjust WMS"), "adjustwms", 42 tr("Adjust the position of the selected WMS layer"), mapFrame, 43 ImageProvider.getCursor("normal", "move")); 44 } 45 46 47 48 @Override public void enterMode() { 49 super.enterMode(); 50 if (!hasWMSLayersToAdjust()) { 51 warnNoWMSLayers(); 52 return; 53 } 54 List<WMSLayer> wmsLayers = Main.map.mapView.getLayersOfType(WMSLayer.class); 55 if (wmsLayers.size() == 1) { 56 adjustingLayer = wmsLayers.get(0); 57 } else { 58 adjustingLayer = (WMSLayer)askAdjustLayer(Main.map.mapView.getLayersOfType(WMSLayer.class)); 59 } 60 if (adjustingLayer == null) 61 return; 62 if (!adjustingLayer.isVisible()) { 63 adjustingLayer.setVisible(true); 64 } 65 Main.map.mapView.addMouseListener(this); 66 Main.map.mapView.addMouseMotionListener(this); 67 } 68 69 @Override public void exitMode() { 70 super.exitMode(); 71 Main.map.mapView.removeMouseListener(this); 72 Main.map.mapView.removeMouseMotionListener(this); 73 adjustingLayer = null; 74 } 75 76 @Override public void mousePressed(MouseEvent e) { 77 if (e.getButton() != MouseEvent.BUTTON1) 78 return; 79 80 if (adjustingLayer.isVisible()) { 81 prevEastNorth=Main.map.mapView.getEastNorth(e.getX(),e.getY()); 82 selectedImage = adjustingLayer.findImage(prevEastNorth); 83 if(selectedImage!=null) { 84 Main.map.mapView.setCursor 85 (Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); 86 } 87 } 88 } 89 90 @Override public void mouseDragged(MouseEvent e) { 91 if(selectedImage!=null) { 92 EastNorth eastNorth= 93 Main.map.mapView.getEastNorth(e.getX(),e.getY()); 94 adjustingLayer.displace( 95 eastNorth.east()-prevEastNorth.east(), 96 eastNorth.north()-prevEastNorth.north() 97 ); 98 prevEastNorth = eastNorth; 99 Main.map.mapView.repaint(); 100 } 101 } 102 103 @Override public void mouseReleased(MouseEvent e) { 104 Main.map.mapView.repaint(); 105 Main.map.mapView.setCursor(Cursor.getDefaultCursor()); 106 selectedImage = null; 107 prevEastNorth = null; 108 } 109 110 @Override 111 public void mouseEntered(MouseEvent e) { 112 } 113 114 @Override 115 public void mouseExited(MouseEvent e) { 116 } 117 118 @Override 119 public void mouseMoved(MouseEvent e) { 120 } 121 122 @Override public void mouseClicked(MouseEvent e) { 123 } 124 125 @Override public boolean layerIsSupported(Layer l) { 126 return hasWMSLayersToAdjust(); 127 } 128 129 /** 130 * the list cell renderer used to render layer list entries 131 * 132 */ 133 static public class LayerListCellRenderer extends DefaultListCellRenderer { 134 135 protected boolean isActiveLayer(Layer layer) { 136 if (Main.map == null) 137 return false; 138 if (Main.map.mapView == null) 139 return false; 140 return Main.map.mapView.getActiveLayer() == layer; 141 } 142 143 @Override 144 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, 145 boolean cellHasFocus) { 146 Layer layer = (Layer) value; 147 JLabel label = (JLabel) super.getListCellRendererComponent(list, layer.getName(), index, isSelected, 148 cellHasFocus); 149 Icon icon = layer.getIcon(); 150 label.setIcon(icon); 151 label.setToolTipText(layer.getToolTipText()); 152 return label; 153 } 154 } 155 156 /** 157 * Prompts the user with a list of WMS layers which can be adjusted 158 * 159 * @param adjustableLayers the list of adjustable layers 160 * @return the selected layer; null, if no layer was selected 161 */ 162 protected Layer askAdjustLayer(List<? extends Layer> adjustableLayers) { 163 JComboBox layerList = new JComboBox(); 164 layerList.setRenderer(new LayerListCellRenderer()); 165 layerList.setModel(new DefaultComboBoxModel(adjustableLayers.toArray())); 166 layerList.setSelectedIndex(0); 167 168 JPanel pnl = new JPanel(); 169 pnl.setLayout(new GridBagLayout()); 170 pnl.add(new JLabel(tr("Please select the WMS layer to adjust.")), GBC.eol()); 171 pnl.add(layerList, GBC.eol()); 172 173 ExtendedDialog diag = new ExtendedDialog( 174 Main.parent, 175 tr("Select WMS layer"), 176 new String[] { tr("Start adjusting"),tr("Cancel") } 177 ); 178 diag.setContent(pnl); 179 diag.setButtonIcons(new String[] { "mapmode/adjustwms", "cancel" }); 180 diag.showDialog(); 181 int decision = diag.getValue(); 182 if (decision != 1) 183 return null; 184 Layer adjustLayer = (Layer) layerList.getSelectedItem(); 185 return adjustLayer; 186 } 187 188 /** 189 * Displays a warning message if there are no WMS layers to adjust 190 * 191 */ 192 protected void warnNoWMSLayers() { 193 JOptionPane.showMessageDialog( 194 Main.parent, 195 tr("There are currently no WMS layer to adjust."), 196 tr("No layers to adjust"), 197 JOptionPane.WARNING_MESSAGE 198 ); 199 } 200 201 /** 202 * Replies true if there is at least one WMS layer 203 * 204 * @return true if there is at least one WMS layer 205 */ 206 protected boolean hasWMSLayersToAdjust() { 207 if (Main.map == null) return false; 208 if (Main.map.mapView == null) return false; 209 return ! Main.map.mapView.getLayersOfType(WMSLayer.class).isEmpty(); 210 } 211 212 @Override 213 protected void updateEnabledState() { 214 setEnabled(hasWMSLayersToAdjust()); 215 } 216 216 } -
applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSDownloadAction.java
r22677 r23207 10 10 public class WMSDownloadAction extends JosmAction { 11 11 12 12 private final WMSInfo info; 13 13 14 15 super(info.name, "wmsmenu", tr("Download WMS tile from {0}",info.name), null, false);16 putValue("toolbar", "wms_" + info.name);17 18 14 public WMSDownloadAction(WMSInfo info) { 15 super(info.getMenuName(), "wmsmenu", tr("Download WMS tile from {0}",info.name), null, false); 16 putValue("toolbar", "wms_" + info.getToolbarName()); 17 this.info = info; 18 } 19 19 20 public void actionPerformed(ActionEvent e) { 21 //System.out.println(info.url); 22 23 WMSLayer wmsLayer = new WMSLayer(info.name, info.url, info.cookies); 24 Main.main.addLayer(wmsLayer); 25 } 26 27 public static WMSLayer getLayer(WMSInfo info) { 28 // FIXME: move this to WMSPlugin/WMSInfo/preferences. 29 WMSLayer wmsLayer = new WMSLayer(info.name, info.url, info.cookies); 30 Main.main.addLayer(wmsLayer); 31 return wmsLayer; 32 } 20 public void actionPerformed(ActionEvent e) { 21 WMSLayer wmsLayer = new WMSLayer(info); 22 Main.main.addLayer(wmsLayer); 23 } 33 24 }; -
applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSGrabber.java
r22712 r23207 36 36 37 37 public class WMSGrabber extends Grabber { 38 39 40 41 42 43 44 45 46 47 this.baseURL = layer.baseURL;48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 if(layer.cookies != null && !layer.cookies.equals(""))172 conn.setRequestProperty("Cookie", layer.cookies);173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 38 public static boolean isUrlWithPatterns(String url) { 39 return url != null && url.contains("{") && url.contains("}"); 40 } 41 42 protected String baseURL; 43 private final boolean urlWithPatterns; 44 45 WMSGrabber(MapView mv, WMSLayer layer, CacheFiles cache) { 46 super(mv, layer, cache); 47 this.baseURL = layer.info.url; 48 /* URL containing placeholders? */ 49 urlWithPatterns = isUrlWithPatterns(baseURL); 50 } 51 52 @Override 53 void fetch(WMSRequest request) throws Exception{ 54 URL url = null; 55 try { 56 url = getURL( 57 b.min.east(), b.min.north(), 58 b.max.east(), b.max.north(), 59 width(), height()); 60 request.finish(State.IMAGE, grab(url)); 61 62 } catch(Exception e) { 63 e.printStackTrace(); 64 throw new Exception(e.getMessage() + "\nImage couldn't be fetched: " + (url != null ? url.toString() : "")); 65 } 66 } 67 68 public static final NumberFormat latLonFormat = new DecimalFormat("###0.0000000", 69 new DecimalFormatSymbols(Locale.US)); 70 71 protected URL getURL(double w, double s,double e,double n, 72 int wi, int ht) throws MalformedURLException { 73 String myProj = Main.proj.toCode(); 74 if(Main.proj instanceof Mercator) // don't use mercator code directly 75 { 76 LatLon sw = Main.proj.eastNorth2latlon(new EastNorth(w, s)); 77 LatLon ne = Main.proj.eastNorth2latlon(new EastNorth(e, n)); 78 myProj = "EPSG:4326"; 79 s = sw.lat(); 80 w = sw.lon(); 81 n = ne.lat(); 82 e = ne.lon(); 83 } 84 85 String str = baseURL; 86 String bbox = latLonFormat.format(w) + "," 87 + latLonFormat.format(s) + "," 88 + latLonFormat.format(e) + "," 89 + latLonFormat.format(n); 90 91 if (urlWithPatterns) { 92 str = str.replaceAll("\\{proj\\}", myProj) 93 .replaceAll("\\{bbox\\}", bbox) 94 .replaceAll("\\{w\\}", latLonFormat.format(w)) 95 .replaceAll("\\{s\\}", latLonFormat.format(s)) 96 .replaceAll("\\{e\\}", latLonFormat.format(e)) 97 .replaceAll("\\{n\\}", latLonFormat.format(n)) 98 .replaceAll("\\{width\\}", String.valueOf(wi)) 99 .replaceAll("\\{height\\}", String.valueOf(ht)); 100 } else { 101 str += "bbox=" + bbox 102 + getProjection(baseURL, false) 103 + "&width=" + wi + "&height=" + ht; 104 if (!(baseURL.endsWith("&") || baseURL.endsWith("?"))) { 105 System.out.println(tr("Warning: The base URL ''{0}'' for a WMS service doesn't have a trailing '&' or a trailing '?'.", baseURL)); 106 System.out.println(tr("Warning: Fetching WMS tiles is likely to fail. Please check you preference settings.")); 107 System.out.println(tr("Warning: The complete URL is ''{0}''.", str)); 108 } 109 } 110 return new URL(str.replace(" ", "%20")); 111 } 112 113 static public String getProjection(String baseURL, Boolean warn) 114 { 115 String projname = Main.proj.toCode(); 116 if(Main.proj instanceof Mercator) // don't use mercator code 117 projname = "EPSG:4326"; 118 String res = ""; 119 try 120 { 121 Matcher m = Pattern.compile(".*srs=([a-z0-9:]+).*").matcher(baseURL.toLowerCase()); 122 if(m.matches()) 123 { 124 projname = projname.toLowerCase(); 125 if(!projname.equals(m.group(1)) && warn) 126 { 127 JOptionPane.showMessageDialog(Main.parent, 128 tr("The projection ''{0}'' in URL and current projection ''{1}'' mismatch.\n" 129 + "This may lead to wrong coordinates.", 130 m.group(1), projname), 131 tr("Warning"), 132 JOptionPane.WARNING_MESSAGE); 133 } 134 } 135 else 136 res ="&srs="+projname; 137 } 138 catch(Exception e) 139 { 140 } 141 return res; 142 } 143 144 @Override 145 public boolean loadFromCache(WMSRequest request) { 146 URL url = null; 147 try{ 148 url = getURL( 149 b.min.east(), b.min.north(), 150 b.max.east(), b.max.north(), 151 width(), height()); 152 } catch(Exception e) { 153 return false; 154 } 155 BufferedImage cached = cache.getImg(url.toString()); 156 if((!request.isReal() && !layer.hasAutoDownload()) || cached != null){ 157 if(cached == null){ 158 request.finish(State.NOT_IN_CACHE, null); 159 return true; 160 } 161 request.finish(State.IMAGE, cached); 162 return true; 163 } 164 return false; 165 } 166 167 protected BufferedImage grab(URL url) throws IOException, OsmTransferException { 168 System.out.println("Grabbing WMS " + url); 169 170 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 171 if(layer.info.cookies != null && !layer.info.cookies.equals("")) 172 conn.setRequestProperty("Cookie", layer.info.cookies); 173 conn.setRequestProperty("User-Agent", Main.pref.get("wmsplugin.user_agent", Version.getInstance().getAgentString())); 174 conn.setConnectTimeout(Main.pref.getInteger("wmsplugin.timeout.connect", 30) * 1000); 175 conn.setReadTimeout(Main.pref.getInteger("wmsplugin.timeout.read", 30) * 1000); 176 177 String contentType = conn.getHeaderField("Content-Type"); 178 if( conn.getResponseCode() != 200 179 || contentType != null && !contentType.startsWith("image") ) { 180 throw new IOException(readException(conn)); 181 } 182 183 InputStream is = new ProgressInputStream(conn, null); 184 BufferedImage img = ImageIO.read(is); 185 is.close(); 186 187 cache.saveImg(url.toString(), img); 188 return img; 189 } 190 191 protected String readException(URLConnection conn) throws IOException { 192 StringBuilder exception = new StringBuilder(); 193 InputStream in = conn.getInputStream(); 194 BufferedReader br = new BufferedReader(new InputStreamReader(in)); 195 196 String line = null; 197 while( (line = br.readLine()) != null) { 198 // filter non-ASCII characters and control characters 199 exception.append(line.replaceAll("[^\\p{Print}]", "")); 200 exception.append('\n'); 201 } 202 return exception.toString(); 203 } 204 204 } -
applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSInfo.java
r19417 r23207 1 1 package wmsplugin; 2 3 import java.util.ArrayList; 4 import java.util.Collection; 2 5 3 6 import org.openstreetmap.josm.Main; … … 11 14 12 15 String name; 13 String url; 14 String cookies; 15 int prefid; 16 String url=null; 17 String cookies = null; 18 boolean html = false; 19 double pixelPerDegree = 0.0; 16 20 17 public WMSInfo(String name , String url, int prefid) {18 this (name, url, null, prefid);21 public WMSInfo(String name) { 22 this.name=name; 19 23 } 20 24 21 public WMSInfo(String name, String url , String cookies, int prefid) {25 public WMSInfo(String name, String url) { 22 26 this.name=name; 23 this.url=url; 24 this.cookies=cookies; 25 this.prefid=prefid; 27 setURL(url); 26 28 } 27 29 28 public void save() { 29 Main.pref.put("wmsplugin.url." + prefid + ".name", name); 30 Main.pref.put("wmsplugin.url." + prefid + ".url", url); 30 public WMSInfo(String name, String url, String cookies) { 31 this.name=name; 32 setURL(url); 33 this.cookies=cookies; 34 } 35 36 public WMSInfo(String name, String url, String cookies, double pixelPerDegree) { 37 this.name=name; 38 setURL(url); 39 this.cookies=cookies; 40 this.pixelPerDegree=pixelPerDegree; 41 } 42 43 public ArrayList<String> getInfoArray() { 44 String e2 = null; 45 String e3 = null; 46 String e4 = null; 47 if(url != null && !url.isEmpty()) e2 = getFullURL(); 48 if(cookies != null && !cookies.isEmpty()) e3 = cookies; 49 if(pixelPerDegree != 0.0) e4 = String.valueOf(pixelPerDegree); 50 if(e4 != null && e3 == null) e3 = ""; 51 if(e3 != null && e2 == null) e2 = ""; 52 53 ArrayList<String> res = new ArrayList<String>(); 54 res.add(name); 55 if(e2 != null) res.add(e2); 56 if(e3 != null) res.add(e3); 57 if(e4 != null) res.add(e4); 58 return res; 59 } 60 61 public WMSInfo(Collection<String> list) { 62 ArrayList<String> array = new ArrayList<String>(list); 63 this.name=array.get(0); 64 if(array.size() >= 2) setURL(array.get(1)); 65 if(array.size() >= 3) this.cookies=array.get(2); 66 if(array.size() >= 4) this.pixelPerDegree=Double.valueOf(array.get(3)); 67 } 68 69 public WMSInfo(WMSInfo i) { 70 this.name=i.name; 71 this.url=i.url; 72 this.cookies=i.cookies; 73 this.html=i.html; 74 this.pixelPerDegree=i.pixelPerDegree; 31 75 } 32 76 … … 37 81 i = url.compareTo(in.url); 38 82 if(i == 0) 39 i = prefid-in.prefid;83 i = Double.compare(pixelPerDegree, in.pixelPerDegree); 40 84 return i; 41 85 } 86 87 public boolean equalsBaseValues(WMSInfo in) 88 { 89 return url.equals(in.url); 90 } 91 92 public void setPixelPerDegree(double ppd) { 93 this.pixelPerDegree = ppd; 94 } 95 96 public void setURL(String url) { 97 if(url.startsWith("html:")) { 98 this.url = url.substring(5); 99 html = true; 100 } else { 101 this.url = url; 102 } 103 } 104 105 public String getFullURL() { 106 return html ? "html:" + url : url; 107 } 108 109 public String getToolbarName() 110 { 111 String res = name; 112 if(pixelPerDegree != 0.0) 113 res += "#PPD="+pixelPerDegree; 114 return res; 115 } 116 117 public String getMenuName() 118 { 119 String res = name; 120 if(pixelPerDegree != 0.0) 121 res += " ("+pixelPerDegree+")"; 122 return res; 123 } 42 124 } -
applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSLayer.java
r22954 r23207 54 54 public class WMSLayer extends Layer implements PreferenceChangedListener { 55 55 56 protected static final Icon icon = 57 new ImageIcon(Toolkit.getDefaultToolkit().createImage(WMSPlugin.class.getResource("/images/wms_small.png"))); 58 59 public static final BooleanProperty PROP_ALPHA_CHANNEL = new BooleanProperty("wmsplugin.alpha_channel", true); 60 61 public int messageNum = 5; //limit for messages per layer 62 protected MapView mv; 63 protected String resolution; 64 protected int imageSize = 500; 65 protected int dax = 10; 66 protected int day = 10; 67 protected int daStep = 5; 68 protected int minZoom = 3; 69 70 protected double dx = 0.0; 71 protected double dy = 0.0; 72 73 protected double pixelPerDegree; 74 protected GeorefImage[][] images; 75 protected String baseURL; 76 protected String cookies; 77 protected final int serializeFormatVersion = 5; 78 protected boolean autoDownloadEnabled = true; 79 protected boolean settingsChanged; 80 81 // Image index boundary for current view 82 private volatile int bminx; 83 private volatile int bminy; 84 private volatile int bmaxx; 85 private volatile int bmaxy; 86 private volatile int leftEdge; 87 private volatile int bottomEdge; 88 89 // Request queue 90 private final List<WMSRequest> requestQueue = new ArrayList<WMSRequest>(); 91 private final List<WMSRequest> finishedRequests = new ArrayList<WMSRequest>(); 92 private final Lock requestQueueLock = new ReentrantLock(); 93 private final Condition queueEmpty = requestQueueLock.newCondition(); 94 private final List<Grabber> grabbers = new ArrayList<Grabber>(); 95 private final List<Thread> grabberThreads = new ArrayList<Thread>(); 96 private int threadCount; 97 private int workingThreadCount; 98 private boolean canceled; 99 100 101 /** set to true if this layer uses an invalid base url */ 102 private boolean usesInvalidUrl = false; 103 /** set to true if the user confirmed to use an potentially invalid WMS base url */ 104 private boolean isInvalidUrlConfirmed = false; 105 106 public WMSLayer() { 107 this(tr("Blank Layer"), null, null); 108 initializeImages(); 109 mv = Main.map.mapView; 110 } 111 112 public WMSLayer(String name, String baseURL, String cookies) { 113 super(name); 114 setBackgroundLayer(true); /* set global background variable */ 115 initializeImages(); 116 this.baseURL = baseURL; 117 this.cookies = cookies; 118 WMSGrabber.getProjection(baseURL, true); 119 mv = Main.map.mapView; 120 121 // quick hack to predefine the PixelDensity to reuse the cache 122 int codeIndex = getName().indexOf("#PPD="); 123 if (codeIndex != -1) { 124 pixelPerDegree = Double.valueOf(getName().substring(codeIndex+5)); 125 } else { 126 pixelPerDegree = getPPD(); 127 } 128 resolution = mv.getDist100PixelText(); 129 130 if(baseURL != null) 131 { 132 startGrabberThreads(); 133 } 134 if (baseURL != null && !baseURL.startsWith("html:") && !WMSGrabber.isUrlWithPatterns(baseURL)) { 135 if (!(baseURL.endsWith("&") || baseURL.endsWith("?"))) { 136 if (!confirmMalformedUrl(baseURL)) { 137 System.out.println(tr("Warning: WMS layer deactivated because of malformed base url ''{0}''", baseURL)); 138 usesInvalidUrl = true; 139 setName(getName() + tr("(deactivated)")); 140 return; 141 } else { 142 isInvalidUrlConfirmed = true; 143 } 144 } 145 } 146 147 Main.pref.addPreferenceChangeListener(this); 148 } 149 150 public boolean hasAutoDownload(){ 151 return autoDownloadEnabled; 152 } 153 154 155 @Override 156 public void destroy() { 157 cancelGrabberThreads(false); 158 Main.pref.removePreferenceChangeListener(this); 159 } 160 161 public void initializeImages() { 162 GeorefImage[][] old = images; 163 images = new GeorefImage[dax][day]; 164 if (old != null) { 165 for (int i=0; i<old.length; i++) { 166 for (int k=0; k<old[i].length; k++) { 167 GeorefImage o = old[i][k]; 168 images[modulo(o.getXIndex(),dax)][modulo(o.getYIndex(),day)] = old[i][k]; 169 } 170 } 171 } 172 for(int x = 0; x<dax; ++x) { 173 for(int y = 0; y<day; ++y) { 174 if (images[x][y] == null) { 175 images[x][y]= new GeorefImage(this); 176 } 177 } 178 } 179 } 180 181 @Override public Icon getIcon() { 182 return icon; 183 } 184 185 @Override public String getToolTipText() { 186 if(autoDownloadEnabled) 187 return tr("WMS layer ({0}), automatically downloading in zoom {1}", getName(), resolution); 188 else 189 return tr("WMS layer ({0}), downloading in zoom {1}", getName(), resolution); 190 } 191 192 @Override public boolean isMergable(Layer other) { 193 return false; 194 } 195 196 @Override public void mergeFrom(Layer from) { 197 } 198 199 private int modulo (int a, int b) { 200 return a % b >= 0 ? a%b : a%b+b; 201 } 202 203 private boolean zoomIsTooBig() { 204 //don't download when it's too outzoomed 205 return pixelPerDegree / getPPD() > minZoom; 206 } 207 208 @Override public void paint(Graphics2D g, final MapView mv, Bounds b) { 209 if(baseURL == null) return; 210 if (usesInvalidUrl && !isInvalidUrlConfirmed) return; 211 212 settingsChanged = false; 213 214 ProjectionBounds bounds = mv.getProjectionBounds(); 215 bminx= getImageXIndex(bounds.min.east()); 216 bminy= getImageYIndex(bounds.min.north()); 217 bmaxx= getImageXIndex(bounds.max.east()); 218 bmaxy= getImageYIndex(bounds.max.north()); 219 220 leftEdge = (int)(bounds.min.east() * getPPD()); 221 bottomEdge = (int)(bounds.min.north() * getPPD()); 222 223 if (zoomIsTooBig()) { 224 for(int x = bminx; x<=bmaxx; ++x) { 225 for(int y = bminy; y<=bmaxy; ++y) { 226 images[modulo(x,dax)][modulo(y,day)].paint(g, mv, x, y, leftEdge, bottomEdge); 227 } 228 } 229 } else { 230 downloadAndPaintVisible(g, mv, false); 231 } 232 } 233 234 protected boolean confirmMalformedUrl(String url) { 235 if (isInvalidUrlConfirmed) 236 return true; 237 String msg = tr("<html>The base URL<br>" 238 + "''{0}''<br>" 239 + "for this WMS layer does neither end with a ''&'' nor with a ''?''.<br>" 240 + "This is likely to lead to invalid WMS request. You should check your<br>" 241 + "preference settings.<br>" 242 + "Do you want to fetch WMS tiles anyway?", 243 url); 244 String [] options = new String[] { 245 tr("Yes, fetch images"), 246 tr("No, abort") 247 }; 248 int ret = JOptionPane.showOptionDialog( 249 Main.parent, 250 msg, 251 tr("Invalid URL?"), 252 JOptionPane.YES_NO_OPTION, 253 JOptionPane.WARNING_MESSAGE, 254 null, 255 options, options[1] 256 ); 257 switch(ret) { 258 case JOptionPane.YES_OPTION: return true; 259 default: return false; 260 } 261 } 262 263 public double getPPD(){ 264 ProjectionBounds bounds = mv.getProjectionBounds(); 265 return mv.getWidth() / (bounds.max.east() - bounds.min.east()); 266 } 267 268 public void displace(double dx, double dy) { 269 settingsChanged = true; 270 this.dx += dx; 271 this.dy += dy; 272 } 273 274 public int getImageXIndex(double coord) { 275 return (int)Math.floor( ((coord - dx) * pixelPerDegree) / imageSize); 276 } 277 278 public int getImageYIndex(double coord) { 279 return (int)Math.floor( ((coord - dy) * pixelPerDegree) / imageSize); 280 } 281 282 public int getImageX(int imageIndex) { 283 return (int)(imageIndex * imageSize * (getPPD() / pixelPerDegree) + dx * getPPD()); 284 } 285 286 public int getImageY(int imageIndex) { 287 return (int)(imageIndex * imageSize * (getPPD() / pixelPerDegree) + dy * getPPD()); 288 } 289 290 public int getImageWidth(int xIndex) { 291 int overlap = (int)(WMSPlugin.PROP_OVERLAP.get()?WMSPlugin.PROP_OVERLAP_EAST.get() * imageSize * getPPD() / pixelPerDegree / 100:0); 292 return getImageX(xIndex + 1) - getImageX(xIndex) + overlap; 293 } 294 295 public int getImageHeight(int yIndex) { 296 int overlap = (int)(WMSPlugin.PROP_OVERLAP.get()?WMSPlugin.PROP_OVERLAP_NORTH.get() * imageSize * getPPD() / pixelPerDegree / 100:0); 297 return getImageY(yIndex + 1) - getImageY(yIndex) + overlap; 298 } 299 300 /** 301 * 302 * @return Size of image in original zoom 303 */ 304 public int getBaseImageWidth() { 305 int overlap = (WMSPlugin.PROP_OVERLAP.get()?WMSPlugin.PROP_OVERLAP_EAST.get() * imageSize / 100:0); 306 return imageSize + overlap; 307 } 308 309 /** 310 * 311 * @return Size of image in original zoom 312 */ 313 public int getBaseImageHeight() { 314 int overlap = (WMSPlugin.PROP_OVERLAP.get()?WMSPlugin.PROP_OVERLAP_NORTH.get() * imageSize / 100:0); 315 return imageSize + overlap; 316 } 317 318 319 /** 320 * 321 * @param xIndex 322 * @param yIndex 323 * @return Real EastNorth of given tile. dx/dy is not counted in 324 */ 325 public EastNorth getEastNorth(int xIndex, int yIndex) { 326 return new EastNorth((xIndex * imageSize) / pixelPerDegree, (yIndex * imageSize) / pixelPerDegree); 327 } 328 329 330 protected void downloadAndPaintVisible(Graphics g, final MapView mv, boolean real){ 331 332 int newDax = dax; 333 int newDay = day; 334 335 if (bmaxx - bminx >= dax || bmaxx - bminx < dax - 2 * daStep) { 336 newDax = ((bmaxx - bminx) / daStep + 1) * daStep; 337 } 338 339 if (bmaxy - bminy >= day || bmaxy - bminx < day - 2 * daStep) { 340 newDay = ((bmaxy - bminy) / daStep + 1) * daStep; 341 } 342 343 if (newDax != dax || newDay != day) { 344 dax = newDax; 345 day = newDay; 346 initializeImages(); 347 } 348 349 for(int x = bminx; x<=bmaxx; ++x) { 350 for(int y = bminy; y<=bmaxy; ++y){ 351 images[modulo(x,dax)][modulo(y,day)].changePosition(x, y); 352 } 353 } 354 355 gatherFinishedRequests(); 356 357 for(int x = bminx; x<=bmaxx; ++x) { 358 for(int y = bminy; y<=bmaxy; ++y){ 359 GeorefImage img = images[modulo(x,dax)][modulo(y,day)]; 360 if (!img.paint(g, mv, x, y, leftEdge, bottomEdge)) { 361 WMSRequest request = new WMSRequest(x, y, pixelPerDegree, real); 362 addRequest(request); 363 } 364 } 365 } 366 } 367 368 @Override public void visitBoundingBox(BoundingXYVisitor v) { 369 for(int x = 0; x<dax; ++x) { 370 for(int y = 0; y<day; ++y) 371 if(images[x][y].getImage() != null){ 372 v.visit(images[x][y].getMin()); 373 v.visit(images[x][y].getMax()); 374 } 375 } 376 } 377 378 @Override public Object getInfoComponent() { 379 return getToolTipText(); 380 } 381 382 @Override public Action[] getMenuEntries() { 383 return new Action[]{ 384 LayerListDialog.getInstance().createActivateLayerAction(this), 385 LayerListDialog.getInstance().createShowHideLayerAction(), 386 LayerListDialog.getInstance().createDeleteLayerAction(), 387 SeparatorLayerAction.INSTANCE, 388 new LoadWmsAction(), 389 new SaveWmsAction(), 390 new BookmarkWmsAction(), 391 SeparatorLayerAction.INSTANCE, 392 new StartStopAction(), 393 new ToggleAlphaAction(), 394 new ChangeResolutionAction(), 395 new ReloadErrorTilesAction(), 396 new DownloadAction(), 397 SeparatorLayerAction.INSTANCE, 398 new LayerListPopup.InfoAction(this) 399 }; 400 } 401 402 public GeorefImage findImage(EastNorth eastNorth) { 403 int xIndex = getImageXIndex(eastNorth.east()); 404 int yIndex = getImageYIndex(eastNorth.north()); 405 GeorefImage result = images[modulo(xIndex, dax)][modulo(yIndex, day)]; 406 if (result.getXIndex() == xIndex && result.getYIndex() == yIndex) { 407 return result; 408 } else { 409 return null; 410 } 411 } 412 413 /** 414 * 415 * @param request 416 * @return -1 if request is no longer needed, otherwise priority of request (lower number <=> more important request) 417 */ 418 private int getRequestPriority(WMSRequest request) { 419 if (request.getPixelPerDegree() != pixelPerDegree) { 420 return -1; 421 } 422 if (bminx > request.getXIndex() 423 || bmaxx < request.getXIndex() 424 || bminy > request.getYIndex() 425 || bmaxy < request.getYIndex()) { 426 return -1; 427 } 428 429 EastNorth cursorEastNorth = mv.getEastNorth(mv.lastMEvent.getX(), mv.lastMEvent.getY()); 430 int mouseX = getImageXIndex(cursorEastNorth.east()); 431 int mouseY = getImageYIndex(cursorEastNorth.north()); 432 int dx = request.getXIndex() - mouseX; 433 int dy = request.getYIndex() - mouseY; 434 435 return dx * dx + dy * dy; 436 } 437 438 public WMSRequest getRequest() { 439 requestQueueLock.lock(); 440 try { 441 workingThreadCount--; 442 Iterator<WMSRequest> it = requestQueue.iterator(); 443 while (it.hasNext()) { 444 WMSRequest item = it.next(); 445 int priority = getRequestPriority(item); 446 if (priority == -1) { 447 it.remove(); 448 } else { 449 item.setPriority(priority); 450 } 451 } 452 Collections.sort(requestQueue); 453 454 EastNorth cursorEastNorth = mv.getEastNorth(mv.lastMEvent.getX(), mv.lastMEvent.getY()); 455 int mouseX = getImageXIndex(cursorEastNorth.east()); 456 int mouseY = getImageYIndex(cursorEastNorth.north()); 457 boolean isOnMouse = requestQueue.size() > 0 && requestQueue.get(0).getXIndex() == mouseX && requestQueue.get(0).getYIndex() == mouseY; 458 459 // If there is only one thread left then keep it in case we need to download other tile urgently 460 while (!canceled && 461 (requestQueue.isEmpty() || (!isOnMouse && threadCount - workingThreadCount == 0 && threadCount > 1))) { 462 try { 463 queueEmpty.await(); 464 } catch (InterruptedException e) { 465 // Shouldn't happen 466 } 467 } 468 469 workingThreadCount++; 470 if (canceled) { 471 return null; 472 } else { 473 return requestQueue.remove(0); 474 } 475 476 } finally { 477 requestQueueLock.unlock(); 478 } 479 } 480 481 public void finishRequest(WMSRequest request) { 482 if (request.getState() == null) { 483 throw new IllegalArgumentException("Finished request without state"); 484 } 485 requestQueueLock.lock(); 486 try { 487 finishedRequests.add(request); 488 } finally { 489 requestQueueLock.unlock(); 490 } 491 } 492 493 public void addRequest(WMSRequest request) { 494 requestQueueLock.lock(); 495 try { 496 if (!requestQueue.contains(request)) { 497 requestQueue.add(request); 498 queueEmpty.signalAll(); 499 } 500 } finally { 501 requestQueueLock.unlock(); 502 } 503 } 504 505 public boolean requestIsValid(WMSRequest request) { 506 return bminx <= request.getXIndex() && bmaxx >= request.getXIndex() && bminy <= request.getYIndex() && bmaxy >= request.getYIndex(); 507 } 508 509 private void gatherFinishedRequests() { 510 requestQueueLock.lock(); 511 try { 512 for (WMSRequest request: finishedRequests) { 513 GeorefImage img = images[modulo(request.getXIndex(),dax)][modulo(request.getYIndex(),day)]; 514 if (img.equalPosition(request.getXIndex(), request.getYIndex())) { 515 img.changeImage(request.getState(), request.getImage()); 516 } 517 } 518 } finally { 519 finishedRequests.clear(); 520 requestQueueLock.unlock(); 521 } 522 } 523 524 public class DownloadAction extends AbstractAction { 525 private static final long serialVersionUID = -7183852461015284020L; 526 public DownloadAction() { 527 super(tr("Download visible tiles")); 528 } 529 public void actionPerformed(ActionEvent ev) { 530 if (zoomIsTooBig()) { 531 JOptionPane.showMessageDialog( 532 Main.parent, 533 tr("The requested area is too big. Please zoom in a little, or change resolution"), 534 tr("Error"), 535 JOptionPane.ERROR_MESSAGE 536 ); 537 } else { 538 downloadAndPaintVisible(mv.getGraphics(), mv, true); 539 } 540 } 541 } 542 543 public class ChangeResolutionAction extends AbstractAction { 544 public ChangeResolutionAction() { 545 super(tr("Change resolution")); 546 } 547 public void actionPerformed(ActionEvent ev) { 548 initializeImages(); 549 resolution = mv.getDist100PixelText(); 550 pixelPerDegree = getPPD(); 551 settingsChanged = true; 552 mv.repaint(); 553 } 554 } 555 556 public class ReloadErrorTilesAction extends AbstractAction { 557 public ReloadErrorTilesAction() { 558 super(tr("Reload erroneous tiles")); 559 } 560 public void actionPerformed(ActionEvent ev) { 561 // Delete small files, because they're probably blank tiles. 562 // See https://josm.openstreetmap.de/ticket/2307 563 WMSPlugin.cache.customCleanUp(CacheFiles.CLEAN_SMALL_FILES, 4096); 564 565 for (int x = 0; x < dax; ++x) { 566 for (int y = 0; y < day; ++y) { 567 GeorefImage img = images[modulo(x,dax)][modulo(y,day)]; 568 if(img.getState() == State.FAILED){ 569 addRequest(new WMSRequest(img.getXIndex(), img.getYIndex(), pixelPerDegree, true)); 570 mv.repaint(); 571 } 572 } 573 } 574 } 575 } 576 577 public class ToggleAlphaAction extends AbstractAction implements LayerAction { 578 public ToggleAlphaAction() { 579 super(tr("Alpha channel")); 580 } 581 public void actionPerformed(ActionEvent ev) { 582 JCheckBoxMenuItem checkbox = (JCheckBoxMenuItem) ev.getSource(); 583 boolean alphaChannel = checkbox.isSelected(); 584 PROP_ALPHA_CHANNEL.put(alphaChannel); 585 586 // clear all resized cached instances and repaint the layer 587 for (int x = 0; x < dax; ++x) { 588 for (int y = 0; y < day; ++y) { 589 GeorefImage img = images[modulo(x, dax)][modulo(y, day)]; 590 img.flushedResizedCachedInstance(); 591 } 592 } 593 mv.repaint(); 594 } 595 public Component createMenuComponent() { 596 JCheckBoxMenuItem item = new JCheckBoxMenuItem(this); 597 item.setSelected(PROP_ALPHA_CHANNEL.get()); 598 return item; 599 } 600 public boolean supportLayers(List<Layer> layers) { 601 return layers.size() == 1 && layers.get(0) instanceof WMSLayer; 602 } 603 } 604 605 public class SaveWmsAction extends AbstractAction { 606 public SaveWmsAction() { 607 super(tr("Save WMS layer to file"), ImageProvider.get("save")); 608 } 609 public void actionPerformed(ActionEvent ev) { 610 File f = SaveActionBase.createAndOpenSaveFileChooser( 611 tr("Save WMS layer"), ".wms"); 612 try { 613 if (f != null) { 614 ObjectOutputStream oos = new ObjectOutputStream( 615 new FileOutputStream(f) 616 ); 617 oos.writeInt(serializeFormatVersion); 618 oos.writeInt(dax); 619 oos.writeInt(day); 620 oos.writeInt(imageSize); 621 oos.writeDouble(pixelPerDegree); 622 oos.writeObject(getName()); 623 oos.writeObject(baseURL); 624 oos.writeObject(images); 625 oos.close(); 626 } 627 } catch (Exception ex) { 628 ex.printStackTrace(System.out); 629 } 630 } 631 } 632 633 public class LoadWmsAction extends AbstractAction { 634 public LoadWmsAction() { 635 super(tr("Load WMS layer from file"), ImageProvider.get("load")); 636 } 637 public void actionPerformed(ActionEvent ev) { 638 JFileChooser fc = DiskAccessAction.createAndOpenFileChooser(true, 639 false, tr("Load WMS layer"), "wms"); 640 if(fc == null) return; 641 File f = fc.getSelectedFile(); 642 if (f == null) return; 643 try 644 { 645 FileInputStream fis = new FileInputStream(f); 646 ObjectInputStream ois = new ObjectInputStream(fis); 647 int sfv = ois.readInt(); 648 if (sfv != serializeFormatVersion) { 649 JOptionPane.showMessageDialog(Main.parent, 650 tr("Unsupported WMS file version; found {0}, expected {1}", sfv, serializeFormatVersion), 651 tr("File Format Error"), 652 JOptionPane.ERROR_MESSAGE); 653 return; 654 } 655 autoDownloadEnabled = false; 656 dax = ois.readInt(); 657 day = ois.readInt(); 658 imageSize = ois.readInt(); 659 pixelPerDegree = ois.readDouble(); 660 setName((String)ois.readObject()); 661 baseURL = (String) ois.readObject(); 662 images = (GeorefImage[][])ois.readObject(); 663 ois.close(); 664 fis.close(); 665 for (GeorefImage[] imgs : images) { 666 for (GeorefImage img : imgs) { 667 if (img != null) { 668 img.setLayer(WMSLayer.this); 669 } 670 } 671 } 672 settingsChanged = true; 673 mv.repaint(); 674 if(baseURL != null) 675 { 676 startGrabberThreads(); 677 } 678 } 679 catch (Exception ex) { 680 // FIXME be more specific 681 ex.printStackTrace(System.out); 682 JOptionPane.showMessageDialog(Main.parent, 683 tr("Error loading file"), 684 tr("Error"), 685 JOptionPane.ERROR_MESSAGE); 686 return; 687 } 688 } 689 } 690 /** 691 * This action will add a WMS layer menu entry with the current WMS layer 692 * URL and name extended by the current resolution. 693 * When using the menu entry again, the WMS cache will be used properly. 694 */ 695 public class BookmarkWmsAction extends AbstractAction { 696 public BookmarkWmsAction() { 697 super(tr("Set WMS Bookmark")); 698 } 699 public void actionPerformed(ActionEvent ev) { 700 int i = 0; 701 while (Main.pref.hasKey("wmsplugin.url."+i+".url")) { 702 i++; 703 } 704 String baseName; 705 // cut old parameter 706 int parameterIndex = getName().indexOf("#PPD="); 707 if (parameterIndex != -1) { 708 baseName = getName().substring(0,parameterIndex); 709 } 710 else { 711 baseName = getName(); 712 } 713 Main.pref.put("wmsplugin.url."+ i +".url",baseURL ); 714 Main.pref.put("wmsplugin.url."+String.valueOf(i)+".name", 715 baseName + "#PPD=" + pixelPerDegree ); 716 WMSPlugin.refreshMenu(); 717 } 718 } 719 720 private class StartStopAction extends AbstractAction implements LayerAction { 721 722 public StartStopAction() { 723 super(tr("Automatic downloading")); 724 } 725 726 public Component createMenuComponent() { 727 JCheckBoxMenuItem item = new JCheckBoxMenuItem(this); 728 item.setSelected(autoDownloadEnabled); 729 return item; 730 } 731 732 public boolean supportLayers(List<Layer> layers) { 733 return layers.size() == 1 && layers.get(0) instanceof WMSLayer; 734 } 735 736 public void actionPerformed(ActionEvent e) { 737 autoDownloadEnabled = !autoDownloadEnabled; 738 if (autoDownloadEnabled) { 739 mv.repaint(); 740 } 741 } 742 } 743 744 private void cancelGrabberThreads(boolean wait) { 745 requestQueueLock.lock(); 746 try { 747 canceled = true; 748 for (Grabber grabber: grabbers) { 749 grabber.cancel(); 750 } 751 queueEmpty.signalAll(); 752 } finally { 753 requestQueueLock.unlock(); 754 } 755 if (wait) { 756 for (Thread t: grabberThreads) { 757 try { 758 t.join(); 759 } catch (InterruptedException e) { 760 // Shouldn't happen 761 e.printStackTrace(); 762 } 763 } 764 } 765 } 766 767 private void startGrabberThreads() { 768 int threadCount = WMSPlugin.PROP_SIMULTANEOUS_CONNECTIONS.get(); 769 requestQueueLock.lock(); 770 try { 771 canceled = false; 772 grabbers.clear(); 773 grabberThreads.clear(); 774 for (int i=0; i<threadCount; i++) { 775 Grabber grabber = WMSPlugin.getGrabber(mv, this); 776 grabbers.add(grabber); 777 Thread t = new Thread(grabber, "WMS " + getName() + " " + i); 778 t.setDaemon(true); 779 t.start(); 780 grabberThreads.add(t); 781 } 782 this.workingThreadCount = grabbers.size(); 783 this.threadCount = grabbers.size(); 784 } finally { 785 requestQueueLock.unlock(); 786 } 787 } 788 789 @Override 790 public boolean isChanged() { 791 requestQueueLock.lock(); 792 try { 793 return !finishedRequests.isEmpty() || settingsChanged; 794 } finally { 795 requestQueueLock.unlock(); 796 } 797 } 798 799 public void preferenceChanged(PreferenceChangeEvent event) { 800 if (event.getKey().equals(WMSPlugin.PROP_SIMULTANEOUS_CONNECTIONS.getKey())) { 801 cancelGrabberThreads(true); 802 startGrabberThreads(); 803 } else if ( 804 event.getKey().equals(WMSPlugin.PROP_OVERLAP.getKey()) 805 || event.getKey().equals(WMSPlugin.PROP_OVERLAP_EAST.getKey()) 806 || event.getKey().equals(WMSPlugin.PROP_OVERLAP_NORTH.getKey())) { 807 for (int i=0; i<images.length; i++) { 808 for (int k=0; k<images[i].length; k++) { 809 images[i][k] = new GeorefImage(this); 810 } 811 } 812 813 settingsChanged = true; 814 } 815 } 56 protected static final Icon icon = 57 new ImageIcon(Toolkit.getDefaultToolkit().createImage(WMSPlugin.class.getResource("/images/wms_small.png"))); 58 59 public static final BooleanProperty PROP_ALPHA_CHANNEL = new BooleanProperty("wmsplugin.alpha_channel", true); 60 WMSPlugin plugin = WMSPlugin.instance; 61 62 public int messageNum = 5; //limit for messages per layer 63 protected MapView mv; 64 protected String resolution; 65 protected int imageSize = 500; 66 protected int dax = 10; 67 protected int day = 10; 68 protected int daStep = 5; 69 protected int minZoom = 3; 70 71 protected double dx = 0.0; 72 protected double dy = 0.0; 73 74 protected GeorefImage[][] images; 75 protected final int serializeFormatVersion = 5; 76 protected boolean autoDownloadEnabled = true; 77 protected boolean settingsChanged; 78 protected WMSInfo info; 79 80 // Image index boundary for current view 81 private volatile int bminx; 82 private volatile int bminy; 83 private volatile int bmaxx; 84 private volatile int bmaxy; 85 private volatile int leftEdge; 86 private volatile int bottomEdge; 87 88 // Request queue 89 private final List<WMSRequest> requestQueue = new ArrayList<WMSRequest>(); 90 private final List<WMSRequest> finishedRequests = new ArrayList<WMSRequest>(); 91 private final Lock requestQueueLock = new ReentrantLock(); 92 private final Condition queueEmpty = requestQueueLock.newCondition(); 93 private final List<Grabber> grabbers = new ArrayList<Grabber>(); 94 private final List<Thread> grabberThreads = new ArrayList<Thread>(); 95 private int threadCount; 96 private int workingThreadCount; 97 private boolean canceled; 98 99 100 /** set to true if this layer uses an invalid base url */ 101 private boolean usesInvalidUrl = false; 102 /** set to true if the user confirmed to use an potentially invalid WMS base url */ 103 private boolean isInvalidUrlConfirmed = false; 104 105 public WMSLayer() { 106 this(new WMSInfo(tr("Blank Layer"))); 107 } 108 109 public WMSLayer(WMSInfo info) { 110 super(info.name); 111 setBackgroundLayer(true); /* set global background variable */ 112 initializeImages(); 113 this.info = new WMSInfo(info); 114 mv = Main.map.mapView; 115 if(this.info.pixelPerDegree == 0.0) 116 this.info.setPixelPerDegree(getPPD()); 117 resolution = mv.getDist100PixelText(); 118 119 if(info.url != null) { 120 WMSGrabber.getProjection(info.url, true); 121 startGrabberThreads(); 122 if(!info.url.startsWith("html:") && !WMSGrabber.isUrlWithPatterns(info.url)) { 123 if (!(info.url.endsWith("&") || info.url.endsWith("?"))) { 124 if (!confirmMalformedUrl(info.url)) { 125 System.out.println(tr("Warning: WMS layer deactivated because of malformed base url ''{0}''", info.url)); 126 usesInvalidUrl = true; 127 setName(getName() + tr("(deactivated)")); 128 return; 129 } else { 130 isInvalidUrlConfirmed = true; 131 } 132 } 133 } 134 } 135 136 Main.pref.addPreferenceChangeListener(this); 137 } 138 139 public void doSetName(String name) { 140 setName(name); 141 info.name = name; 142 } 143 144 public boolean hasAutoDownload(){ 145 return autoDownloadEnabled; 146 } 147 148 149 @Override 150 public void destroy() { 151 cancelGrabberThreads(false); 152 Main.pref.removePreferenceChangeListener(this); 153 } 154 155 public void initializeImages() { 156 GeorefImage[][] old = images; 157 images = new GeorefImage[dax][day]; 158 if (old != null) { 159 for (int i=0; i<old.length; i++) { 160 for (int k=0; k<old[i].length; k++) { 161 GeorefImage o = old[i][k]; 162 images[modulo(o.getXIndex(),dax)][modulo(o.getYIndex(),day)] = old[i][k]; 163 } 164 } 165 } 166 for(int x = 0; x<dax; ++x) { 167 for(int y = 0; y<day; ++y) { 168 if (images[x][y] == null) { 169 images[x][y]= new GeorefImage(this); 170 } 171 } 172 } 173 } 174 175 @Override public Icon getIcon() { 176 return icon; 177 } 178 179 @Override public String getToolTipText() { 180 if(autoDownloadEnabled) 181 return tr("WMS layer ({0}), automatically downloading in zoom {1}", getName(), resolution); 182 else 183 return tr("WMS layer ({0}), downloading in zoom {1}", getName(), resolution); 184 } 185 186 @Override public boolean isMergable(Layer other) { 187 return false; 188 } 189 190 @Override public void mergeFrom(Layer from) { 191 } 192 193 private int modulo (int a, int b) { 194 return a % b >= 0 ? a%b : a%b+b; 195 } 196 197 private boolean zoomIsTooBig() { 198 //don't download when it's too outzoomed 199 return info.pixelPerDegree / getPPD() > minZoom; 200 } 201 202 @Override public void paint(Graphics2D g, final MapView mv, Bounds b) { 203 if(info.url == null || (usesInvalidUrl && !isInvalidUrlConfirmed)) return; 204 205 settingsChanged = false; 206 207 ProjectionBounds bounds = mv.getProjectionBounds(); 208 bminx= getImageXIndex(bounds.min.east()); 209 bminy= getImageYIndex(bounds.min.north()); 210 bmaxx= getImageXIndex(bounds.max.east()); 211 bmaxy= getImageYIndex(bounds.max.north()); 212 213 leftEdge = (int)(bounds.min.east() * getPPD()); 214 bottomEdge = (int)(bounds.min.north() * getPPD()); 215 216 if (zoomIsTooBig()) { 217 for(int x = bminx; x<=bmaxx; ++x) { 218 for(int y = bminy; y<=bmaxy; ++y) { 219 images[modulo(x,dax)][modulo(y,day)].paint(g, mv, x, y, leftEdge, bottomEdge); 220 } 221 } 222 } else { 223 downloadAndPaintVisible(g, mv, false); 224 } 225 } 226 227 protected boolean confirmMalformedUrl(String url) { 228 if (isInvalidUrlConfirmed) 229 return true; 230 String msg = tr("<html>The base URL<br>" 231 + "''{0}''<br>" 232 + "for this WMS layer does neither end with a ''&'' nor with a ''?''.<br>" 233 + "This is likely to lead to invalid WMS request. You should check your<br>" 234 + "preference settings.<br>" 235 + "Do you want to fetch WMS tiles anyway?", 236 url); 237 String [] options = new String[] { 238 tr("Yes, fetch images"), 239 tr("No, abort") 240 }; 241 int ret = JOptionPane.showOptionDialog( 242 Main.parent, 243 msg, 244 tr("Invalid URL?"), 245 JOptionPane.YES_NO_OPTION, 246 JOptionPane.WARNING_MESSAGE, 247 null, 248 options, options[1] 249 ); 250 switch(ret) { 251 case JOptionPane.YES_OPTION: return true; 252 default: return false; 253 } 254 } 255 256 public double getPPD(){ 257 ProjectionBounds bounds = mv.getProjectionBounds(); 258 return mv.getWidth() / (bounds.max.east() - bounds.min.east()); 259 } 260 261 public void displace(double dx, double dy) { 262 settingsChanged = true; 263 this.dx += dx; 264 this.dy += dy; 265 } 266 267 public int getImageXIndex(double coord) { 268 return (int)Math.floor( ((coord - dx) * info.pixelPerDegree) / imageSize); 269 } 270 271 public int getImageYIndex(double coord) { 272 return (int)Math.floor( ((coord - dy) * info.pixelPerDegree) / imageSize); 273 } 274 275 public int getImageX(int imageIndex) { 276 return (int)(imageIndex * imageSize * (getPPD() / info.pixelPerDegree) + dx * getPPD()); 277 } 278 279 public int getImageY(int imageIndex) { 280 return (int)(imageIndex * imageSize * (getPPD() / info.pixelPerDegree) + dy * getPPD()); 281 } 282 283 public int getImageWidth(int xIndex) { 284 int overlap = (int)(plugin.PROP_OVERLAP.get()?plugin.PROP_OVERLAP_EAST.get() * imageSize * getPPD() / info.pixelPerDegree / 100:0); 285 return getImageX(xIndex + 1) - getImageX(xIndex) + overlap; 286 } 287 288 public int getImageHeight(int yIndex) { 289 int overlap = (int)(plugin.PROP_OVERLAP.get()?plugin.PROP_OVERLAP_NORTH.get() * imageSize * getPPD() / info.pixelPerDegree / 100:0); 290 return getImageY(yIndex + 1) - getImageY(yIndex) + overlap; 291 } 292 293 /** 294 * 295 * @return Size of image in original zoom 296 */ 297 public int getBaseImageWidth() { 298 int overlap = (plugin.PROP_OVERLAP.get()?plugin.PROP_OVERLAP_EAST.get() * imageSize / 100:0); 299 return imageSize + overlap; 300 } 301 302 /** 303 * 304 * @return Size of image in original zoom 305 */ 306 public int getBaseImageHeight() { 307 int overlap = (plugin.PROP_OVERLAP.get()?plugin.PROP_OVERLAP_NORTH.get() * imageSize / 100:0); 308 return imageSize + overlap; 309 } 310 311 312 /** 313 * 314 * @param xIndex 315 * @param yIndex 316 * @return Real EastNorth of given tile. dx/dy is not counted in 317 */ 318 public EastNorth getEastNorth(int xIndex, int yIndex) { 319 return new EastNorth((xIndex * imageSize) / info.pixelPerDegree, (yIndex * imageSize) / info.pixelPerDegree); 320 } 321 322 323 protected void downloadAndPaintVisible(Graphics g, final MapView mv, boolean real){ 324 325 int newDax = dax; 326 int newDay = day; 327 328 if (bmaxx - bminx >= dax || bmaxx - bminx < dax - 2 * daStep) { 329 newDax = ((bmaxx - bminx) / daStep + 1) * daStep; 330 } 331 332 if (bmaxy - bminy >= day || bmaxy - bminx < day - 2 * daStep) { 333 newDay = ((bmaxy - bminy) / daStep + 1) * daStep; 334 } 335 336 if (newDax != dax || newDay != day) { 337 dax = newDax; 338 day = newDay; 339 initializeImages(); 340 } 341 342 for(int x = bminx; x<=bmaxx; ++x) { 343 for(int y = bminy; y<=bmaxy; ++y){ 344 images[modulo(x,dax)][modulo(y,day)].changePosition(x, y); 345 } 346 } 347 348 gatherFinishedRequests(); 349 350 for(int x = bminx; x<=bmaxx; ++x) { 351 for(int y = bminy; y<=bmaxy; ++y){ 352 GeorefImage img = images[modulo(x,dax)][modulo(y,day)]; 353 if (!img.paint(g, mv, x, y, leftEdge, bottomEdge)) { 354 WMSRequest request = new WMSRequest(x, y, info.pixelPerDegree, real); 355 addRequest(request); 356 } 357 } 358 } 359 } 360 361 @Override public void visitBoundingBox(BoundingXYVisitor v) { 362 for(int x = 0; x<dax; ++x) { 363 for(int y = 0; y<day; ++y) 364 if(images[x][y].getImage() != null){ 365 v.visit(images[x][y].getMin()); 366 v.visit(images[x][y].getMax()); 367 } 368 } 369 } 370 371 @Override public Object getInfoComponent() { 372 return getToolTipText(); 373 } 374 375 @Override public Action[] getMenuEntries() { 376 return new Action[]{ 377 LayerListDialog.getInstance().createActivateLayerAction(this), 378 LayerListDialog.getInstance().createShowHideLayerAction(), 379 LayerListDialog.getInstance().createDeleteLayerAction(), 380 SeparatorLayerAction.INSTANCE, 381 new LoadWmsAction(), 382 new SaveWmsAction(), 383 new BookmarkWmsAction(), 384 SeparatorLayerAction.INSTANCE, 385 new StartStopAction(), 386 new ToggleAlphaAction(), 387 new ChangeResolutionAction(), 388 new ReloadErrorTilesAction(), 389 new DownloadAction(), 390 SeparatorLayerAction.INSTANCE, 391 new LayerListPopup.InfoAction(this) 392 }; 393 } 394 395 public GeorefImage findImage(EastNorth eastNorth) { 396 int xIndex = getImageXIndex(eastNorth.east()); 397 int yIndex = getImageYIndex(eastNorth.north()); 398 GeorefImage result = images[modulo(xIndex, dax)][modulo(yIndex, day)]; 399 if (result.getXIndex() == xIndex && result.getYIndex() == yIndex) { 400 return result; 401 } else { 402 return null; 403 } 404 } 405 406 /** 407 * 408 * @param request 409 * @return -1 if request is no longer needed, otherwise priority of request (lower number <=> more important request) 410 */ 411 private int getRequestPriority(WMSRequest request) { 412 if (request.getPixelPerDegree() != info.pixelPerDegree) { 413 return -1; 414 } 415 if (bminx > request.getXIndex() 416 || bmaxx < request.getXIndex() 417 || bminy > request.getYIndex() 418 || bmaxy < request.getYIndex()) { 419 return -1; 420 } 421 422 EastNorth cursorEastNorth = mv.getEastNorth(mv.lastMEvent.getX(), mv.lastMEvent.getY()); 423 int mouseX = getImageXIndex(cursorEastNorth.east()); 424 int mouseY = getImageYIndex(cursorEastNorth.north()); 425 int dx = request.getXIndex() - mouseX; 426 int dy = request.getYIndex() - mouseY; 427 428 return dx * dx + dy * dy; 429 } 430 431 public WMSRequest getRequest() { 432 requestQueueLock.lock(); 433 try { 434 workingThreadCount--; 435 Iterator<WMSRequest> it = requestQueue.iterator(); 436 while (it.hasNext()) { 437 WMSRequest item = it.next(); 438 int priority = getRequestPriority(item); 439 if (priority == -1) { 440 it.remove(); 441 } else { 442 item.setPriority(priority); 443 } 444 } 445 Collections.sort(requestQueue); 446 447 EastNorth cursorEastNorth = mv.getEastNorth(mv.lastMEvent.getX(), mv.lastMEvent.getY()); 448 int mouseX = getImageXIndex(cursorEastNorth.east()); 449 int mouseY = getImageYIndex(cursorEastNorth.north()); 450 boolean isOnMouse = requestQueue.size() > 0 && requestQueue.get(0).getXIndex() == mouseX && requestQueue.get(0).getYIndex() == mouseY; 451 452 // If there is only one thread left then keep it in case we need to download other tile urgently 453 while (!canceled && 454 (requestQueue.isEmpty() || (!isOnMouse && threadCount - workingThreadCount == 0 && threadCount > 1))) { 455 try { 456 queueEmpty.await(); 457 } catch (InterruptedException e) { 458 // Shouldn't happen 459 } 460 } 461 462 workingThreadCount++; 463 if (canceled) { 464 return null; 465 } else { 466 return requestQueue.remove(0); 467 } 468 469 } finally { 470 requestQueueLock.unlock(); 471 } 472 } 473 474 public void finishRequest(WMSRequest request) { 475 if (request.getState() == null) { 476 throw new IllegalArgumentException("Finished request without state"); 477 } 478 requestQueueLock.lock(); 479 try { 480 finishedRequests.add(request); 481 } finally { 482 requestQueueLock.unlock(); 483 } 484 } 485 486 public void addRequest(WMSRequest request) { 487 requestQueueLock.lock(); 488 try { 489 if (!requestQueue.contains(request)) { 490 requestQueue.add(request); 491 queueEmpty.signalAll(); 492 } 493 } finally { 494 requestQueueLock.unlock(); 495 } 496 } 497 498 public boolean requestIsValid(WMSRequest request) { 499 return bminx <= request.getXIndex() && bmaxx >= request.getXIndex() && bminy <= request.getYIndex() && bmaxy >= request.getYIndex(); 500 } 501 502 private void gatherFinishedRequests() { 503 requestQueueLock.lock(); 504 try { 505 for (WMSRequest request: finishedRequests) { 506 GeorefImage img = images[modulo(request.getXIndex(),dax)][modulo(request.getYIndex(),day)]; 507 if (img.equalPosition(request.getXIndex(), request.getYIndex())) { 508 img.changeImage(request.getState(), request.getImage()); 509 } 510 } 511 } finally { 512 finishedRequests.clear(); 513 requestQueueLock.unlock(); 514 } 515 } 516 517 public class DownloadAction extends AbstractAction { 518 private static final long serialVersionUID = -7183852461015284020L; 519 public DownloadAction() { 520 super(tr("Download visible tiles")); 521 } 522 public void actionPerformed(ActionEvent ev) { 523 if (zoomIsTooBig()) { 524 JOptionPane.showMessageDialog( 525 Main.parent, 526 tr("The requested area is too big. Please zoom in a little, or change resolution"), 527 tr("Error"), 528 JOptionPane.ERROR_MESSAGE 529 ); 530 } else { 531 downloadAndPaintVisible(mv.getGraphics(), mv, true); 532 } 533 } 534 } 535 536 public class ChangeResolutionAction extends AbstractAction { 537 public ChangeResolutionAction() { 538 super(tr("Change resolution")); 539 } 540 public void actionPerformed(ActionEvent ev) { 541 initializeImages(); 542 resolution = mv.getDist100PixelText(); 543 info.setPixelPerDegree(getPPD()); 544 settingsChanged = true; 545 mv.repaint(); 546 } 547 } 548 549 public class ReloadErrorTilesAction extends AbstractAction { 550 public ReloadErrorTilesAction() { 551 super(tr("Reload erroneous tiles")); 552 } 553 public void actionPerformed(ActionEvent ev) { 554 // Delete small files, because they're probably blank tiles. 555 // See https://josm.openstreetmap.de/ticket/2307 556 plugin.cache.customCleanUp(CacheFiles.CLEAN_SMALL_FILES, 4096); 557 558 for (int x = 0; x < dax; ++x) { 559 for (int y = 0; y < day; ++y) { 560 GeorefImage img = images[modulo(x,dax)][modulo(y,day)]; 561 if(img.getState() == State.FAILED){ 562 addRequest(new WMSRequest(img.getXIndex(), img.getYIndex(), info.pixelPerDegree, true)); 563 mv.repaint(); 564 } 565 } 566 } 567 } 568 } 569 570 public class ToggleAlphaAction extends AbstractAction implements LayerAction { 571 public ToggleAlphaAction() { 572 super(tr("Alpha channel")); 573 } 574 public void actionPerformed(ActionEvent ev) { 575 JCheckBoxMenuItem checkbox = (JCheckBoxMenuItem) ev.getSource(); 576 boolean alphaChannel = checkbox.isSelected(); 577 PROP_ALPHA_CHANNEL.put(alphaChannel); 578 579 // clear all resized cached instances and repaint the layer 580 for (int x = 0; x < dax; ++x) { 581 for (int y = 0; y < day; ++y) { 582 GeorefImage img = images[modulo(x, dax)][modulo(y, day)]; 583 img.flushedResizedCachedInstance(); 584 } 585 } 586 mv.repaint(); 587 } 588 public Component createMenuComponent() { 589 JCheckBoxMenuItem item = new JCheckBoxMenuItem(this); 590 item.setSelected(PROP_ALPHA_CHANNEL.get()); 591 return item; 592 } 593 public boolean supportLayers(List<Layer> layers) { 594 return layers.size() == 1 && layers.get(0) instanceof WMSLayer; 595 } 596 } 597 598 public class SaveWmsAction extends AbstractAction { 599 public SaveWmsAction() { 600 super(tr("Save WMS layer to file"), ImageProvider.get("save")); 601 } 602 public void actionPerformed(ActionEvent ev) { 603 File f = SaveActionBase.createAndOpenSaveFileChooser( 604 tr("Save WMS layer"), ".wms"); 605 try { 606 if (f != null) { 607 ObjectOutputStream oos = new ObjectOutputStream( 608 new FileOutputStream(f) 609 ); 610 oos.writeInt(serializeFormatVersion); 611 oos.writeInt(dax); 612 oos.writeInt(day); 613 oos.writeInt(imageSize); 614 oos.writeDouble(info.pixelPerDegree); 615 oos.writeObject(info.name); 616 oos.writeObject(info.getFullURL()); 617 oos.writeObject(images); 618 oos.close(); 619 } 620 } catch (Exception ex) { 621 ex.printStackTrace(System.out); 622 } 623 } 624 } 625 626 public class LoadWmsAction extends AbstractAction { 627 public LoadWmsAction() { 628 super(tr("Load WMS layer from file"), ImageProvider.get("load")); 629 } 630 public void actionPerformed(ActionEvent ev) { 631 JFileChooser fc = DiskAccessAction.createAndOpenFileChooser(true, 632 false, tr("Load WMS layer"), "wms"); 633 if(fc == null) return; 634 File f = fc.getSelectedFile(); 635 if (f == null) return; 636 try 637 { 638 FileInputStream fis = new FileInputStream(f); 639 ObjectInputStream ois = new ObjectInputStream(fis); 640 int sfv = ois.readInt(); 641 if (sfv != serializeFormatVersion) { 642 JOptionPane.showMessageDialog(Main.parent, 643 tr("Unsupported WMS file version; found {0}, expected {1}", sfv, serializeFormatVersion), 644 tr("File Format Error"), 645 JOptionPane.ERROR_MESSAGE); 646 return; 647 } 648 autoDownloadEnabled = false; 649 dax = ois.readInt(); 650 day = ois.readInt(); 651 imageSize = ois.readInt(); 652 info.setPixelPerDegree(ois.readDouble()); 653 doSetName((String)ois.readObject()); 654 info.setURL((String) ois.readObject()); 655 images = (GeorefImage[][])ois.readObject(); 656 ois.close(); 657 fis.close(); 658 for (GeorefImage[] imgs : images) { 659 for (GeorefImage img : imgs) { 660 if (img != null) { 661 img.setLayer(WMSLayer.this); 662 } 663 } 664 } 665 settingsChanged = true; 666 mv.repaint(); 667 if(info.url != null) 668 { 669 startGrabberThreads(); 670 } 671 } 672 catch (Exception ex) { 673 // FIXME be more specific 674 ex.printStackTrace(System.out); 675 JOptionPane.showMessageDialog(Main.parent, 676 tr("Error loading file"), 677 tr("Error"), 678 JOptionPane.ERROR_MESSAGE); 679 return; 680 } 681 } 682 } 683 /** 684 * This action will add a WMS layer menu entry with the current WMS layer 685 * URL and name extended by the current resolution. 686 * When using the menu entry again, the WMS cache will be used properly. 687 */ 688 public class BookmarkWmsAction extends AbstractAction { 689 public BookmarkWmsAction() { 690 super(tr("Set WMS Bookmark")); 691 } 692 public void actionPerformed(ActionEvent ev) { 693 plugin.addLayer(new WMSInfo(info)); 694 } 695 } 696 697 private class StartStopAction extends AbstractAction implements LayerAction { 698 699 public StartStopAction() { 700 super(tr("Automatic downloading")); 701 } 702 703 public Component createMenuComponent() { 704 JCheckBoxMenuItem item = new JCheckBoxMenuItem(this); 705 item.setSelected(autoDownloadEnabled); 706 return item; 707 } 708 709 public boolean supportLayers(List<Layer> layers) { 710 return layers.size() == 1 && layers.get(0) instanceof WMSLayer; 711 } 712 713 public void actionPerformed(ActionEvent e) { 714 autoDownloadEnabled = !autoDownloadEnabled; 715 if (autoDownloadEnabled) { 716 mv.repaint(); 717 } 718 } 719 } 720 721 private void cancelGrabberThreads(boolean wait) { 722 requestQueueLock.lock(); 723 try { 724 canceled = true; 725 for (Grabber grabber: grabbers) { 726 grabber.cancel(); 727 } 728 queueEmpty.signalAll(); 729 } finally { 730 requestQueueLock.unlock(); 731 } 732 if (wait) { 733 for (Thread t: grabberThreads) { 734 try { 735 t.join(); 736 } catch (InterruptedException e) { 737 // Shouldn't happen 738 e.printStackTrace(); 739 } 740 } 741 } 742 } 743 744 private void startGrabberThreads() { 745 int threadCount = plugin.PROP_SIMULTANEOUS_CONNECTIONS.get(); 746 requestQueueLock.lock(); 747 try { 748 canceled = false; 749 grabbers.clear(); 750 grabberThreads.clear(); 751 for (int i=0; i<threadCount; i++) { 752 Grabber grabber = plugin.getGrabber(mv, this); 753 grabbers.add(grabber); 754 Thread t = new Thread(grabber, "WMS " + getName() + " " + i); 755 t.setDaemon(true); 756 t.start(); 757 grabberThreads.add(t); 758 } 759 this.workingThreadCount = grabbers.size(); 760 this.threadCount = grabbers.size(); 761 } finally { 762 requestQueueLock.unlock(); 763 } 764 } 765 766 @Override 767 public boolean isChanged() { 768 requestQueueLock.lock(); 769 try { 770 return !finishedRequests.isEmpty() || settingsChanged; 771 } finally { 772 requestQueueLock.unlock(); 773 } 774 } 775 776 public void preferenceChanged(PreferenceChangeEvent event) { 777 if (event.getKey().equals(plugin.PROP_SIMULTANEOUS_CONNECTIONS.getKey())) { 778 cancelGrabberThreads(true); 779 startGrabberThreads(); 780 } else if ( 781 event.getKey().equals(plugin.PROP_OVERLAP.getKey()) 782 || event.getKey().equals(plugin.PROP_OVERLAP_EAST.getKey()) 783 || event.getKey().equals(plugin.PROP_OVERLAP_NORTH.getKey())) { 784 for (int i=0; i<images.length; i++) { 785 for (int k=0; k<images[i].length; k++) { 786 images[i][k] = new GeorefImage(this); 787 } 788 } 789 790 settingsChanged = true; 791 } 792 } 816 793 817 794 } -
applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSPlugin.java
r22960 r23207 7 7 import java.awt.event.ActionEvent; 8 8 import java.awt.event.KeyEvent; 9 import java.io.BufferedReader;10 9 import java.io.File; 11 import java.io.FileNotFoundException;12 import java.io.FileOutputStream;13 import java.io.IOException;14 import java.io.InputStream;15 import java.io.InputStreamReader;16 import java.io.UnsupportedEncodingException;17 10 import java.lang.reflect.InvocationTargetException; 18 11 import java.lang.reflect.Method; 19 import java.util.ArrayList;20 import java.util.Collections;21 import java.util.Map;22 import java.util.TreeMap;23 import java.util.TreeSet;24 12 25 13 import javax.swing.JMenu; … … 38 26 import org.openstreetmap.josm.gui.preferences.PreferenceSetting; 39 27 import org.openstreetmap.josm.io.CacheFiles; 40 import org.openstreetmap.josm.io.MirroredInputStream;41 28 import org.openstreetmap.josm.plugins.Plugin; 42 29 import org.openstreetmap.josm.plugins.PluginHandler; … … 48 35 49 36 public class WMSPlugin extends Plugin { 50 static CacheFiles cache = new CacheFiles("wmsplugin"); 51 52 public static final IntegerProperty PROP_SIMULTANEOUS_CONNECTIONS = new IntegerProperty("wmsplugin.simultaneousConnections", 3); 53 public static final BooleanProperty PROP_OVERLAP = new BooleanProperty("wmsplugin.url.overlap", false); 54 public static final IntegerProperty PROP_OVERLAP_EAST = new IntegerProperty("wmsplugin.url.overlapEast", 14); 55 public static final IntegerProperty PROP_OVERLAP_NORTH = new IntegerProperty("wmsplugin.url.overlapNorth", 4); 56 57 WMSLayer wmsLayer; 58 static JMenu wmsJMenu; 59 60 static ArrayList<WMSInfo> wmsList = new ArrayList<WMSInfo>(); 61 static TreeMap<String,String> wmsListDefault = new TreeMap<String,String>(); 62 63 // remember state of menu item to restore on changed preferences 64 static private boolean menuEnabled = false; 65 66 /*************************************************************** 67 * Remote control initialization: 68 * If you need remote control in some other plug-in 69 * copy this stuff and the call to initRemoteControl below 70 * and replace the RequestHandler subclass in initRemoteControl 71 ***************************************************************/ 72 73 /** name of remote control plugin */ 74 private static final String REMOTECONTROL_NAME = "remotecontrol"; 75 76 /* if necessary change these version numbers to ensure compatibility */ 77 78 /** RemoteControlPlugin older than this SVN revision is not compatible */ 79 static final int REMOTECONTROL_MIN_REVISION = 22734; 80 /** WMSPlugin needs this specific API major version of RemoteControlPlugin */ 81 static final int REMOTECONTROL_NEED_API_MAJOR = 1; 82 /** All API minor versions starting from this should be compatible */ 83 static final int REMOTECONTROL_MIN_API_MINOR = 0; 84 85 /* these fields will contain state and version of remote control plug-in */ 86 boolean remoteControlAvailable = false; 87 boolean remoteControlCompatible = true; 88 boolean remoteControlInitialized = false; 89 int remoteControlRevision = 0; 90 int remoteControlApiMajor = 0; 91 int remoteControlApiMinor = 0; 92 int remoteControlProtocolMajor = 0; 93 int remoteControlProtocolMinor = 0; 94 95 /** 96 * Check if remote control plug-in is available and if its version is 97 * high enough and register remote control command for this plug-in. 98 */ 99 private void initRemoteControl() { 100 for(PluginProxy pp: PluginHandler.pluginList) 101 { 102 PluginInformation info = pp.getPluginInformation(); 103 if(REMOTECONTROL_NAME.equals(info.name)) 104 { 105 remoteControlAvailable = true; 106 remoteControlRevision = Integer.parseInt(info.version); 107 if(REMOTECONTROL_MIN_REVISION > remoteControlRevision) 108 { 109 remoteControlCompatible = false; 110 } 111 } 112 } 113 114 if(remoteControlAvailable && remoteControlCompatible) 115 { 116 Plugin plugin = 117 (Plugin) PluginHandler.getPlugin(REMOTECONTROL_NAME); 118 try { 119 Method method; 120 method = plugin.getClass().getMethod("getVersion"); 121 Object obj = method.invoke(plugin); 122 if((obj != null ) && (obj instanceof int[])) 123 { 124 int[] versions = (int[]) obj; 125 if(versions.length >= 4) 126 { 127 remoteControlApiMajor = versions[0]; 128 remoteControlApiMinor = versions[1]; 129 remoteControlProtocolMajor = versions[2]; 130 remoteControlProtocolMinor = versions[3]; 131 } 132 } 133 134 if((remoteControlApiMajor != REMOTECONTROL_NEED_API_MAJOR) || 135 (remoteControlApiMinor < REMOTECONTROL_MIN_API_MINOR)) 136 { 137 remoteControlCompatible = false; 138 } 139 if(remoteControlCompatible) 140 { 141 System.out.println(this.getClass().getSimpleName() + ": initializing remote control"); 142 method = plugin.getClass().getMethod("addRequestHandler", String.class, Class.class); 143 // replace command and class when you copy this to some other plug-in 144 // for compatibility with old remotecontrol add leading "/" 145 method.invoke(plugin, "/" + WMSRemoteHandler.command, WMSRemoteHandler.class); 146 remoteControlInitialized = true; 147 } 148 } catch (SecurityException e) { 149 e.printStackTrace(); 150 } catch (NoSuchMethodException e) { 151 e.printStackTrace(); 152 } catch (IllegalArgumentException e) { 153 e.printStackTrace(); 154 } catch (IllegalAccessException e) { 155 e.printStackTrace(); 156 } catch (InvocationTargetException e) { 157 e.printStackTrace(); 158 } 159 } 160 if(remoteControlAvailable) 161 { 162 String msg = null; 163 164 if(remoteControlCompatible) 165 { 166 if(!remoteControlInitialized) 167 { 168 msg = tr("Could not initialize remote control."); 169 } 170 } 171 else 172 { 173 msg = tr("Remote control plugin is not compatible with {0}.", 174 this.getClass().getSimpleName()); 175 } 176 177 if(msg != null) 178 { 179 String additionalMessage = tr("{0} will work but remote control for this plugin is disabled.\n" 180 + "You should update the plugins.", 181 this.getClass().getSimpleName()); 182 String versionMessage = tr("Current version of \"{1}\": {2}, internal version {3}. " 183 + "Need version {4}, internal version {5}.\n" 184 + "If updating the plugins does not help report a bug for \"{0}\".", 185 this.getClass().getSimpleName(), 186 REMOTECONTROL_NAME, 187 ""+remoteControlRevision, 188 (remoteControlApiMajor != 0) ? 189 ""+remoteControlApiMajor+"."+remoteControlApiMinor : 190 tr("unknown"), 191 ""+REMOTECONTROL_MIN_REVISION, 192 ""+REMOTECONTROL_NEED_API_MAJOR+"."+REMOTECONTROL_MIN_API_MINOR ); 193 194 String title = tr("{0}: Problem with remote control", 195 this.getClass().getSimpleName()); 196 197 System.out.println(this.getClass().getSimpleName() + ": " + 198 msg + "\n" + versionMessage); 199 200 JOptionPane.showMessageDialog( 201 Main.parent, 202 msg + "\n" + additionalMessage, 203 title, 204 JOptionPane.WARNING_MESSAGE 205 ); 206 } 207 } 208 209 if(!remoteControlAvailable) { 210 System.out.println(this.getClass().getSimpleName() + ": remote control not available"); 211 } 212 } 213 214 /*************************************** 215 * end of remote control initialization 216 ***************************************/ 217 218 protected void initExporterAndImporter() { 219 ExtensionFileFilter.exporters.add(new WMSLayerExporter()); 220 ExtensionFileFilter.importers.add(new WMSLayerImporter()); 221 } 222 223 public WMSPlugin(PluginInformation info) { 224 super(info); 225 /* 226 System.out.println("constructor " + this.getClass().getName() + " (" + info.name + 227 " v " + info.version + " stage " + info.stage + ")"); 228 */ 229 refreshMenu(); 230 cache.setExpire(CacheFiles.EXPIRE_MONTHLY, false); 231 cache.setMaxSize(70, false); 232 initExporterAndImporter(); 233 initRemoteControl(); 234 } 235 236 // this parses the preferences settings. preferences for the wms plugin have to 237 // look like this: 238 // wmsplugin.1.name=Landsat 239 // wmsplugin.1.url=http://and.so.on/ 240 241 @Override 242 public void copy(String from, String to) throws FileNotFoundException, IOException 243 { 244 File pluginDir = new File(getPrefsPath()); 245 if (!pluginDir.exists()) 246 pluginDir.mkdirs(); 247 FileOutputStream out = new FileOutputStream(getPrefsPath() + to); 248 InputStream in = WMSPlugin.class.getResourceAsStream(from); 249 byte[] buffer = new byte[8192]; 250 for(int len = in.read(buffer); len > 0; len = in.read(buffer)) 251 out.write(buffer, 0, len); 252 in.close(); 253 out.close(); 254 } 255 256 257 public static void refreshMenu() { 258 wmsList.clear(); 259 Map<String,String> prefs = Main.pref.getAllPrefix("wmsplugin.url."); 260 261 TreeSet<String> keys = new TreeSet<String>(prefs.keySet()); 262 263 // And then the names+urls of WMS servers 264 int prefid = 0; 265 String name = null; 266 String url = null; 267 String cookies = ""; 268 int lastid = -1; 269 for (String key : keys) { 270 String[] elements = key.split("\\."); 271 if (elements.length != 4) continue; 272 try { 273 prefid = Integer.parseInt(elements[2]); 274 } catch(NumberFormatException e) { 275 continue; 276 } 277 if (prefid != lastid) { 278 name = url = null; lastid = prefid; 279 } 280 if (elements[3].equals("name")) 281 name = prefs.get(key); 282 else if (elements[3].equals("url")) 283 { 284 /* FIXME: Remove the if clause after some time */ 285 if(!prefs.get(key).startsWith("yahoo:")) /* legacy stuff */ 286 url = prefs.get(key); 287 } 288 else if (elements[3].equals("cookies")) 289 cookies = prefs.get(key); 290 if (name != null && url != null) 291 wmsList.add(new WMSInfo(name, url, cookies, prefid)); 292 } 293 String source = "http://svn.openstreetmap.org/applications/editors/josm/plugins/wmsplugin/sources.cfg"; 294 try 295 { 296 MirroredInputStream s = new MirroredInputStream(source, 297 Main.pref.getPreferencesDir() + "plugins/wmsplugin/", -1); 298 InputStreamReader r; 299 try 300 { 301 r = new InputStreamReader(s, "UTF-8"); 302 } 303 catch (UnsupportedEncodingException e) 304 { 305 r = new InputStreamReader(s); 306 } 307 BufferedReader reader = new BufferedReader(r); 308 String line; 309 while((line = reader.readLine()) != null) 310 { 311 String val[] = line.split(";"); 312 if(!line.startsWith("#") && val.length == 3) 313 setDefault("true".equals(val[0]), tr(val[1]), val[2]); 314 } 315 } 316 catch (IOException e) 317 { 318 } 319 320 Collections.sort(wmsList); 321 MainMenu menu = Main.main.menu; 322 323 if (wmsJMenu == null) 324 wmsJMenu = menu.addMenu(marktr("WMS"), KeyEvent.VK_W, menu.defaultMenuPos, ht("/Plugin/WMS")); 325 else 326 wmsJMenu.removeAll(); 327 328 // for each configured WMSInfo, add a menu entry. 329 for (final WMSInfo u : wmsList) { 330 wmsJMenu.add(new JMenuItem(new WMSDownloadAction(u))); 331 } 332 wmsJMenu.addSeparator(); 333 wmsJMenu.add(new JMenuItem(new Map_Rectifier_WMSmenuAction())); 334 335 wmsJMenu.addSeparator(); 336 wmsJMenu.add(new JMenuItem(new 337 JosmAction(tr("Blank Layer"), "blankmenu", tr("Open a blank WMS layer to load data from a file"), null, false) { 338 public void actionPerformed(ActionEvent ev) { 339 Main.main.addLayer(new WMSLayer()); 340 } 341 })); 342 setEnabledAll(menuEnabled); 343 } 344 345 /* add a default entry in case the URL does not yet exist */ 346 private static void setDefault(Boolean force, String name, String url) 347 { 348 String testurl = url.replaceAll("=", "_"); 349 wmsListDefault.put(name, url); 350 351 if(force && !Main.pref.getBoolean("wmsplugin.default."+testurl)) 352 { 353 Main.pref.put("wmsplugin.default."+testurl, true); 354 int id = -1; 355 for(WMSInfo i : wmsList) 356 { 357 if(url.equals(i.url)) 358 return; 359 if(i.prefid > id) 360 id = i.prefid; 361 } 362 WMSInfo newinfo = new WMSInfo(name, url, id+1); 363 newinfo.save(); 364 wmsList.add(newinfo); 365 } 366 } 367 368 public static Grabber getGrabber(MapView mv, WMSLayer layer){ 369 if(layer.baseURL.startsWith("html:")) 370 return new HTMLGrabber(mv, layer, cache); 371 else 372 return new WMSGrabber(mv, layer, cache); 373 } 374 375 private static void setEnabledAll(boolean isEnabled) { 376 for(int i=0; i < wmsJMenu.getItemCount(); i++) { 377 JMenuItem item = wmsJMenu.getItem(i); 378 379 if(item != null) item.setEnabled(isEnabled); 380 } 381 menuEnabled = isEnabled; 382 } 383 384 @Override 385 public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) { 386 if (oldFrame==null && newFrame!=null) { 387 setEnabledAll(true); 388 Main.map.addMapMode(new IconToggleButton 389 (new WMSAdjustAction(Main.map))); 390 } else if (oldFrame!=null && newFrame==null ) { 391 setEnabledAll(false); 392 } 393 } 394 395 @Override 396 public PreferenceSetting getPreferenceSetting() { 397 return new WMSPreferenceEditor(); 398 } 399 400 static public String getPrefsPath() 401 { 402 return Main.pref.getPluginsDirectory().getPath() + "/wmsplugin/"; 403 } 37 CacheFiles cache = new CacheFiles("wmsplugin"); 38 39 public final IntegerProperty PROP_SIMULTANEOUS_CONNECTIONS = new IntegerProperty("wmsplugin.simultaneousConnections", 3); 40 public final BooleanProperty PROP_OVERLAP = new BooleanProperty("wmsplugin.url.overlap", false); 41 public final IntegerProperty PROP_OVERLAP_EAST = new IntegerProperty("wmsplugin.url.overlapEast", 14); 42 public final IntegerProperty PROP_OVERLAP_NORTH = new IntegerProperty("wmsplugin.url.overlapNorth", 4); 43 44 JMenu wmsJMenu; 45 static WMSPlugin instance; 46 47 public WMSLayerInfo info = new WMSLayerInfo(); 48 49 // remember state of menu item to restore on changed preferences 50 private boolean menuEnabled = false; 51 52 /*************************************************************** 53 * Remote control initialization: 54 * If you need remote control in some other plug-in 55 * copy this stuff and the call to initRemoteControl below 56 * and replace the RequestHandler subclass in initRemoteControl 57 ***************************************************************/ 58 59 /** name of remote control plugin */ 60 private final String REMOTECONTROL_NAME = "remotecontrol"; 61 62 /* if necessary change these version numbers to ensure compatibility */ 63 64 /** RemoteControlPlugin older than this SVN revision is not compatible */ 65 final int REMOTECONTROL_MIN_REVISION = 22734; 66 /** WMSPlugin needs this specific API major version of RemoteControlPlugin */ 67 final int REMOTECONTROL_NEED_API_MAJOR = 1; 68 /** All API minor versions starting from this should be compatible */ 69 final int REMOTECONTROL_MIN_API_MINOR = 0; 70 71 /* these fields will contain state and version of remote control plug-in */ 72 boolean remoteControlAvailable = false; 73 boolean remoteControlCompatible = true; 74 boolean remoteControlInitialized = false; 75 int remoteControlRevision = 0; 76 int remoteControlApiMajor = 0; 77 int remoteControlApiMinor = 0; 78 int remoteControlProtocolMajor = 0; 79 int remoteControlProtocolMinor = 0; 80 81 /** 82 * Check if remote control plug-in is available and if its version is 83 * high enough and register remote control command for this plug-in. 84 */ 85 private void initRemoteControl() { 86 for(PluginProxy pp: PluginHandler.pluginList) 87 { 88 PluginInformation info = pp.getPluginInformation(); 89 if(REMOTECONTROL_NAME.equals(info.name)) 90 { 91 remoteControlAvailable = true; 92 remoteControlRevision = Integer.parseInt(info.version); 93 if(REMOTECONTROL_MIN_REVISION > remoteControlRevision) 94 { 95 remoteControlCompatible = false; 96 } 97 } 98 } 99 100 if(remoteControlAvailable && remoteControlCompatible) 101 { 102 Plugin plugin = 103 (Plugin) PluginHandler.getPlugin(REMOTECONTROL_NAME); 104 try { 105 Method method; 106 method = plugin.getClass().getMethod("getVersion"); 107 Object obj = method.invoke(plugin); 108 if((obj != null ) && (obj instanceof int[])) 109 { 110 int[] versions = (int[]) obj; 111 if(versions.length >= 4) 112 { 113 remoteControlApiMajor = versions[0]; 114 remoteControlApiMinor = versions[1]; 115 remoteControlProtocolMajor = versions[2]; 116 remoteControlProtocolMinor = versions[3]; 117 } 118 } 119 120 if((remoteControlApiMajor != REMOTECONTROL_NEED_API_MAJOR) || 121 (remoteControlApiMinor < REMOTECONTROL_MIN_API_MINOR)) 122 { 123 remoteControlCompatible = false; 124 } 125 if(remoteControlCompatible) 126 { 127 System.out.println(this.getClass().getSimpleName() + ": initializing remote control"); 128 method = plugin.getClass().getMethod("addRequestHandler", String.class, Class.class); 129 // replace command and class when you copy this to some other plug-in 130 // for compatibility with old remotecontrol add leading "/" 131 method.invoke(plugin, "/" + WMSRemoteHandler.command, WMSRemoteHandler.class); 132 remoteControlInitialized = true; 133 } 134 } catch (SecurityException e) { 135 e.printStackTrace(); 136 } catch (NoSuchMethodException e) { 137 e.printStackTrace(); 138 } catch (IllegalArgumentException e) { 139 e.printStackTrace(); 140 } catch (IllegalAccessException e) { 141 e.printStackTrace(); 142 } catch (InvocationTargetException e) { 143 e.printStackTrace(); 144 } 145 } 146 if(remoteControlAvailable) 147 { 148 String msg = null; 149 150 if(remoteControlCompatible) 151 { 152 if(!remoteControlInitialized) 153 { 154 msg = tr("Could not initialize remote control."); 155 } 156 } 157 else 158 { 159 msg = tr("Remote control plugin is not compatible with {0}.", 160 this.getClass().getSimpleName()); 161 } 162 163 if(msg != null) 164 { 165 String additionalMessage = tr("{0} will work but remote control for this plugin is disabled.\n" 166 + "You should update the plugins.", 167 this.getClass().getSimpleName()); 168 String versionMessage = tr("Current version of \"{1}\": {2}, internal version {3}. " 169 + "Need version {4}, internal version {5}.\n" 170 + "If updating the plugins does not help report a bug for \"{0}\".", 171 this.getClass().getSimpleName(), 172 REMOTECONTROL_NAME, 173 ""+remoteControlRevision, 174 (remoteControlApiMajor != 0) ? 175 ""+remoteControlApiMajor+"."+remoteControlApiMinor : 176 tr("unknown"), 177 ""+REMOTECONTROL_MIN_REVISION, 178 ""+REMOTECONTROL_NEED_API_MAJOR+"."+REMOTECONTROL_MIN_API_MINOR ); 179 180 String title = tr("{0}: Problem with remote control", 181 this.getClass().getSimpleName()); 182 183 System.out.println(this.getClass().getSimpleName() + ": " + 184 msg + "\n" + versionMessage); 185 186 JOptionPane.showMessageDialog( 187 Main.parent, 188 msg + "\n" + additionalMessage, 189 title, 190 JOptionPane.WARNING_MESSAGE 191 ); 192 } 193 } 194 195 if(!remoteControlAvailable) { 196 System.out.println(this.getClass().getSimpleName() + ": remote control not available"); 197 } 198 } 199 200 /*************************************** 201 * end of remote control initialization 202 ***************************************/ 203 204 protected void initExporterAndImporter() { 205 ExtensionFileFilter.exporters.add(new WMSLayerExporter()); 206 ExtensionFileFilter.importers.add(new WMSLayerImporter()); 207 } 208 209 public WMSPlugin(PluginInformation info) { 210 super(info); 211 instance = this; 212 this.info.load(); 213 refreshMenu(); 214 cache.setExpire(CacheFiles.EXPIRE_MONTHLY, false); 215 cache.setMaxSize(70, false); 216 initExporterAndImporter(); 217 initRemoteControl(); 218 } 219 220 public void addLayer(WMSInfo info) { 221 this.info.add(info); 222 this.info.save(); 223 refreshMenu(); 224 } 225 226 public void refreshMenu() { 227 MainMenu menu = Main.main.menu; 228 229 if (wmsJMenu == null) 230 wmsJMenu = menu.addMenu(marktr("WMS"), KeyEvent.VK_W, menu.defaultMenuPos, ht("/Plugin/WMS")); 231 else 232 wmsJMenu.removeAll(); 233 234 // for each configured WMSInfo, add a menu entry. 235 for (final WMSInfo u : info.layers) { 236 wmsJMenu.add(new JMenuItem(new WMSDownloadAction(u))); 237 } 238 wmsJMenu.addSeparator(); 239 wmsJMenu.add(new JMenuItem(new Map_Rectifier_WMSmenuAction())); 240 241 wmsJMenu.addSeparator(); 242 wmsJMenu.add(new JMenuItem(new 243 JosmAction(tr("Blank Layer"), "blankmenu", tr("Open a blank WMS layer to load data from a file"), null, false) { 244 public void actionPerformed(ActionEvent ev) { 245 Main.main.addLayer(new WMSLayer()); 246 } 247 })); 248 setEnabledAll(menuEnabled); 249 } 250 251 public Grabber getGrabber(MapView mv, WMSLayer layer){ 252 if(layer.info.html) 253 return new HTMLGrabber(mv, layer, cache); 254 else 255 return new WMSGrabber(mv, layer, cache); 256 } 257 258 private void setEnabledAll(boolean isEnabled) { 259 for(int i=0; i < wmsJMenu.getItemCount(); i++) { 260 JMenuItem item = wmsJMenu.getItem(i); 261 262 if(item != null) item.setEnabled(isEnabled); 263 } 264 menuEnabled = isEnabled; 265 } 266 267 @Override 268 public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) { 269 if (oldFrame==null && newFrame!=null) { 270 setEnabledAll(true); 271 Main.map.addMapMode(new IconToggleButton 272 (new WMSAdjustAction(Main.map))); 273 } else if (oldFrame!=null && newFrame==null ) { 274 setEnabledAll(false); 275 } 276 } 277 278 @Override 279 public PreferenceSetting getPreferenceSetting() { 280 return new WMSPreferenceEditor(); 281 } 282 283 @Override 284 public String getPluginDir() 285 { 286 return new File(Main.pref.getPluginsDirectory(), "wmsplugin").getPath(); 287 } 404 288 } -
applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSPreferenceEditor.java
r22936 r23207 2 2 3 3 import static org.openstreetmap.josm.tools.I18n.tr; 4 import static org.openstreetmap.josm.tools.I18n.trc; 4 5 5 6 import java.awt.Dimension; … … 8 9 import java.awt.event.ActionEvent; 9 10 import java.awt.event.ActionListener; 11 import java.awt.event.MouseEvent; 12 import java.util.ArrayList; 10 13 import java.util.HashMap; 11 14 import java.util.Map; … … 23 26 import javax.swing.SpinnerNumberModel; 24 27 import javax.swing.table.DefaultTableModel; 28 import javax.swing.table.TableColumnModel; 25 29 26 30 import org.openstreetmap.josm.Main; … … 30 34 31 35 public class WMSPreferenceEditor implements PreferenceSetting { 32 private DefaultTableModel model;36 private WMSLayerTableModel model; 33 37 private JComboBox browser; 34 private HashMap<Integer, WMSInfo> oldValues = new HashMap<Integer, WMSInfo>();35 38 36 39 JCheckBox overlapCheckBox; … … 40 43 JCheckBox remoteCheckBox; 41 44 boolean allowRemoteControl = true; 45 WMSPlugin plugin = WMSPlugin.instance; 42 46 43 47 public void addGui(final PreferenceTabbedPane gui) { 44 48 JPanel p = gui.createPreferenceTab("wms", tr("WMS Plugin Preferences"), tr("Modify list of WMS servers displayed in the WMS plugin menu")); 45 49 46 model = new DefaultTableModel(new String[]{tr("Menu Name"), tr("WMS URL")}, 0); 47 final JTable list = new JTable(model); 50 model = new WMSLayerTableModel(); 51 final JTable list = new JTable(model) { 52 @Override 53 public String getToolTipText(MouseEvent e) { 54 java.awt.Point p = e.getPoint(); 55 return (String) model.getValueAt(rowAtPoint(p), columnAtPoint(p)); 56 } 57 }; 48 58 JScrollPane scroll = new JScrollPane(list); 49 59 p.add(scroll, GBC.eol().fill(GridBagConstraints.BOTH)); 50 60 scroll.setPreferredSize(new Dimension(200,200)); 51 61 52 for (WMSInfo i : WMSPlugin.wmsList) { 53 oldValues.put(i.prefid, i); 54 model.addRow(new String[]{i.name, i.url}); 55 } 56 57 final DefaultTableModel modeldef = new DefaultTableModel( 58 new String[]{tr("Menu Name (Default)"), tr("WMS URL (Default)")}, 0); 59 final JTable listdef = new JTable(modeldef){ 62 final WMSDefaultLayerTableModel modeldef = new WMSDefaultLayerTableModel(); 63 final JTable listdef = new JTable(modeldef) { 60 64 @Override 61 public boolean isCellEditable(int row,int column){return false;} 65 public String getToolTipText(MouseEvent e) { 66 java.awt.Point p = e.getPoint(); 67 return (String) modeldef.getValueAt(rowAtPoint(p), columnAtPoint(p)); 68 } 62 69 }; 63 70 JScrollPane scrolldef = new JScrollPane(listdef); … … 66 73 scrolldef.setPreferredSize(new Dimension(200,200)); 67 74 68 for (Map.Entry<String,String> i : WMSPlugin.wmsListDefault.entrySet()) { 69 modeldef.addRow(new String[]{i.getKey(), i.getValue()}); 70 } 75 TableColumnModel mod = listdef.getColumnModel(); 76 mod.getColumn(1).setPreferredWidth(800); 77 mod.getColumn(0).setPreferredWidth(200); 78 mod = list.getColumnModel(); 79 mod.getColumn(2).setPreferredWidth(50); 80 mod.getColumn(1).setPreferredWidth(800); 81 mod.getColumn(0).setPreferredWidth(200); 71 82 72 83 JPanel buttonPanel = new JPanel(new FlowLayout()); … … 82 93 JOptionPane.OK_CANCEL_OPTION); 83 94 if (answer == JOptionPane.OK_OPTION) { 84 model.addRow(new String[]{p.getUrlName(), p.getUrl()});95 model.addRow(new WMSInfo(p.getUrlName(), p.getUrl())); 85 96 } 86 97 } … … 118 129 119 130 outer: for(int i = 0; i < lines.length; i++) { 120 String c1 = modeldef.getValueAt(lines[i], 0).toString(); 121 String c2 = modeldef.getValueAt(lines[i], 1).toString(); 131 WMSInfo info = modeldef.getRow(lines[i]); 122 132 123 133 // Check if an entry with exactly the same values already 124 134 // exists 125 135 for(int j = 0; j < model.getRowCount(); j++) { 126 if(c1.equals(model.getValueAt(j, 0).toString()) 127 && c2.equals(model.getValueAt(j, 1).toString())) { 136 if(info.equalsBaseValues(model.getRow(j))) { 128 137 // Select the already existing row so the user has 129 138 // some feedback in case an entry exists … … 134 143 } 135 144 136 model.addRow(new String[] {c1, c2});145 model.addRow(new WMSInfo(info)); 137 146 int lastLine = model.getRowCount() - 1; 138 147 list.getSelectionModel().setSelectionInterval(lastLine, lastLine); … … 151 160 "gnome-web-photo --mode=photo --format=png {0} /dev/stdout", 152 161 "gnome-web-photo-fixed {0}", 153 "webkit-image-gtk {0}"});162 "webkit-image-gtk {0}"}); 154 163 browser.setEditable(true); 155 164 browser.setSelectedItem(Main.pref.get("wmsplugin.browser", "webkit-image {0}")); … … 160 169 p.add(Box.createHorizontalGlue(), GBC.eol().fill(GridBagConstraints.HORIZONTAL)); 161 170 162 overlapCheckBox = new JCheckBox(tr("Overlap tiles"), WMSPlugin.PROP_OVERLAP.get() );171 overlapCheckBox = new JCheckBox(tr("Overlap tiles"), plugin.PROP_OVERLAP.get() ); 163 172 JLabel labelEast = new JLabel(tr("% of east:")); 164 173 JLabel labelNorth = new JLabel(tr("% of north:")); 165 spinEast = new JSpinner(new SpinnerNumberModel( WMSPlugin.PROP_OVERLAP_EAST.get(), 1, 50, 1));166 spinNorth = new JSpinner(new SpinnerNumberModel( WMSPlugin.PROP_OVERLAP_NORTH.get(), 1, 50, 1));174 spinEast = new JSpinner(new SpinnerNumberModel(plugin.PROP_OVERLAP_EAST.get(), 1, 50, 1)); 175 spinNorth = new JSpinner(new SpinnerNumberModel(plugin.PROP_OVERLAP_NORTH.get(), 1, 50, 1)); 167 176 168 177 JPanel overlapPanel = new JPanel(new FlowLayout()); … … 178 187 p.add(Box.createHorizontalGlue(), GBC.eol().fill(GridBagConstraints.HORIZONTAL)); 179 188 JLabel labelSimConn = new JLabel(tr("Simultaneous connections")); 180 spinSimConn = new JSpinner(new SpinnerNumberModel( WMSPlugin.PROP_SIMULTANEOUS_CONNECTIONS.get(), 1, 30, 1));189 spinSimConn = new JSpinner(new SpinnerNumberModel(plugin.PROP_SIMULTANEOUS_CONNECTIONS.get(), 1, 30, 1)); 181 190 JPanel overlapPanelSimConn = new JPanel(new FlowLayout()); 182 191 overlapPanelSimConn.add(labelSimConn); … … 191 200 192 201 p.add(remotePanel); 193 194 202 } 195 203 196 204 public boolean ok() { 197 boolean change = false; 198 for (int i = 0; i < model.getRowCount(); ++i) { 199 String name = model.getValueAt(i,0).toString(); 200 String url = model.getValueAt(i,1).toString(); 201 202 WMSInfo origValue = oldValues.get(i); 203 if (origValue == null) 204 { 205 new WMSInfo(name, url, i).save(); 206 change = true; 207 } 208 else 209 { 210 if (!origValue.name.equals(name) || !origValue.url.equals(url)) 211 { 212 origValue.name = name; 213 origValue.url = url; 214 origValue.save(); 215 change = true; 216 } 217 oldValues.remove(i); 218 } 219 } 220 221 // using null values instead of empty string really deletes 222 // the preferences entry 223 for (WMSInfo i : oldValues.values()) 224 { 225 i.url = null; 226 i.name = null; 227 i.save(); 228 change = true; 229 } 230 231 if (change) WMSPlugin.refreshMenu(); 232 233 WMSPlugin.PROP_OVERLAP.put(overlapCheckBox.getModel().isSelected()); 234 WMSPlugin.PROP_OVERLAP_EAST.put((Integer) spinEast.getModel().getValue()); 235 WMSPlugin.PROP_OVERLAP_NORTH.put((Integer) spinNorth.getModel().getValue()); 236 WMSPlugin.PROP_SIMULTANEOUS_CONNECTIONS.put((Integer) spinSimConn.getModel().getValue()); 205 plugin.info.save(); 206 plugin.refreshMenu(); 207 208 plugin.PROP_OVERLAP.put(overlapCheckBox.getModel().isSelected()); 209 plugin.PROP_OVERLAP_EAST.put((Integer) spinEast.getModel().getValue()); 210 plugin.PROP_OVERLAP_NORTH.put((Integer) spinNorth.getModel().getValue()); 211 plugin.PROP_SIMULTANEOUS_CONNECTIONS.put((Integer) spinSimConn.getModel().getValue()); 237 212 allowRemoteControl = remoteCheckBox.getModel().isSelected(); 238 213 … … 279 254 return null; 280 255 } 256 257 /** 258 * The table model for the WMS layer 259 * 260 */ 261 class WMSLayerTableModel extends DefaultTableModel { 262 public WMSLayerTableModel() { 263 setColumnIdentifiers(new String[]{tr("Menu Name"), tr("WMS URL"), trc("layer", "Zoom")}); 264 } 265 266 public WMSInfo getRow(int row) { 267 return plugin.info.layers.get(row); 268 } 269 270 public void addRow(WMSInfo i) { 271 plugin.info.add(i); 272 int p = getRowCount()-1; 273 fireTableRowsInserted(p,p); 274 } 275 276 public void removeRow(int i) { 277 plugin.info.remove(getRow(i)); 278 fireTableRowsDeleted(i,i); 279 } 280 281 @Override 282 public int getRowCount() { 283 return plugin.info.layers.size(); 284 } 285 286 @Override 287 public Object getValueAt(int row, int column) { 288 WMSInfo info = plugin.info.layers.get(row); 289 switch(column) { 290 case 0: return info.name; 291 case 1: return info.getFullURL(); 292 case 2: return info.pixelPerDegree == 0.0 ? "" : info.pixelPerDegree; 293 } 294 return null; 295 } 296 297 @Override 298 public boolean isCellEditable(int row, int column) { 299 return (column != 2); 300 } 301 } 302 303 /** 304 * The table model for the WMS layer 305 * 306 */ 307 class WMSDefaultLayerTableModel extends DefaultTableModel { 308 public WMSDefaultLayerTableModel() { 309 setColumnIdentifiers(new String[]{tr("Menu Name (Default)"), tr("WMS URL (Default)")}); 310 } 311 312 public WMSInfo getRow(int row) { 313 return plugin.info.defaultLayers.get(row); 314 } 315 316 @Override 317 public int getRowCount() { 318 return plugin.info.defaultLayers.size(); 319 } 320 321 @Override 322 public Object getValueAt(int row, int column) { 323 WMSInfo info = plugin.info.defaultLayers.get(row); 324 switch(column) { 325 case 0: return info.name; 326 case 1: return info.getFullURL(); 327 } 328 return null; 329 } 330 331 @Override 332 public boolean isCellEditable(int row, int column) { 333 return false; 334 } 335 } 281 336 } 282 -
applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSRemoteHandler.java
r22735 r23207 15 15 public class WMSRemoteHandler extends RequestHandler { 16 16 17 17 public static final String command = "wms"; 18 18 19 20 21 22 23 19 @Override 20 public String getPermissionMessage() { 21 return tr("Remote Control has been asked to load a WMS layer from the following URL:") + 22 "<br>" + args.get("url"); 23 } 24 24 25 26 27 28 29 30 31 32 25 @Override 26 public PermissionPrefWithDefault getPermissionPref() 27 { 28 return new PermissionPrefWithDefault( 29 "wmsplugin.remotecontrol", 30 true, 31 "RemoteControl: WMS forbidden by preferences"); 32 } 33 33 34 35 36 37 38 34 @Override 35 protected String[] getMandatoryParams() 36 { 37 return new String[] { "url" }; 38 } 39 39 40 @Override 41 protected void handleRequest() throws RequestHandlerErrorException { 42 String url = args.get("url"); 43 String title = args.get("title"); 44 if((title == null) || (title.length() == 0)) 45 { 46 title = "remote WMS"; 47 } 48 String cookies = args.get("cookies"); 49 if(cookies == null) 50 { 51 cookies = ""; 52 } 53 WMSLayer wmsLayer = new WMSLayer(title, url, cookies); 54 Main.main.addLayer(wmsLayer); 40 @Override 41 protected void handleRequest() throws RequestHandlerErrorException { 42 String url = args.get("url"); 43 String title = args.get("title"); 44 if((title == null) || (title.length() == 0)) 45 { 46 title = tr("Remote WMS"); 47 } 48 String cookies = args.get("cookies"); 49 WMSLayer wmsLayer = new WMSLayer(new WMSInfo(title, url, cookies)); 50 Main.main.addLayer(wmsLayer); 55 51 56 52 } 57 53 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 54 @Override 55 public void parseArgs() { 56 StringTokenizer st = new StringTokenizer(request, "&?"); 57 HashMap<String, String> args = new HashMap<String, String>(); 58 // skip first element which is the command 59 if(st.hasMoreTokens()) st.nextToken(); 60 while (st.hasMoreTokens()) { 61 String param = st.nextToken(); 62 int eq = param.indexOf("="); 63 if (eq > -1) 64 { 65 String key = param.substring(0, eq); 66 /* "url=" terminates normal parameters 67 * and will be handled separately 68 */ 69 if("url".equals(key)) break; 74 70 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 71 String value = param.substring(eq + 1); 72 // urldecode all normal values 73 try { 74 value = URLDecoder.decode(value, "UTF-8"); 75 } catch (UnsupportedEncodingException e) { 76 // TODO Auto-generated catch block 77 e.printStackTrace(); 78 } 79 args.put(key, 80 value); 81 } 82 } 83 // url as second or later parameter 84 int urlpos = request.indexOf("&url="); 85 // url as first (and only) parameter 86 if(urlpos < 0) urlpos = request.indexOf("?url="); 87 // url found? 88 if(urlpos >= 0) { 89 // URL value 90 String value = request.substring(urlpos + 5); 91 // allow skipping URL decoding with urldecode=false 92 String urldecode = args.get("urldecode"); 93 if((urldecode == null) || (Boolean.valueOf(urldecode) == true)) 94 { 95 try { 96 value = URLDecoder.decode(value, "UTF-8"); 97 } catch (UnsupportedEncodingException e) { 98 // TODO Auto-generated catch block 99 e.printStackTrace(); 100 } 101 } 102 args.put("url", value); 103 } 104 this.args = args; 105 } 110 106 } -
applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSRequest.java
r22712 r23207 6 6 7 7 public class WMSRequest implements Comparable<WMSRequest> { 8 9 10 11 12 13 14 15 8 private final int xIndex; 9 private final int yIndex; 10 private final double pixelPerDegree; 11 private final boolean real; // Download even if autodownloading is disabled 12 private int priority; 13 // Result 14 private State state; 15 private BufferedImage image; 16 16 17 18 19 20 21 22 17 public WMSRequest(int xIndex, int yIndex, double pixelPerDegree, boolean real) { 18 this.xIndex = xIndex; 19 this.yIndex = yIndex; 20 this.pixelPerDegree = pixelPerDegree; 21 this.real = real; 22 } 23 23 24 25 26 27 24 public void finish(State state, BufferedImage image) { 25 this.state = state; 26 this.image = image; 27 } 28 28 29 30 31 29 public int getXIndex() { 30 return xIndex; 31 } 32 32 33 34 35 33 public int getYIndex() { 34 return yIndex; 35 } 36 36 37 38 39 37 public double getPixelPerDegree() { 38 return pixelPerDegree; 39 } 40 40 41 42 43 44 45 46 47 48 49 50 51 41 @Override 42 public int hashCode() { 43 final int prime = 31; 44 int result = 1; 45 long temp; 46 temp = Double.doubleToLongBits(pixelPerDegree); 47 result = prime * result + (int) (temp ^ (temp >>> 32)); 48 result = prime * result + xIndex; 49 result = prime * result + yIndex; 50 return result; 51 } 52 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 53 @Override 54 public boolean equals(Object obj) { 55 if (this == obj) 56 return true; 57 if (obj == null) 58 return false; 59 if (getClass() != obj.getClass()) 60 return false; 61 WMSRequest other = (WMSRequest) obj; 62 if (Double.doubleToLongBits(pixelPerDegree) != Double 63 .doubleToLongBits(other.pixelPerDegree)) 64 return false; 65 if (xIndex != other.xIndex) 66 return false; 67 if (yIndex != other.yIndex) 68 return false; 69 return true; 70 } 71 71 72 73 74 72 public void setPriority(int priority) { 73 this.priority = priority; 74 } 75 75 76 77 78 76 public int getPriority() { 77 return priority; 78 } 79 79 80 81 82 80 public int compareTo(WMSRequest o) { 81 return priority - o.priority; 82 } 83 83 84 85 86 84 public State getState() { 85 return state; 86 } 87 87 88 89 90 88 public BufferedImage getImage() { 89 return image; 90 } 91 91 92 93 94 95 96 92 @Override 93 public String toString() { 94 return "WMSRequest [xIndex=" + xIndex + ", yIndex=" + yIndex 95 + ", pixelPerDegree=" + pixelPerDegree + "]"; 96 } 97 97 98 99 100 98 public boolean isReal() { 99 return real; 100 } 101 101 } -
applications/editors/josm/plugins/wmsplugin/src/wmsplugin/io/WMSLayerExporter.java
r17397 r23207 7 7 8 8 public class WMSLayerExporter extends FileExporter{ 9 10 11 12 9 10 public WMSLayerExporter() { 11 super(new ExtensionFileFilter("wms", "wms", tr("WMS Files (*.wms)"))); 12 } 13 13 } -
applications/editors/josm/plugins/wmsplugin/src/wmsplugin/io/WMSLayerImporter.java
r17397 r23207 7 7 public class WMSLayerImporter extends FileImporter{ 8 8 9 10 11 12 9 public WMSLayerImporter() { 10 super(new ExtensionFileFilter("wms", "wms", tr("WMS Files (*.wms)"))); 11 } 12 13 13 }
Note:
See TracChangeset
for help on using the changeset viewer.