Codebase list mkgmap / c030eea
New upstream version 0.0.0+svn4454 Bas Couwenberg 4 years ago
15 changed file(s) with 355 addition(s) and 131 deletion(s). Raw diff Collapse all Expand all
3737
3838 <property name="src" value="src"/>
3939 <property name="test" value="test"/>
40 <property name="tools" value="tools"/>
4041
4142 <property name="doc" value="doc"/>
4243 <property name="javadoc" value="${doc}/api"/>
369370 <attribute name="Implementation-Version" value="${project.version}" />
370371 </manifest>
371372 <include name="**/*.class"/>
373 <exclude name="**/buildoptions/**"/>
372374 <include name="*.csv"/>
373375 <include name="known-hgt.bin"/>
374376 <include name="*.xml"/>
421423 </javadoc>
422424 </target>
423425
426 <!-- Compile the tools -->
427 <target name="compile-tools" depends="prepare" description="tool compilation">
428
429 <javac srcdir="${tools}" destdir="${build.classes}" encoding="utf-8" debug="true" includeantruntime="false">
430 <include name="**/*.java" />
431 <classpath refid="main"/>
432 </javac>
433 </target>
434
435 <!-- Generate help file options from doc file options.txt -->
436 <target name="gen-options-file" depends="compile-tools">
437 <exec executable="java" dir="${build.classes}" failonerror="true" failifexecutionfails="true" input="doc/options.txt">
438 <arg value="buildoptions.OptionsBuilder" />
439 <arg value="../../resources/help/en/options" />
440 </exec>
441 </target>
442
443
424444 <target name="macker" depends="build, resolve-macker">
425445 <taskdef name="macker"
426446 classname="net.innig.macker.ant.MackerAntTask"
4343 This documents the language that is accepted by the TYP compiler that
4444 is included within mkgmap.
4545
46 [/doc/options '''Logging''']
46 [/doc/logging '''Logging''']
4747 Instructions on how to control messages that are logged and to where
4848 they are written.
4949
4242 log file, with warning and error messages being also sent to stdout.
4343
4444 Further information can be found at
45 https://docs.oracle.com/javase/8/docs/technotes/guides/logging/overview.html
45 [https://docs.oracle.com/javase/8/docs/technotes/guides/logging/overview.html https://docs.oracle.com/javase/8/docs/technotes/guides/logging/overview.html]
4646
0 === Command line ===
1
02 The command line is of the format:
13
24 : java.exe [java-options] -jar mkgmap.jar [mkgmap-options]
2830 disable specific logging messages. This is useful if you want to see
2931 certain types of message that are not logged by default or choose where
3032 the messages should be written.
33
3134 === Mkgmap options ===
3235
3336 The order of the options is significant in that options only apply
4851
4952 === File options ===
5053
51 filename
54 ;filename
5255 ;--input-file=filename
5356 : Read input data from the given file. This option (or just a
5457 filename) may be specified more than once. Make sure you set all
0 === Command line ===
1
02 The command line is of the format:
13
24 java.exe [java-options] -jar mkgmap.jar [mkgmap-options]
2527 Specifies a logging configuration file that allows you to enable and
2628 disable specific logging messages. This is useful if you want to see
2729 certain types of message that are not logged by default or choose where the
28 messages should be written. === Mkgmap options ===
30 messages should be written.
31
32 === Mkgmap options ===
2933
3034 The order of the options is significant in that options only apply to
3135 subsequent input files. If you are using splitter, you probably will need to
0 svn.version: 4434
1 build.timestamp: 2020-01-31T07:23:47+0000
0 svn.version: 4454
1 build.timestamp: 2020-02-20T09:16:25+0000
5050 private final boolean ignoreNumberOnlyNodes;
5151
5252 LinePreparer(Polyline line) {
53 extraBit = line.isRoad() && line.getSubdiv().getZoom().getLevel() == 0 && line.hasInternalNodes();
53 extraBit = line.isRoad() && line.getSubdiv().getZoom().getLevel() == 0
54 && (line.hasInternalNodes() || !line.isLastSegment());
5455 ignoreNumberOnlyNodes = !line.hasHouseNumbers();
5556 extTypeLine = line.hasExtendedType();
5657
143143 * @throws FileNotFoundException If the file doesn't actually exist.
144144 */
145145 public static FileInfo getFileInfo(String inputName) throws FileNotFoundException {
146
147146 int end = inputName.length();
148 String ext = inputName.substring(end - 3).toUpperCase(Locale.ENGLISH);
147 File f = new File(inputName);
148 if (f.isDirectory() || end < 3)
149 return new FileInfo(inputName, UNKNOWN_KIND);
150
151 String ext = end < 3 ? "" : inputName.substring(end - 3).toUpperCase(Locale.ENGLISH);
149152 FileInfo info;
150153
151154 if (Objects.equals(ext, "IMG")) {
4949 }
5050
5151 public String makeMap(CommandArgs args, String filename) {
52 if (new File(filename).isDirectory()) {
53 System.err.println("Need a single file, not a directory: " + filename);
54 return filename;
55 }
5256 try {
5357 LoadableMapDataSource src = loadFromFile(args, filename);
5458 sort = args.getSort();
3535 private final int index;
3636 private final Way way; // with tags after Style processing
3737 private final GType gt;
38 private final boolean isRoad;
3839
3940 private byte roadClass; // 0-4
4041 private byte roadSpeed; // 0-7
4142 private byte mkgmapAccess; // bit mask, see ACCESS_TAGS
4243 private final byte routeFlags; // bit mask, see ROUTING_TAGS
43 private boolean isRoad;
4444 private boolean reversed; // points were reversed
4545 private boolean overlay; // this is a non-routable overlay line that for a road
4646
7272 // copy all other attributes
7373 this.index = other.index;
7474 this.gt = other.gt;
75 this.isRoad = other.isRoad;
7576 this.roadClass = other.roadClass;
7677 this.roadSpeed = other.roadSpeed;
7778 this.mkgmapAccess = other.mkgmapAccess;
7879 this.routeFlags = other.routeFlags;
80 this.overlay = other.overlay;
7981 }
8082
8183 public int getIndex(){
2727 import java.util.LinkedHashSet;
2828 import java.util.List;
2929 import java.util.Map;
30 import java.util.Map.Entry;
3031 import java.util.Set;
31 import java.util.Map.Entry;
3232 import java.util.logging.Level;
3333
3434 import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
117117 private IdentityHashMap<Coord, CoordNode> nodeIdMap = new IdentityHashMap<>();
118118
119119 public static final String WAY_POI_NODE_IDS = "mkgmap:way-poi-node-ids";
120 private final HashMap<Integer, Map<String,MapPoint>> pointMap;
120 private final HashMap<Integer, Map<String, MapPoint>> pointMap;
121121
122122 /** boundary ways with admin_level=2 */
123123 private final Set<Way> borders = new LinkedHashSet<>();
127127
128128 private List<ConvertedWay> roads = new ArrayList<>();
129129 private List<ConvertedWay> lines = new ArrayList<>();
130 private HashMap<Long, ConvertedWay> modifiedRoads = new HashMap<>();
131 private HashSet<Long> deletedRoads = new HashSet<>();
130 private List<MapPoint> renderedCoordPOI = new ArrayList<>();
132131
133132 private int nextRoadId = 1;
134133
587586 return;
588587 }
589588
589 if (node.getLocation() instanceof CoordPOI) {
590 renderedCoordPOI.add(mp);
591 }
590592 collector.addPoint(mp);
591593 }
592594
882884 borders.clear();
883885
884886 setHighwayCounts();
887 HashMap<Long, ConvertedWay> modifiedRoads = new HashMap<>();
885888 findUnconnectedRoads();
886 rotateClosedWaysToFirstNode();
889 rotateClosedWaysToFirstNode(modifiedRoads);
887890 filterCoordPOI();
891
892 HashSet<Long> deletedRoads = new HashSet<>();
888893 WrongAngleFixer wrongAngleFixer = new WrongAngleFixer(bbox);
889 wrongAngleFixer.optimizeWays(roads, lines, modifiedRoads, deletedRoads, restrictions);
890
894 wrongAngleFixer.optimizeWays(roads, lines, modifiedRoads, deletedRoads, restrictions, renderedCoordPOI);
895
891896 // make sure that copies of modified roads have equal points
892897 for (ConvertedWay line : lines){
893898 if (!line.isValid())
914919 log.warn("Way that is used in valid restriction relation was removed, id:",wayId);
915920 }
916921 }
917 deletedRoads = null;
918 modifiedRoads = null;
922 deletedRoads.clear();
923 modifiedRoads.clear();
919924 mergeRoads();
920925
921926 resetHighwayCounts();
9971002 * Try to make sure that closed ways start with a point that is
9981003 * also part of another road. This reduces the number of nodes
9991004 * a little bit.
1005 * @param modifiedRoads Will contain roads modified by this routine
10001006 *
10011007 */
1002 private void rotateClosedWaysToFirstNode() {
1008 private void rotateClosedWaysToFirstNode(HashMap<Long, ConvertedWay> modifiedRoads ) {
10031009 for (ConvertedWay cw: roads){
10041010 if (!cw.isValid())
10051011 continue;
19431949
19441950 return trailingWay;
19451951 }
1946
19471952
19481953 /**
19491954 * Increment the highway counter for each coord of each road.
2828 import uk.me.parabola.imgfmt.app.Coord;
2929 import uk.me.parabola.log.Logger;
3030 import uk.me.parabola.mkgmap.filters.DouglasPeuckerFilter;
31 import uk.me.parabola.mkgmap.general.MapPoint;
3132 import uk.me.parabola.mkgmap.reader.osm.CoordPOI;
3233 import uk.me.parabola.mkgmap.reader.osm.FakeIdGenerator;
3334 import uk.me.parabola.mkgmap.reader.osm.Node;
5960
6061 private final Area bbox;
6162 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;
6563 private int pass;
6664 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 */
6874 public WrongAngleFixer(Area bbox) {
6975 this.bbox = bbox;
7076 if (DEBUG_PATH != null && bbox != null && (long) bbox.getWidth() * bbox.getHeight() < 100000) {
8692 * @param lines list of non-routable ways
8793 * @param modifiedRoads Will be enlarged by all roads modified in this method
8894 * @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
9097 */
9198 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) {
93100 printBadAngles("bad_angles_start", roads);
94101 writeOSM("roads_orig", roads);
95102 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);
101111 writeOSM("roads_post_rem_wrong_angles", roads);
102 removeObsoletePoints(roads, modifiedRoads);
112 removeObsoletePoints(modifiedRoads);
103113 writeOSM("roads_post_rem_obsolete_points", roads);
104114 printBadAngles("bad_angles_finish", roads);
105 this.mode = MODE_LINES;
106 writeOSM("lines_after_roads", lines);
107 removeWrongAngles(null, lines, modifiedRoads, null, restrictions);
108115 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 }
111134 }
112135
113136 /**
115138 * @param convertedWays
116139 * @param coordMap
117140 */
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<>();
120143 for (ConvertedWay cw : convertedWays) {
121 if (!cw.isValid() || cw.isOverlay())
144 if (!cw.isValid())
122145 continue;
123146 Way way = cw.getWay();
124147 List<Coord> points = way.getPoints();
194217 /**
195218 * Find wrong angles caused by rounding to map units. Try to fix them by
196219 * 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
199220 * @param modifiedRoads map of modified routable ways (modified by this routine)
200221 * @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
202223 */
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) {
205225 // replacements maps those nodes that have been replaced to
206226 // the node that replaces them
207227 Map<Coord, Coord> replacements = new IdentityHashMap<>();
211231 HashSet<Way> waysWithBearingErrors = new HashSet<>();
212232 HashSet<Long> waysThatMapToOnePoint = new HashSet<>();
213233
214 List<ConvertedWay> convertedWays = (roads != null) ? roads: lines;
215
216234 // filter with Douglas Peucker algo
217 prepWithDouglasPeucker(convertedWays, modifiedRoads);
235 prepWithDouglasPeucker(modifiedRoads);
218236
219237 Way lastWay = null;
220238 boolean anotherPassRequired = true;
224242 break;
225243 anotherPassRequired = false;
226244 log.info("Removing wrong angles - PASS", pass);
227 writeOSM(((mode == MODE_LINES) ? "lines" : "roads") + "_pass_" + pass, convertedWays);
245 writeOSM("pass_" + pass, convertedWays);
228246
229247 // Step 1: detect points which are parts of line segments with wrong bearings
230248 lastWay = null;
231249 for (ConvertedWay cw : convertedWays) {
232 if (!cw.isValid() || cw.isOverlay())
250 if (!cw.isValid())
233251 continue;
234252 Way way = cw.getWay();
235253 if (way.equals(lastWay))
250268 if (pass == 1)
251269 p.setRemove(false);
252270 p = getReplacement(p, way, replacements);
253 if (i == 0 || i == points.size() - 1) {
271 if (!cw.isRoad() && (i == 0 || i == points.size() - 1)) {
254272 p.setEndOfWay(true);
255273 }
256274
278296
279297 lastWay = null;
280298 for (ConvertedWay cw : convertedWays) {
281 if (!cw.isValid() || cw.isOverlay() || cw.getWay().equals(lastWay))
299 if (!cw.isValid() || cw.getWay().equals(lastWay))
282300 continue;
283301 Way way = cw.getWay();
284302 if (pass != 1 && !waysWithBearingErrors.contains(way))
298316 if (p == prev) {
299317 points.remove(i);
300318 --i;
301 if (mode == MODE_ROADS)
319 if (cw.isRoad()) {
302320 modifiedRoads.put(way.getId(), cw);
321 }
303322 continue;
304323 }
305324 if (p.isPartOfBadAngle() || prev.isPartOfBadAngle()) {
307326 // save both points with their neighbour
308327 Coord p1 = prev;
309328 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);
312331 coa1.addNeighbour(coa2);
313332 coa2.addNeighbour(coa1);
314333 if (points.size() == 2) {
315334 // way has only two points, don't merge them
316335 coa1.addBadMergeCandidate(coa2);
317336 }
318 if (mode == MODE_ROADS && p1.getHighwayCount() >= 2 && p2.getHighwayCount() >= 2
337 if (cw.isRoad() && p1.getHighwayCount() >= 2 && p2.getHighwayCount() >= 2
319338 && cw.isRoundabout()) {
320339 // avoid to merge exits of roundabouts
321340 coa1.addBadMergeCandidate(coa2);
332351 // Step 3: Update list of ways with bearing errors or points next to them
333352 lastWay = null;
334353 for (ConvertedWay cw : convertedWays) {
335 if (!cw.isValid() || cw.isOverlay() || cw.getWay().equals(lastWay))
354 if (!cw.isValid() || cw.getWay().equals(lastWay))
336355 continue;
337356 Way way = cw.getWay();
338357 lastWay = way;
380399 boolean lastWayModified = false;
381400 ConvertedWay lastConvertedWay = null;
382401 for (ConvertedWay cw : convertedWays) {
383 if (!cw.isValid() || cw.isOverlay() || !waysWithBearingErrors.contains(cw.getWay()))
402 if (!cw.isValid() || !waysWithBearingErrors.contains(cw.getWay()))
384403 continue;
385404 Way way = cw.getWay();
386405 List<Coord> points = way.getPoints();
441460 anotherPassRequired = true;
442461 }
443462 }
444 if (lastWayModified && mode == MODE_ROADS) {
463 if (lastWayModified && cw.isRoad()) {
445464 modifiedRoads.put(way.getId(), cw);
446465 }
447466 }
469488 boolean lastWayModified = false;
470489 ConvertedWay lastConvertedWay = null;
471490 for (ConvertedWay cw : convertedWays) {
472 if (cw.isOverlay())
473 continue;
474491 Way way = cw.getWay();
475492
476493 List<Coord> points = way.getPoints();
477494 if (points.size() < 2) {
478495 if (log.isInfoEnabled())
479496 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())) {
481498 log.warn("non-routable way ", way.getId(), "was removed");
482499 }
483500
484 if (mode == MODE_ROADS)
501 if (cw.isRoad())
485502 deletedRoads.add(way.getId());
486503 ++numWaysDeleted;
487504 continue;
510527 prev = p;
511528 }
512529 }
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
545551 if (DEBUG_PATH != null) {
546552 GpxCreator.createGpx(
547 Utils.joinPath(DEBUG_PATH, (mode == MODE_ROADS ? "roads_" : "lines_") + "solved_badAngles"),
553 Utils.joinPath(DEBUG_PATH, "solved_badAngles"),
548554 bbox.toCoords(), new ArrayList<>(changedPlaces));
549555 }
550556 if (anotherPassRequired) {
553559 log.info("Removing wrong angles - finished in", pass, "passes (", numNodesMerged, "nodes merged,",
554560 numWaysDeleted, "ways deleted)");
555561 }
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,
559566 List<CenterOfAngle> centers, Map<Coord, Set<Way>> overlaps) {
560567 CenterOfAngle coa = centerMap.get(p);
561568 if (coa == null) {
562569 coa = new CenterOfAngle(p, centerMap.size() + 1);
563570 centerMap.put(p, coa);
564571 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());
567574 }
568575 }
569576 return coa;
589596 * very close to 180 degrees angles in the real line or wrong points.
590597 * Wrong points are those that produce wrong angles, so that
591598 * removing them reduces the error.
592 * @param convertedWays
593599 * @param modifiedRoads
594600 */
595 private void removeObsoletePoints(List<ConvertedWay> convertedWays, Map<Long, ConvertedWay> modifiedRoads) {
601 private void removeObsoletePoints(Map<Long, ConvertedWay> modifiedRoads) {
596602 ConvertedWay lastConvertedWay = null;
597603 int numPointsRemoved = 0;
598604 boolean lastWasModified = false;
599605 List<Coord> removedInWay = new ArrayList<>();
600606 List<Coord> obsoletePoints = new ArrayList<>();
601607 List<Coord> modifiedPoints = new ArrayList<>();
602
608 markEndPoints(convertedWays, true); // don't allow to remove end points of ways
603609 for (ConvertedWay cw : convertedWays) {
604 if (!cw.isValid() || cw.isOverlay())
610 if (!cw.isValid())
605611 continue;
606612 Way way = cw.getWay();
607613 if (lastConvertedWay != null && way.equals(lastConvertedWay.getWay())) {
666672 keepThis = false;
667673 } else if (c1.equals(c2)) {
668674 // spike / overlap
669 log.debug("pass", pass, "roads=" + (mode == MODE_ROADS),
675 log.debug("pass", pass, "roads=" + (cw.isRoad()),
670676 "extra remove to remove spike or overlap near", cm.toDegreeString());
671677 keepThis = false;
672678
692698 modifiedPoints.add(points.get(points.size() - 1));
693699 points.clear();
694700 points.addAll(modifiedPoints);
695 if (mode == MODE_ROADS)
701 if (cw.isRoad())
696702 modifiedRoads.put(way.getId(), cw);
697703 if (DEBUG_PATH != null && (draw || cw.isRoundabout())) {
698704 GpxCreator.createGpx(Utils.joinPath(DEBUG_PATH, way.getId() + "_dpmod"), points, removedInWay);
700706 }
701707 }
702708 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"),
704710 bbox.toCoords(), new ArrayList<>(obsoletePoints));
705711 }
706 log.info("Removed", numPointsRemoved, "obsolete points in lines");
712 log.info("Removed", numPointsRemoved, "obsolete points from lines");
707713 }
708714
709715 /**
767773 if (hasBadAngles)
768774 badWays.add(cw);
769775 }
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));
771777 writeOSM(name, badWays);
772778 }
773779
774780 /**
775 * Check if the point can safely be removed from a road.
781 * Check if the point can safely be removed.
776782 * @param p
777783 * @return true if remove is okay
778784 */
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())
781787 return false;
782 if (mode == MODE_LINES && p.isEndOfWay())
788 if (p instanceof CoordPOI && ((CoordPOI) p).isUsed())
783789 return false;
784 if (p instanceof CoordPOI && ((CoordPOI) p).isUsed()){
785 return false;
786 }
790
787791 return p.getHighwayCount() < 2 && !p.isViaNodeOfRestriction();
788792 }
789793
891895 toRepl.setReplaced(true);
892896 if (toRepl instanceof CoordPOI) {
893897 CoordPOI cp = (CoordPOI) toRepl;
894 if (cp.isUsed()) {
898 if (cp.isUsed() ) {
895899 replacement = new CoordPOI(replacement);
896900 ((CoordPOI) replacement).setNode(cp.getNode());
897901 ((CoordPOI) replacement).setUsed(true);
974978 List<Coord> altPositions = currentCenter.getAlternativePositions();
975979 for (Coord altCenter : altPositions) {
976980 if (dupCheck.contains(altCenter)) {
977 log.debug("pass", pass, "roads=" + (mode == MODE_ROADS),
981 log.debug("pass", pass,
978982 "extra move to remove spike or overlap near", currentCenter.toDegreeString());
979983 replaceCoord(currentCenter, altCenter, replacements);
980984 return true;
12901294
12911295 @SuppressWarnings("unused")
12921296 private void createGPX(String gpxName, Map<Coord, Coord> replacements) {
1293 if (gpxName == null || DEBUG_PATH == null)
1297 if (DEBUG_PATH == null || gpxName == null)
12941298 return;
12951299 if (gpxName.isEmpty())
12961300 gpxName = Utils.joinPath(DEBUG_PATH, id + "_no_info");
14041408 * @param convertedWays list of ways to filter
14051409 * @param modifiedRoads if ways are routable we add the modified ways to this map
14061410 */
1407 private void prepWithDouglasPeucker(List<ConvertedWay> convertedWays, Map<Long, ConvertedWay> modifiedRoads) {
1411 private void prepWithDouglasPeucker(Map<Long, ConvertedWay> modifiedRoads) {
14081412 // we don't want to remove end points of ways
1409 markEndPoints(convertedWays);
1413 markEndPoints(convertedWays, true);
14101414 double maxErrorDistance = 0.05;
14111415 Way lastWay = null;
14121416 for (ConvertedWay cw : convertedWays) {
1413 if (!cw.isValid() || cw.isOverlay() || cw.getWay().equals(lastWay))
1417 if (!cw.isValid() || cw.getWay().equals(lastWay))
14141418 continue;
14151419 Way way = cw.getWay();
14161420 lastWay = way;
14321436 // Simplify the rest
14331437 DouglasPeuckerFilter.douglasPeucker(coords, 0, endIndex, maxErrorDistance);
14341438 if (coords.size() != points.size()) {
1435 if (mode == MODE_ROADS) {
1439 if (cw.isRoad()) {
14361440 modifiedRoads.put(way.getId(), cw);
14371441 }
14381442 points.clear();
14391443 points.addAll(coords);
14401444 }
14411445 }
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) {
14451450 Way lastWay = null;
14461451 for (ConvertedWay cw : convertedWays) {
1447 if (!cw.isValid() || cw.isOverlay() || cw.getWay().equals(lastWay))
1452 if (!cw.isValid() || cw.getWay().equals(lastWay))
14481453 continue;
14491454 Way way = cw.getWay();
14501455 lastWay = way;
1451 way.getFirstPoint().setEndOfWay(true);
1452 way.getLastPoint().setEndOfWay(true);
1456 way.getFirstPoint().setEndOfWay(b);
1457 way.getLastPoint().setEndOfWay(b);
14531458 }
14541459 }
14551460
147147 public void load(String name, boolean addBackground) throws FileNotFoundException {
148148 try (InputStream is = Utils.openFile(name)) {
149149 parse(is, name);
150 } catch (FileNotFoundException e) {
151 throw e;
150152 } catch (IOException e) {
151153 // exception thrown from implicit call to close() on resource variable 'is'
152154 }
5555 import uk.me.parabola.mkgmap.general.ZipCodeInfo;
5656 import uk.me.parabola.mkgmap.reader.MapperBasedMapDataSource;
5757 import uk.me.parabola.mkgmap.reader.osm.FakeIdGenerator;
58 import uk.me.parabola.mkgmap.reader.osm.FeatureKind;
5859 import uk.me.parabola.mkgmap.reader.osm.GType;
5960 import uk.me.parabola.mkgmap.reader.osm.GeneralRelation;
6061 import uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation;
418419 if (type <= 0xff)
419420 type <<= 8;
420421 point.setType(type);
422 checkType(FeatureKind.POINT, point.getType());
421423 } else if (name.equals("SubType")) {
422424 int subtype = Integer.decode(value);
423425 int type = point.getType();
424426 point.setType(type | subtype);
427 checkType(FeatureKind.POINT, point.getType());
425428 } else if (name.startsWith("Data") || name.startsWith("Origin")) {
426429 Coord co = makeCoord(value);
427430 setResolution(point, name);
446449 private void line(String name, String value) {
447450 if (name.equals("Type")) {
448451 polyline.setType(Integer.decode(value));
452 checkType(FeatureKind.POLYLINE, polyline.getType());
449453 } else if (name.startsWith("Data")) {
450454 extractResolution(name);
451455 addLineString(value, false);
584588 if (type == 0x4a00)
585589 type = 0x4a;
586590 shape.setType(type);
591 checkType(FeatureKind.POLYGON, type);
587592 if(type == 0x4b)
588593 havePolygon4B = true;
589594 } else if (name.startsWith("Data")) {
10221027 public String getDefaultRegion() {
10231028 return defaultRegion;
10241029 }
1030
1031 private void checkType(FeatureKind kind, int type) {
1032 if (!GType.checkType(kind, type)) {
1033 throw new MapFailedException("invalid type " + GType.formatType(type) + " for " + kind + ", line " + lineNo);
1034 }
1035 }
1036
10251037 }
0 /*
1 * Copyright (C) 2019.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 3 or
5 * version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful, but
8 * WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * General Public License for more details.
11 */
12 package buildoptions;
13
14 import java.io.BufferedReader;
15 import java.io.BufferedWriter;
16 import java.io.File;
17 import java.io.FileWriter;
18 import java.io.IOException;
19 import java.io.InputStreamReader;
20
21 /**
22 * Convert options.txt to format needed in help file options.
23 * Saves some manual edits to keep both files in sync.
24 * Used with ant gen-options-file
25 * @author Mike Baggaley
26 *
27 */
28 public class OptionsBuilder {
29
30 public static void main(String[] args) {
31 final int indentSize = 4;
32 boolean hasNonAscii = false;
33 int lineNumber = 0;
34 File outputFile = new File(args[0]);
35 System.setProperty("line.separator", "\n");
36 try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
37 BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile))) {
38 String line;
39 String previousLine = "";
40 int indent = 0;
41 boolean preformatted = false;
42 while((line = br.readLine()) != null) {
43 ++lineNumber;
44 if (!line.matches("\\p{ASCII}*")) {
45 System.err.println("Line " + lineNumber + " contains one or more non-ASCII characters.\r\n" + line);
46 hasNonAscii = true;
47 }
48 if (preformatted) {
49 if (line.trim().compareToIgnoreCase("</pre>") == 0)
50 preformatted = false;
51 else {
52 bw.write(line);
53 bw.newLine();
54 }
55 } else {
56 line = line.replaceAll("\\s+", " ");
57 if (line.length() > 0) {
58 if (line.startsWith(";")) {
59 line = line.substring(1);
60 indent = 0;
61 if (!previousLine.isEmpty()) {
62 bw.write(previousLine);
63 bw.newLine();
64 previousLine = "";
65 }
66 }
67 else if (line.charAt(0) == ':') {
68 if (!previousLine.isEmpty()) {
69 bw.write(previousLine);
70 bw.newLine();
71 previousLine = "";
72 }
73 indent = 1;
74 line = line.substring(1);
75 while (line.charAt(0) == ':') {
76 indent++;
77 line = line.substring(1);
78 }
79 if (line.charAt(0) == ';')
80 line = line.substring(1);
81 }
82 else if (line.trim().compareToIgnoreCase("<p>") == 0) {
83 if (!previousLine.isEmpty()) {
84 bw.write(previousLine);
85 bw.newLine();
86 previousLine = "";
87 }
88 line = "";
89 bw.newLine();
90 }
91 else if (line.trim().compareToIgnoreCase("<pre>") == 0) {
92 if (!previousLine.isEmpty()) {
93 bw.write(previousLine);
94 bw.newLine();
95 previousLine = "";
96 }
97 line = "";
98 preformatted = true;
99 }
100 line = line.trim();
101 if (!previousLine.isEmpty()) {
102 if (!line.isEmpty()) {
103 previousLine += " " + line;
104 }
105 } else {
106 previousLine = line;
107 for (int i = 0; i < indent; i++) {
108 for (int j = 0; j < indentSize; j++)
109 previousLine = " " + previousLine;
110 }
111 }
112 while (previousLine.length() > 79) {
113 line = previousLine.substring(0, 80);
114 int lastSpaceIndex = line.lastIndexOf(' ');
115 int firstNonSpaceIndex = 0;
116 while (firstNonSpaceIndex < 79) {
117 if (line.charAt(firstNonSpaceIndex) != ' ')
118 break;
119 firstNonSpaceIndex++;
120 }
121 if (lastSpaceIndex > firstNonSpaceIndex) {
122 line = line.substring(0, lastSpaceIndex);
123 previousLine = previousLine.substring(lastSpaceIndex + 1);
124 for (int i = 0; i < indent; i++) {
125 for (int j = 0; j < indentSize; j++)
126 previousLine = " " + previousLine;
127 }
128 bw.write(line);
129 bw.newLine();
130 }
131 else {
132 bw.write(previousLine);
133 bw.newLine();
134 previousLine = "";
135 }
136 }
137 }
138 else {
139 indent = 0;
140 if (!previousLine.isEmpty()) {
141 bw.write(previousLine);
142 bw.newLine();
143 previousLine = "";
144 }
145 bw.newLine();
146 }
147 }
148 }
149 if (!previousLine.isEmpty()) {
150 bw.write(previousLine);
151 bw.newLine();
152 }
153 } catch (IOException e) {
154 e.printStackTrace();
155 System.exit(-1);
156 }
157 if (hasNonAscii) {
158 System.exit(-1);
159 }
160 }
161
162 }