Changeset 7425 in josm for trunk


Ignore:
Timestamp:
2014-08-16T23:50:43+02:00 (10 years ago)
Author:
Don-vip
Message:

fix #3916 - WMS: Improve exception handling. Proper message is now displayed on tiles.

Location:
trunk/src/org/openstreetmap/josm
Files:
1 added
1 deleted
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/imagery/GeorefImage.java

    r7132 r7425  
    7373            image.flush();
    7474        }
    75         changeImage(null, null);
    76     }
    77 
    78     public void changeImage(State state, BufferedImage image) {
     75        changeImage(null, null, null);
     76    }
     77
     78    public void changeImage(State state, BufferedImage image, String errorMsg) {
    7979        flushResizedCachedInstance();
    8080        this.image = image;
     
    8585        case FAILED:
    8686            BufferedImage imgFailed = createImage();
    87             layer.drawErrorTile(imgFailed);
     87            layer.drawErrorTile(imgFailed, errorMsg);
    8888            this.image = imgFailed;
    8989            break;
  • trunk/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java

    r7291 r7425  
    99import java.awt.Component;
    1010import java.awt.Font;
    11 import java.awt.Graphics;
     11import java.awt.Graphics2D;
    1212import java.awt.GridBagLayout;
    1313import java.awt.event.ActionEvent;
     14import java.awt.font.FontRenderContext;
     15import java.awt.font.LineBreakMeasurer;
     16import java.awt.font.TextAttribute;
     17import java.awt.font.TextLayout;
    1418import java.awt.image.BufferedImage;
    1519import java.awt.image.BufferedImageOp;
    1620import java.awt.image.ConvolveOp;
    1721import java.awt.image.Kernel;
     22import java.text.AttributedCharacterIterator;
     23import java.text.AttributedString;
     24import java.util.Hashtable;
    1825import java.util.List;
     26import java.util.Map;
    1927
    2028import javax.swing.AbstractAction;
     
    6876    private final ImageryAdjustAction adjustAction = new ImageryAdjustAction(this);
    6977
     78    /**
     79     * Constructs a new {@code ImageryLayer}.
     80     */
    7081    public ImageryLayer(ImageryInfo info) {
    7182        super(info.getName());
     
    8192    }
    8293
    83     public double getPPD(){
     94    public double getPPD() {
    8495        if (!Main.isDisplayingMapView()) return Main.getProjection().getDefaultZoomInPPD();
    8596        ProjectionBounds bounds = Main.map.mapView.getProjectionBounds();
     
    231242    }
    232243
    233     public void drawErrorTile(BufferedImage img) {
    234         Graphics g = img.getGraphics();
     244    /**
     245     * Draws a red error tile when imagery tile cannot be fetched.
     246     * @param img The buffered image
     247     * @param message Additional error message to display
     248     */
     249    public void drawErrorTile(BufferedImage img, String message) {
     250        Graphics2D g = (Graphics2D) img.getGraphics();
    235251        g.setColor(Color.RED);
    236252        g.fillRect(0, 0, img.getWidth(), img.getHeight());
    237         g.setFont(g.getFont().deriveFont(Font.PLAIN).deriveFont(36.0f));
     253        g.setFont(g.getFont().deriveFont(Font.PLAIN).deriveFont(24.0f));
    238254        g.setColor(Color.BLACK);
    239255
    240256        String text = tr("ERROR");
    241         g.drawString(text, (img.getWidth() + g.getFontMetrics().stringWidth(text)) / 2, img.getHeight()/2);
    242     }
    243 
    244     /* (non-Javadoc)
    245      * @see org.openstreetmap.josm.gui.layer.Layer#destroy()
    246      */
     257        g.drawString(text, (img.getWidth() - g.getFontMetrics().stringWidth(text)) / 2, g.getFontMetrics().getHeight()+5);
     258        if (message != null) {
     259            float drawPosY = 2.5f*g.getFontMetrics().getHeight()+10;
     260            if (!message.contains(" ")) {
     261                g.setFont(g.getFont().deriveFont(Font.PLAIN).deriveFont(18.0f));
     262                g.drawString(message, 5, (int)drawPosY);
     263            } else {
     264                // Draw message on several lines
     265                Map<TextAttribute, Object> map = new Hashtable<TextAttribute, Object>();
     266                map.put(TextAttribute.FAMILY, "Serif");
     267                map.put(TextAttribute.SIZE, new Float(18.0));
     268                AttributedString vanGogh = new AttributedString(message, map);
     269                // Create a new LineBreakMeasurer from the text
     270                AttributedCharacterIterator paragraph = vanGogh.getIterator();
     271                int paragraphStart = paragraph.getBeginIndex();
     272                int paragraphEnd = paragraph.getEndIndex();
     273                FontRenderContext frc = g.getFontRenderContext();
     274                LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, frc);
     275                // Set break width to width of image with some margin
     276                float breakWidth = img.getWidth()-10;
     277                // Set position to the index of the first character in the text
     278                lineMeasurer.setPosition(paragraphStart);
     279                // Get lines until the entire paragraph has been displayed
     280                while (lineMeasurer.getPosition() < paragraphEnd) {
     281                    // Retrieve next layout
     282                    TextLayout layout = lineMeasurer.nextLayout(breakWidth);
     283
     284                    // Compute pen x position
     285                    float drawPosX = layout.isLeftToRight() ? 0 : breakWidth - layout.getAdvance();
     286
     287                    // Move y-coordinate by the ascent of the layout
     288                    drawPosY += layout.getAscent();
     289
     290                    // Draw the TextLayout at (drawPosX, drawPosY)
     291                    layout.draw(g, drawPosX, drawPosY);
     292
     293                    // Move y-coordinate in preparation for next layout
     294                    drawPosY += layout.getDescent() + layout.getLeading();
     295                }
     296            }
     297        }
     298    }
     299
    247300    @Override
    248301    public void destroy() {
  • trunk/src/org/openstreetmap/josm/gui/layer/WMSLayer.java

    r7140 r7425  
    6262import org.openstreetmap.josm.gui.progress.ProgressMonitor;
    6363import org.openstreetmap.josm.io.WMSLayerImporter;
    64 import org.openstreetmap.josm.io.imagery.Grabber;
    6564import org.openstreetmap.josm.io.imagery.HTMLGrabber;
     65import org.openstreetmap.josm.io.imagery.WMSException;
    6666import org.openstreetmap.josm.io.imagery.WMSGrabber;
    6767import org.openstreetmap.josm.io.imagery.WMSRequest;
     
    147147    private final Lock requestQueueLock = new ReentrantLock();
    148148    private final Condition queueEmpty = requestQueueLock.newCondition();
    149     private final List<Grabber> grabbers = new ArrayList<>();
     149    private final List<WMSGrabber> grabbers = new ArrayList<>();
    150150    private final List<Thread> grabberThreads = new ArrayList<>();
    151151    private boolean canceled;
     
    163163    }
    164164
     165    /**
     166     * Constructs a new {@code WMSLayer}.
     167     */
    165168    public WMSLayer(ImageryInfo info) {
    166169        super(info);
     
    648651                GeorefImage img = images[modulo(request.getXIndex(),dax)][modulo(request.getYIndex(),day)];
    649652                if (img.equalPosition(request.getXIndex(), request.getYIndex())) {
    650                     img.changeImage(request.getState(), request.getImage());
     653                    WMSException we = request.getException();
     654                    img.changeImage(request.getState(), request.getImage(), we != null ? we.getMessage() : null);
    651655                }
    652656            }
     
    931935            Main.map.mapView.zoomTo(Main.map.mapView.getCenter(), 1 / info.getPixelPerDegree());
    932936        }
    933 
    934937    }
    935938
     
    938941        try {
    939942            canceled = true;
    940             for (Grabber grabber: grabbers) {
     943            for (WMSGrabber grabber: grabbers) {
    941944                grabber.cancel();
    942945            }
     
    964967            grabberThreads.clear();
    965968            for (int i=0; i<threadCount; i++) {
    966                 Grabber grabber = getGrabber(i == 0 && threadCount > 1);
     969                WMSGrabber grabber = getGrabber(i == 0 && threadCount > 1);
    967970                grabbers.add(grabber);
    968971                Thread t = new Thread(grabber, "WMS " + getName() + " " + i);
     
    10051008    }
    10061009
    1007     protected Grabber getGrabber(boolean localOnly) {
     1010    protected WMSGrabber getGrabber(boolean localOnly) {
    10081011        if (getInfo().getImageryType() == ImageryType.HTML)
    10091012            return new HTMLGrabber(Main.map.mapView, this, localOnly);
  • trunk/src/org/openstreetmap/josm/io/imagery/WMSGrabber.java

    r7132 r7425  
    99import java.io.InputStream;
    1010import java.io.InputStreamReader;
     11import java.io.StringReader;
    1112import java.net.HttpURLConnection;
    1213import java.net.MalformedURLException;
     
    1718import java.text.DecimalFormatSymbols;
    1819import java.text.NumberFormat;
     20import java.util.ArrayList;
    1921import java.util.HashMap;
     22import java.util.List;
    2023import java.util.Locale;
    2124import java.util.Map;
     
    2427import java.util.regex.Pattern;
    2528
     29import javax.xml.parsers.DocumentBuilder;
     30import javax.xml.parsers.DocumentBuilderFactory;
     31import javax.xml.parsers.ParserConfigurationException;
     32
    2633import org.openstreetmap.josm.Main;
     34import org.openstreetmap.josm.data.ProjectionBounds;
    2735import org.openstreetmap.josm.data.coor.EastNorth;
    2836import org.openstreetmap.josm.data.coor.LatLon;
     
    3543import org.openstreetmap.josm.tools.ImageProvider;
    3644import org.openstreetmap.josm.tools.Utils;
    37 
    38 public class WMSGrabber extends Grabber {
     45import org.w3c.dom.Document;
     46import org.w3c.dom.NodeList;
     47import org.xml.sax.InputSource;
     48import org.xml.sax.SAXException;
     49
     50/**
     51 * WMS grabber, fetching tiles from WMS server.
     52 * @since 3715
     53 */
     54public class WMSGrabber implements Runnable {
     55
     56    protected final MapView mv;
     57    protected final WMSLayer layer;
     58    private final boolean localOnly;
     59
     60    protected ProjectionBounds b;
     61    protected volatile boolean canceled;
    3962
    4063    protected String baseURL;
     
    4265    private Map<String, String> props = new HashMap<>();
    4366
     67    /**
     68     * Constructs a new {@code WMSGrabber}.
     69     * @param mv Map view
     70     * @param layer WMS layer
     71     */
    4472    public WMSGrabber(MapView mv, WMSLayer layer, boolean localOnly) {
    45         super(mv, layer, localOnly);
     73        this.mv = mv;
     74        this.layer = layer;
     75        this.localOnly = localOnly;
    4676        this.info = layer.getInfo();
    4777        this.baseURL = info.getUrl();
    48         if(layer.getInfo().getCookies() != null && !layer.getInfo().getCookies().isEmpty()) {
     78        if (layer.getInfo().getCookies() != null && !layer.getInfo().getCookies().isEmpty()) {
    4979            props.put("Cookie", layer.getInfo().getCookies());
    5080        }
     
    6090    }
    6191
     92    int width() {
     93        return layer.getBaseImageWidth();
     94    }
     95
     96    int height() {
     97        return layer.getBaseImageHeight();
     98    }
     99
    62100    @Override
    63     void fetch(WMSRequest request, int attempt) throws Exception{
     101    public void run() {
     102        while (true) {
     103            if (canceled)
     104                return;
     105            WMSRequest request = layer.getRequest(localOnly);
     106            if (request == null)
     107                return;
     108            this.b = layer.getBounds(request);
     109            if (request.isPrecacheOnly()) {
     110                if (!layer.cache.hasExactMatch(Main.getProjection(), request.getPixelPerDegree(), b.minEast, b.minNorth)) {
     111                    attempt(request);
     112                } else if (Main.isDebugEnabled()) {
     113                    Main.debug("Ignoring "+request+" (precache only + exact match)");
     114                }
     115            } else if (!loadFromCache(request)){
     116                attempt(request);
     117            } else if (Main.isDebugEnabled()) {
     118                Main.debug("Ignoring "+request+" (loaded from cache)");
     119            }
     120            layer.finishRequest(request);
     121        }
     122    }
     123
     124    protected void attempt(WMSRequest request){ // try to fetch the image
     125        int maxTries = 5; // n tries for every image
     126        for (int i = 1; i <= maxTries; i++) {
     127            if (canceled)
     128                return;
     129            try {
     130                if (!request.isPrecacheOnly() && !layer.requestIsVisible(request))
     131                    return;
     132                fetch(request, i);
     133                break; // break out of the retry loop
     134            } catch (IOException e) {
     135                try { // sleep some time and then ask the server again
     136                    Thread.sleep(random(1000, 2000));
     137                } catch (InterruptedException e1) {
     138                    Main.debug("InterruptedException in "+getClass().getSimpleName()+" during WMS request");
     139                }
     140                if (i == maxTries) {
     141                    Main.error(e);
     142                    request.finish(State.FAILED, null, null);
     143                }
     144            } catch (WMSException e) {
     145                // Fail fast in case of WMS Service exception: useless to retry:
     146                // either the URL is wrong or the server suffers huge problems
     147                Main.error("WMS service exception while requesting "+e.getUrl()+":\n"+e.getMessage().trim());
     148                request.finish(State.FAILED, null, e);
     149                break; // break out of the retry loop
     150            }
     151        }
     152    }
     153
     154    public static int random(int min, int max) {
     155        return (int)(Math.random() * ((max+1)-min) ) + min;
     156    }
     157
     158    public final void cancel() {
     159        canceled = true;
     160    }
     161
     162    private void fetch(WMSRequest request, int attempt) throws IOException, WMSException {
    64163        URL url = null;
    65164        try {
     
    68167                    b.maxEast, b.maxNorth,
    69168                    width(), height());
    70             request.finish(State.IMAGE, grab(request, url, attempt));
    71 
    72         } catch(Exception e) {
     169            request.finish(State.IMAGE, grab(request, url, attempt), null);
     170
     171        } catch (IOException | OsmTransferException e) {
    73172            Main.error(e);
    74             throw new Exception(e.getMessage() + "\nImage couldn't be fetched: " + (url != null ? url.toString() : ""), e);
    75         }
    76     }
    77 
    78     public static final NumberFormat latLonFormat = new DecimalFormat("###0.0000000",
    79             new DecimalFormatSymbols(Locale.US));
     173            throw new IOException(e.getMessage() + "\nImage couldn't be fetched: " + (url != null ? url.toString() : ""), e);
     174        }
     175    }
     176
     177    public static final NumberFormat latLonFormat = new DecimalFormat("###0.0000000", new DecimalFormatSymbols(Locale.US));
    80178
    81179    protected URL getURL(double w, double s,double e,double n,
     
    133231    }
    134232
    135     @Override
    136233    public boolean loadFromCache(WMSRequest request) {
    137234        BufferedImage cached = layer.cache.getExactMatch(
     
    139236
    140237        if (cached != null) {
    141             request.finish(State.IMAGE, cached);
     238            request.finish(State.IMAGE, cached, null);
    142239            return true;
    143240        } else if (request.isAllowPartialCacheMatch()) {
     
    145242                    Main.getProjection(), request.getPixelPerDegree(), b.minEast, b.minNorth);
    146243            if (partialMatch != null) {
    147                 request.finish(State.PARTLY_IN_CACHE, partialMatch);
     244                request.finish(State.PARTLY_IN_CACHE, partialMatch, null);
    148245                return true;
    149246            }
    150247        }
    151248
    152         if((!request.isReal() && !layer.hasAutoDownload())){
    153             request.finish(State.NOT_IN_CACHE, null);
     249        if ((!request.isReal() && !layer.hasAutoDownload())){
     250            request.finish(State.NOT_IN_CACHE, null, null);
    154251            return true;
    155252        }
     
    158255    }
    159256
    160     protected BufferedImage grab(WMSRequest request, URL url, int attempt) throws IOException, OsmTransferException {
     257    protected BufferedImage grab(WMSRequest request, URL url, int attempt) throws WMSException, IOException, OsmTransferException {
    161258        Main.info("Grabbing WMS " + (attempt > 1? "(attempt " + attempt + ") ":"") + url);
    162259
    163260        HttpURLConnection conn = Utils.openHttpConnection(url);
    164         for(Entry<String, String> e : props.entrySet()) {
     261        for (Entry<String, String> e : props.entrySet()) {
    165262            conn.setRequestProperty(e.getKey(), e.getValue());
    166263        }
     
    169266
    170267        String contentType = conn.getHeaderField("Content-Type");
    171         if( conn.getResponseCode() != 200
    172                 || contentType != null && !contentType.startsWith("image") )
    173             throw new IOException(readException(conn));
     268        if (conn.getResponseCode() != 200
     269                || contentType != null && !contentType.startsWith("image") ) {
     270            String xml = readException(conn);
     271            try {
     272                DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
     273                InputSource is = new InputSource(new StringReader(xml));
     274                Document doc = db.parse(is);
     275                NodeList nodes = doc.getElementsByTagName("ServiceException");
     276                List<String> exceptions = new ArrayList<>(nodes.getLength());
     277                for (int i = 0; i < nodes.getLength(); i++) {
     278                    exceptions.add(nodes.item(i).getTextContent());
     279                }
     280                throw new WMSException(request, url, exceptions);
     281            } catch (SAXException | ParserConfigurationException ex) {
     282                throw new IOException(xml, ex);
     283            }
     284        }
    174285
    175286        ByteArrayOutputStream baos = new ByteArrayOutputStream();
  • trunk/src/org/openstreetmap/josm/io/imagery/WMSRequest.java

    r4745 r7425  
    1919    private State state;
    2020    private BufferedImage image;
     21    private WMSException exception;
    2122
    2223    public WMSRequest(int xIndex, int yIndex, double pixelPerDegree, boolean real, boolean allowPartialCacheMatch) {
     
    3334    }
    3435
    35 
    36     public void finish(State state, BufferedImage image) {
     36    public void finish(State state, BufferedImage image, WMSException exception) {
    3737        this.state = state;
    3838        this.image = image;
     39        this.exception = exception;
    3940    }
    4041
     
    9798    }
    9899
     100    /**
     101     * Replies the resulting state.
     102     * @return the resulting state
     103     */
    99104    public State getState() {
    100105        return state;
    101106    }
    102107
     108    /**
     109     * Replies the resulting image, if any.
     110     * @return the resulting image, or {@code null}
     111     */
    103112    public BufferedImage getImage() {
    104113        return image;
     114    }
     115
     116    /**
     117     * Replies the resulting exception, if any.
     118     * @return the resulting exception, or {@code null}
     119     * @since 7425
     120     */
     121    public WMSException getException() {
     122        return exception;
    105123    }
    106124
Note: See TracChangeset for help on using the changeset viewer.