source: josm/trunk/src/org/openstreetmap/josm/actions/mapmode/ImproveWayAccuracyHelper.java@ 17414

Last change on this file since 17414 was 16438, checked in by simon04, 4 years ago

see #19251 - Java 8: use Stream

  • Property svn:eol-style set to native
File size: 5.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.actions.mapmode;
3
4import java.awt.Point;
5import java.util.List;
6import java.util.Optional;
7
8import org.openstreetmap.josm.data.coor.EastNorth;
9import org.openstreetmap.josm.data.osm.Node;
10import org.openstreetmap.josm.data.osm.OsmPrimitive;
11import org.openstreetmap.josm.data.osm.Way;
12import org.openstreetmap.josm.data.osm.WaySegment;
13import org.openstreetmap.josm.gui.MainApplication;
14import org.openstreetmap.josm.gui.MapView;
15import org.openstreetmap.josm.tools.Geometry;
16import org.openstreetmap.josm.tools.Pair;
17
18/**
19 * This static class contains functions used to find target way, node to move or
20 * segment to divide.
21 *
22 * @author Alexander Kachkaev <alexander@kachkaev.ru>, 2011
23 */
24final class ImproveWayAccuracyHelper {
25
26 private ImproveWayAccuracyHelper() {
27 // Hide default constructor for utils classes
28 }
29
30 /**
31 * Finds the way to work on. If the mouse is on the node, extracts one of
32 * the ways containing it. If the mouse is on the way, simply returns it.
33 *
34 * @param mv the current map view
35 * @param p the cursor position
36 * @return {@code Way} or {@code null} in case there is nothing under the cursor.
37 */
38 public static Way findWay(MapView mv, Point p) {
39 if (mv == null || p == null) {
40 return null;
41 }
42
43 Node node = mv.getNearestNode(p, OsmPrimitive::isSelectable);
44
45 if (node != null) {
46 Optional<Way> candidate = node.referrers(Way.class).findFirst();
47 if (candidate.isPresent()) {
48 return candidate.get();
49 }
50 }
51
52 return MainApplication.getMap().mapView.getNearestWay(p, OsmPrimitive::isSelectable);
53 }
54
55 /**
56 * Returns the nearest node to cursor. All nodes that are “behind” segments
57 * are neglected. This is to avoid way self-intersection after moving the
58 * candidateNode to a new place.
59 *
60 * @param mv the current map view
61 * @param w the way to check
62 * @param p the cursor position
63 * @return nearest node to cursor
64 */
65 public static Node findCandidateNode(MapView mv, Way w, Point p) {
66 if (mv == null || w == null || p == null) {
67 return null;
68 }
69
70 EastNorth pEN = mv.getEastNorth(p.x, p.y);
71
72 Double bestDistance = Double.MAX_VALUE;
73 Double currentDistance;
74 List<Pair<Node, Node>> wpps = w.getNodePairs(false);
75
76 Node result = null;
77
78 mainLoop:
79 for (Node n : w.getNodes()) {
80 EastNorth nEN = n.getEastNorth();
81
82 if (nEN == null) {
83 // Might happen if lat/lon for that point are not known.
84 continue;
85 }
86
87 currentDistance = pEN.distance(nEN);
88
89 if (currentDistance < bestDistance) {
90 // Making sure this candidate is not behind any segment.
91 for (Pair<Node, Node> wpp : wpps) {
92 if (!wpp.a.equals(n)
93 && !wpp.b.equals(n)
94 && Geometry.getSegmentSegmentIntersection(
95 wpp.a.getEastNorth(), wpp.b.getEastNorth(),
96 pEN, nEN) != null) {
97 continue mainLoop;
98 }
99 }
100 result = n;
101 bestDistance = currentDistance;
102 }
103 }
104
105 return result;
106 }
107
108 /**
109 * Returns the nearest way segment to cursor. The distance to segment ab is
110 * the length of altitude from p to ab (say, c) or the minimum distance from
111 * p to a or b if c is out of ab.
112 *
113 * The priority is given to segments where c is in ab. Otherwise, a segment
114 * with the largest angle apb is chosen.
115 *
116 * @param mv the current map view
117 * @param w the way to check
118 * @param p the cursor position
119 * @return nearest way segment to cursor
120 */
121 public static WaySegment findCandidateSegment(MapView mv, Way w, Point p) {
122 if (mv == null || w == null || p == null) {
123 return null;
124 }
125
126 EastNorth pEN = mv.getEastNorth(p.x, p.y);
127
128 Double currentDistance;
129 Double currentAngle;
130 Double bestDistance = Double.MAX_VALUE;
131 Double bestAngle = 0.0;
132
133 int candidate = -1;
134
135 List<Pair<Node, Node>> wpps = w.getNodePairs(true);
136
137 int i = -1;
138 for (Pair<Node, Node> wpp : wpps) {
139 ++i;
140
141 EastNorth a = wpp.a.getEastNorth();
142 EastNorth b = wpp.b.getEastNorth();
143
144 // Finding intersection of the segment with its altitude from p
145 EastNorth altitudeIntersection = Geometry.closestPointToSegment(a, b, pEN);
146 currentDistance = pEN.distance(altitudeIntersection);
147
148 if (!altitudeIntersection.equals(a) && !altitudeIntersection.equals(b)) {
149 // If the segment intersects with the altitude from p,
150 // make an angle too big to let this candidate win any others
151 // having the same distance.
152 currentAngle = Double.MAX_VALUE;
153 } else {
154 // Otherwise measure the angle
155 currentAngle = Math.abs(Geometry.getCornerAngle(a, pEN, b));
156 }
157
158 if (currentDistance < bestDistance
159 || (currentAngle > bestAngle && currentDistance < bestDistance * 1.0001 /*
160 * equality
161 */)) {
162 candidate = i;
163 bestAngle = currentAngle;
164 bestDistance = currentDistance;
165 }
166
167 }
168 return candidate != -1 ? new WaySegment(w, candidate) : null;
169 }
170}
Note: See TracBrowser for help on using the repository browser.