001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.imagery;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.text.MessageFormat;
007import java.util.ArrayList;
008import java.util.List;
009import java.util.Objects;
010
011import org.openstreetmap.gui.jmapviewer.Coordinate;
012import org.openstreetmap.josm.data.coor.LatLon;
013import org.openstreetmap.josm.data.osm.Node;
014import org.openstreetmap.josm.tools.CheckParameterUtil;
015import org.openstreetmap.josm.tools.Geometry;
016
017/**
018 * Data class to store the outline for background imagery coverage.
019 *
020 * Configuration data for imagery to describe the coverage area ({@link ImageryInfo.ImageryBounds}).
021 * @author Vincent
022 */
023public class Shape {
024
025    private final List<Coordinate> coords = new ArrayList<>();
026
027    public Shape(String asString, String separator) {
028        CheckParameterUtil.ensureParameterNotNull(asString, "asString");
029        String[] components = asString.split(separator);
030        if (components.length % 2 != 0)
031            throw new IllegalArgumentException(MessageFormat.format("Even number of doubles expected in string, got {0}: {1}",
032                    components.length, asString));
033        for (int i = 0; i < components.length; i += 2) {
034            addPoint(components[i], components[i+1]);
035        }
036    }
037
038    /**
039     * Constructs a new empty {@code Shape}.
040     */
041    public Shape() {
042        // shape contents can be set later with addPoint()
043    }
044
045    public String encodeAsString(String separator) {
046        StringBuilder sb = new StringBuilder();
047        for (Coordinate c : coords) {
048            if (sb.length() != 0) {
049                sb.append(separator);
050            }
051            sb.append(c.getLat()).append(separator).append(c.getLon());
052        }
053        return sb.toString();
054    }
055
056    public List<Coordinate> getPoints() {
057        return coords;
058    }
059
060    public boolean contains(LatLon latlon) {
061        if (latlon == null)
062            return false;
063        List<Node> nodes = new ArrayList<>(coords.size());
064        for (Coordinate c : coords) {
065            nodes.add(new Node(new LatLon(c.getLat(), c.getLon())));
066        }
067        return Geometry.nodeInsidePolygon(new Node(latlon), nodes);
068    }
069
070    public void addPoint(String sLat, String sLon) {
071        CheckParameterUtil.ensureParameterNotNull(sLat, "sLat");
072        CheckParameterUtil.ensureParameterNotNull(sLon, "sLon");
073
074        double lat, lon;
075
076        try {
077            lat = Double.parseDouble(sLat);
078            if (!LatLon.isValidLat(lat))
079                throw new IllegalArgumentException(tr("Illegal latitude value ''{0}''", lat));
080        } catch (NumberFormatException e) {
081            throw new IllegalArgumentException(MessageFormat.format("Illegal double value ''{0}''", sLat), e);
082        }
083
084        try {
085            lon = Double.parseDouble(sLon);
086            if (!LatLon.isValidLon(lon))
087                throw new IllegalArgumentException(tr("Illegal longitude value ''{0}''", lon));
088        } catch (NumberFormatException e) {
089            throw new IllegalArgumentException(MessageFormat.format("Illegal double value ''{0}''", sLon), e);
090        }
091
092        coords.add(new Coordinate(LatLon.roundToOsmPrecision(lat), LatLon.roundToOsmPrecision(lon)));
093    }
094
095    @Override
096    public int hashCode() {
097        return Objects.hash(coords);
098    }
099
100    @Override
101    public boolean equals(Object obj) {
102        if (this == obj) return true;
103        if (obj == null || getClass() != obj.getClass()) return false;
104        Shape shape = (Shape) obj;
105        return Objects.equals(coords, shape.coords);
106    }
107}