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}