diff --git a/src/uk/me/parabola/mkgmap/main/MapMaker.java b/src/uk/me/parabola/mkgmap/main/MapMaker.java index b7245bf..9a1e6a9 100644 --- a/src/uk/me/parabola/mkgmap/main/MapMaker.java +++ b/src/uk/me/parabola/mkgmap/main/MapMaker.java @@ -17,6 +17,11 @@ package uk.me.parabola.mkgmap.main; import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.HashSet; +import java.util.HashMap; import uk.me.parabola.imgfmt.ExitException; import uk.me.parabola.imgfmt.FileExistsException; @@ -30,6 +35,11 @@ import uk.me.parabola.mkgmap.build.MapBuilder; import uk.me.parabola.mkgmap.general.LoadableMapDataSource; import uk.me.parabola.mkgmap.reader.plugin.MapReader; +import uk.me.parabola.imgfmt.app.Coord; +import uk.me.parabola.mkgmap.general.MapLine; +import uk.me.parabola.mkgmap.general.MapPoint; +import uk.me.parabola.mkgmap.general.MapRoad; + /** * Main routine for the command line map-making utility. * @@ -123,6 +133,165 @@ public class MapMaker implements MapProcessor { LoadableMapDataSource src = MapReader.createMapReader(name); src.config(args.getProperties()); src.load(name); + makeRoadNamePOIS(args, src); return src; } + + void makeRoadNamePOIS(CommandArgs args, LoadableMapDataSource src) { + String rnp = args.getProperties().getProperty("road-name-pois", null); + // are road name POIS wanted? + if(rnp != null) { + int rnpt = 0x640a; // Garmin type 'Locale' + rnp = rnp.toUpperCase(); + if(rnp.length() > 0) { + // override type code + rnpt = Integer.decode(rnp); + } + // collect lists of roads that have the same name + java.util.Map> namedRoads = new HashMap>(); + for(MapLine l : src.getLines()) { + // isRoad() needed until OSM loader marks lines as roads + if(l.isRoad() || isRoad(l)) { + String ln = l.getName(); + if(ln != null) { + List rl = namedRoads.get(ln); + if(rl == null) { + rl = new ArrayList(); + namedRoads.put(ln, rl); + } + rl.add(l); + } + } + } + + // generate a POI for each named road + for(List lr : findConnectedRoadsWithSameName(namedRoads)) { + // connected roads are not ordered so just use first in list + src.getPoints().add(makeRoadNamePOI(lr.get(0), rnpt)); + } + } + } + + private boolean roadsAreJoined(MapLine r1, MapLine r2) { + + if(r1 != r2) { + for(Coord c1 : r1.getPoints()) { + for(Coord c2 : r2.getPoints()) { + if(c1 == c2) + return true; + } + } + } + return false; + } + + // hairy function to build a set of lists - each list contains + // the roads that have the same name and are connected + + private Set> findConnectedRoadsWithSameName(java.util.Map> namedRoads) { + // roadGroups is a set to avoid duplicate groups + Set> roadGroups = new HashSet>(); + + // loop over the lists of roads that have the same name + for(List allRoadsWithSameName : namedRoads.values()) { + // for each road that has the same name, keep track of its group + java.util.Map> roadGroupMap = new HashMap>(); + + // loop over all of the roads with the same name + for(int i = 0; i < allRoadsWithSameName.size(); ++i) { + boolean roadWasJoined = false; + for(int j = 0; j < allRoadsWithSameName.size(); ++j) { + if(i != j) { + // see if these two roads are joined + MapLine ri = allRoadsWithSameName.get(i); + MapLine rj = allRoadsWithSameName.get(j); + if(roadsAreJoined(ri, rj)) { + // yes, the're joined so put both in a group + // and associate the group with each road + roadWasJoined = true; + List groupi = roadGroupMap.get(ri); + List groupj = roadGroupMap.get(rj); + if(groupi == null) { + // ri is not in a group yet + if(groupj == null) { + // neither is rj so make a new group + groupi = new ArrayList(); + groupi.add(ri); + groupi.add(rj); + roadGroupMap.put(ri, groupi); + roadGroupMap.put(rj, groupi); + } + else { + // add ri to groupj + groupj.add(ri); + roadGroupMap.put(ri, groupj); + } + } + else if(groupj == null) { + // add rj to groupi + groupi.add(rj); + roadGroupMap.put(rj, groupi); + } + else if(groupi != groupj) { + // ri and rj are in separate groups so put + // all the roads in groupj into groupi + for(MapLine r : groupj) + roadGroupMap.put(r, groupi); + groupi.addAll(groupj); + } + } + } + } + if(!roadWasJoined) { + // make a group with just one entry + MapLine ri = allRoadsWithSameName.get(i); + Listgroup = new ArrayList(); + group.add(ri); + roadGroupMap.put(ri, group); + } + } + + // now add the new group(s) to the final result + for(List l : roadGroupMap.values()) + roadGroups.add(l); + } + return roadGroups; + } + + private MapPoint makeRoadNamePOI(MapLine road, int type) { + List points = road.getPoints(); + int numPoints = points.size(); + Coord coord; + if((numPoints & 1) != 0) + coord = points.get(numPoints / 2); + else { + int i2 = numPoints / 2; + int i1 = i2 - 1; + coord = new Coord((points.get(i1).getLatitude() + + points.get(i2).getLatitude()) / 2, + (points.get(i1).getLongitude() + + points.get(i2).getLongitude()) / 2); + } + MapPoint rnp = new MapPoint(); + rnp.setName(road.getName()); + rnp.setType(type); + rnp.setLocation(coord); + return rnp; + } + + // this is only needed until OSM reader identifies roads + boolean isRoad(MapLine l) { + switch(l.getType()) { + case 0x01: + case 0x02: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + return true; + + default: + return false; + } + } }