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.LinkedHashSet;
008import java.util.Map;
009import java.util.Objects;
010import java.util.Set;
011
012import org.openstreetmap.josm.data.StructUtils;
013import org.openstreetmap.josm.data.imagery.ImageryInfo;
014import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryPreferenceEntry;
015import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
016import org.openstreetmap.josm.data.imagery.ImageryLayerInfo;
017import org.openstreetmap.josm.gui.MainApplication;
018import org.openstreetmap.josm.gui.layer.ImageryLayer;
019import org.openstreetmap.josm.gui.util.GuiHelper;
020import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
021import org.openstreetmap.josm.tools.CheckParameterUtil;
022import org.openstreetmap.josm.tools.Logging;
023import org.openstreetmap.josm.tools.Utils;
024
025/**
026 * Adds an imagery (WMS/TMS) layer. For instance, {@code /imagery?title=...&type=...&url=...}.
027 * @since 3715
028 */
029public class ImageryHandler extends RequestHandler.RawURLParseRequestHandler {
030
031    /**
032     * The remote control command name used to add an imagery layer.
033     */
034    public static final String command = "imagery";
035
036    @Override
037    public String getPermissionMessage() {
038        return tr("Remote Control has been asked to load an imagery layer from the following URL:")
039                + "<br>" + args.getOrDefault("url", args.get("id"));
040    }
041
042    @Override
043    public String[] getMandatoryParams() {
044        return new String[0];
045    }
046
047    @Override
048    public String[] getOptionalParams() {
049        Set<String> params = new LinkedHashSet<>();
050        params.add("url");
051        params.add("id");
052        Map<String, String> struct = StructUtils.serializeStruct(new ImageryPreferenceEntry(), ImageryPreferenceEntry.class,
053                StructUtils.SerializeOptions.INCLUDE_NULL, StructUtils.SerializeOptions.INCLUDE_DEFAULT);
054        params.addAll(struct.keySet());
055        return params.toArray(new String[0]);
056    }
057
058    @Override
059    public PermissionPrefWithDefault getPermissionPref() {
060        return PermissionPrefWithDefault.LOAD_IMAGERY;
061    }
062
063    protected ImageryInfo buildImageryInfo() {
064        String id = args.get("id");
065        if (id != null) {
066            return ImageryLayerInfo.instance.getAllDefaultLayers().stream()
067                    .filter(l -> Objects.equals(l.getId(), id))
068                    .findFirst()
069                    .orElseThrow(() -> new IllegalArgumentException("Cannot find layer for id " + id));
070        }
071        args.computeIfAbsent("type", ignore -> ImageryType.WMS.getDefault().getTypeString());
072        args.computeIfAbsent("name", ignore -> args.getOrDefault("title", tr("Remote imagery")));
073        ImageryPreferenceEntry imageryPreferenceEntry = StructUtils.deserializeStruct(args, ImageryPreferenceEntry.class);
074        return new ImageryInfo(imageryPreferenceEntry);
075    }
076
077    @Override
078    protected void handleRequest() throws RequestHandlerErrorException {
079        final ImageryInfo imgInfo = buildImageryInfo();
080        if (MainApplication.isDisplayingMapView()) {
081            for (ImageryLayer layer : MainApplication.getLayerManager().getLayersOfType(ImageryLayer.class)) {
082                if (layer.getInfo().equals(imgInfo)) {
083                    Logging.info("Imagery layer already exists: "+imgInfo);
084                    return;
085                }
086            }
087        }
088        GuiHelper.runInEDT(() -> {
089            try {
090                MainApplication.getLayerManager().addLayer(ImageryLayer.create(imgInfo));
091            } catch (IllegalArgumentException e) {
092                Logging.log(Logging.LEVEL_ERROR, e);
093            }
094        });
095    }
096
097    @Override
098    protected void validateRequest() throws RequestHandlerBadRequestException {
099        try {
100            CheckParameterUtil.ensureParameterNotNull(args);
101            CheckParameterUtil.ensureThat(args.containsKey("url") || args.containsKey("id"),
102                    tr("The following keys are mandatory, but have not been provided: {0}", "url/id"));
103            ImageryLayer.create(buildImageryInfo());
104        } catch (IllegalArgumentException e) {
105            throw new RequestHandlerBadRequestException(e.getMessage(), e);
106        }
107    }
108
109    @Override
110    public String getUsage() {
111        return "adds an imagery layer (e.g. WMS, TMS)";
112    }
113
114    @Override
115    public String[] getUsageExamples() {
116        final String types = String.join("|", Utils.transform(Arrays.asList(ImageryInfo.ImageryType.values()),
117                ImageryType::getTypeString));
118        return new String[] {
119            "/imagery?id=Bing",
120            "/imagery?title=osm&type=tms&url=https://a.tile.openstreetmap.org/%7Bzoom%7D/%7Bx%7D/%7By%7D.png",
121            "/imagery?title=landsat&type=wms&url=http://irs.gis-lab.info/?" +
122                    "layers=landsat&SRS=%7Bproj%7D&WIDTH=%7Bwidth%7D&HEIGHT=%7Bheight%7D&BBOX=%7Bbbox%7D",
123            "/imagery?title=...&type={"+types+"}&url=....[&cookies=...][&min_zoom=...][&max_zoom=...]"
124            };
125    }
126}