Changeset 22712 in osm for applications


Ignore:
Timestamp:
2010-08-20T22:44:35+02:00 (14 years ago)
Author:
jttt
Message:

Speed optimizations:

  • use always the same size for tile, so no resizing caused by rounding errors is necessary
  • load images from cache also in background thread
  • try to load first tiles close to mouse cursor
Location:
applications/editors/josm/plugins/wmsplugin
Files:
2 added
8 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/wmsplugin/build.xml

    r22677 r22712  
    2929
    3030        <property name="commit.message" value="add commit message" />
    31         <property name="plugin.main.version" value="3408" />
     31        <property name="plugin.main.version" value="3451" />
    3232
    3333
  • applications/editors/josm/plugins/wmsplugin/src/wmsplugin/GeorefImage.java

    r22581 r22712  
    11package wmsplugin;
    22
    3 import java.awt.Dimension;
     3import static org.openstreetmap.josm.tools.I18n.tr;
     4
     5import java.awt.Color;
     6import java.awt.Font;
    47import java.awt.Graphics;
    58import java.awt.Image;
    6 import java.awt.Point;
    79import java.awt.Transparency;
    810import java.awt.image.BufferedImage;
     
    1820
    1921public class GeorefImage implements Serializable {
    20         public BufferedImage image = null;
    21         private Image reImg = null;
    22         private Dimension reImgHash = new Dimension(0, 0);
    23         public EastNorth min, max;
    24         public boolean downloadingStarted;
    25         public boolean failed = false;
    26         public boolean infotext = false;
    27 
    28         public GeorefImage(boolean downloadingStarted) {
    29                 this.downloadingStarted = downloadingStarted;
    30         }
    31 
    32         public boolean contains(EastNorth en, double dx, double dy) {
    33                 return min.east()+dx <= en.east() && en.east() <= max.east()+dx
    34                 && min.north()+dy <= en.north() && en.north() <= max.north()+dy;
    35         }
    36 
    37         public boolean isVisible(NavigatableComponent nc, double dx, double dy) {
    38                 EastNorth mi = new EastNorth(min.east()+dx, min.north()+dy);
    39                 EastNorth ma = new EastNorth(max.east()+dx, max.north()+dy);
    40                 Point minPt = nc.getPoint(mi), maxPt = nc.getPoint(ma);
    41                 Graphics g = nc.getGraphics();
    42 
    43                 return (g.hitClip(minPt.x, maxPt.y,
    44                                 maxPt.x - minPt.x, minPt.y - maxPt.y));
    45         }
    46 
    47         public boolean paint(Graphics g, NavigatableComponent nc, double dx, double dy) {
    48                 if (image == null || min == null || max == null) return false;
    49 
    50                 EastNorth mi = new EastNorth(min.east()+dx, min.north()+dy);
    51                 EastNorth ma = new EastNorth(max.east()+dx, max.north()+dy);
    52                 Point minPt = nc.getPoint(mi), maxPt = nc.getPoint(ma);
    53 
    54                 if(!isVisible(nc, dx, dy)){
     22        private static final long serialVersionUID = 1L;
     23
     24        public enum State { IMAGE, NOT_IN_CACHE, FAILED};
     25
     26        private final WMSLayer layer;
     27        private State state;
     28
     29        private BufferedImage image;
     30        private BufferedImage reImg = null;
     31        private int xIndex;
     32        private int yIndex;
     33
     34
     35        public EastNorth getMin() {
     36                return layer.getEastNorth(xIndex, yIndex);
     37        }
     38
     39        public EastNorth getMax() {
     40                return layer.getEastNorth(xIndex+1, yIndex+1);
     41        }
     42
     43
     44        public GeorefImage(WMSLayer layer) {
     45                this.layer = layer;
     46        }
     47
     48        public void changePosition(int xIndex, int yIndex) {
     49                if (!equalPosition(xIndex, yIndex)) {
     50                        this.xIndex = xIndex;
     51                        this.yIndex = yIndex;
     52                        this.image = null;
     53                        this.reImg = null;
     54                }
     55        }
     56
     57        public boolean equalPosition(int xIndex, int yIndex) {
     58                return this.xIndex == xIndex && this.yIndex == yIndex;
     59        }
     60
     61        public void changeImage(State state, BufferedImage image) {
     62                this.image = image;
     63                this.reImg = null;
     64                this.state = state;
     65
     66                switch (state) {
     67                case FAILED:
     68                {
     69                        BufferedImage img = createImage();
     70                        Graphics g = img.getGraphics();
     71                        g.setColor(Color.RED);
     72                        g.fillRect(0, 0, img.getWidth(), img.getHeight());
     73                        g.setFont(g.getFont().deriveFont(Font.PLAIN).deriveFont(36.0f));
     74                        g.setColor(Color.BLACK);
     75                        g.drawString(tr("Exception occurred"), 10, img.getHeight()/2);
     76                        this.image = img;
     77                        break;
     78                }
     79                case NOT_IN_CACHE:
     80                {
     81                        BufferedImage img = createImage();
     82                        Graphics g = img.getGraphics();
     83                        g.setColor(Color.GRAY);
     84                        g.fillRect(0, 0, img.getWidth(), img.getHeight());
     85                        Font font = g.getFont();
     86                        Font tempFont = font.deriveFont(Font.PLAIN).deriveFont(36.0f);
     87                        g.setFont(tempFont);
     88                        g.setColor(Color.BLACK);
     89                        g.drawString(tr("Not in cache"), 10, img.getHeight()/2);
     90                        g.setFont(font);
     91                        this.image = img;
     92                        break;
     93                }
     94                default:
     95                        break;
     96                }
     97        }
     98
     99        private BufferedImage createImage() {
     100                int left = layer.getImageX(xIndex);
     101                int bottom = layer.getImageY(yIndex);
     102                int width = layer.getImageX(xIndex + 1) - left;
     103                int height = layer.getImageY(yIndex + 1) - bottom;
     104
     105                return new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
     106        }
     107
     108        public boolean paint(Graphics g, NavigatableComponent nc, int xIndex, int yIndex, int leftEdge, int bottomEdge) {
     109                if (image == null)
    55110                        return false;
    56                 }
    57 
    58                 // Width and height flicker about 2 pixels due to rounding errors, typically only 1
    59                 int width = Math.abs(maxPt.x-minPt.x);
    60                 int height = Math.abs(minPt.y-maxPt.y);
    61                 int diffx, diffy;
    62 
    63                 diffx = reImgHash.width - width;
    64                 diffy = reImgHash.height - height;
     111
     112                if(!(this.xIndex == xIndex && this.yIndex == yIndex)){
     113                        return false;
     114                }
     115
     116                int left = layer.getImageX(xIndex);
     117                int bottom = layer.getImageY(yIndex);
     118                int width = layer.getImageX(xIndex + 1) - left;
     119                int height = layer.getImageY(yIndex + 1) - bottom;
     120
     121                int x = left - leftEdge;
     122                int y = nc.getHeight() - (bottom - bottomEdge) - height;
     123
    65124                // This happens if you zoom outside the world
    66125                if(width == 0 || height == 0)
    67126                        return false;
    68127
    69                 // We still need to re-render if the requested size is larger (otherwise we'll have black lines)
    70                 // If it's only up to two pixels smaller, just draw the old image, the errors are minimal
    71                 // but the performance improvements when moving are huge
    72                 // Zooming is still slow because the images need to be resized
    73                 if(diffx >= 0 && diffx <= 2 && diffy >= 0 && diffy <= 2 && reImg != null) {
    74                         /*g.setColor(Color.RED);
    75               g.drawRect(minPt.x, minPt.y-height, width, height);*/
    76                         g.drawImage(reImg, minPt.x, maxPt.y, null);
     128                if(reImg != null && reImg.getWidth() == width && reImg.getHeight() == height) {
     129                        g.drawImage(reImg, x, y, null);
    77130                        return true;
    78131                }
    79132
    80                 boolean alphaChannel = WMSLayer.PROP_ALPHA_CHANNEL.get() && image.getTransparency() != Transparency.OPAQUE;
     133                boolean alphaChannel = WMSLayer.PROP_ALPHA_CHANNEL.get() && getImage().getTransparency() != Transparency.OPAQUE;
    81134
    82135                try {
     
    93146                        // Also prevent caching if we're out of memory soon
    94147                        if(width > 2000 || height > 2000 || width*height*multipl > freeMem) {
    95                                 fallbackDraw(g, image, minPt, maxPt);
     148                                fallbackDraw(g, getImage(), x, y, width, height);
    96149                        } else {
    97150                                // We haven't got a saved resized copy, so resize and cache it
    98151                                reImg = new BufferedImage(width, height, alphaChannel?BufferedImage.TYPE_INT_ARGB:BufferedImage.TYPE_3BYTE_BGR);
    99                                 reImg.getGraphics().drawImage(image,
     152                                reImg.getGraphics().drawImage(getImage(),
    100153                                                0, 0, width, height, // dest
    101                                                 0, 0, image.getWidth(null), image.getHeight(null), // src
     154                                                0, 0, getImage().getWidth(null), getImage().getHeight(null), // src
    102155                                                null);
    103156                                reImg.getGraphics().dispose();
    104 
    105                                 reImgHash.setSize(width, height);
    106                                 /*g.setColor(Color.RED);
    107                   g.drawRect(minPt.x, minPt.y-height, width, height);*/
    108                                 g.drawImage(reImg, minPt.x, maxPt.y, null);
     157                                g.drawImage(reImg, x, y, null);
    109158                        }
    110159                } catch(Exception e) {
    111                         fallbackDraw(g, image, minPt, maxPt);
     160                        fallbackDraw(g, getImage(), x, y, width, height);
    112161                }
    113162                return true;
    114163        }
    115164
    116         private void fallbackDraw(Graphics g, Image img, Point min, Point max) {
     165        private void fallbackDraw(Graphics g, Image img, int x, int y, int width, int height) {
    117166                if(reImg != null) {
    118167                        reImg.flush();
    119168                        reImg = null;
    120169                }
    121                 g.drawImage(img,
    122                                 min.x, max.y, max.x, min.y, // dest
    123                                 0, 0, img.getWidth(null), img.getHeight(null), // src
     170                g.drawImage(
     171                                img, x, y, x + width, y + height,
     172                                0, 0, img.getWidth(null), img.getHeight(null),
    124173                                null);
    125174        }
    126175
    127176        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    128                 max = (EastNorth) in.readObject();
    129                 min = (EastNorth) in.readObject();
    130177                boolean hasImage = in.readBoolean();
    131178                if (hasImage)
    132                         image = ImageIO.read(ImageIO.createImageInputStream(in));
     179                        image = (ImageIO.read(ImageIO.createImageInputStream(in)));
    133180                else {
    134181                        in.readObject(); // read null from input stream
     
    138185
    139186        private void writeObject(ObjectOutputStream out) throws IOException {
    140                 out.writeObject(max);
    141                 out.writeObject(min);
    142                 if(image == null) {
     187                if(getImage() == null) {
    143188                        out.writeBoolean(false);
    144189                        out.writeObject(null);
    145190                } else {
    146191                        out.writeBoolean(true);
    147                         ImageIO.write(image, "png", ImageIO.createImageOutputStream(out));
     192                        ImageIO.write(getImage(), "png", ImageIO.createImageOutputStream(out));
    148193                }
    149194        }
     
    152197                reImg = null;
    153198        }
     199
     200
     201        public BufferedImage getImage() {
     202                return image;
     203        }
     204
     205        public State getState() {
     206                return state;
     207        }
     208
     209        public int getXIndex() {
     210                return xIndex;
     211        }
     212
     213        public int getYIndex() {
     214                return yIndex;
     215        }
    154216}
  • applications/editors/josm/plugins/wmsplugin/src/wmsplugin/Grabber.java

    r22286 r22712  
    11package wmsplugin;
    2 
    3 import static org.openstreetmap.josm.tools.I18n.tr;
    4 
    5 import java.awt.Color;
    6 import java.awt.Font;
    7 import java.awt.Graphics;
    8 import java.awt.image.BufferedImage;
    92
    103import org.openstreetmap.josm.Main;
     
    158import org.openstreetmap.josm.io.CacheFiles;
    169
    17 abstract public class Grabber extends Thread {
    18     protected ProjectionBounds b;
    19     protected Projection proj;
    20     protected double pixelPerDegree;
    21     protected MapView mv;
    22     protected WMSLayer layer;
    23     protected GeorefImage image;
    24     protected CacheFiles cache;
     10import wmsplugin.GeorefImage.State;
    2511
    26     Grabber(ProjectionBounds b, GeorefImage image, MapView mv, WMSLayer layer, CacheFiles cache)
    27     {
    28         if (b.min != null && b.max != null && WMSPlugin.doOverlap) {
    29             double eastSize =  b.max.east() - b.min.east();
    30             double northSize =  b.max.north() - b.min.north();
     12abstract public class Grabber implements Runnable {
     13        protected final MapView mv;
     14        protected final WMSLayer layer;
     15        protected final CacheFiles cache;
    3116
    32             double eastCoef = WMSPlugin.overlapEast / 100.0;
    33             double northCoef = WMSPlugin.overlapNorth / 100.0;
     17        protected ProjectionBounds b;
     18        protected Projection proj;
     19        protected double pixelPerDegree;
     20        protected volatile boolean canceled;
    3421
    35             this.b = new ProjectionBounds( new EastNorth(b.min.east(),
    36                                             b.min.north()),
    37                                  new EastNorth(b.max.east() + eastCoef * eastSize,
    38                                             b.max.north() + northCoef * northSize));
    39         } else
    40            this.b = b;
     22        Grabber(MapView mv, WMSLayer layer, CacheFiles cache) {
     23                this.mv = mv;
     24                this.layer = layer;
     25                this.cache = cache;
     26        }
    4127
    42         this.proj = Main.proj;
    43         this.pixelPerDegree = layer.pixelPerDegree;
    44         this.image = image;
    45         this.mv = mv;
    46         this.layer = layer;
    47         this.cache = cache;
     28        private void updateState(WMSRequest request) {
     29                b = new ProjectionBounds(
     30                                layer.getEastNorth(request.getXIndex(), request.getYIndex()),
     31                                layer.getEastNorth(request.getXIndex() + 1, request.getYIndex() + 1));
     32                if (b.min != null && b.max != null && WMSPlugin.doOverlap) {
     33                        double eastSize =  b.max.east() - b.min.east();
     34                        double northSize =  b.max.north() - b.min.north();
    4835
    49     }
     36                        double eastCoef = WMSPlugin.overlapEast / 100.0;
     37                        double northCoef = WMSPlugin.overlapNorth / 100.0;
    5038
    51     abstract void fetch() throws Exception; // the image fetch code
     39                        this.b = new ProjectionBounds( new EastNorth(b.min.east(),
     40                                        b.min.north()),
     41                                        new EastNorth(b.max.east() + eastCoef * eastSize,
     42                                                        b.max.north() + northCoef * northSize));
     43                }
    5244
    53     int width(){
    54         return (int) ((b.max.north() - b.min.north()) * pixelPerDegree);
    55     }
    56     int height(){
    57         return (int) ((b.max.east() - b.min.east()) * pixelPerDegree);
    58     }
     45                this.proj = Main.proj;
     46                this.pixelPerDegree = request.getPixelPerDegree();
     47        }
    5948
    60     protected void grabError(Exception e){ // report error when grabing image
    61         e.printStackTrace();
     49        abstract void fetch(WMSRequest request) throws Exception; // the image fetch code
    6250
    63         BufferedImage img = new BufferedImage(width(), height(), BufferedImage.TYPE_INT_ARGB);
    64         Graphics g = img.getGraphics();
    65         g.setColor(Color.RED);
    66         g.fillRect(0, 0, width(), height());
    67         Font font = g.getFont();
    68         Font tempFont = font.deriveFont(Font.PLAIN).deriveFont(36.0f);
    69         g.setFont(tempFont);
    70         g.setColor(Color.BLACK);
    71         g.drawString(tr("Exception occurred"), 10, height()/2);
    72         image.image = img;
    73         image.flushedResizedCachedInstance();
    74         image.failed = true;
    75         image.downloadingStarted = false;
    76         g.setFont(font);
    77     }
     51        int width(){
     52                return (int) ((b.max.north() - b.min.north()) * pixelPerDegree);
     53        }
     54        int height(){
     55                return (int) ((b.max.east() - b.min.east()) * pixelPerDegree);
     56        }
    7857
    79     protected void grabNotInCache(){ // report not in cache
    80         BufferedImage img = new BufferedImage(width(), height(), BufferedImage.TYPE_INT_ARGB);
    81         Graphics g = img.getGraphics();
    82         g.setColor(Color.GRAY);
    83         g.fillRect(0, 0, width(), height());
    84         Font font = g.getFont();
    85         Font tempFont = font.deriveFont(Font.PLAIN).deriveFont(36.0f);
    86         g.setFont(tempFont);
    87         g.setColor(Color.BLACK);
    88         g.drawString(tr("Not in cache"), 10, height()/2);
    89         image.image = img;
    90         image.flushedResizedCachedInstance();
    91         image.infotext = true;
    92         image.downloadingStarted = false;
    93         g.setFont(font);
    94     }
     58        @Override
     59        public void run() {
     60                while (true) {
     61                        if (canceled) {
     62                                return;
     63                        }
     64                        WMSRequest request = layer.getRequest();
     65                        if (request == null) {
     66                                return;
     67                        }
     68                        updateState(request);
     69                        if(!loadFromCache(request)){
     70                                attempt(request);
     71                        }
     72                        if (canceled) {
     73                                return;
     74                        }
     75                        layer.finishRequest(request);
     76                        mv.repaint();
     77                }
     78        }
    9579
    96     protected void attempt(){ // try to fetch the image
    97         int maxTries = 5; // n tries for every image
    98         for (int i = 1; i <= maxTries; i++) {
    99             try {
    100                 fetch();
    101                 break; // break out of the retry loop
    102             } catch (Exception e) {
    103                 try { // sleep some time and then ask the server again
    104                     Thread.sleep(random(1000, 2000));
    105                 } catch (InterruptedException e1) {}
     80        protected void attempt(WMSRequest request){ // try to fetch the image
     81                int maxTries = 5; // n tries for every image
     82                for (int i = 1; i <= maxTries; i++) {
     83                        if (canceled) {
     84                                return;
     85                        }
     86                        try {
     87                                if (!layer.requestIsValid(request)) {
     88                                        return;
     89                                }
     90                                fetch(request);
     91                                break; // break out of the retry loop
     92                        } catch (Exception e) {
     93                                try { // sleep some time and then ask the server again
     94                                        Thread.sleep(random(1000, 2000));
     95                                } catch (InterruptedException e1) {}
    10696
    107                 if(i == maxTries) grabError(e);
    108             }
    109         }
    110     }
     97                                if(i == maxTries) {
     98                                        e.printStackTrace();
     99                                        request.finish(State.FAILED, null);
     100                                }
     101                        }
     102                }
     103        }
    111104
    112     public static int random(int min, int max) {
    113         return (int)(Math.random() * ((max+1)-min) ) + min;
    114     }
     105        public static int random(int min, int max) {
     106                return (int)(Math.random() * ((max+1)-min) ) + min;
     107        }
    115108
    116     abstract public boolean loadFromCache(boolean real);
     109        abstract public boolean loadFromCache(WMSRequest request);
     110
     111        public void cancel() {
     112                canceled = true;
     113        }
    117114}
  • applications/editors/josm/plugins/wmsplugin/src/wmsplugin/HTMLGrabber.java

    r19240 r22712  
    1111
    1212import org.openstreetmap.josm.Main;
    13 import org.openstreetmap.josm.data.ProjectionBounds;
    1413import org.openstreetmap.josm.gui.MapView;
    1514import org.openstreetmap.josm.io.CacheFiles;
    1615
    1716public class HTMLGrabber extends WMSGrabber {
    18     HTMLGrabber(ProjectionBounds b, GeorefImage image, MapView mv, WMSLayer layer, CacheFiles cache) {
    19         super(b, image, mv, layer, cache);
    20         this.baseURL = layer.baseURL.replaceFirst("html:", "");
    21     }
     17        HTMLGrabber(MapView mv, WMSLayer layer, CacheFiles cache) {
     18                super(mv, layer, cache);
     19                this.baseURL = layer.baseURL.replaceFirst("html:", "");
     20        }
    2221
    23     @Override
    24     protected BufferedImage grab(URL url) throws IOException {
    25         String urlstring = url.toExternalForm();
     22        @Override
     23        protected BufferedImage grab(URL url) throws IOException {
     24                String urlstring = url.toExternalForm();
    2625
    27         System.out.println("Grabbing HTML " + url);
     26                System.out.println("Grabbing HTML " + url);
    2827
    29         ArrayList<String> cmdParams = new ArrayList<String>();
    30         StringTokenizer st = new StringTokenizer(MessageFormat.format(
    31         Main.pref.get("wmsplugin.browser", "webkit-image {0}"), urlstring));
    32         while( st.hasMoreTokens() )
    33             cmdParams.add(st.nextToken());
     28                ArrayList<String> cmdParams = new ArrayList<String>();
     29                StringTokenizer st = new StringTokenizer(MessageFormat.format(
     30                                Main.pref.get("wmsplugin.browser", "webkit-image {0}"), urlstring));
     31                while( st.hasMoreTokens() )
     32                        cmdParams.add(st.nextToken());
    3433
    35         ProcessBuilder builder = new ProcessBuilder( cmdParams);
     34                ProcessBuilder builder = new ProcessBuilder( cmdParams);
    3635
    37         Process browser;
    38         try {
    39             browser = builder.start();
    40         } catch(IOException ioe) {
    41             throw new IOException( "Could not start browser. Please check that the executable path is correct.\n" + ioe.getMessage() );
    42         }
     36                Process browser;
     37                try {
     38                        browser = builder.start();
     39                } catch(IOException ioe) {
     40                        throw new IOException( "Could not start browser. Please check that the executable path is correct.\n" + ioe.getMessage() );
     41                }
    4342
    44         BufferedImage img = ImageIO.read(browser.getInputStream());
    45         cache.saveImg(urlstring, img);
    46         return img;
    47     }
     43                BufferedImage img = ImageIO.read(browser.getInputStream());
     44                cache.saveImg(urlstring, img);
     45                return img;
     46        }
    4847}
  • applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSGrabber.java

    r22286 r22712  
    2323
    2424import org.openstreetmap.josm.Main;
    25 import org.openstreetmap.josm.data.ProjectionBounds;
     25import org.openstreetmap.josm.data.Version;
    2626import org.openstreetmap.josm.data.coor.EastNorth;
    2727import org.openstreetmap.josm.data.coor.LatLon;
    2828import org.openstreetmap.josm.data.projection.Mercator;
    29 import org.openstreetmap.josm.data.Version;
    3029import org.openstreetmap.josm.gui.MapView;
    3130import org.openstreetmap.josm.io.CacheFiles;
     
    3332import org.openstreetmap.josm.io.ProgressInputStream;
    3433
     34import wmsplugin.GeorefImage.State;
     35
    3536
    3637public class WMSGrabber extends Grabber {
    37     public static boolean isUrlWithPatterns(String url) {
    38         return url != null && url.contains("{") && url.contains("}");
    39     }
    40 
    41     protected String baseURL;
    42     private final boolean urlWithPatterns;
    43 
    44     WMSGrabber(ProjectionBounds b, GeorefImage image, MapView mv, WMSLayer layer, CacheFiles cache) {
    45         super(b, image, mv, layer, cache);
    46         this.baseURL = layer.baseURL;
    47         /* URL containing placeholders? */
    48         urlWithPatterns = isUrlWithPatterns(baseURL);
    49     }
    50 
    51     @Override
    52         public void run() {
    53         attempt();
    54         mv.repaint();
    55     }
    56 
    57     @Override
    58     void fetch() throws Exception{
    59         URL url = null;
    60         try {
    61             url = getURL(
    62                 b.min.east(), b.min.north(),
    63                 b.max.east(), b.max.north(),
    64                 width(), height());
    65 
    66             image.min = b.min;
    67             image.max = b.max;
    68 
    69             if(image.isVisible(mv, layer.getDx(), layer.getDy())) { //don't download, if the image isn't visible already
    70                 image.image = grab(url);
    71                 image.flushedResizedCachedInstance();
    72             }
    73             image.downloadingStarted = false;
    74         } catch(Exception e) {
    75             e.printStackTrace();
    76             throw new Exception(e.getMessage() + "\nImage couldn't be fetched: " + (url != null ? url.toString() : ""));
    77         }
    78     }
    79 
    80     public static final NumberFormat latLonFormat = new DecimalFormat("###0.0000000",
    81             new DecimalFormatSymbols(Locale.US));
    82 
    83     protected URL getURL(double w, double s,double e,double n,
    84             int wi, int ht) throws MalformedURLException {
    85         String myProj = Main.proj.toCode();
    86         if(Main.proj instanceof Mercator) // don't use mercator code directly
    87         {
    88             LatLon sw = Main.proj.eastNorth2latlon(new EastNorth(w, s));
    89             LatLon ne = Main.proj.eastNorth2latlon(new EastNorth(e, n));
    90             myProj = "EPSG:4326";
    91             s = sw.lat();
    92             w = sw.lon();
    93             n = ne.lat();
    94             e = ne.lon();
    95         }
    96 
    97         String str = baseURL;
    98         String bbox = latLonFormat.format(w) + ","
    99                            + latLonFormat.format(s) + ","
    100                            + latLonFormat.format(e) + ","
    101                            + latLonFormat.format(n);
    102 
    103         if (urlWithPatterns) {
    104             str = str.replaceAll("\\{proj\\}", myProj)
    105             .replaceAll("\\{bbox\\}", bbox)
    106             .replaceAll("\\{w\\}", latLonFormat.format(w))
    107             .replaceAll("\\{s\\}", latLonFormat.format(s))
    108             .replaceAll("\\{e\\}", latLonFormat.format(e))
    109             .replaceAll("\\{n\\}", latLonFormat.format(n))
    110             .replaceAll("\\{width\\}", String.valueOf(wi))
    111             .replaceAll("\\{height\\}", String.valueOf(ht));
    112         } else {
    113             str += "bbox=" + bbox
    114                 + getProjection(baseURL, false)
    115                 + "&width=" + wi + "&height=" + ht;
    116             if (!(baseURL.endsWith("&") || baseURL.endsWith("?"))) {
    117                 System.out.println(tr("Warning: The base URL ''{0}'' for a WMS service doesn't have a trailing '&' or a trailing '?'.", baseURL));
    118                 System.out.println(tr("Warning: Fetching WMS tiles is likely to fail. Please check you preference settings."));
    119                 System.out.println(tr("Warning: The complete URL is ''{0}''.", str));
    120             }
    121         }
    122         return new URL(str.replace(" ", "%20"));
    123     }
    124 
    125     static public String getProjection(String baseURL, Boolean warn)
    126     {
    127         String projname = Main.proj.toCode();
    128         if(Main.proj instanceof Mercator) // don't use mercator code
    129             projname = "EPSG:4326";
    130         String res = "";
    131         try
    132         {
    133             Matcher m = Pattern.compile(".*srs=([a-z0-9:]+).*").matcher(baseURL.toLowerCase());
    134             if(m.matches())
    135             {
    136                 projname = projname.toLowerCase();
    137                 if(!projname.equals(m.group(1)) && warn)
    138                 {
    139                     JOptionPane.showMessageDialog(Main.parent,
    140                     tr("The projection ''{0}'' in URL and current projection ''{1}'' mismatch.\n"
    141                     + "This may lead to wrong coordinates.",
    142                     m.group(1), projname),
    143                     tr("Warning"),
    144                     JOptionPane.WARNING_MESSAGE);
    145                 }
    146             }
    147             else
    148                 res ="&srs="+projname;
    149         }
    150         catch(Exception e)
    151         {
    152         }
    153         return res;
    154     }
    155 
    156     @Override
    157         public boolean loadFromCache(boolean real){
    158         URL url = null;
    159         try{
    160            url = getURL(
    161               b.min.east(), b.min.north(),
    162               b.max.east(), b.max.north(),
    163               width(), height());
    164         } catch(Exception e) {
    165            return false;
    166         }
    167         BufferedImage cached = cache.getImg(url.toString());
    168         if((!real && !layer.hasAutoDownload()) || cached != null){
    169            image.min = b.min;
    170            image.max = b.max;
    171            if(cached == null){
    172               grabNotInCache();
    173               return true;
    174            }
    175            image.image = cached;
    176            image.flushedResizedCachedInstance();
    177            image.downloadingStarted = false;
    178            return true;
    179         }
    180         return false;
    181     }
    182 
    183     protected BufferedImage grab(URL url) throws IOException, OsmTransferException {
    184         System.out.println("Grabbing WMS " + url);
    185 
    186         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    187         if(layer.cookies != null && !layer.cookies.equals(""))
    188             conn.setRequestProperty("Cookie", layer.cookies);
    189         conn.setRequestProperty("User-Agent", Main.pref.get("wmsplugin.user_agent", Version.getInstance().getAgentString()));
    190         conn.setConnectTimeout(Main.pref.getInteger("wmsplugin.timeout.connect", 30) * 1000);
    191         conn.setReadTimeout(Main.pref.getInteger("wmsplugin.timeout.read", 30) * 1000);
    192 
    193         String contentType = conn.getHeaderField("Content-Type");
    194         if( conn.getResponseCode() != 200
    195                 || contentType != null && !contentType.startsWith("image") ) {
    196             throw new IOException(readException(conn));
    197         }
    198 
    199         InputStream is = new ProgressInputStream(conn, null);
    200         BufferedImage img = ImageIO.read(is);
    201         is.close();
    202 
    203         cache.saveImg(url.toString(), img);
    204         return img;
    205     }
    206 
    207     protected String readException(URLConnection conn) throws IOException {
    208         StringBuilder exception = new StringBuilder();
    209         InputStream in = conn.getInputStream();
    210         BufferedReader br = new BufferedReader(new InputStreamReader(in));
    211 
    212         String line = null;
    213         while( (line = br.readLine()) != null) {
    214             // filter non-ASCII characters and control characters
    215             exception.append(line.replaceAll("[^\\p{Print}]", ""));
    216             exception.append('\n');
    217         }
    218         return exception.toString();
    219     }
     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.baseURL;
     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.cookies != null && !layer.cookies.equals(""))
     172                        conn.setRequestProperty("Cookie", layer.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        }
    220204}
  • applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSLayer.java

    r22677 r22712  
    1313import java.io.ObjectInputStream;
    1414import java.io.ObjectOutputStream;
     15import java.util.ArrayList;
     16import java.util.Collections;
     17import java.util.Iterator;
    1518import java.util.List;
    16 import java.util.concurrent.ExecutorService;
    17 import java.util.concurrent.Executors;
     19import java.util.concurrent.locks.Condition;
     20import java.util.concurrent.locks.Lock;
     21import java.util.concurrent.locks.ReentrantLock;
    1822
    1923import javax.swing.AbstractAction;
     
    2933import org.openstreetmap.josm.actions.SaveActionBase;
    3034import org.openstreetmap.josm.data.Bounds;
     35import org.openstreetmap.josm.data.ProjectionBounds;
    3136import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
    3237import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
    33 import org.openstreetmap.josm.data.ProjectionBounds;
    3438import org.openstreetmap.josm.data.coor.EastNorth;
    3539import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
     
    4145import org.openstreetmap.josm.io.CacheFiles;
    4246import org.openstreetmap.josm.tools.ImageProvider;
     47
     48import wmsplugin.GeorefImage.State;
    4349
    4450/**
     
    6975        protected boolean autoDownloadEnabled = true;
    7076
    71         private ExecutorService executor = null;
     77        // Image index boundary for current view
     78        private volatile int bminx;
     79        private volatile int bminy;
     80        private volatile int bmaxx;
     81        private volatile int bmaxy;
     82        private volatile int leftEdge;
     83        private volatile int bottomEdge;
     84
     85
     86        private final List<WMSRequest> requestQueue = new ArrayList<WMSRequest>();
     87        private final List<WMSRequest> finishedRequests = new ArrayList<WMSRequest>();
     88        private final Lock requestQueueLock = new ReentrantLock();
     89        private final Condition queueEmpty = requestQueueLock.newCondition();
     90        private final List<Grabber> grabbers = new ArrayList<Grabber>();
     91        private final List<Thread> grabberThreads = new ArrayList<Thread>();
     92        private int threadCount;
     93        private int workingThreadCount;
     94        private boolean canceled;
     95
    7296
    7397        /** set to true if this layer uses an invalid base url */
     
    100124                resolution = mv.getDist100PixelText();
    101125
    102                 executor = Executors.newFixedThreadPool(
    103                                 Main.pref.getInteger("wmsplugin.numThreads",
    104                                                 WMSPlugin.simultaneousConnections));
     126                startGrabberThreads();
    105127                if (baseURL != null && !baseURL.startsWith("html:") && !WMSGrabber.isUrlWithPatterns(baseURL)) {
    106128                        if (!(baseURL.endsWith("&") || baseURL.endsWith("?"))) {
     
    123145        }
    124146
    125         public double getDx(){
    126                 return dx;
    127         }
    128 
    129         public double getDy(){
    130                 return dy;
    131         }
    132147
    133148        @Override
    134149        public void destroy() {
    135                 try {
    136                         executor.shutdownNow();
    137                         // Might not be initialized, so catch NullPointer as well
    138                 } catch(Exception x) {
    139                         x.printStackTrace();
    140                 }
    141         }
    142 
    143         public double getPPD(){
    144                 ProjectionBounds bounds = mv.getProjectionBounds();
    145                 return mv.getWidth() / (bounds.max.east() - bounds.min.east());
     150                cancelGrabberThreads(false);
    146151        }
    147152
     
    150155                for(int x = 0; x<dax; ++x) {
    151156                        for(int y = 0; y<day; ++y) {
    152                                 images[x][y]= new GeorefImage(false);
     157                                images[x][y]= new GeorefImage(this);
    153158                        }
    154159                }
     
    173178        }
    174179
    175         private ProjectionBounds XYtoBounds (int x, int y) {
    176                 return new ProjectionBounds(
    177                                 new EastNorth(      x * imageSize / pixelPerDegree,       y * imageSize / pixelPerDegree),
    178                                 new EastNorth((x + 1) * imageSize / pixelPerDegree, (y + 1) * imageSize / pixelPerDegree));
    179         }
    180 
    181180        private int modulo (int a, int b) {
    182181                return a % b >= 0 ? a%b : a%b+b;
     
    188187        }
    189188
    190         @Override public void paint(Graphics2D g, final MapView mv, Bounds bounds) {
     189        @Override public void paint(Graphics2D g, final MapView mv, Bounds b) {
    191190                if(baseURL == null) return;
    192191                if (usesInvalidUrl && !isInvalidUrlConfirmed) return;
    193192
     193                ProjectionBounds bounds = mv.getProjectionBounds();
     194                bminx= getImageXIndex(bounds.min.east());
     195                bminy= getImageYIndex(bounds.min.north());
     196                bmaxx= getImageXIndex(bounds.max.east());
     197                bmaxy= getImageYIndex(bounds.max.north());
     198
     199                leftEdge = (int)(bounds.min.east() * getPPD());
     200                bottomEdge = (int)(bounds.min.north() * getPPD());
     201
    194202                if (zoomIsTooBig()) {
    195                         for(int x = 0; x<dax; ++x) {
    196                                 for(int y = 0; y<day; ++y) {
    197                                         images[modulo(x,dax)][modulo(y,day)].paint(g, mv, dx, dy);
     203                        for(int x = bminx; x<=bmaxx; ++x) {
     204                                for(int y = bminy; y<=bmaxy; ++y) {
     205                                        images[modulo(x,dax)][modulo(y,day)].paint(g, mv, x, y, leftEdge, bottomEdge);
    198206                                }
    199207                        }
     
    201209                        downloadAndPaintVisible(g, mv, false);
    202210                }
    203         }
    204 
    205         public void displace(double dx, double dy) {
    206                 this.dx += dx;
    207                 this.dy += dy;
    208211        }
    209212
     
    237240        }
    238241
    239         protected void downloadAndPaintVisible(Graphics g, final MapView mv,
    240                         boolean real){
    241                 if (usesInvalidUrl)
    242                         return;
    243 
     242        public double getPPD(){
    244243                ProjectionBounds bounds = mv.getProjectionBounds();
    245                 int bminx= (int)Math.floor (((bounds.min.east() - dx) * pixelPerDegree) / imageSize );
    246                 int bminy= (int)Math.floor (((bounds.min.north() - dy) * pixelPerDegree) / imageSize );
    247                 int bmaxx= (int)Math.ceil  (((bounds.max.east() - dx) * pixelPerDegree) / imageSize );
    248                 int bmaxy= (int)Math.ceil  (((bounds.max.north() - dy) * pixelPerDegree) / imageSize );
    249 
    250                 for(int x = bminx; x<bmaxx; ++x) {
    251                         for(int y = bminy; y<bmaxy; ++y){
     244                return mv.getWidth() / (bounds.max.east() - bounds.min.east());
     245        }
     246
     247        public void displace(double dx, double dy) {
     248                this.dx += dx;
     249                this.dy += dy;
     250        }
     251
     252        public int getImageXIndex(double coord) {
     253                return (int)Math.floor( ((coord - dx) * pixelPerDegree) / imageSize);
     254        }
     255
     256        public int getImageYIndex(double coord) {
     257                return (int)Math.floor( ((coord - dy) * pixelPerDegree) / imageSize);
     258        }
     259
     260        public int getImageX(int imageIndex) {
     261                return (int)(imageIndex * imageSize * (getPPD() / pixelPerDegree) + dx * getPPD());
     262        }
     263
     264        public int getImageY(int imageIndex) {
     265                return (int)(imageIndex * imageSize * (getPPD() / pixelPerDegree) + dy * getPPD());
     266        }
     267
     268        /**
     269         *
     270         * @param xIndex
     271         * @param yIndex
     272         * @return Real EastNorth of given tile. dx/dy is not counted in
     273         */
     274        public EastNorth getEastNorth(int xIndex, int yIndex) {
     275                return new EastNorth((xIndex * imageSize) / pixelPerDegree, (yIndex * imageSize) / pixelPerDegree);
     276        }
     277
     278
     279        protected void downloadAndPaintVisible(Graphics g, final MapView mv, boolean real){
     280
     281                for(int x = bminx; x<=bmaxx; ++x) {
     282                        for(int y = bminy; y<=bmaxy; ++y){
     283                                images[modulo(x,dax)][modulo(y,day)].changePosition(x, y);
     284                        }
     285                }
     286
     287                gatherFinishedRequests();
     288
     289                for(int x = bminx; x<=bmaxx; ++x) {
     290                        for(int y = bminy; y<=bmaxy; ++y){
    252291                                GeorefImage img = images[modulo(x,dax)][modulo(y,day)];
    253                                 if((!img.paint(g, mv, dx, dy) || img.infotext) && !img.downloadingStarted){
    254                                         img.downloadingStarted = true;
    255                                         img.image = null;
    256                                         img.flushedResizedCachedInstance();
    257                                         Grabber gr = WMSPlugin.getGrabber(XYtoBounds(x,y), img, mv, this);
    258                                         if(!gr.loadFromCache(real)){
    259                                                 gr.setPriority(1);
    260                                                 executor.submit(gr);
    261                                         }
     292                                if (!img.paint(g, mv, x, y, leftEdge, bottomEdge)) {
     293                                        WMSRequest request = new WMSRequest(x, y, pixelPerDegree, real);
     294                                        addRequest(request);
    262295                                }
    263296                        }
     
    268301                for(int x = 0; x<dax; ++x) {
    269302                        for(int y = 0; y<day; ++y)
    270                                 if(images[x][y].image!=null){
    271                                         v.visit(images[x][y].min);
    272                                         v.visit(images[x][y].max);
     303                                if(images[x][y].getImage() != null){
     304                                        v.visit(images[x][y].getMin());
     305                                        v.visit(images[x][y].getMax());
    273306                                }
    274307                }
     
    300333
    301334        public GeorefImage findImage(EastNorth eastNorth) {
    302                 for(int x = 0; x<dax; ++x) {
    303                         for(int y = 0; y<day; ++y)
    304                                 if(images[x][y].image!=null && images[x][y].min!=null && images[x][y].max!=null)
    305                                         if(images[x][y].contains(eastNorth, dx, dy))
    306                                                 return images[x][y];
    307                 }
    308                 return null;
     335                int xIndex = getImageXIndex(eastNorth.east());
     336                int yIndex = getImageYIndex(eastNorth.north());
     337                GeorefImage result = images[modulo(xIndex, dax)][modulo(yIndex, day)];
     338                if (result.getXIndex() == xIndex && result.getYIndex() == yIndex) {
     339                        return result;
     340                } else {
     341                        return null;
     342                }
     343        }
     344
     345        /**
     346         *
     347         * @param request
     348         * @return -1 if request is no longer needed, otherwise priority of request (lower number <=> more important request)
     349         */
     350        private int getRequestPriority(WMSRequest request) {
     351                if (request.getPixelPerDegree() != pixelPerDegree) {
     352                        return -1;
     353                }
     354                if (bminx > request.getXIndex()
     355                                || bmaxx < request.getXIndex()
     356                                || bminy > request.getYIndex()
     357                                || bmaxy < request.getYIndex()) {
     358                        return -1;
     359                }
     360
     361                EastNorth cursorEastNorth = mv.getEastNorth(mv.lastMEvent.getX(), mv.lastMEvent.getY());
     362                int mouseX = getImageXIndex(cursorEastNorth.east());
     363                int mouseY = getImageYIndex(cursorEastNorth.north());
     364                int dx = request.getXIndex() - mouseX;
     365                int dy = request.getYIndex() - mouseY;
     366
     367                return dx * dx + dy * dy;
     368        }
     369
     370        public WMSRequest getRequest() {
     371                requestQueueLock.lock();
     372                try {
     373                        workingThreadCount--;
     374                        Iterator<WMSRequest> it = requestQueue.iterator();
     375                        while (it.hasNext()) {
     376                                WMSRequest item = it.next();
     377                                int priority = getRequestPriority(item);
     378                                if (priority == -1) {
     379                                        it.remove();
     380                                } else {
     381                                        item.setPriority(priority);
     382                                }
     383                        }
     384                        Collections.sort(requestQueue);
     385
     386                        EastNorth cursorEastNorth = mv.getEastNorth(mv.lastMEvent.getX(), mv.lastMEvent.getY());
     387                        int mouseX = getImageXIndex(cursorEastNorth.east());
     388                        int mouseY = getImageYIndex(cursorEastNorth.north());
     389                        boolean isOnMouse = requestQueue.size() > 0 && requestQueue.get(0).getXIndex() == mouseX && requestQueue.get(0).getYIndex() == mouseY;
     390
     391                        // If there is only one thread left then keep it in case we need to download other tile urgently
     392                        while (!canceled &&
     393                                        (requestQueue.isEmpty() || (!isOnMouse && threadCount - workingThreadCount == 0 && threadCount > 1))) {
     394                                try {
     395                                        queueEmpty.await();
     396                                } catch (InterruptedException e) {
     397                                        // Shouldn't happen
     398                                }
     399                        }
     400
     401                        workingThreadCount++;
     402                        if (canceled) {
     403                                return null;
     404                        } else {
     405                                return requestQueue.remove(0);
     406                        }
     407
     408                } finally {
     409                        requestQueueLock.unlock();
     410                }
     411        }
     412
     413        public void finishRequest(WMSRequest request) {
     414                requestQueueLock.lock();
     415                try {
     416                        finishedRequests.add(request);
     417                } finally {
     418                        requestQueueLock.unlock();
     419                }
     420        }
     421
     422        public void addRequest(WMSRequest request) {
     423                requestQueueLock.lock();
     424                try {
     425                        if (!requestQueue.contains(request)) {
     426                                requestQueue.add(request);
     427                                queueEmpty.signalAll();
     428                        }
     429                } finally {
     430                        requestQueueLock.unlock();
     431                }
     432        }
     433
     434        public boolean requestIsValid(WMSRequest request) {
     435                return bminx <= request.getXIndex() && bmaxx >= request.getXIndex() && bminy <= request.getYIndex() && bmaxy >= request.getYIndex();
     436        }
     437
     438        private void gatherFinishedRequests() {
     439                requestQueueLock.lock();
     440                try {
     441                        for (WMSRequest request: finishedRequests) {
     442                                GeorefImage img = images[modulo(request.getXIndex(),dax)][modulo(request.getYIndex(),day)];
     443                                if (img.equalPosition(request.getXIndex(), request.getYIndex())) {
     444                                        img.changeImage(request.getState(), request.getImage());
     445                                }
     446                        }
     447                        finishedRequests.clear();
     448                } finally {
     449                        requestQueueLock.unlock();
     450                }
    309451        }
    310452
     
    352494                                for (int y = 0; y < day; ++y) {
    353495                                        GeorefImage img = images[modulo(x,dax)][modulo(y,day)];
    354                                         if(img.failed){
    355                                                 img.image = null;
    356                                                 img.flushedResizedCachedInstance();
    357                                                 img.downloadingStarted = false;
    358                                                 img.failed = false;
     496                                        if(img.getState() == State.FAILED){
     497                                                addRequest(new WMSRequest(img.getXIndex(), img.getYIndex(), pixelPerDegree, true));
    359498                                                mv.repaint();
    360499                                        }
     
    513652                public void actionPerformed(ActionEvent e) {
    514653                        autoDownloadEnabled = !autoDownloadEnabled;
    515                 }
    516 
     654                        if (autoDownloadEnabled) {
     655                                mv.repaint();
     656                        }
     657                }
     658        }
     659
     660        private void cancelGrabberThreads(boolean wait) {
     661                requestQueueLock.lock();
     662                try {
     663                        canceled = true;
     664                        for (Grabber grabber: grabbers) {
     665                                grabber.cancel();
     666                        }
     667                        queueEmpty.signalAll();
     668                } finally {
     669                        requestQueueLock.unlock();
     670                }
     671                if (wait) {
     672                        for (Thread t: grabberThreads) {
     673                                try {
     674                                        t.join();
     675                                } catch (InterruptedException e) {
     676                                        // Shouldn't happen
     677                                        e.printStackTrace();
     678                                }
     679                        }
     680                }
     681        }
     682
     683        private void startGrabberThreads() {
     684                int threadCount = WMSPlugin.PROP_SIMULTANEOUS_CONNECTIONS.get();
     685                requestQueueLock.lock();
     686                try {
     687                        canceled = false;
     688                        grabbers.clear();
     689                        grabberThreads.clear();
     690                        for (int i=0; i<threadCount; i++) {
     691                                Grabber grabber = WMSPlugin.getGrabber(mv, this);
     692                                grabbers.add(grabber);
     693                                Thread t = new Thread(grabber, "WMS " + getName() + " " + i);
     694                                t.setDaemon(true);
     695                                t.start();
     696                                grabberThreads.add(t);
     697                        }
     698                        this.workingThreadCount = grabbers.size();
     699                        this.threadCount = grabbers.size();
     700                } finally {
     701                        requestQueueLock.unlock();
     702                }
    517703        }
    518704
    519705        public void preferenceChanged(PreferenceChangeEvent event) {
    520                 if (event.getKey().equals("wmsplugin.simultaneousConnections")) {
    521                         executor.shutdownNow();
    522                         executor = Executors.newFixedThreadPool(
    523                                         Main.pref.getInteger("wmsplugin.numThreads",
    524                                                         WMSPlugin.simultaneousConnections));
     706                if (event.getKey().equals(WMSPlugin.PROP_SIMULTANEOUS_CONNECTIONS.getKey())) {
     707                        cancelGrabberThreads(true);
     708                        startGrabberThreads();
    525709                }
    526710        }
  • applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSPlugin.java

    r22686 r22712  
    2929import org.openstreetmap.josm.actions.ExtensionFileFilter;
    3030import org.openstreetmap.josm.actions.JosmAction;
    31 import org.openstreetmap.josm.data.ProjectionBounds;
     31import org.openstreetmap.josm.data.preferences.IntegerProperty;
    3232import org.openstreetmap.josm.gui.IconToggleButton;
    3333import org.openstreetmap.josm.gui.MainMenu;
     
    4848        static CacheFiles cache = new CacheFiles("wmsplugin");
    4949
     50        public static final IntegerProperty PROP_SIMULTANEOUS_CONNECTIONS = new IntegerProperty("wmsplugin.simultaneousConnections", 3);
     51
    5052        WMSLayer wmsLayer;
    5153        static JMenu wmsJMenu;
     
    5759        static int overlapEast = 14;
    5860        static int overlapNorth = 4;
    59         static int simultaneousConnections = 3;
    6061        // remember state of menu item to restore on changed preferences
    6162        static private boolean menuEnabled = false;
     
    177178                } catch (Exception e) {} // If sth fails, we drop to default settings.
    178179
    179                 // Load the settings for number of simultaneous connections
    180                 try {
    181                         simultaneousConnections = Integer.valueOf(Main.pref.get("wmsplugin.simultanousConnections"));
    182                 } catch (Exception e) {} // If sth fails, we drop to default settings.
    183 
    184180                // And then the names+urls of WMS servers
    185181                int prefid = 0;
     
    287283        }
    288284
    289         public static Grabber getGrabber(ProjectionBounds bounds, GeorefImage img, MapView mv, WMSLayer layer){
     285        public static Grabber getGrabber(MapView mv, WMSLayer layer){
    290286                if(layer.baseURL.startsWith("html:"))
    291                         return new HTMLGrabber(bounds, img, mv, layer, cache);
     287                        return new HTMLGrabber(mv, layer, cache);
    292288                else
    293                         return new WMSGrabber(bounds, img, mv, layer, cache);
     289                        return new WMSGrabber(mv, layer, cache);
    294290        }
    295291
  • applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSPreferenceEditor.java

    r22677 r22712  
    187187                p.add(Box.createHorizontalGlue(), GBC.eol().fill(GridBagConstraints.HORIZONTAL));
    188188                JLabel labelSimConn = new JLabel(tr("Simultaneous connections"));
    189                 spinSimConn = new JSpinner(new SpinnerNumberModel(WMSPlugin.simultaneousConnections, 1, 30, 1));
     189                spinSimConn = new JSpinner(new SpinnerNumberModel(WMSPlugin.PROP_SIMULTANEOUS_CONNECTIONS.get(), 1, 30, 1));
    190190                JPanel overlapPanelSimConn = new JPanel(new FlowLayout());
    191191                overlapPanelSimConn.add(labelSimConn);
     
    243243                WMSPlugin.overlapEast = (Integer) spinEast.getModel().getValue();
    244244                WMSPlugin.overlapNorth = (Integer) spinNorth.getModel().getValue();
    245                 WMSPlugin.simultaneousConnections = (Integer) spinSimConn.getModel().getValue();
     245                WMSPlugin.PROP_SIMULTANEOUS_CONNECTIONS.put((Integer) spinSimConn.getModel().getValue());
    246246                allowRemoteControl = remoteCheckBox.getModel().isSelected();
    247247
     
    251251
    252252                Main.pref.put("wmsplugin.browser", browser.getEditor().getItem().toString());
    253                 Main.pref.put("wmsplugin.simultaneousConnections", String.valueOf(WMSPlugin.simultaneousConnections));
    254253
    255254                Main.pref.put("wmsplugin.remotecontrol",    String.valueOf(allowRemoteControl));
Note: See TracChangeset for help on using the changeset viewer.