001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.imagery.vectortile.mapbox;
003import static org.openstreetmap.josm.tools.I18n.tr;
004
005import java.io.IOException;
006import java.io.InputStream;
007import java.nio.file.InvalidPathException;
008import java.util.List;
009import java.util.Objects;
010import java.util.stream.Collectors;
011
012import javax.json.Json;
013import javax.json.JsonException;
014import javax.json.JsonObject;
015import javax.json.JsonReader;
016
017import org.openstreetmap.josm.data.imagery.ImageryInfo;
018import org.openstreetmap.josm.data.imagery.JosmTemplatedTMSTileSource;
019import org.openstreetmap.josm.data.imagery.vectortile.mapbox.style.MapboxVectorStyle;
020import org.openstreetmap.josm.data.imagery.vectortile.mapbox.style.Source;
021import org.openstreetmap.josm.gui.ExtendedDialog;
022import org.openstreetmap.josm.gui.MainApplication;
023import org.openstreetmap.josm.gui.util.GuiHelper;
024import org.openstreetmap.josm.gui.widgets.JosmComboBox;
025import org.openstreetmap.josm.io.CachedFile;
026import org.openstreetmap.josm.tools.Logging;
027
028/**
029 * Tile Source handling for Mapbox Vector Tile sources
030 * @author Taylor Smock
031 * @since 17862
032 */
033public class MapboxVectorTileSource extends JosmTemplatedTMSTileSource {
034    private final MapboxVectorStyle styleSource;
035
036    /**
037     * Create a new {@link MapboxVectorTileSource} from an {@link ImageryInfo}
038     * @param info The info to create the source from
039     */
040    public MapboxVectorTileSource(ImageryInfo info) {
041        super(info);
042        MapboxVectorStyle mapBoxVectorStyle = null;
043        try (CachedFile style = new CachedFile(info.getUrl());
044          InputStream inputStream = style.getInputStream();
045          JsonReader reader = Json.createReader(inputStream)) {
046            JsonObject object = reader.readObject();
047            // OK, we may have a stylesheet. "version", "layers", and "sources" are all required.
048            if (object.containsKey("version") && object.containsKey("layers") && object.containsKey("sources")) {
049                mapBoxVectorStyle = MapboxVectorStyle.getMapboxVectorStyle(info.getUrl());
050            }
051        } catch (IOException | InvalidPathException | JsonException e) {
052            Logging.trace(e);
053        }
054        this.styleSource = mapBoxVectorStyle;
055        if (this.styleSource != null) {
056            final Source source;
057            List<Source> sources = this.styleSource.getSources().keySet().stream().filter(Objects::nonNull)
058              .collect(Collectors.toList());
059            if (sources.size() == 1) {
060                source = sources.get(0);
061            } else if (!sources.isEmpty()) {
062                // Ask user what source they want.
063                source = GuiHelper.runInEDTAndWaitAndReturn(() -> {
064                    ExtendedDialog dialog = new ExtendedDialog(MainApplication.getMainFrame(),
065                      tr("Select Vector Tile Layers"), tr("Add layers"));
066                    JosmComboBox<Source> comboBox = new JosmComboBox<>(sources.toArray(new Source[0]));
067                    comboBox.setSelectedIndex(0);
068                    dialog.setContent(comboBox);
069                    dialog.showDialog();
070                    return (Source) comboBox.getSelectedItem();
071                });
072            } else {
073                // Umm. What happened? We probably have an invalid style source.
074                throw new InvalidMapboxVectorTileException(tr("Cannot understand style source: {0}", info.getUrl()));
075            }
076            if (source != null) {
077                this.name = name + ": " + source.getName();
078                // There can technically be multiple URL's for this field; unfortunately, JOSM can only handle one right now.
079                this.baseUrl = source.getUrls().get(0);
080                this.minZoom = source.getMinZoom();
081                this.maxZoom = source.getMaxZoom();
082                if (source.getAttributionText() != null) {
083                    this.setAttributionText(source.getAttributionText());
084                }
085            }
086        }
087    }
088
089    /**
090     * Get the style source for this Vector Tile source
091     * @return The source to use for styling
092     */
093    public MapboxVectorStyle getStyleSource() {
094        return this.styleSource;
095    }
096}