28 | 28 |
import uk.me.parabola.imgfmt.app.Coord;
|
29 | 29 |
import uk.me.parabola.log.Logger;
|
30 | 30 |
import uk.me.parabola.mkgmap.filters.DouglasPeuckerFilter;
|
|
31 |
import uk.me.parabola.mkgmap.general.MapPoint;
|
31 | 32 |
import uk.me.parabola.mkgmap.reader.osm.CoordPOI;
|
32 | 33 |
import uk.me.parabola.mkgmap.reader.osm.FakeIdGenerator;
|
33 | 34 |
import uk.me.parabola.mkgmap.reader.osm.Node;
|
|
59 | 60 |
|
60 | 61 |
private final Area bbox;
|
61 | 62 |
private static final String DEBUG_PATH = null;
|
62 | |
static final int MODE_ROADS = 0;
|
63 | |
static final int MODE_LINES = 1;
|
64 | |
private int mode = MODE_ROADS;
|
65 | 63 |
private int pass;
|
66 | 64 |
private boolean extraPass;
|
67 | |
|
|
65 |
|
|
66 |
private ArrayList<ConvertedWay> convertedWays;
|
|
67 |
|
|
68 |
/**
|
|
69 |
* Construct new WrongAngleFixer
|
|
70 |
* @param bbox the bounding box, ignored if null, else might be used for debugging
|
|
71 |
* @param allCoordPOI all {@code MapPoint} instances which refer to {@code CoordP} instances. There location might be changed
|
|
72 |
* in this class.
|
|
73 |
*/
|
68 | 74 |
public WrongAngleFixer(Area bbox) {
|
69 | 75 |
this.bbox = bbox;
|
70 | 76 |
if (DEBUG_PATH != null && bbox != null && (long) bbox.getWidth() * bbox.getHeight() < 100000) {
|
|
86 | 92 |
* @param lines list of non-routable ways
|
87 | 93 |
* @param modifiedRoads Will be enlarged by all roads modified in this method
|
88 | 94 |
* @param deletedRoads Will be enlarged by all roads in roads that were set to null by this method
|
89 | |
* @param restrictions Map with restriction relations
|
|
95 |
* @param restrictions Map with restriction relations
|
|
96 |
* @param renderedPOI all MapPoint instances for {@code CoordPOI} instances
|
90 | 97 |
*/
|
91 | 98 |
public void optimizeWays(List<ConvertedWay> roads, List<ConvertedWay> lines, Map<Long, ConvertedWay> modifiedRoads,
|
92 | |
Set<Long> deletedRoads, List<RestrictionRelation> restrictions) {
|
|
99 |
Set<Long> deletedRoads, List<RestrictionRelation> restrictions, List<MapPoint> renderedPOI) {
|
93 | 100 |
printBadAngles("bad_angles_start", roads);
|
94 | 101 |
writeOSM("roads_orig", roads);
|
95 | 102 |
writeOSM("lines_orig", lines);
|
96 | |
Long2ObjectOpenHashMap<Coord> coordMap = new Long2ObjectOpenHashMap<>();
|
97 | |
replaceDuplicateBoundaryNodes(roads, coordMap);
|
98 | |
replaceDuplicateBoundaryNodes(lines, coordMap);
|
99 | |
coordMap.clear();
|
100 | |
removeWrongAngles(roads, lines, modifiedRoads, deletedRoads, restrictions);
|
|
103 |
|
|
104 |
convertedWays = new ArrayList<>();
|
|
105 |
convertedWays.addAll(roads);
|
|
106 |
convertedWays.addAll(lines);
|
|
107 |
convertedWays.removeIf(ConvertedWay::isOverlay);
|
|
108 |
convertedWays.sort((o1,o2) -> Long.compare(o1.getWay().getId(), o2.getWay().getId()));
|
|
109 |
replaceDuplicateBoundaryNodes(convertedWays);
|
|
110 |
Map<Coord, Coord> replacements = removeWrongAngles(modifiedRoads, deletedRoads);
|
101 | 111 |
writeOSM("roads_post_rem_wrong_angles", roads);
|
102 | |
removeObsoletePoints(roads, modifiedRoads);
|
|
112 |
removeObsoletePoints(modifiedRoads);
|
103 | 113 |
writeOSM("roads_post_rem_obsolete_points", roads);
|
104 | 114 |
printBadAngles("bad_angles_finish", roads);
|
105 | |
this.mode = MODE_LINES;
|
106 | |
writeOSM("lines_after_roads", lines);
|
107 | |
removeWrongAngles(null, lines, modifiedRoads, null, restrictions);
|
108 | 115 |
writeOSM("lines_post_rem_wrong_angles", lines);
|
109 | |
removeObsoletePoints(lines, modifiedRoads);
|
110 | |
writeOSM("lines_final", lines);
|
|
116 |
convertedWays = null;
|
|
117 |
|
|
118 |
for (RestrictionRelation rr : restrictions) {
|
|
119 |
for (Coord p : rr.getViaCoords()) {
|
|
120 |
Coord replacement = getReplacement(p, null, replacements);
|
|
121 |
if (p != replacement) {
|
|
122 |
rr.replaceViaCoord(p, replacement);
|
|
123 |
}
|
|
124 |
}
|
|
125 |
}
|
|
126 |
|
|
127 |
// update positions of the POIs which were already created.
|
|
128 |
for (MapPoint mp : renderedPOI) {
|
|
129 |
Coord replacement = getReplacement(mp.getLocation(), null, replacements);
|
|
130 |
if (mp.getLocation() != replacement) {
|
|
131 |
mp.setLocation(replacement);
|
|
132 |
}
|
|
133 |
}
|
111 | 134 |
}
|
112 | 135 |
|
113 | 136 |
/**
|
|
115 | 138 |
* @param convertedWays
|
116 | 139 |
* @param coordMap
|
117 | 140 |
*/
|
118 | |
private static void replaceDuplicateBoundaryNodes(List<ConvertedWay> convertedWays,
|
119 | |
Long2ObjectOpenHashMap<Coord> coordMap) {
|
|
141 |
private static void replaceDuplicateBoundaryNodes(List<ConvertedWay> convertedWays) {
|
|
142 |
Long2ObjectOpenHashMap<Coord> coordMap = new Long2ObjectOpenHashMap<>();
|
120 | 143 |
for (ConvertedWay cw : convertedWays) {
|
121 | |
if (!cw.isValid() || cw.isOverlay())
|
|
144 |
if (!cw.isValid())
|
122 | 145 |
continue;
|
123 | 146 |
Way way = cw.getWay();
|
124 | 147 |
List<Coord> points = way.getPoints();
|
|
194 | 217 |
/**
|
195 | 218 |
* Find wrong angles caused by rounding to map units. Try to fix them by
|
196 | 219 |
* moving, removing or merging points.
|
197 | |
* @param roads list with routable ways or null, if lines should be optimized
|
198 | |
* @param lines list with non-routable ways
|
199 | 220 |
* @param modifiedRoads map of modified routable ways (modified by this routine)
|
200 | 221 |
* @param deletedRoads set of ids of deleted routable ways (modified by this routine)
|
201 | |
* @param restrictions Map with restriction relations. The restriction relations may be modified by this routine
|
|
222 |
* @return
|
202 | 223 |
*/
|
203 | |
private void removeWrongAngles(List<ConvertedWay> roads, List<ConvertedWay> lines,
|
204 | |
Map<Long, ConvertedWay> modifiedRoads, Set<Long> deletedRoads, List<RestrictionRelation> restrictions) {
|
|
224 |
private Map<Coord, Coord> removeWrongAngles(Map<Long, ConvertedWay> modifiedRoads, Set<Long> deletedRoads) {
|
205 | 225 |
// replacements maps those nodes that have been replaced to
|
206 | 226 |
// the node that replaces them
|
207 | 227 |
Map<Coord, Coord> replacements = new IdentityHashMap<>();
|
|
211 | 231 |
HashSet<Way> waysWithBearingErrors = new HashSet<>();
|
212 | 232 |
HashSet<Long> waysThatMapToOnePoint = new HashSet<>();
|
213 | 233 |
|
214 | |
List<ConvertedWay> convertedWays = (roads != null) ? roads: lines;
|
215 | |
|
216 | 234 |
// filter with Douglas Peucker algo
|
217 | |
prepWithDouglasPeucker(convertedWays, modifiedRoads);
|
|
235 |
prepWithDouglasPeucker(modifiedRoads);
|
218 | 236 |
|
219 | 237 |
Way lastWay = null;
|
220 | 238 |
boolean anotherPassRequired = true;
|
|
224 | 242 |
break;
|
225 | 243 |
anotherPassRequired = false;
|
226 | 244 |
log.info("Removing wrong angles - PASS", pass);
|
227 | |
writeOSM(((mode == MODE_LINES) ? "lines" : "roads") + "_pass_" + pass, convertedWays);
|
|
245 |
writeOSM("pass_" + pass, convertedWays);
|
228 | 246 |
|
229 | 247 |
// Step 1: detect points which are parts of line segments with wrong bearings
|
230 | 248 |
lastWay = null;
|
231 | 249 |
for (ConvertedWay cw : convertedWays) {
|
232 | |
if (!cw.isValid() || cw.isOverlay())
|
|
250 |
if (!cw.isValid())
|
233 | 251 |
continue;
|
234 | 252 |
Way way = cw.getWay();
|
235 | 253 |
if (way.equals(lastWay))
|
|
250 | 268 |
if (pass == 1)
|
251 | 269 |
p.setRemove(false);
|
252 | 270 |
p = getReplacement(p, way, replacements);
|
253 | |
if (i == 0 || i == points.size() - 1) {
|
|
271 |
if (!cw.isRoad() && (i == 0 || i == points.size() - 1)) {
|
254 | 272 |
p.setEndOfWay(true);
|
255 | 273 |
}
|
256 | 274 |
|
|
278 | 296 |
|
279 | 297 |
lastWay = null;
|
280 | 298 |
for (ConvertedWay cw : convertedWays) {
|
281 | |
if (!cw.isValid() || cw.isOverlay() || cw.getWay().equals(lastWay))
|
|
299 |
if (!cw.isValid() || cw.getWay().equals(lastWay))
|
282 | 300 |
continue;
|
283 | 301 |
Way way = cw.getWay();
|
284 | 302 |
if (pass != 1 && !waysWithBearingErrors.contains(way))
|
|
298 | 316 |
if (p == prev) {
|
299 | 317 |
points.remove(i);
|
300 | 318 |
--i;
|
301 | |
if (mode == MODE_ROADS)
|
|
319 |
if (cw.isRoad()) {
|
302 | 320 |
modifiedRoads.put(way.getId(), cw);
|
|
321 |
}
|
303 | 322 |
continue;
|
304 | 323 |
}
|
305 | 324 |
if (p.isPartOfBadAngle() || prev.isPartOfBadAngle()) {
|
|
307 | 326 |
// save both points with their neighbour
|
308 | 327 |
Coord p1 = prev;
|
309 | 328 |
Coord p2 = p;
|
310 | |
CenterOfAngle coa1 = getOrCreateCenter(p, way, centerMap, centers, overlaps);
|
311 | |
CenterOfAngle coa2 = getOrCreateCenter(prev, way, centerMap, centers, overlaps);
|
|
329 |
CenterOfAngle coa1 = getOrCreateCenter(p, cw, centerMap, centers, overlaps);
|
|
330 |
CenterOfAngle coa2 = getOrCreateCenter(prev, cw, centerMap, centers, overlaps);
|
312 | 331 |
coa1.addNeighbour(coa2);
|
313 | 332 |
coa2.addNeighbour(coa1);
|
314 | 333 |
if (points.size() == 2) {
|
315 | 334 |
// way has only two points, don't merge them
|
316 | 335 |
coa1.addBadMergeCandidate(coa2);
|
317 | 336 |
}
|
318 | |
if (mode == MODE_ROADS && p1.getHighwayCount() >= 2 && p2.getHighwayCount() >= 2
|
|
337 |
if (cw.isRoad() && p1.getHighwayCount() >= 2 && p2.getHighwayCount() >= 2
|
319 | 338 |
&& cw.isRoundabout()) {
|
320 | 339 |
// avoid to merge exits of roundabouts
|
321 | 340 |
coa1.addBadMergeCandidate(coa2);
|
|
332 | 351 |
// Step 3: Update list of ways with bearing errors or points next to them
|
333 | 352 |
lastWay = null;
|
334 | 353 |
for (ConvertedWay cw : convertedWays) {
|
335 | |
if (!cw.isValid() || cw.isOverlay() || cw.getWay().equals(lastWay))
|
|
354 |
if (!cw.isValid() || cw.getWay().equals(lastWay))
|
336 | 355 |
continue;
|
337 | 356 |
Way way = cw.getWay();
|
338 | 357 |
lastWay = way;
|
|
380 | 399 |
boolean lastWayModified = false;
|
381 | 400 |
ConvertedWay lastConvertedWay = null;
|
382 | 401 |
for (ConvertedWay cw : convertedWays) {
|
383 | |
if (!cw.isValid() || cw.isOverlay() || !waysWithBearingErrors.contains(cw.getWay()))
|
|
402 |
if (!cw.isValid() || !waysWithBearingErrors.contains(cw.getWay()))
|
384 | 403 |
continue;
|
385 | 404 |
Way way = cw.getWay();
|
386 | 405 |
List<Coord> points = way.getPoints();
|
|
441 | 460 |
anotherPassRequired = true;
|
442 | 461 |
}
|
443 | 462 |
}
|
444 | |
if (lastWayModified && mode == MODE_ROADS) {
|
|
463 |
if (lastWayModified && cw.isRoad()) {
|
445 | 464 |
modifiedRoads.put(way.getId(), cw);
|
446 | 465 |
}
|
447 | 466 |
}
|
|
469 | 488 |
boolean lastWayModified = false;
|
470 | 489 |
ConvertedWay lastConvertedWay = null;
|
471 | 490 |
for (ConvertedWay cw : convertedWays) {
|
472 | |
if (cw.isOverlay())
|
473 | |
continue;
|
474 | 491 |
Way way = cw.getWay();
|
475 | 492 |
|
476 | 493 |
List<Coord> points = way.getPoints();
|
477 | 494 |
if (points.size() < 2) {
|
478 | 495 |
if (log.isInfoEnabled())
|
479 | 496 |
log.info(" Way " + way.getTag("name") + " (" + way.toBrowseURL() + ") has less than 2 points - deleting it");
|
480 | |
if (mode == MODE_LINES && !waysThatMapToOnePoint.contains(way.getId())) {
|
|
497 |
if (!cw.isRoad() && !waysThatMapToOnePoint.contains(way.getId())) {
|
481 | 498 |
log.warn("non-routable way ", way.getId(), "was removed");
|
482 | 499 |
}
|
483 | 500 |
|
484 | |
if (mode == MODE_ROADS)
|
|
501 |
if (cw.isRoad())
|
485 | 502 |
deletedRoads.add(way.getId());
|
486 | 503 |
++numWaysDeleted;
|
487 | 504 |
continue;
|
|
510 | 527 |
prev = p;
|
511 | 528 |
}
|
512 | 529 |
}
|
513 | |
if (mode == MODE_ROADS) {
|
514 | |
// treat special case: non-routable ways may be connected to moved
|
515 | |
// points in roads
|
516 | |
for (ConvertedWay cw : lines) {
|
517 | |
if (!cw.isValid() || cw.isOverlay())
|
518 | |
continue;
|
519 | |
Way way = cw.getWay();
|
520 | |
List<Coord> points = way.getPoints();
|
521 | |
int n = points.size();
|
522 | |
boolean hasReplacedPoints = false;
|
523 | |
for (int i = 0; i < n; i++) {
|
524 | |
Coord p = points.get(i);
|
525 | |
if (p.isReplaced()) {
|
526 | |
hasReplacedPoints = true;
|
527 | |
points.set(i, getReplacement(p, null, replacements));
|
528 | |
}
|
529 | |
}
|
530 | |
if (hasReplacedPoints && DEBUG_PATH != null) {
|
531 | |
GpxCreator.createGpx(Utils.joinPath(DEBUG_PATH, way.getId() + "_mod_non_routable"), points);
|
532 | |
}
|
533 | |
}
|
534 | |
|
535 | |
for (RestrictionRelation rr : restrictions) {
|
536 | |
for (Coord p : rr.getViaCoords()) {
|
537 | |
Coord replacement = getReplacement(p, null, replacements);
|
538 | |
if (p != replacement) {
|
539 | |
rr.replaceViaCoord(p, replacement);
|
540 | |
}
|
541 | |
}
|
542 | |
}
|
543 | |
}
|
544 | |
|
|
530 |
// treat special case: non-routable ways may be connected to moved
|
|
531 |
// points in roads
|
|
532 |
for (ConvertedWay cw : convertedWays) {
|
|
533 |
if (!cw.isValid() || cw.isRoad())
|
|
534 |
continue;
|
|
535 |
Way way = cw.getWay();
|
|
536 |
List<Coord> points = way.getPoints();
|
|
537 |
int n = points.size();
|
|
538 |
boolean hasReplacedPoints = false;
|
|
539 |
for (int i = 0; i < n; i++) {
|
|
540 |
Coord p = points.get(i);
|
|
541 |
if (p.isReplaced()) {
|
|
542 |
hasReplacedPoints = true;
|
|
543 |
points.set(i, getReplacement(p, null, replacements));
|
|
544 |
}
|
|
545 |
}
|
|
546 |
if (hasReplacedPoints && DEBUG_PATH != null) {
|
|
547 |
GpxCreator.createGpx(Utils.joinPath(DEBUG_PATH, way.getId() + "_mod_non_routable"), points);
|
|
548 |
}
|
|
549 |
}
|
|
550 |
|
545 | 551 |
if (DEBUG_PATH != null) {
|
546 | 552 |
GpxCreator.createGpx(
|
547 | |
Utils.joinPath(DEBUG_PATH, (mode == MODE_ROADS ? "roads_" : "lines_") + "solved_badAngles"),
|
|
553 |
Utils.joinPath(DEBUG_PATH, "solved_badAngles"),
|
548 | 554 |
bbox.toCoords(), new ArrayList<>(changedPlaces));
|
549 | 555 |
}
|
550 | 556 |
if (anotherPassRequired) {
|
|
553 | 559 |
log.info("Removing wrong angles - finished in", pass, "passes (", numNodesMerged, "nodes merged,",
|
554 | 560 |
numWaysDeleted, "ways deleted)");
|
555 | 561 |
}
|
556 | |
}
|
557 | |
|
558 | |
private CenterOfAngle getOrCreateCenter(Coord p, Way way, IdentityHashMap<Coord, CenterOfAngle> centerMap,
|
|
562 |
return replacements;
|
|
563 |
}
|
|
564 |
|
|
565 |
private CenterOfAngle getOrCreateCenter(Coord p, ConvertedWay cw, IdentityHashMap<Coord, CenterOfAngle> centerMap,
|
559 | 566 |
List<CenterOfAngle> centers, Map<Coord, Set<Way>> overlaps) {
|
560 | 567 |
CenterOfAngle coa = centerMap.get(p);
|
561 | 568 |
if (coa == null) {
|
562 | 569 |
coa = new CenterOfAngle(p, centerMap.size() + 1);
|
563 | 570 |
centerMap.put(p, coa);
|
564 | 571 |
centers.add(coa);
|
565 | |
if (mode == MODE_ROADS && pass > 1) {
|
566 | |
overlaps.computeIfAbsent(p, k -> new HashSet<>()).add(way);
|
|
572 |
if (cw.isRoad() && pass > 1) {
|
|
573 |
overlaps.computeIfAbsent(p, k -> new HashSet<>()).add(cw.getWay());
|
567 | 574 |
}
|
568 | 575 |
}
|
569 | 576 |
return coa;
|
|
589 | 596 |
* very close to 180 degrees angles in the real line or wrong points.
|
590 | 597 |
* Wrong points are those that produce wrong angles, so that
|
591 | 598 |
* removing them reduces the error.
|
592 | |
* @param convertedWays
|
593 | 599 |
* @param modifiedRoads
|
594 | 600 |
*/
|
595 | |
private void removeObsoletePoints(List<ConvertedWay> convertedWays, Map<Long, ConvertedWay> modifiedRoads) {
|
|
601 |
private void removeObsoletePoints(Map<Long, ConvertedWay> modifiedRoads) {
|
596 | 602 |
ConvertedWay lastConvertedWay = null;
|
597 | 603 |
int numPointsRemoved = 0;
|
598 | 604 |
boolean lastWasModified = false;
|
599 | 605 |
List<Coord> removedInWay = new ArrayList<>();
|
600 | 606 |
List<Coord> obsoletePoints = new ArrayList<>();
|
601 | 607 |
List<Coord> modifiedPoints = new ArrayList<>();
|
602 | |
|
|
608 |
markEndPoints(convertedWays, true); // don't allow to remove end points of ways
|
603 | 609 |
for (ConvertedWay cw : convertedWays) {
|
604 | |
if (!cw.isValid() || cw.isOverlay())
|
|
610 |
if (!cw.isValid())
|
605 | 611 |
continue;
|
606 | 612 |
Way way = cw.getWay();
|
607 | 613 |
if (lastConvertedWay != null && way.equals(lastConvertedWay.getWay())) {
|
|
666 | 672 |
keepThis = false;
|
667 | 673 |
} else if (c1.equals(c2)) {
|
668 | 674 |
// spike / overlap
|
669 | |
log.debug("pass", pass, "roads=" + (mode == MODE_ROADS),
|
|
675 |
log.debug("pass", pass, "roads=" + (cw.isRoad()),
|
670 | 676 |
"extra remove to remove spike or overlap near", cm.toDegreeString());
|
671 | 677 |
keepThis = false;
|
672 | 678 |
|
|
692 | 698 |
modifiedPoints.add(points.get(points.size() - 1));
|
693 | 699 |
points.clear();
|
694 | 700 |
points.addAll(modifiedPoints);
|
695 | |
if (mode == MODE_ROADS)
|
|
701 |
if (cw.isRoad())
|
696 | 702 |
modifiedRoads.put(way.getId(), cw);
|
697 | 703 |
if (DEBUG_PATH != null && (draw || cw.isRoundabout())) {
|
698 | 704 |
GpxCreator.createGpx(Utils.joinPath(DEBUG_PATH, way.getId() + "_dpmod"), points, removedInWay);
|
|
700 | 706 |
}
|
701 | 707 |
}
|
702 | 708 |
if (DEBUG_PATH != null) {
|
703 | |
GpxCreator.createGpx(Utils.joinPath(DEBUG_PATH, (mode == MODE_ROADS ? "roads" : "lines") + "_obsolete"),
|
|
709 |
GpxCreator.createGpx(Utils.joinPath(DEBUG_PATH, "_obsolete"),
|
704 | 710 |
bbox.toCoords(), new ArrayList<>(obsoletePoints));
|
705 | 711 |
}
|
706 | |
log.info("Removed", numPointsRemoved, "obsolete points in lines");
|
|
712 |
log.info("Removed", numPointsRemoved, "obsolete points from lines");
|
707 | 713 |
}
|
708 | 714 |
|
709 | 715 |
/**
|
|
767 | 773 |
if (hasBadAngles)
|
768 | 774 |
badWays.add(cw);
|
769 | 775 |
}
|
770 | |
GpxCreator.createGpx(Utils.joinPath(DEBUG_PATH, (mode==MODE_ROADS ? "roads_" : "lines_")+name), bbox.toCoords(), new ArrayList<>(badAngles));
|
|
776 |
GpxCreator.createGpx(Utils.joinPath(DEBUG_PATH, name), bbox.toCoords(), new ArrayList<>(badAngles));
|
771 | 777 |
writeOSM(name, badWays);
|
772 | 778 |
}
|
773 | 779 |
|
774 | 780 |
/**
|
775 | |
* Check if the point can safely be removed from a road.
|
|
781 |
* Check if the point can safely be removed.
|
776 | 782 |
* @param p
|
777 | 783 |
* @return true if remove is okay
|
778 | 784 |
*/
|
779 | |
private boolean allowedToRemove(Coord p){
|
780 | |
if (p.getOnBoundary() || p.getOnCountryBorder())
|
|
785 |
private static boolean allowedToRemove(Coord p) {
|
|
786 |
if (p.getOnBoundary() || p.getOnCountryBorder() || p.isEndOfWay())
|
781 | 787 |
return false;
|
782 | |
if (mode == MODE_LINES && p.isEndOfWay())
|
|
788 |
if (p instanceof CoordPOI && ((CoordPOI) p).isUsed())
|
783 | 789 |
return false;
|
784 | |
if (p instanceof CoordPOI && ((CoordPOI) p).isUsed()){
|
785 | |
return false;
|
786 | |
}
|
|
790 |
|
787 | 791 |
return p.getHighwayCount() < 2 && !p.isViaNodeOfRestriction();
|
788 | 792 |
}
|
789 | 793 |
|
|
891 | 895 |
toRepl.setReplaced(true);
|
892 | 896 |
if (toRepl instanceof CoordPOI) {
|
893 | 897 |
CoordPOI cp = (CoordPOI) toRepl;
|
894 | |
if (cp.isUsed()) {
|
|
898 |
if (cp.isUsed() ) {
|
895 | 899 |
replacement = new CoordPOI(replacement);
|
896 | 900 |
((CoordPOI) replacement).setNode(cp.getNode());
|
897 | 901 |
((CoordPOI) replacement).setUsed(true);
|
|
974 | 978 |
List<Coord> altPositions = currentCenter.getAlternativePositions();
|
975 | 979 |
for (Coord altCenter : altPositions) {
|
976 | 980 |
if (dupCheck.contains(altCenter)) {
|
977 | |
log.debug("pass", pass, "roads=" + (mode == MODE_ROADS),
|
|
981 |
log.debug("pass", pass,
|
978 | 982 |
"extra move to remove spike or overlap near", currentCenter.toDegreeString());
|
979 | 983 |
replaceCoord(currentCenter, altCenter, replacements);
|
980 | 984 |
return true;
|
|
1290 | 1294 |
|
1291 | 1295 |
@SuppressWarnings("unused")
|
1292 | 1296 |
private void createGPX(String gpxName, Map<Coord, Coord> replacements) {
|
1293 | |
if (gpxName == null || DEBUG_PATH == null)
|
|
1297 |
if (DEBUG_PATH == null || gpxName == null)
|
1294 | 1298 |
return;
|
1295 | 1299 |
if (gpxName.isEmpty())
|
1296 | 1300 |
gpxName = Utils.joinPath(DEBUG_PATH, id + "_no_info");
|
|
1404 | 1408 |
* @param convertedWays list of ways to filter
|
1405 | 1409 |
* @param modifiedRoads if ways are routable we add the modified ways to this map
|
1406 | 1410 |
*/
|
1407 | |
private void prepWithDouglasPeucker(List<ConvertedWay> convertedWays, Map<Long, ConvertedWay> modifiedRoads) {
|
|
1411 |
private void prepWithDouglasPeucker(Map<Long, ConvertedWay> modifiedRoads) {
|
1408 | 1412 |
// we don't want to remove end points of ways
|
1409 | |
markEndPoints(convertedWays);
|
|
1413 |
markEndPoints(convertedWays, true);
|
1410 | 1414 |
double maxErrorDistance = 0.05;
|
1411 | 1415 |
Way lastWay = null;
|
1412 | 1416 |
for (ConvertedWay cw : convertedWays) {
|
1413 | |
if (!cw.isValid() || cw.isOverlay() || cw.getWay().equals(lastWay))
|
|
1417 |
if (!cw.isValid() || cw.getWay().equals(lastWay))
|
1414 | 1418 |
continue;
|
1415 | 1419 |
Way way = cw.getWay();
|
1416 | 1420 |
lastWay = way;
|
|
1432 | 1436 |
// Simplify the rest
|
1433 | 1437 |
DouglasPeuckerFilter.douglasPeucker(coords, 0, endIndex, maxErrorDistance);
|
1434 | 1438 |
if (coords.size() != points.size()) {
|
1435 | |
if (mode == MODE_ROADS) {
|
|
1439 |
if (cw.isRoad()) {
|
1436 | 1440 |
modifiedRoads.put(way.getId(), cw);
|
1437 | 1441 |
}
|
1438 | 1442 |
points.clear();
|
1439 | 1443 |
points.addAll(coords);
|
1440 | 1444 |
}
|
1441 | 1445 |
}
|
1442 | |
}
|
1443 | |
|
1444 | |
private static void markEndPoints(List<ConvertedWay> convertedWays) {
|
|
1446 |
markEndPoints(convertedWays, false);
|
|
1447 |
}
|
|
1448 |
|
|
1449 |
private static void markEndPoints(List<ConvertedWay> convertedWays, boolean b) {
|
1445 | 1450 |
Way lastWay = null;
|
1446 | 1451 |
for (ConvertedWay cw : convertedWays) {
|
1447 | |
if (!cw.isValid() || cw.isOverlay() || cw.getWay().equals(lastWay))
|
|
1452 |
if (!cw.isValid() || cw.getWay().equals(lastWay))
|
1448 | 1453 |
continue;
|
1449 | 1454 |
Way way = cw.getWay();
|
1450 | 1455 |
lastWay = way;
|
1451 | |
way.getFirstPoint().setEndOfWay(true);
|
1452 | |
way.getLastPoint().setEndOfWay(true);
|
|
1456 |
way.getFirstPoint().setEndOfWay(b);
|
|
1457 |
way.getLastPoint().setEndOfWay(b);
|
1453 | 1458 |
}
|
1454 | 1459 |
}
|
1455 | 1460 |
|