/** * ***************************************************************************** * Copyright 2013 Johannes Mitlmeier * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * **************************************************************************** */ package de.fub.agg2graph.roadgen; import de.fub.agg2graph.agg.AggNode; import de.fub.agg2graph.structs.GPSCalc; import de.fub.agg2graph.structs.IEdge; import de.fub.agg2graph.structs.ILocation; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.logging.Logger; public class DefaultRoadObjectMerger implements IRoadObjectMerger { private static final Logger logger = Logger.getLogger("agg2graph.roadgen.merger"); private double maxIntersectionMergeDistance = 30; private double maxRoadMergeDistance = 50; public double getMaxIntersectionMergeDistance() { return maxIntersectionMergeDistance; } public void setMaxIntersectionMergeDistance(double maxIntersectionMergeDistance) { this.maxIntersectionMergeDistance = maxIntersectionMergeDistance; } public double getMaxRoadMergeDistance() { return maxRoadMergeDistance; } public void setMaxRoadMergeDistance(double maxRoadMergeDistance) { this.maxRoadMergeDistance = maxRoadMergeDistance; } @Override public void mergeInteresections(RoadNetwork roadNetwork) { List<Intersection[]> mergeableIntersections = new ArrayList<Intersection[]>(100); for (Intersection i1 : roadNetwork.getIntersections()) { if (!i1.isVisible()) { continue; } // get close intersections Set<AggNode> closeNodes = i1.baseNode.getAggContainer() .getCachingStrategy() .getCloseNodes(i1.baseNode, maxIntersectionMergeDistance); for (AggNode node : closeNodes) { Intersection i2 = node.getIntersection(); if (i2 == null || !i2.isVisible()) { continue; } // we have a close intersection... boolean connected = i1.isDirectlyConnectedTo(i2); // boolean justOnePseudo = i1.isPseudo() ^ i2.isPseudo(); // boolean minOnePseudo = i1.isPseudo() || i2.isPseudo(); boolean maxOnePseudo = !i1.isPseudo() || !i2.isPseudo(); boolean allPseudo = i1.isPseudo() && i2.isPseudo(); if ((connected && maxOnePseudo) || (!connected && allPseudo)) { // ...that can be merged mergeableIntersections.add(new Intersection[]{i1, i2}); } } } // do the merges for (Intersection[] i : mergeableIntersections) { mergeIntersectionPair(roadNetwork, i[0], i[1]); } // remove the rest for (Intersection i : roadNetwork.getIntersections()) { if (i.mergedTo != null) { i = null; } } } @Override public void mergeIntersectionPair(RoadNetwork roadNetwork, Intersection i1, Intersection i2) { if (i1 == i2 || i1.equals(i2) || (i1.mergedTo != null && i2.mergedTo != null)) { return; } if (i1.mergedTo != null) { i1 = i1.mergedTo; } if (i2.mergedTo != null) { i2 = i2.mergedTo; } logger.fine(MessageFormat.format("merging {0} with {1} dist: {2}", i1, i2, GPSCalc.getDistance(i1, i2))); AggNode aggNode = new AggNode(GPSCalc.getMidwayLocation(i1, i2), i1.baseNode.getAggContainer()); Intersection newIntersection = new Intersection(aggNode); newIntersection.in = i1.in; newIntersection.in.addAll(i2.in); newIntersection.out = i1.out; newIntersection.out.addAll(i2.out); for (Road r : newIntersection.in) { r.setTo(newIntersection); } for (Road r : newIntersection.out) { r.setFrom(newIntersection); } i1.mergedTo = newIntersection; i2.mergedTo = newIntersection; int before = roadNetwork.getIntersections().size(); roadNetwork.getIntersections().remove(i1); roadNetwork.getIntersections().remove(i2); roadNetwork.getIntersections().add(newIntersection); // assert before - 1 == roadNetwork.intersections.size(); } @Override public void mergeRoads(RoadNetwork roadNetwork) { List<Road[]> mergeableRoads = new ArrayList<Road[]>(10); for (Intersection i : roadNetwork.getIntersections()) { for (Road r1 : i.out) { if (r1.getTo() != null) { // is there a road in the backwards direction? Set<Road> backward = r1.getTo().out; Iterator<Road> backwardIt = backward.iterator(); while (backwardIt.hasNext()) { Road r2 = backwardIt.next(); logger.info(r1.toString()); logger.info(r2.toString()); if (r2.getTo().equals(r1.getFrom())) { mergeableRoads.add(new Road[]{r1, r2}); } } } } } // do the merges for (Road[] r : mergeableRoads) { mergeRoadPair(roadNetwork, r[0], r[1]); } // remove the rest for (Road r : roadNetwork.getRoads()) { if (r.mergedTo != null) { r = null; } } } @Override public void mergeRoadPair(RoadNetwork roadNetwork, Road r1, Road r2) { if (r1 == r2 || r1.equals(r2) || (r1.mergedTo != null && r2.mergedTo != null)) { return; } if (r1.mergedTo != null) { r1 = r1.mergedTo; } if (r2.mergedTo != null) { r2 = r2.mergedTo; } // close enough? boolean close = true; List<? extends ILocation> r2nodes = r2.getNodes(), r1nodes = r1 .getNodes(); for (IEdge<AggNode> edge : r1.getPath()) { if (!GPSCalc.isDistancePointToTraceBelowLimit(edge.getFrom(), r2nodes, maxRoadMergeDistance)) { close = false; break; } } if (close) { for (IEdge<AggNode> edge : r2.getPath()) { if (!GPSCalc.isDistancePointToTraceBelowLimit(edge.getFrom(), r1nodes, maxRoadMergeDistance)) { close = false; break; } } } // merge and change the oneway attribute's value if (close) { logger.info(MessageFormat.format("merging {0} with {1}", r1, r2)); r1.setOneWay(false); r2.getFrom().out.remove(r2); r2.getTo().in.remove(r2); roadNetwork.getRoads().remove(r2); r2.mergedTo = r1; r1.mergedTo = r1; } } }