    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}