// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.pt_assistant.gui;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import javax.swing.Action;
import javax.swing.Icon;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.actions.RenameLayerAction;
import org.openstreetmap.josm.data.Bounds;
import org.openstreetmap.josm.data.SelectionChangedListener;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
import org.openstreetmap.josm.data.osm.Relation;
import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
import org.openstreetmap.josm.gui.MapView;
import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
import org.openstreetmap.josm.gui.dialogs.relation.GenericRelationEditor;
import org.openstreetmap.josm.gui.layer.Layer;
import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
import org.openstreetmap.josm.gui.layer.LayerPositionStrategy;
import org.openstreetmap.josm.gui.layer.OsmDataLayer;
import org.openstreetmap.josm.plugins.pt_assistant.data.PTWay;
import org.openstreetmap.josm.plugins.pt_assistant.utils.RouteUtils;
import org.openstreetmap.josm.tools.ImageProvider;
/**
* Layer that visualizes the routes in a more convenient way
*
* @author darya
*
*/
public final class PTAssistantLayer extends Layer
implements SelectionChangedListener, PropertyChangeListener, LayerChangeListener {
private static PTAssistantLayer layer;
private List<OsmPrimitive> primitives = new ArrayList<>();
private PTAssistantPaintVisitor paintVisitor;
private HashMap<Character, List<PTWay>> fixVariants = new HashMap<>();
private HashMap<Way, List<Character>> wayColoring = new HashMap<>();
private PTAssistantLayer() {
super("pt_assistant layer");
KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener(this);
Main.getLayerManager().addLayerChangeListener(this);
layer = this;
}
public static PTAssistantLayer getLayer() {
if (layer == null) {
new PTAssistantLayer();
}
return layer;
}
/**
* Adds a primitive (route) to be displayed in this layer
*
* @param primitive primitive (route)
*/
public void addPrimitive(OsmPrimitive primitive) {
this.primitives.add(primitive);
}
/**
* Clears all primitives (routes) from being displayed.
*/
public void clear() {
this.primitives.clear();
}
public void clearFixVariants() {
fixVariants.clear();
wayColoring.clear();
Main.map.mapView.repaint();
}
/**
* Adds the first 5 fix variants to be displayed in the pt_assistant layer
*
* @param fixVariants fix variants
*/
public void addFixVariants(List<List<PTWay>> fixVariants) {
HashMap<List<PTWay>, Character> fixVariantLetterMap = new HashMap<>();
char alphabet = 'A';
for (int i = 0; i < 5 && i < fixVariants.size(); i++) {
List<PTWay> fixVariant = fixVariants.get(i);
this.fixVariants.put(alphabet, fixVariant);
fixVariantLetterMap.put(fixVariant, alphabet);
alphabet++;
}
for (Character currentFixVariantLetter : this.fixVariants.keySet()) {
List<PTWay> fixVariant = this.fixVariants.get(currentFixVariantLetter);
for (PTWay ptway : fixVariant) {
for (Way way : ptway.getWays()) {
if (wayColoring.containsKey(way)) {
if (!wayColoring.get(way).contains(currentFixVariantLetter)) {
wayColoring.get(way).add(currentFixVariantLetter);
}
} else {
List<Character> letterList = new ArrayList<>();
letterList.add(currentFixVariantLetter);
wayColoring.put(way, letterList);
}
}
}
}
}
/**
* Returns fix variant (represented by a list of PTWays) that corresponds to
* the given character.
*
* @param c character
* @return fix variant
*/
public List<PTWay> getFixVariant(char c) {
return this.fixVariants.get(Character.toUpperCase(c));
}
@Override
public void paint(final Graphics2D g, final MapView mv, Bounds bounds) {
paintVisitor = new PTAssistantPaintVisitor(g, mv);
for (OsmPrimitive primitive : primitives) {
paintVisitor.visit(primitive);
}
paintVisitor.visitFixVariants(this.fixVariants, this.wayColoring);
}
@Override
public Icon getIcon() {
return ImageProvider.get("layer", "osmdata_small");
}
@Override
public Object getInfoComponent() {
return getToolTipText();
}
@Override
public Action[] getMenuEntries() {
return new Action[] {LayerListDialog.getInstance().createShowHideLayerAction(),
LayerListDialog.getInstance().createDeleteLayerAction(), SeparatorLayerAction.INSTANCE,
new RenameLayerAction(null, this), SeparatorLayerAction.INSTANCE, new LayerListPopup.InfoAction(this)};
}
@Override
public String getToolTipText() {
return "pt_assistant layer";
}
@Override
public boolean isMergable(Layer arg0) {
return false;
}
@Override
public void mergeFrom(Layer arg0) {
// do nothing
}
@Override
public void visitBoundingBox(BoundingXYVisitor arg0) {
// do nothing
}
@Override
public LayerPositionStrategy getDefaultLayerPosition() {
return LayerPositionStrategy.IN_FRONT;
}
/**
* Listens to a selection change
*/
@Override
public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
ArrayList<Relation> routes = new ArrayList<>();
for (OsmPrimitive primitive : newSelection) {
if (primitive.getType().equals(OsmPrimitiveType.RELATION)) {
Relation relation = (Relation) primitive;
if (RouteUtils.isTwoDirectionRoute(relation)) {
routes.add(relation);
}
}
}
if (!routes.isEmpty()) {
this.primitives.clear();
this.primitives.addAll(routes);
if (!Main.getLayerManager().containsLayer(this)) {
Main.getLayerManager().addLayer(this);
}
}
}
/**
* Listens to a focus change, sets the primitives attribute to the route
* relation in the top Relation Editor and repaints the map
*/
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("focusedWindow".equals(evt.getPropertyName())) {
if (evt.getNewValue() == null) {
return;
}
if (evt.getNewValue().getClass().equals(GenericRelationEditor.class)) {
GenericRelationEditor editor = (GenericRelationEditor) evt.getNewValue();
Relation relation = editor.getRelation();
if (RouteUtils.isTwoDirectionRoute(relation)) {
this.repaint(relation);
}
}
}
}
/**
* Repaints the layer in cases when there was no selection change
*
* @param relation relation
*/
public void repaint(Relation relation) {
this.primitives.clear();
this.primitives.add(relation);
if (!Main.getLayerManager().containsLayer(this)) {
Main.getLayerManager().addLayer(this);
}
if (paintVisitor == null) {
Graphics g = Main.map.mapView.getGraphics();
MapView mv = Main.map.mapView;
paintVisitor = new PTAssistantPaintVisitor(g, mv);
}
for (OsmPrimitive primitive : primitives) {
paintVisitor.visit(primitive);
}
paintVisitor.visitFixVariants(this.fixVariants, this.wayColoring);
Main.map.mapView.repaint();
}
@Override
public void layerAdded(LayerAddEvent arg0) {
// do nothing
}
@Override
public void layerOrderChanged(LayerOrderChangeEvent arg0) {
// do nothing
}
@Override
public void layerRemoving(LayerRemoveEvent event) {
if (event.getRemovedLayer() instanceof OsmDataLayer) {
this.primitives.clear();
this.fixVariants.clear();
this.wayColoring.clear();
Main.map.mapView.repaint();
}
}
}