001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.plugins.streetside.utils;
003
004import java.awt.Graphics2D;
005import java.awt.Point;
006import java.awt.Rectangle;
007import java.awt.Shape;
008import java.awt.geom.Area;
009import java.awt.geom.Path2D;
010
011import org.openstreetmap.josm.data.Bounds;
012import org.openstreetmap.josm.gui.MapView;
013import org.openstreetmap.josm.gui.NavigatableComponent;
014import org.openstreetmap.josm.plugins.streetside.StreetsideAbstractImage;
015import org.openstreetmap.josm.plugins.streetside.StreetsideSequence;
016
017/**
018 * Utility class to convert entities like {@link Bounds} and {@link StreetsideSequence} into {@link Shape}s that
019 * can then easily be drawn on a {@link MapView}s {@link Graphics2D}-context.
020 */
021public final  class MapViewGeometryUtil {
022  private MapViewGeometryUtil() {
023    // Private constructor to avoid instantiation
024  }
025
026  /**
027   * Subtracts the download bounds from the rectangular bounds of the map view.
028   * @param mv the MapView that is used for the LatLon-to-Point-conversion and that determines
029   *     the Bounds from which the downloaded Bounds are subtracted
030   * @param downloadBounds multiple {@link Bounds} objects that represent the downloaded area
031   * @return the difference between the {@link MapView}s bounds and the downloaded area
032   */
033  public static Area getNonDownloadedArea(MapView mv, Iterable<Bounds> downloadBounds) {
034    Rectangle b = mv.getBounds();
035    // on some platforms viewport bounds seem to be offset from the left,
036    // over-grow it just to be sure
037    b.grow(100, 100);
038    Area a = new Area(b);
039    // now successively subtract downloaded areas
040    for (Bounds bounds : downloadBounds) {
041      Point p1 = mv.getPoint(bounds.getMin());
042      Point p2 = mv.getPoint(bounds.getMax());
043      Rectangle r = new Rectangle(Math.min(p1.x, p2.x), Math.min(p1.y, p2.y),
044          Math.abs(p2.x - p1.x), Math.abs(p2.y - p1.y));
045      a.subtract(new Area(r));
046    }
047    return a;
048  }
049
050  /**
051   * Converts a {@link StreetsideSequence} into a {@link Path2D} that can be drawn
052   * on the specified {@link NavigatableComponent}'s {@link Graphics2D}-context.
053   * @param nc the {@link NavigatableComponent} for which this conversion should be performed, typically a {@link MapView}
054   * @param seq the sequence to convert
055   * @return the {@link Path2D} object to which the {@link StreetsideSequence} has been converted
056   */
057  public static Path2D getSequencePath(NavigatableComponent nc, StreetsideSequence seq) {
058    final Path2D.Double path = new Path2D.Double();
059    seq.getImages().stream().filter(StreetsideAbstractImage::isVisible).forEach(img -> {
060      Point p = nc.getPoint(img.getMovingLatLon());
061      if (path.getCurrentPoint() == null) {
062        path.moveTo(p.getX(), p.getY());
063      } else {
064        path.lineTo(p.getX(), p.getY());
065      }
066    });
067    return path;
068  }
069}