// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.turnlanes.gui;
import static java.lang.Math.sqrt;
import static org.openstreetmap.josm.plugins.turnlanes.gui.GuiUtil.locs;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Stroke;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.coor.EastNorth;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.plugins.turnlanes.model.Junction;
import org.openstreetmap.josm.plugins.turnlanes.model.Lane;
import org.openstreetmap.josm.plugins.turnlanes.model.ModelContainer;
import org.openstreetmap.josm.plugins.turnlanes.model.Road;
class GuiContainer {
static final Color RED = new Color(234, 66, 108);
static final Color GREEN = new Color(66, 234, 108);
private static final GuiContainer EMPTY = new GuiContainer(ModelContainer.empty());
public static GuiContainer empty() {
return EMPTY;
}
private final ModelContainer mc;
private final Point2D translation;
/**
* Meters per pixel.
*/
private final double mpp;
private final double scale;
private final double laneWidth;
private final Map<Junction, JunctionGui> junctions = new HashMap<>();
private final Map<Road, RoadGui> roads = new HashMap<>();
private final Stroke connectionStroke;
GuiContainer(ModelContainer mc) {
final Point2D origin = avgOrigin(locs(mc.getPrimaryJunctions()));
final LatLon originCoor = Main.getProjection().eastNorth2latlon(new EastNorth(origin.getX(), origin.getY()));
final LatLon relCoor = Main.getProjection().eastNorth2latlon(
new EastNorth(origin.getX() + 1, origin.getY() + 1));
// meters per source unit
final double mpsu = relCoor.greatCircleDistance(originCoor) / sqrt(2);
this.mc = mc;
this.translation = new Point2D.Double(-origin.getX(), -origin.getY());
this.mpp = 0.2;
this.scale = mpsu / mpp;
this.laneWidth = 2 / mpp;
this.connectionStroke = new BasicStroke((float) (laneWidth / 4), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
for (Junction j : mc.getPrimaryJunctions()) {
getGui(j);
}
}
private static Point2D avgOrigin(List<Point2D> locs) {
if (locs.isEmpty()) {
return new Point2D.Double(0, 0);
}
double x = 0;
double y = 0;
for (Point2D l : locs) {
x += l.getX();
y += l.getY();
}
return new Point2D.Double(x / locs.size(), y / locs.size());
}
public JunctionGui getGui(Junction j) {
final JunctionGui existing = junctions.get(j);
if (existing != null) {
return existing;
}
return new JunctionGui(this, j);
}
void register(JunctionGui j) {
if (junctions.put(j.getModel(), j) != null) {
throw new IllegalStateException();
}
}
public RoadGui getGui(Road r) {
final RoadGui gui = roads.get(r);
if (gui == null) {
final RoadGui newGui = new RoadGui(this, r);
roads.put(r, newGui);
return newGui;
}
return gui;
}
Point2D translateAndScale(Point2D loc) {
return new Point2D.Double((loc.getX() + translation.getX()) * scale, (loc.getY() + translation.getY()) * scale);
}
/**
* @return meters per pixel
*/
public double getMpp() {
return mpp;
}
public double getScale() {
return scale;
}
public double getLaneWidth() {
return laneWidth;
}
public Stroke getConnectionStroke() {
return connectionStroke;
}
public LaneGui getGui(Lane lane) {
final RoadGui roadGui = roads.get(lane.getRoad());
for (LaneGui l : roadGui.getLanes()) {
if (l.getModel().equals(lane)) {
return l;
}
}
throw new IllegalArgumentException(tr("No such lane."));
}
public ModelContainer getModel() {
return mc;
}
public Rectangle2D getBounds() {
if (isEmpty()) {
return new Rectangle2D.Double(-1, -1, 2, 2);
}
final List<Junction> primaries = new ArrayList<>(mc.getPrimaryJunctions());
final List<Double> top = new ArrayList<>();
final List<Double> left = new ArrayList<>();
final List<Double> right = new ArrayList<>();
final List<Double> bottom = new ArrayList<>();
for (Junction j : primaries) {
final JunctionGui g = getGui(j);
final Rectangle2D b = g.getBounds();
top.add(b.getMinY());
left.add(b.getMinX());
right.add(b.getMaxX());
bottom.add(b.getMaxY());
}
final double t = Collections.min(top);
final double l = Collections.min(left);
final double r = Collections.max(right);
final double b = Collections.max(bottom);
return new Rectangle2D.Double(l, t, r - l, b - t);
}
public GuiContainer recalculate() {
return new GuiContainer(mc.recalculate());
}
public Iterable<RoadGui> getRoads() {
return roads.values();
}
public Iterable<JunctionGui> getJunctions() {
return junctions.values();
}
public boolean isEmpty() {
return mc.isEmpty();
}
}