001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.imagery;
003
004import java.awt.Polygon;
005import java.text.MessageFormat;
006import java.util.AbstractList;
007import java.util.List;
008import java.util.Objects;
009import java.util.stream.Collectors;
010import java.util.stream.Stream;
011
012import org.openstreetmap.gui.jmapviewer.Coordinate;
013import org.openstreetmap.josm.data.coor.LatLon;
014import org.openstreetmap.josm.tools.CheckParameterUtil;
015
016import static org.openstreetmap.josm.tools.I18n.tr;
017
018/**
019 * Data class to store the outline for background imagery coverage.
020 *
021 * Configuration data for imagery to describe the coverage area ({@link ImageryInfo.ImageryBounds}).
022 * @author Vincent
023 */
024public class Shape {
025
026    private final Polygon coords;
027
028    public Shape(String asString, String separator) {
029        CheckParameterUtil.ensureParameterNotNull(asString, "asString");
030        String[] components = asString.split(separator, -1);
031        if (components.length % 2 != 0)
032            throw new IllegalArgumentException(MessageFormat.format("Even number of doubles expected in string, got {0}: {1}",
033                    components.length, asString));
034        int size = components.length / 2;
035        this.coords = new Polygon(new int[size], new int[size], 0);
036        for (int i = 0; i < components.length; i += 2) {
037            addPoint(components[i], components[i+1]);
038        }
039    }
040
041    /**
042     * Constructs a new empty {@code Shape}.
043     */
044    public Shape() {
045        coords = new Polygon();
046        // shape contents can be set later with addPoint()
047    }
048
049    /**
050     * Encodes this as a string so that it may be parsed using {@link #Shape(String, String)}
051     * @param separator The separator
052     * @return The string encoded shape
053     */
054    public String encodeAsString(String separator) {
055        return getPoints().stream()
056                .flatMap(c -> Stream.of(c.getLat(), c.getLon()))
057                .map(String::valueOf)
058                .collect(Collectors.joining(separator));
059    }
060
061    /**
062     * Encodes the shapes as a string using {@code ,} and {@code ;} as separators
063     * @param shapes The shapes to encode
064     * @return The string encoded shapes
065     */
066    public static String encodeAsString(List<Shape> shapes) {
067        return shapes.stream()
068                .map(s -> s.encodeAsString(","))
069                .collect(Collectors.joining(";"));
070    }
071
072    public List<Coordinate> getPoints() {
073        return new AbstractList<Coordinate>() {
074            @Override
075            public Coordinate get(int index) {
076                double lat = coords.ypoints[index] / LatLon.MAX_SERVER_INV_PRECISION;
077                double lon = coords.xpoints[index] / LatLon.MAX_SERVER_INV_PRECISION;
078                return new Coordinate(lat, lon);
079            }
080
081            @Override
082            public int size() {
083                return coords.npoints;
084            }
085        };
086    }
087
088    public boolean contains(LatLon latlon) {
089        return coords.contains(
090                latlon.getX() * LatLon.MAX_SERVER_INV_PRECISION,
091                latlon.getY() * LatLon.MAX_SERVER_INV_PRECISION);
092    }
093
094    public void addPoint(String sLat, String sLon) {
095        CheckParameterUtil.ensureParameterNotNull(sLat, "sLat");
096        CheckParameterUtil.ensureParameterNotNull(sLon, "sLon");
097
098        double lat, lon;
099
100        try {
101            lat = Double.parseDouble(sLat);
102            if (!LatLon.isValidLat(lat))
103                throw new IllegalArgumentException(tr("Illegal latitude value ''{0}''", lat));
104        } catch (NumberFormatException e) {
105            throw new IllegalArgumentException(MessageFormat.format("Illegal double value ''{0}''", sLat), e);
106        }
107
108        try {
109            lon = Double.parseDouble(sLon);
110            if (!LatLon.isValidLon(lon))
111                throw new IllegalArgumentException(tr("Illegal longitude value ''{0}''", lon));
112        } catch (NumberFormatException e) {
113            throw new IllegalArgumentException(MessageFormat.format("Illegal double value ''{0}''", sLon), e);
114        }
115
116        coords.addPoint(
117                (int) (lon * LatLon.MAX_SERVER_INV_PRECISION),
118                (int) (lat * LatLon.MAX_SERVER_INV_PRECISION));
119    }
120
121    @Override
122    public int hashCode() {
123        return Objects.hash(getPoints());
124    }
125
126    @Override
127    public boolean equals(Object obj) {
128        if (this == obj) return true;
129        if (obj == null || getClass() != obj.getClass()) return false;
130        Shape shape = (Shape) obj;
131        return Objects.equals(getPoints(), shape.getPoints());
132    }
133
134    @Override
135    public String toString() {
136        return "Shape{coords=" + getPoints() + '}';
137    }
138}