// License: GPL. For details, see LICENSE file.
package relcontext.actions;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.swing.AbstractAction;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.command.ChangeCommand;
import org.openstreetmap.josm.command.Command;
import org.openstreetmap.josm.data.coor.EastNorth;
import org.openstreetmap.josm.data.osm.Node;
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.RelationMember;
import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.gui.dialogs.relation.sort.RelationSorter;
import org.openstreetmap.josm.tools.Geometry;
import org.openstreetmap.josm.tools.ImageProvider;
import relcontext.ChosenRelation;
import relcontext.ChosenRelationListener;
/**
* Build in order stop/platforms, stop/platforms ... route
* @author freeExec
*/
public class ReconstructRouteAction extends AbstractAction implements ChosenRelationListener {
private final ChosenRelation rel;
public ReconstructRouteAction(ChosenRelation rel) {
super(tr("Reconstruct route"));
putValue(SMALL_ICON, ImageProvider.get("dialogs", "filter"));
putValue(LONG_DESCRIPTION, "Reconstruct route relation to scheme of public_transport");
this.rel = rel;
rel.addChosenRelationListener(this);
setEnabled(isSuitableRelation(rel.get()));
}
@Override
public void actionPerformed(ActionEvent e) {
Relation r = rel.get();
Relation recRel = new Relation(r);
recRel.removeMembersFor(recRel.getMemberPrimitives());
Map<OsmPrimitive, RelationMember> stopMembers = new LinkedHashMap<>();
Map<String, List<RelationMember>> platformMembers = new LinkedHashMap<>();
List<RelationMember> routeMembers = new ArrayList<>();
List<RelationMember> wtfMembers = new ArrayList<>();
int mCount = r.getMembersCount();
for (int i = 0; i < mCount; i++) {
RelationMember m = r.getMember(i);
if (PublicTransportHelper.isMemberStop(m)) {
RelationMember rm = new RelationMember(
m.hasRole() ? m.getRole() : PublicTransportHelper.STOP,
m.getMember());
stopMembers.put(rm.getMember(), rm);
} else if (PublicTransportHelper.isMemberPlatform(m)) {
RelationMember rm = new RelationMember(
m.hasRole() ? m.getRole() : PublicTransportHelper.PLATFORM,
m.getMember());
String platformName = PublicTransportHelper.getNameViaStoparea(rm);
if (platformName == null) {
platformName = "";
}
if (platformMembers.containsKey(platformName)) {
platformMembers.get(platformName).add(rm);
} else {
List<RelationMember> nList = new ArrayList<>();
nList.add(rm);
platformMembers.put(platformName, nList);
}
} else if (PublicTransportHelper.isMemberRouteway(m)) {
routeMembers.add(new RelationMember(m));
} else {
wtfMembers.add(new RelationMember(m));
}
}
routeMembers = RelationSorter.sortMembersByConnectivity(routeMembers);
Node lastNode = null;
for (int rIndex = 0; rIndex < routeMembers.size(); rIndex++) {
Way w = (Way) routeMembers.get(rIndex).getMember();
boolean dirForward = false;
if (lastNode == null) { // first segment
if (routeMembers.size() > 2) {
Way nextWay = (Way) routeMembers.get(rIndex + 1).getMember();
if (w.lastNode().equals(nextWay.lastNode()) || w.lastNode().equals(nextWay.firstNode())) {
dirForward = true;
lastNode = w.lastNode();
} else {
lastNode = w.firstNode();
}
} // else one segment - direction unknown
} else {
if (lastNode.equals(w.firstNode())) {
dirForward = true; lastNode = w.lastNode();
} else {
lastNode = w.firstNode();
}
}
final int wayNodeBeginIndex = (dirForward ? 0 : w.getNodesCount() - 1);
final int wayNodeEndIndex = (dirForward ? w.getNodesCount() - 1 : 0);
final int increment = (dirForward ? 1 : -1);
for (int nIndex = wayNodeBeginIndex;
nIndex != wayNodeEndIndex;
nIndex += increment) {
Node refNode = w.getNode(nIndex);
if (PublicTransportHelper.isNodeStop(refNode)) {
if (stopMembers.containsKey(refNode)) {
recRel.addMember(stopMembers.get(refNode));
stopMembers.remove(refNode);
String stopName = PublicTransportHelper.getNameViaStoparea(refNode);
if (stopName == null) {
stopName = "";
}
boolean existsPlatform = platformMembers.containsKey(stopName);
if (!existsPlatform) {
stopName = ""; // find of the nameless
}
if (existsPlatform || platformMembers.containsKey(stopName)) {
List<RelationMember> lMember = platformMembers.get(stopName);
if (lMember.size() == 1) {
recRel.addMember(lMember.get(0));
lMember.remove(0);
} else {
// choose closest
RelationMember candidat = getClosestPlatform(lMember, refNode);
if (candidat != null) {
recRel.addMember(candidat);
lMember.remove(candidat);
}
}
if (lMember.isEmpty()) {
platformMembers.remove(stopName);
}
}
}
}
}
}
for (RelationMember stop : stopMembers.values()) {
recRel.addMember(stop);
String stopName = PublicTransportHelper.getNameViaStoparea(stop);
boolean existsPlatform = platformMembers.containsKey(stopName);
if (!existsPlatform) {
stopName = ""; // find of the nameless
}
if (existsPlatform || platformMembers.containsKey(stopName)) {
List<RelationMember> lMember = platformMembers.get(stopName);
if (lMember.size() == 1) {
recRel.addMember(lMember.get(0));
lMember.remove(0);
} else {
// choose closest
RelationMember candidat = getClosestPlatform(lMember, stop.getNode());
if (candidat != null) {
recRel.addMember(candidat);
lMember.remove(candidat);
}
}
if (lMember.isEmpty()) {
platformMembers.remove(stopName);
}
}
}
for (List<RelationMember> lPlatforms : platformMembers.values()) {
for (RelationMember platform : lPlatforms) {
recRel.addMember(platform);
}
}
for (RelationMember route : routeMembers) {
recRel.addMember(route);
}
for (RelationMember wtf : wtfMembers) {
recRel.addMember(wtf);
}
Command command = new ChangeCommand(r, recRel);
Main.main.undoRedo.add(command);
}
private static final double maxSqrDistBetweenStopAndPlatform = 2000; // ~ 26m
private RelationMember getClosestPlatform(List<RelationMember> members, Node stop) {
if (stop == null || members.isEmpty()) return null;
double maxDist = maxSqrDistBetweenStopAndPlatform;
RelationMember result = null;
for (RelationMember member : members) {
if (member.getType() == OsmPrimitiveType.NODE) {
Node node = member.getNode();
double sqrDist = stop.getEastNorth().distanceSq(node.getEastNorth());
if (sqrDist < maxDist) {
maxDist = sqrDist;
result = member;
}
} else if (member.getType() == OsmPrimitiveType.WAY) {
Way way = member.getWay();
EastNorth closest = Geometry.closestPointToSegment(
way.firstNode().getEastNorth(),
way.lastNode().getEastNorth(),
stop.getEastNorth()
);
double sqrDist = stop.getEastNorth().distanceSq(closest);
if (sqrDist < maxDist) {
maxDist = sqrDist;
result = member;
}
}
}
return result;
}
@Override
public void chosenRelationChanged(Relation oldRelation, Relation newRelation) {
setEnabled(isSuitableRelation(newRelation));
}
private boolean isSuitableRelation(Relation newRelation) {
return !(newRelation == null || !"route".equals(newRelation.get("type")) || newRelation.getMembersCount() == 0);
}
}