001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.io.remotecontrol.handler;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.util.Arrays;
007import java.util.HashMap;
008
009import org.openstreetmap.josm.Main;
010import org.openstreetmap.josm.data.imagery.ImageryInfo;
011import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
012import org.openstreetmap.josm.data.imagery.ImageryLayerInfo;
013import org.openstreetmap.josm.gui.layer.ImageryLayer;
014import org.openstreetmap.josm.gui.util.GuiHelper;
015import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
016import org.openstreetmap.josm.tools.Utils;
017
018/**
019 * Adds an imagery (WMS/TMS) layer. For instance, {@code /imagery?title=...&type=...&url=...}.
020 * @since 3715
021 */
022public class ImageryHandler extends RequestHandler {
023
024    /**
025     * The remote control command name used to add an imagery layer.
026     */
027    public static final String command = "imagery";
028
029    @Override
030    public String getPermissionMessage() {
031        return tr("Remote Control has been asked to load an imagery layer from the following URL:")
032                + "<br>" + args.get("url");
033    }
034
035    @Override
036    public String[] getMandatoryParams() {
037        return new String[]{"url"};
038    }
039
040    @Override
041    public String[] getOptionalParams() {
042        return new String[] { "title", "type", "cookies", "min_zoom", "max_zoom"};
043    }
044
045    @Override
046    public PermissionPrefWithDefault getPermissionPref() {
047        return PermissionPrefWithDefault.LOAD_IMAGERY;
048    }
049
050    protected static ImageryInfo findBingEntry() {
051        for (ImageryInfo i : ImageryLayerInfo.instance.getDefaultLayers()) {
052            if (ImageryType.BING.equals(i.getImageryType())) {
053                return i;
054            }
055        }
056        return null;
057    }
058
059    protected ImageryInfo buildImageryInfo() {
060        String url = args.get("url");
061        String title = args.get("title");
062        String type = args.get("type");
063        final ImageryInfo bing = ImageryType.BING.getTypeString().equals(type) ? findBingEntry() : null;
064        if ((title == null || title.isEmpty()) && bing != null) {
065            title = bing.getName();
066        }
067        if (title == null || title.isEmpty()) {
068            title = tr("Remote imagery");
069        }
070        String cookies = args.get("cookies");
071        final ImageryInfo imgInfo = new ImageryInfo(title, url, type, null, cookies);
072        if (bing != null) {
073            imgInfo.setIcon(bing.getIcon());
074        }
075        String minZoom = args.get("min_zoom");
076        if (minZoom != null && !minZoom.isEmpty()) {
077            try {
078                imgInfo.setDefaultMinZoom(Integer.parseInt(minZoom));
079            } catch (NumberFormatException e) {
080                Main.error(e);
081            }
082        }
083        String maxZoom = args.get("max_zoom");
084        if (maxZoom != null && !maxZoom.isEmpty()) {
085            try {
086                imgInfo.setDefaultMaxZoom(Integer.parseInt(maxZoom));
087            } catch (NumberFormatException e) {
088                Main.error(e);
089            }
090        }
091        return imgInfo;
092    }
093
094    @Override
095    protected void handleRequest() throws RequestHandlerErrorException {
096        final ImageryInfo imgInfo = buildImageryInfo();
097        if (Main.isDisplayingMapView()) {
098            for (ImageryLayer layer : Main.map.mapView.getLayersOfType(ImageryLayer.class)) {
099                if (layer.getInfo().equals(imgInfo)) {
100                    Main.info("Imagery layer already exists: "+imgInfo);
101                    return;
102                }
103            }
104        }
105        GuiHelper.runInEDT(new Runnable() {
106            @Override
107            public void run() {
108                try {
109                    Main.main.addLayer(ImageryLayer.create(imgInfo));
110                } catch (IllegalArgumentException e) {
111                    Main.error(e, false);
112                }
113            }
114        });
115    }
116
117    @Override
118    protected void parseArgs() {
119        HashMap<String, String> args = new HashMap<>();
120        if (request.indexOf('?') != -1) {
121            String query = request.substring(request.indexOf('?') + 1);
122            if (query.indexOf("url=") == 0) {
123                args.put("url", decodeParam(query.substring(4)));
124            } else {
125                int urlIdx = query.indexOf("&url=");
126                if (urlIdx != -1) {
127                    args.put("url", decodeParam(query.substring(urlIdx + 5)));
128                    query = query.substring(0, urlIdx);
129                } else if (query.indexOf('#') != -1) {
130                    query = query.substring(0, query.indexOf('#'));
131                }
132                String[] params = query.split("&", -1);
133                for (String param : params) {
134                    int eq = param.indexOf('=');
135                    if (eq != -1) {
136                        args.put(param.substring(0, eq), decodeParam(param.substring(eq + 1)));
137                    }
138                }
139            }
140        }
141        this.args = args;
142    }
143
144    @Override
145    protected void validateRequest() throws RequestHandlerBadRequestException {
146        String url = args.get("url");
147        String type = args.get("type");
148        try {
149            ImageryLayer.create(new ImageryInfo(null, url, type, null, null));
150        } catch (IllegalArgumentException e) {
151            throw new RequestHandlerBadRequestException(e.getMessage(), e);
152        }
153    }
154
155    @Override
156    public String getUsage() {
157        return "adds an imagery layer (e.g. WMS, TMS)";
158    }
159
160    @Override
161    public String[] getUsageExamples() {
162        final String types = Utils.join("|", Utils.transform(Arrays.asList(ImageryInfo.ImageryType.values()), new Utils.Function<ImageryInfo.ImageryType, String>() {
163            @Override
164            public String apply(ImageryInfo.ImageryType x) {
165                return x.getTypeString();
166            }
167        }));
168        return new String[] { "/imagery?title=osm&type=tms&url=https://a.tile.openstreetmap.org/%7Bzoom%7D/%7Bx%7D/%7By%7D.png",
169            "/imagery?title=landsat&type=wms&url=http://irs.gis-lab.info/?layers=landsat&SRS=%7Bproj%7D&WIDTH=%7Bwidth%7D&HEIGHT=%7Bheight%7D&BBOX=%7Bbbox%7D",
170            "/imagery?title=...&type={"+types+"}&url=....[&cookies=...][&min_zoom=...][&max_zoom=...]"};
171    }
172}