diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/CountryUtils.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/CountryUtils.java new file mode 100644 index 0000000..85fa447 --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/CountryUtils.java @@ -0,0 +1,106 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.plugins.mapwithai.data.mapwithai; + +import static org.openstreetmap.josm.tools.I18n.tr; + +import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import org.openstreetmap.josm.data.coor.LatLon; +import org.openstreetmap.josm.data.imagery.ImageryInfo; +import org.openstreetmap.josm.data.imagery.Shape; +import org.openstreetmap.josm.data.osm.BBox; +import org.openstreetmap.josm.data.sources.SourceBounds; +import org.openstreetmap.josm.tools.DefaultGeoProperty; +import org.openstreetmap.josm.tools.GeoPropertyIndex; +import org.openstreetmap.josm.tools.Logging; +import org.openstreetmap.josm.tools.Territories; + +/** + * Get country data for use in info classes + */ +public final class CountryUtils { + private CountryUtils() { + /* Hide constructor */ + } + + /** + * Get the country shape + * + * @param country The country to get the shape for + * @return The (optional) bounds (may be empty if no country matched) + */ + public static Optional getCountryShape(String country) { + GeoPropertyIndex geoPropertyIndex = Territories.getGeoPropertyIndex(country); + if (geoPropertyIndex.getGeoProperty() instanceof DefaultGeoProperty) { + DefaultGeoProperty prop = (DefaultGeoProperty) geoPropertyIndex.getGeoProperty(); + Rectangle2D areaBounds = prop.getArea().getBounds2D(); + ImageryInfo.ImageryBounds tmp = new ImageryInfo.ImageryBounds(bboxToBoundsString( + new BBox(areaBounds.getMinX(), areaBounds.getMinY(), areaBounds.getMaxX(), areaBounds.getMaxY())), + ","); + areaToShapes(prop.getArea()).forEach(tmp::addShape); + return Optional.of(tmp); + } + return Optional.empty(); + } + + /** + * Get the country for a given shape + * + * @param shape The shape to get a country for + * @return The country, if found + */ + public static Optional shapeToCountry(Shape shape) { + for (String country : Territories.getKnownIso3166Codes()) { + List shapes = getCountryShape(country).map(SourceBounds::getShapes) + .orElseGet(Collections::emptyList); + for (Shape checkShape : shapes) { + if (Objects.equals(shape, checkShape)) { + return Optional.of(country); + } + } + } + return Optional.empty(); + } + + private static Collection areaToShapes(java.awt.Shape shape) { + PathIterator iterator = shape.getPathIterator(new AffineTransform()); + Shape defaultShape = new Shape(); + Collection shapes = new ArrayList<>(); + float[] moveTo = null; + float[] coords = new float[6]; + while (!iterator.isDone()) { + int type = iterator.currentSegment(coords); + if (type == PathIterator.SEG_MOVETO || type == PathIterator.SEG_LINETO) { + if (type == PathIterator.SEG_MOVETO) { + moveTo = coords; + } + defaultShape.addPoint(Float.toString(coords[1]), Float.toString(coords[0])); + } else if (type == PathIterator.SEG_CLOSE && moveTo != null) { + defaultShape.addPoint(Float.toString(moveTo[1]), Float.toString(moveTo[0])); + shapes.add(defaultShape); + defaultShape = new Shape(); + } else { + Logging.error(tr("No implementation for converting a segment of type {0} to coordinates", type)); + } + iterator.next(); + } + if (!defaultShape.getPoints().isEmpty()) { + shapes.add(defaultShape); + } + return shapes; + } + + private static String bboxToBoundsString(BBox bbox) { + return String.join(",", LatLon.cDdFormatter.format(bbox.getBottomRightLat()), + LatLon.cDdFormatter.format(bbox.getTopLeftLon()), LatLon.cDdFormatter.format(bbox.getTopLeftLat()), + LatLon.cDdFormatter.format(bbox.getBottomRightLon())); + } +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIInfo.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIInfo.java index c5511b8..1124fe4 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIInfo.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAIInfo.java @@ -18,6 +18,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -27,6 +28,7 @@ import org.openstreetmap.josm.data.imagery.ImageryInfo; import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds; import org.openstreetmap.josm.data.imagery.Shape; import org.openstreetmap.josm.data.preferences.BooleanProperty; +import org.openstreetmap.josm.data.sources.SourceBounds; import org.openstreetmap.josm.data.sources.SourceInfo; import org.openstreetmap.josm.data.sources.SourcePreferenceEntry; import org.openstreetmap.josm.tools.CheckParameterUtil; @@ -116,6 +118,22 @@ public class MapWithAIInfo extends .collect(Collectors.joining(";")); } alreadyConflatedKey = i.alreadyConflatedKey; + if (i.bounds != null && this.shapes != null && this.shapes.length() > Byte.MAX_VALUE) { + List parts = new ArrayList<>(i.bounds.getShapes().size()); + for (Shape s : i.bounds.getShapes()) { + Optional country = CountryUtils.shapeToCountry(s); + if (country.isPresent()) { + if (!parts.contains(country.get())) { + parts.add(country.get()); + } + } else { + parts.add(s.encodeAsString(",")); + } + } + if (!parts.isEmpty()) { + shapes = String.join(";", parts); + } + } } @Override @@ -211,7 +229,12 @@ public class MapWithAIInfo extends if (e.shapes != null) { try { for (String s : e.shapes.split(";", -1)) { - bounds.addShape(new Shape(s, ",")); + if (s.matches("[\\d,]+")) { + bounds.addShape(new Shape(s, ",")); + } else { + CountryUtils.getCountryShape(s).map(SourceBounds::getShapes) + .orElseThrow(IllegalStateException::new).forEach(bounds::addShape); + } } } catch (IllegalArgumentException ex) { Logging.warn(ex); diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAILayerInfo.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAILayerInfo.java index 55a9d12..b5b7c27 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAILayerInfo.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/data/mapwithai/MapWithAILayerInfo.java @@ -35,7 +35,6 @@ import org.openstreetmap.josm.gui.PleaseWaitRunnable; import org.openstreetmap.josm.io.CachedFile; import org.openstreetmap.josm.io.NetworkManager; import org.openstreetmap.josm.io.imagery.ImageryReader; -import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIDataUtils; import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo.MapWithAIPreferenceEntry; import org.openstreetmap.josm.plugins.mapwithai.io.mapwithai.ESRISourceReader; import org.openstreetmap.josm.plugins.mapwithai.io.mapwithai.MapWithAISourceReader; @@ -193,7 +192,7 @@ public class MapWithAILayerInfo { if (System.getSecurityManager() != null) { Logging.trace("MapWithAI loaded: {0}", ESRISourceReader.SOURCE_CACHE.getClass()); } - loadDefaults(false, MapWithAIDataUtils.getForkJoinPool(), fastFail, listener); + loadDefaults(false, MainApplication.worker, fastFail, listener); } /** diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/MapWithAISourceReader.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/MapWithAISourceReader.java index 7402c3f..c2c1f53 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/MapWithAISourceReader.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/io/mapwithai/MapWithAISourceReader.java @@ -1,19 +1,13 @@ // License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.mapwithai.io.mapwithai; -import static org.openstreetmap.josm.tools.I18n.tr; - import javax.json.JsonArray; import javax.json.JsonObject; import javax.json.JsonString; import javax.json.JsonValue; -import java.awt.geom.AffineTransform; -import java.awt.geom.PathIterator; -import java.awt.geom.Rectangle2D; import java.io.Closeable; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; @@ -21,16 +15,12 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.openstreetmap.josm.data.coor.LatLon; import org.openstreetmap.josm.data.imagery.ImageryInfo; import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds; -import org.openstreetmap.josm.data.imagery.Shape; -import org.openstreetmap.josm.data.osm.BBox; +import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.CountryUtils; import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAICategory; import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo; import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIType; -import org.openstreetmap.josm.tools.DefaultGeoProperty; -import org.openstreetmap.josm.tools.GeoPropertyIndex; import org.openstreetmap.josm.tools.Logging; import org.openstreetmap.josm.tools.Territories; @@ -43,9 +33,6 @@ import org.openstreetmap.josm.tools.Territories; * source. */ public class MapWithAISourceReader extends CommonSourceReader> implements Closeable { - - private static final int COORD_ARRAY_SIZE = 6; - /** * Constructs a {@code MapWithAISourceReader} from a given filename, URL or * internal resource. @@ -143,15 +130,7 @@ public class MapWithAISourceReader extends CommonSourceReader bounds = new ArrayList<>(); for (Map.Entry country : countries.asJsonObject().entrySet()) { if (codes.contains(country.getKey())) { - GeoPropertyIndex geoPropertyIndex = Territories.getGeoPropertyIndex(country.getKey()); - if (geoPropertyIndex.getGeoProperty() instanceof DefaultGeoProperty) { - DefaultGeoProperty prop = (DefaultGeoProperty) geoPropertyIndex.getGeoProperty(); - Rectangle2D areaBounds = prop.getArea().getBounds2D(); - ImageryBounds tmp = new ImageryBounds(bboxToBoundsString(new BBox(areaBounds.getMinX(), - areaBounds.getMinY(), areaBounds.getMaxX(), areaBounds.getMaxY())), ","); - areaToShapes(prop.getArea()).forEach(tmp::addShape); - bounds.add(tmp); - } + CountryUtils.getCountryShape(country.getKey()).ifPresent(bounds::add); } } return bounds; @@ -162,38 +141,4 @@ public class MapWithAISourceReader extends CommonSourceReader(); } - - private static Collection areaToShapes(java.awt.Shape shape) { - PathIterator iterator = shape.getPathIterator(new AffineTransform()); - Shape defaultShape = new Shape(); - Collection shapes = new ArrayList<>(); - float[] moveTo = null; - while (!iterator.isDone()) { - float[] coords = new float[COORD_ARRAY_SIZE]; - int type = iterator.currentSegment(coords); - if (type == PathIterator.SEG_MOVETO || type == PathIterator.SEG_LINETO) { - if (type == PathIterator.SEG_MOVETO) { - moveTo = coords; - } - defaultShape.addPoint(Float.toString(coords[1]), Float.toString(coords[0])); - } else if (type == PathIterator.SEG_CLOSE && moveTo != null) { - defaultShape.addPoint(Float.toString(moveTo[1]), Float.toString(moveTo[0])); - shapes.add(defaultShape); - defaultShape = new Shape(); - } else { - Logging.error(tr("No implementation for converting a segment of type {0} to coordinates", type)); - } - iterator.next(); - } - if (!defaultShape.getPoints().isEmpty()) { - shapes.add(defaultShape); - } - return shapes; - } - - private static String bboxToBoundsString(BBox bbox) { - return String.join(",", LatLon.cDdFormatter.format(bbox.getBottomRightLat()), - LatLon.cDdFormatter.format(bbox.getTopLeftLon()), LatLon.cDdFormatter.format(bbox.getTopLeftLat()), - LatLon.cDdFormatter.format(bbox.getBottomRightLon())); - } }