21 package org.matsim.core.router.util;
23 import java.awt.geom.Rectangle2D;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Iterator;
28 import java.util.TreeMap;
29 import java.util.TreeSet;
31 import org.apache.logging.log4j.LogManager;
32 import org.apache.logging.log4j.Logger;
39 class LandmarkerPieSlices {
41 private Node[] landmarks;
43 private Coord center = null;
45 private final Rectangle2D.Double travelZone;
47 private static final Logger log = LogManager.getLogger(LandmarkerPieSlices.class);
49 private static final double ZONE_EXPANSION = 0.1;
51 LandmarkerPieSlices(
final int landmarkCount,
final Rectangle2D.Double travelZone) {
52 this.landmarks =
new Node[landmarkCount];
53 this.travelZone = travelZone;
56 public void run(
final Network network) {
57 Collection<? extends Node> nodes;
58 if (this.travelZone.getHeight() == 0 || this.travelZone.getWidth() == 0) {
59 nodes = network.getNodes().values();
61 nodes = getNodesInTravelZone(network);
66 private Set<Node> getNodesInTravelZone(
final Network network) {
67 double minX = travelZone.getX();
68 double maxX = travelZone.getWidth() + minX;
69 double minY = travelZone.getY();
70 double maxY = travelZone.getHeight() + minY;
73 maxX += (maxX - minX) * ZONE_EXPANSION;
74 minX -= (maxX - minX) * ZONE_EXPANSION;
75 maxY += (maxY - minY) * ZONE_EXPANSION;
76 minY -= (maxY - minY) * ZONE_EXPANSION;
77 Set<Node> resultNodes =
new TreeSet<Node>();
78 for (Node n : network.getNodes().values()) {
79 if (n.getCoord().getX() <= maxX && n.getCoord().getX() >= minX
80 && n.getCoord().getY() <= maxY && n.getCoord().getY() >= minY) {
88 public void run(
final Collection<? extends Node> nodes) {
89 this.center = getCenter(nodes);
90 putLandmarks(nodes, this.landmarks.length);
93 private void putLandmarks(
final Collection<? extends Node> nodes,
final int landmarkCount) {
95 ArrayList<ArrayList<Node>> sectors =
new ArrayList<ArrayList<Node>>();
97 log.info(
"Filling sectors...");
98 double[][] angles = fillSectors(sectors, nodes);
100 if (angles.length < landmarkCount) {
101 log.info(
"Reducing number of landmarks from " + landmarkCount +
" to " + angles.length +
"...");
102 this.landmarks =
new Node[angles.length];
104 for (
int i = 0; i < this.landmarks.length; i++) {
105 this.landmarks[i] = getLandmark(sectors.get(i), angles[i]);
108 log.info(
"Refining landmarks...");
109 refineLandmarks(sectors, angles);
113 private double[][] fillSectors(
final ArrayList<ArrayList<Node>> sectors,
final Collection<? extends Node> nodes) {
114 ArrayList<double[]> angles =
new ArrayList<double[]>();
116 TreeMap<Double, Node[]> sortedNodes =
new TreeMap<Double, Node[]>();
118 for (Node node : nodes) {
119 double x = node.getCoord().getX() - this.center.getX();
120 double y = node.getCoord().getY() - this.center.getY();
121 double angle = Math.atan2(y, x) + Math.PI;
122 nodeList = sortedNodes.get(angle);
123 if (nodeList == null) {
124 nodeList =
new Node[1];
127 Node[] nodeList2 =
new Node[nodeList.length + 1];
128 System.arraycopy(nodeList, 0, nodeList2, 0, nodeList.length);
129 nodeList2[nodeList.length] = node;
130 nodeList = nodeList2;
132 sortedNodes.put(angle, nodeList);
134 double lastAngle = 0;
135 Iterator<Node[]> it = sortedNodes.values().iterator();
138 Node[] tmpNodes = it.next();
140 for (
int i = 0; i < this.landmarks.length; i++) {
142 sectors.add(
new ArrayList<Node>());
144 for (
int j = 0; j < nodes.size() / this.landmarks.length; j++) {
145 if (k == tmpNodes.length) {
146 tmpNodes = it.next();
149 node = tmpNodes[k++];
150 sectors.get(angles.size()).add(node);
153 if (i == this.landmarks.length - 1) {
154 while (it.hasNext() || k < tmpNodes.length) {
155 if (k == tmpNodes.length) {
156 tmpNodes = it.next();
159 node = tmpNodes[k++];
160 sectors.get(angles.size()).add(node);
163 if (sectors.get(angles.size()).isEmpty()) {
164 log.info(
"There is no node in sector " + i +
"!");
165 sectors.remove(angles.size());
168 double x = node.getCoord().getX() - this.center.getX();
169 double y = node.getCoord().getY() - this.center.getY();
170 double angle = Math.atan2(y, x) + Math.PI;
171 double[] tmp =
new double[2];
179 return angles.toArray(
new double[0][2]);
182 private Node getLandmark(
final ArrayList<Node> nodes,
final double[] angles) {
183 double maxDist = Double.NEGATIVE_INFINITY;
184 Node landmark = null;
185 for (Node node : nodes) {
186 if ((node.getOutLinks().size() > 1 && node.getInLinks().size() > 1)
187 || landmark == null) {
188 double x = node.getCoord().getX() - this.center.getX();
189 double y = node.getCoord().getY() - this.center.getY();
190 double angle = Math.atan2(y, x) + Math.PI;
191 double minAngelToBorder = 0;
192 if (angle - angles[0] < angles[1] - angle) {
193 minAngelToBorder = angle - angles[0];
195 minAngelToBorder = angles[1] - angle;
197 double distApprox = Math.sqrt(x * x + y * y) * (1 + minAngelToBorder / (2 * Math.PI));
199 if (distApprox > maxDist) {
201 maxDist = distApprox;
209 private void refineLandmarks(
final ArrayList<ArrayList<Node>> sectors,
final double[][] sectorAngles) {
210 boolean doRefine =
true;
211 double[] landmarkAngels =
new double[this.landmarks.length];
212 for (
int i = 0; i < this.landmarks.length; i++) {
213 double x = this.landmarks[i].getCoord().getX() - this.center.getX();
214 double y = this.landmarks[i].getCoord().getY() - this.center.getY();
215 landmarkAngels[i] = Math.atan2(y, x) + Math.PI;
217 double minAngelFactor = 0.5;
220 for (
int i = 0; i < this.landmarks.length && !sectors.get(i).isEmpty(); i++) {
224 preInd = this.landmarks.length - 1;
225 angelDiff = 2 * Math.PI + landmarkAngels[i] - landmarkAngels[preInd];
227 angelDiff = landmarkAngels[i] - landmarkAngels[preInd];
230 double minSectorSize = sectorAngles[i][1] - sectorAngles[i][0];
231 if (sectorAngles[preInd][1] - sectorAngles[preInd][0] < minSectorSize) {
232 minSectorSize = sectorAngles[preInd][1] - sectorAngles[preInd][0];
234 if (angelDiff < minSectorSize * minAngelFactor) {
236 int indexToChange = 0;
237 if (CoordUtils.calcEuclideanDistance(
this.center,
this.landmarks[preInd].getCoord()) < CoordUtils.calcEuclideanDistance(
this.center,
this.landmarks[i].getCoord())) {
239 sectorAngles[preInd][1] -= minSectorSize * minAngelFactor;
240 indexToChange = preInd;
243 sectorAngles[i][0] += minSectorSize * minAngelFactor;
246 removeNodesFromSector(sectors.get(indexToChange), sectorAngles[indexToChange]);
247 if (sectors.get(indexToChange).isEmpty()) {
248 log.info(
"There is no node in sector " + indexToChange +
" after narrowing it!");
250 this.landmarks[indexToChange] = getLandmark(sectors.get(indexToChange), sectorAngles[indexToChange]);
252 double x = this.landmarks[indexToChange].getCoord().getX() - this.center.getX();
253 double y = this.landmarks[indexToChange].getCoord().getY() - this.center.getY();
254 landmarkAngels[indexToChange] = Math.atan2(y, x) + Math.PI;
262 private void removeNodesFromSector(
final ArrayList<Node> sector,
263 final double[] sectorAngles) {
265 while (i < sector.size()) {
266 Node node = sector.get(i);
267 double x = node.getCoord().getX() - this.center.getX();
268 double y = node.getCoord().getY() - this.center.getY();
269 double angle = Math.atan2(y, x) + Math.PI;
270 if (angle < sectorAngles[0] || angle > sectorAngles[1]) {
278 private Coord getCenter(
final Collection<? extends Node> nodes) {
279 double[] bBox = NetworkUtils.getBoundingBox(nodes);
280 double minX = bBox[0];
281 double minY = bBox[1];
282 double maxX = bBox[2];
283 double maxY = bBox[3];
285 double centerX = (maxX - minX) / 2 + minX;
286 double centerY = (maxY - minY) / 2 + minY;
288 return new Coord(centerX, centerY);
291 public Node[] getLandmarks() {
292 return this.landmarks.clone();