From 5ed52599124c68b5d5532bef80cefc517aa09667 Mon Sep 17 00:00:00 2001 From: afischerdev Date: Thu, 24 Aug 2023 10:04:08 +0200 Subject: [PATCH 1/3] reworked command line start --- .../java/btools/router/RoutingEngine.java | 24 +++- .../src/main/java/btools/server/BRouter.java | 117 ++++++++++-------- 2 files changed, 87 insertions(+), 54 deletions(-) diff --git a/brouter-core/src/main/java/btools/router/RoutingEngine.java b/brouter-core/src/main/java/btools/router/RoutingEngine.java index 59cb4f7..baff3dc 100644 --- a/brouter-core/src/main/java/btools/router/RoutingEngine.java +++ b/brouter-core/src/main/java/btools/router/RoutingEngine.java @@ -158,16 +158,21 @@ public class RoutingEngine extends Thread { switch (engineMode) { case BROUTER_ENGINEMODE_ROUTING: + if (waypoints.size() < 2) { + throw new IllegalArgumentException("we need two lat/lon points at least!"); + } doRouting(maxRunningTime); break; case BROUTER_ENGINEMODE_SEED: /* do nothing, handled the old way */ - break; + throw new IllegalArgumentException("not a valid engine mode"); case BROUTER_ENGINEMODE_GETELEV: + if (waypoints.size() < 1) { + throw new IllegalArgumentException("we need one lat/lon point at least!"); + } doGetElev(); break; default: - doRouting(maxRunningTime); - break; + throw new IllegalArgumentException("not a valid engine mode"); } } @@ -201,6 +206,9 @@ public class RoutingEngine extends Thread { continue; } oldTrack = null; + track.exportWaypoints = routingContext.exportWaypoints; + // doesn't work at the moment + // use routingContext.outputFormat track.writeGpx(filename); foundTrack = track; alternativeIndex = i; @@ -300,8 +308,16 @@ public class RoutingEngine extends Thread { OsmNodeNamed n = new OsmNodeNamed(listOne.get(0).crosspoint); n.selev = startNode != null ? startNode.getSElev() : Short.MIN_VALUE; + // doesn't work at the moment + // use routingContext.outputFormat outputMessage = OsmTrack.formatAsGpxWaypoint(n); - + if (outfileBase != null) { + String filename = outfileBase + ".gpx"; + File out = new File(filename); + FileWriter fw = new FileWriter(filename); + fw.write(outputMessage); + fw.close(); + } long endTime = System.currentTimeMillis(); logInfo("execution time = " + (endTime - startTime) / 1000. + " seconds"); } catch (Exception e) { diff --git a/brouter-server/src/main/java/btools/server/BRouter.java b/brouter-server/src/main/java/btools/server/BRouter.java index 90ded9c..dbfff6d 100644 --- a/brouter-server/src/main/java/btools/server/BRouter.java +++ b/brouter-server/src/main/java/btools/server/BRouter.java @@ -2,72 +2,55 @@ package btools.server; import java.io.BufferedOutputStream; import java.io.DataOutputStream; +import java.io.File; import java.io.FileOutputStream; import java.net.URLDecoder; import java.util.ArrayList; import java.util.List; -import java.io.File; +import java.util.Map; import btools.router.OsmNodeNamed; import btools.router.OsmTrack; import btools.router.RoutingContext; import btools.router.RoutingEngine; +import btools.router.RoutingParamCollector; import btools.router.SearchBoundary; public class BRouter { public static void main(String[] args) throws Exception { - if (args.length == 2) { // cgi-input-mode + if (args.length == 3) { // cgi-input-mode try { - String queryString = args[1]; - int sepIdx = queryString.indexOf('='); - if (sepIdx >= 0) queryString = queryString.substring(sepIdx + 1); + System.setProperty("segmentBaseDir", args[0]); + System.setProperty("profileBaseDir", args[1]); + String queryString = args[2]; + queryString = URLDecoder.decode(queryString, "ISO-8859-1"); - int ntokens = 1; - for (int ic = 0; ic < queryString.length(); ic++) { - if (queryString.charAt(ic) == '_') ntokens++; - } - String[] a2 = new String[ntokens + 1]; - int idx = 1; - int pos = 0; - for (; ; ) { - int p = queryString.indexOf('_', pos); - if (p < 0) { - a2[idx++] = queryString.substring(pos); - break; - } - a2[idx++] = queryString.substring(pos, p); - pos = p + 1; - } + + int lonIdx = queryString.indexOf("lonlats="); + int sepIdx = queryString.indexOf("&", lonIdx); + String lonlats = queryString.substring(lonIdx+8, sepIdx); + + RoutingContext rc = new RoutingContext(); + RoutingParamCollector routingParamCollector = new RoutingParamCollector(); + List wplist = routingParamCollector.getWayPointList(lonlats); + + Map params = routingParamCollector.getUrlParams(queryString); + routingParamCollector.setParams(rc, wplist, params); // cgi-header System.out.println("Content-type: text/plain"); System.out.println(); - OsmNodeNamed from = readPosition(a2, 1, "from"); - OsmNodeNamed to = readPosition(a2, 3, "to"); - int airDistance = from.calcDistance(to); - - String airDistanceLimit = System.getProperty("airDistanceLimit"); - if (airDistanceLimit != null) { - int maxKm = Integer.parseInt(airDistanceLimit); - if (airDistance > maxKm * 1000) { - System.out.println("airDistance " + (airDistance / 1000) + "km exceeds limit for online router (" + maxKm + "km)"); - return; - } - } - long maxRunningTime = 60000; // the cgi gets a 1 Minute timeout String sMaxRunningTime = System.getProperty("maxRunningTime"); if (sMaxRunningTime != null) { maxRunningTime = Integer.parseInt(sMaxRunningTime) * 1000; } - List wplist = new ArrayList<>(); - wplist.add(from); - wplist.add(to); - RoutingEngine re = new RoutingEngine(null, null, new File(args[0]), wplist, readRoutingContext(a2)); + RoutingEngine re = new RoutingEngine(null, null, new File(args[0]), wplist, rc); + re.doRun(maxRunningTime); if (re.getErrorMessage() != null) { System.out.println(re.getErrorMessage()); @@ -78,15 +61,17 @@ public class BRouter { System.exit(0); } System.out.println("BRouter " + OsmTrack.version + " / " + OsmTrack.versionDate); - if (args.length < 6) { + if (args.length < 5) { System.out.println("Find routes in an OSM map"); - System.out.println("usage: java -jar brouter.jar "); - return; + System.out.println("usage: java -jar brouter.jar [parameter-list] [profile-parameter-list] "); + System.out.println(" or: java -cp %CLASSPATH% btools.server.BRouter > [parameter-list] [profile-parameter-list]"); + System.out.println(" or: java -jar brouter.jar "); + System.exit(0); } - List wplist = new ArrayList<>(); - wplist.add(readPosition(args, 1, "from")); RoutingEngine re = null; if ("seed".equals(args[3])) { + List wplist = new ArrayList<>(); + wplist.add(readPosition(args, 1, "from")); int searchRadius = Integer.parseInt(args[4]); // if = 0 search a 5x5 square String filename = SearchBoundary.getFileName(wplist.get(0)); @@ -108,14 +93,46 @@ public class BRouter { } dos.close(); } else { - wplist.add(readPosition(args, 3, "to")); - RoutingContext rc = readRoutingContext(args); - re = new RoutingEngine("mytrack", "mylog", new File(args[0]), wplist, rc); - re.doRun(0); + int engineMode = 0; + try { + engineMode = Integer.parseInt(args[2]); + } catch (NumberFormatException e) { + } - } - if (re.getErrorMessage() != null) { - System.out.println(re.getErrorMessage()); + RoutingParamCollector routingParamCollector = new RoutingParamCollector(); + List wplist = routingParamCollector.getWayPointList(args[4]); + + System.setProperty("segmentBaseDir", args[0]); + System.setProperty("profileBaseDir", args[1]); + String moreParams = null; + String profileParams = null; + if (args.length >= 6) { + moreParams = args[5]; + } + if (args.length == 7) { + profileParams = args[6]; + } + + RoutingContext rc = new RoutingContext(); + rc.localFunction = args[3]; + if (moreParams != null) { + Map params = routingParamCollector.getUrlParams(moreParams); + routingParamCollector.setParams(rc, wplist, params); + } + if (profileParams != null) { + Map params = routingParamCollector.getUrlParams(profileParams); + routingParamCollector.setProfileParams(rc, params); + } + try { + if (engineMode==RoutingEngine.BROUTER_ENGINEMODE_GETELEV) { + re = new RoutingEngine("testinfo", null, new File(args[0]), wplist, rc, engineMode); + } else { + re = new RoutingEngine("testtrack", null, new File(args[0]), wplist, rc, engineMode); + } + re.doRun(0); + } catch (Exception e) { + System.out.println(e.getMessage()); + } } } From ed7f473556710b5fcaac887c679cf37ef6d81553 Mon Sep 17 00:00:00 2001 From: afischerdev Date: Thu, 24 Aug 2023 10:05:38 +0200 Subject: [PATCH 2/3] added one place for parameter --- .../java/btools/router/RoutingContext.java | 3 + .../btools/router/RoutingParamCollector.java | 337 ++++++++++++++++++ 2 files changed, 340 insertions(+) create mode 100644 brouter-core/src/main/java/btools/router/RoutingParamCollector.java diff --git a/brouter-core/src/main/java/btools/router/RoutingContext.java b/brouter-core/src/main/java/btools/router/RoutingContext.java index 43bd733..0cf8adf 100644 --- a/brouter-core/src/main/java/btools/router/RoutingContext.java +++ b/brouter-core/src/main/java/btools/router/RoutingContext.java @@ -216,6 +216,9 @@ public final class RoutingContext { public boolean inverseRouting; public boolean showTime; + public String outputFormat = "gpx"; + public boolean exportWaypoints = false; + public OsmPrePath firstPrePath; public int turnInstructionMode; // 0=none, 1=auto, 2=locus, 3=osmand, 4=comment-style, 5=gpsies-style diff --git a/brouter-core/src/main/java/btools/router/RoutingParamCollector.java b/brouter-core/src/main/java/btools/router/RoutingParamCollector.java new file mode 100644 index 0000000..adc08fe --- /dev/null +++ b/brouter-core/src/main/java/btools/router/RoutingParamCollector.java @@ -0,0 +1,337 @@ +package btools.router; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +public class RoutingParamCollector { + + final static boolean DEBUG = false; + + /** + * get a list of points and optional extra info for the points + * @param lonLats - linked list separated by ';' or '|' + * @return - a list + */ + public List getWayPointList(String lonLats) { + if (lonLats == null) throw new IllegalArgumentException("lonlats parameter not set"); + + String[] coords = lonLats.split(";|\\|"); // use both variantes + if (coords.length < 1 || !coords[0].contains(",")) + throw new IllegalArgumentException("we need one lat/lon point at least!"); + + List wplist = new ArrayList<>(); + for (int i = 0; i < coords.length; i++) { + String[] lonLat = coords[i].split(","); + if (lonLat.length < 1) + throw new IllegalArgumentException("we need one lat/lon point at least!"); + wplist.add(readPosition(lonLat[0], lonLat[1], "via" + i)); + if (lonLat.length > 2) { + if (lonLat[2].equals("d")) { + wplist.get(wplist.size() - 1).direct = true; + } else { + wplist.get(wplist.size() - 1).name = lonLat[2]; + } + } + } + + if (wplist.get(0).name.startsWith("via")) wplist.get(0).name = "from"; + if (wplist.get(wplist.size() - 1).name.startsWith("via")) { + wplist.get(wplist.size() - 1).name = "to"; + } + + return wplist; + } + + /** + * get a list of points (old style, positions only) + * @param lons - array with longitudes + * @param lats - array with latitudes + * @return - a list + */ + public List readPositions(double[] lons, double[] lats) { + List wplist = new ArrayList<>(); + + if (lats == null || lats.length < 2 || lons == null || lons.length < 2) { + return wplist; + } + + for (int i = 0; i < lats.length && i < lons.length; i++) { + OsmNodeNamed n = new OsmNodeNamed(); + n.name = "via" + i; + n.ilon = (int) ((lons[i] + 180.) * 1000000. + 0.5); + n.ilat = (int) ((lats[i] + 90.) * 1000000. + 0.5); + wplist.add(n); + } + + if (wplist.get(0).name.startsWith("via")) wplist.get(0).name = "from"; + if (wplist.get(wplist.size() - 1).name.startsWith("via")) { + wplist.get(wplist.size() - 1).name = "to"; + } + + return wplist; + } + + private OsmNodeNamed readPosition(String vlon, String vlat, String name) { + if (vlon == null) throw new IllegalArgumentException("lon " + name + " not found in input"); + if (vlat == null) throw new IllegalArgumentException("lat " + name + " not found in input"); + + return readPosition(Double.parseDouble(vlon), Double.parseDouble(vlat), name); + } + + private OsmNodeNamed readPosition(double lon, double lat, String name) { + OsmNodeNamed n = new OsmNodeNamed(); + n.name = name; + n.ilon = (int) ((lon + 180.) * 1000000. + 0.5); + n.ilat = (int) ((lat + 90.) * 1000000. + 0.5); + return n; + } + + /** + * read a url like parameter list linked with '&' + * @param url - parameter list + * @return - a hashmap of the parameter + * @throws UnsupportedEncodingException + */ + public Map getUrlParams(String url) throws UnsupportedEncodingException { + HashMap params = new HashMap<>(); + String decoded = URLDecoder.decode(url, "UTF-8"); + StringTokenizer tk = new StringTokenizer(decoded, "?&"); + while (tk.hasMoreTokens()) { + String t = tk.nextToken(); + StringTokenizer tk2 = new StringTokenizer(t, "="); + if (tk2.hasMoreTokens()) { + String key = tk2.nextToken(); + if (tk2.hasMoreTokens()) { + String value = tk2.nextToken(); + params.put(key, value); + } + } + } + return params; + } + + /** + * fill a parameter map into the routing context + * @param rctx - the context + * @param wplist - the list of way points needed for 'straight' parameter + * @param params - the list of parameters + */ + public void setParams(RoutingContext rctx, List wplist, Map params) { + if (params != null) { + if (params.size() == 0) return; + + // prepare nogos extra + if (params.containsKey("profile")) { + rctx.localFunction = params.get("profile"); + } + if (params.containsKey("nogoLats")) { + List nogoList = readNogos(params.get("nogoLons"), params.get("nogoLats"), params.get("nogoRadi")); + if (nogoList != null) { + RoutingContext.prepareNogoPoints(nogoList); + rctx.nogopoints = nogoList; + } + params.remove("nogoLats"); + params.remove("nogoLons"); + params.remove("nogoRadi"); + } + if (params.containsKey("nogos")) { + List nogoList = readNogoList(params.get("nogos")); + if (nogoList != null) { + RoutingContext.prepareNogoPoints(nogoList); + rctx.nogopoints = nogoList; + } + params.remove("nogos"); + } + if (params.containsKey("polylines")) { + List result = new ArrayList<>(); + parseNogoPolygons(params.get("polylines"), result, false); + if (rctx.nogopoints == null) { + rctx.nogopoints = result; + } else { + rctx.nogopoints.addAll(result); + } + params.remove("polylines"); + } + if (params.containsKey("polygons")) { + List result = new ArrayList<>(); + parseNogoPolygons(params.get("polygons"), result, true); + if (rctx.nogopoints == null) { + rctx.nogopoints = result; + } else { + rctx.nogopoints.addAll(result); + } + params.remove("polygons"); + } + + for (Map.Entry e : params.entrySet()) { + String key = e.getKey(); + String value = e.getValue(); + if (DEBUG) System.out.println("params " + key + " " + value); + + if (key.equals("straight")) { + try { + String[] sa = value.split(","); + for (int i = 0; i < sa.length; i++) { + int v = Integer.parseInt(sa[i]); + if (wplist.size() > v) wplist.get(v).direct = true; + } + } catch (Exception ex) { + System.err.println("error " + ex.getStackTrace()[0].getLineNumber() + " " + ex.getStackTrace()[0] + "\n" + ex); + } + } else if (key.equals("pois")) { + rctx.poipoints = readPoisList(value); + } else if (key.equals("heading")) { + rctx.startDirection = Integer.valueOf(value); + rctx.forceUseStartDirection = true; + } else if (key.equals("direction")) { + rctx.startDirection = Integer.valueOf(value); + } else if (key.equals("alternativeidx")) { + rctx.setAlternativeIdx(Integer.parseInt(value)); + } else if (key.equals("turnInstructionMode")) { + rctx.turnInstructionMode = Integer.parseInt(value); + } else if (key.equals("timode")) { + rctx.turnInstructionMode = Integer.parseInt(value); + } else if (key.equals("exportWaypoints")) { + rctx.exportWaypoints = (Integer.parseInt(value) == 1); + } else if (key.equals("format")) { + rctx.outputFormat = ((String) value).toLowerCase(); + } else if (key.equals("trackFormat")) { + rctx.outputFormat = ((String) value).toLowerCase(); + } else if (key.startsWith("profile:")) { + if (rctx.keyValues == null) rctx.keyValues = new HashMap<>(); + rctx.keyValues.put(key.substring(8), value); + } + // ignore other params + } + } + } + + /** + * fill profile parameter list + * @param rctx - the routing context + * @param params - the list of parameters + */ + public void setProfileParams(RoutingContext rctx, Map params) { + if (params != null) { + if (params.size() == 0) return; + if (rctx.keyValues == null) rctx.keyValues = new HashMap<>(); + for (Map.Entry e : params.entrySet()) { + String key = e.getKey(); + String value = e.getValue(); + if (DEBUG) System.out.println("params " + key + " " + value); + rctx.keyValues.put(key, value); + } + } + } + + private void parseNogoPolygons(String polygons, List result, boolean closed) { + if (polygons != null) { + String[] polygonList = polygons.split("\\|"); + for (int i = 0; i < polygonList.length; i++) { + String[] lonLatList = polygonList[i].split(","); + if (lonLatList.length > 1) { + OsmNogoPolygon polygon = new OsmNogoPolygon(closed); + int j; + for (j = 0; j < 2 * (lonLatList.length / 2) - 1; ) { + String slon = lonLatList[j++]; + String slat = lonLatList[j++]; + int lon = (int) ((Double.parseDouble(slon) + 180.) * 1000000. + 0.5); + int lat = (int) ((Double.parseDouble(slat) + 90.) * 1000000. + 0.5); + polygon.addVertex(lon, lat); + } + + String nogoWeight = "NaN"; + if (j < lonLatList.length) { + nogoWeight = lonLatList[j]; + } + polygon.nogoWeight = Double.parseDouble(nogoWeight); + if (polygon.points.size() > 0) { + polygon.calcBoundingCircle(); + result.add(polygon); + } + } + } + } + } + + public List readPoisList(String pois) { + // lon,lat,name|... + if (pois == null) return null; + + String[] lonLatNameList = pois.split("\\|"); + + List poisList = new ArrayList<>(); + for (int i = 0; i < lonLatNameList.length; i++) { + String[] lonLatName = lonLatNameList[i].split(","); + + if (lonLatName.length != 3) + continue; + + OsmNodeNamed n = new OsmNodeNamed(); + n.ilon = (int) ((Double.parseDouble(lonLatName[0]) + 180.) * 1000000. + 0.5); + n.ilat = (int) ((Double.parseDouble(lonLatName[1]) + 90.) * 1000000. + 0.5); + n.name = lonLatName[2]; + poisList.add(n); + } + + return poisList; + } + + public List readNogoList(String nogos) { + // lon,lat,radius[,weight]|... + + if (nogos == null) return null; + + String[] lonLatRadList = nogos.split("\\|"); + + List nogoList = new ArrayList<>(); + for (int i = 0; i < lonLatRadList.length; i++) { + String[] lonLatRad = lonLatRadList[i].split(","); + String nogoWeight = "NaN"; + if (lonLatRad.length > 3) { + nogoWeight = lonLatRad[3]; + } + nogoList.add(readNogo(lonLatRad[0], lonLatRad[1], lonLatRad[2], nogoWeight)); + } + + return nogoList; + } + + public List readNogos(String nogoLons, String nogoLats, String nogoRadi) { + if (nogoLons == null || nogoLats == null || nogoRadi == null) return null; + List nogoList = new ArrayList<>(); + + String[] lons = nogoLons.split(","); + String[] lats = nogoLats.split(","); + String[] radi = nogoRadi.split(","); + String nogoWeight = "undefined"; + for (int i = 0; i < lons.length && i < lats.length && i < radi.length; i++) { + OsmNodeNamed n = readNogo(lons[i].trim(), lats[i].trim(), radi[i].trim(), nogoWeight); + nogoList.add(n); + } + return nogoList; + } + + + private OsmNodeNamed readNogo(String lon, String lat, String radius, String nogoWeight) { + double weight = "undefined".equals(nogoWeight) ? Double.NaN : Double.parseDouble(nogoWeight); + return readNogo(Double.parseDouble(lon), Double.parseDouble(lat), (int) Double.parseDouble(radius), weight); + } + + private OsmNodeNamed readNogo(double lon, double lat, int radius, double nogoWeight) { + OsmNodeNamed n = new OsmNodeNamed(); + n.name = "nogo" + radius; + n.ilon = (int) ((lon + 180.) * 1000000. + 0.5); + n.ilat = (int) ((lat + 90.) * 1000000. + 0.5); + n.isNogo = true; + n.nogoWeight = nogoWeight; + return n; + } + + +} From 6b3cfb4c9141403f17db71402aad6c6d887072c5 Mon Sep 17 00:00:00 2001 From: afischerdev Date: Thu, 24 Aug 2023 10:06:00 +0200 Subject: [PATCH 3/3] added a parameter test --- .../java/btools/router/RouteParamTest.java | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 brouter-core/src/test/java/btools/router/RouteParamTest.java diff --git a/brouter-core/src/test/java/btools/router/RouteParamTest.java b/brouter-core/src/test/java/btools/router/RouteParamTest.java new file mode 100644 index 0000000..4a27d2c --- /dev/null +++ b/brouter-core/src/test/java/btools/router/RouteParamTest.java @@ -0,0 +1,68 @@ +package btools.router; + +import org.junit.Assert; +import org.junit.Test; + +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +public class RouteParamTest { + + @Test(expected = IllegalArgumentException.class) + public void readWptsNull() { + + RoutingParamCollector rpc = new RoutingParamCollector(); + List map = rpc.getWayPointList(null); + + Assert.assertEquals("result content null", 0, map.size()); + + } + + @Test + public void readWpts() { + String data = "1.0,1.2;2.0,2.2"; + RoutingParamCollector rpc = new RoutingParamCollector(); + List map = rpc.getWayPointList(data); + + Assert.assertEquals("result content 1 ", 2, map.size()); + + data = "1.0,1.1|2.0,2.2|3.0,3.3"; + map = rpc.getWayPointList(data); + + Assert.assertEquals("result content 2 ", 3, map.size()); + + data = "1.0,1.2,Name;2.0,2.2"; + map = rpc.getWayPointList(data); + + Assert.assertEquals("result content 3 ", "Name", map.get(0).name); + + data = "1.0,1.2,d;2.0,2.2"; + map = rpc.getWayPointList(data); + + Assert.assertTrue("result content 4 ", map.get(0).direct); + } + + @Test + public void readUrlParams() throws UnsupportedEncodingException { + String url = "lonlats=1,1;2,2&profile=test&more=1"; + RoutingParamCollector rpc = new RoutingParamCollector(); + Map map = rpc.getUrlParams(url); + + Assert.assertEquals("result content ", 3, map.size()); + } + + @Test + public void readParamsFromList() throws UnsupportedEncodingException { + Map params = new HashMap<>(); + params.put("timode", "3"); + RoutingContext rc = new RoutingContext(); + RoutingParamCollector rpc = new RoutingParamCollector(); + rpc.setParams(rc, null, params); + + Assert.assertEquals("result content timode ", 3, rc.turnInstructionMode); + } + +}