diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..0d05009 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,98 @@ +name: Docker + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +on: + schedule: + - cron: '21 9 * * *' + push: + branches: [ "master" ] + # Publish semver tags as releases. + tags: [ 'v*.*.*' ] + pull_request: + branches: [ "master" ] + +env: + # Use docker.io for Docker Hub if empty + REGISTRY: ghcr.io + # github.repository as / + IMAGE_NAME: ${{ github.repository }} + + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + # This is used to complete the identity challenge + # with sigstore/fulcio when running outside of PRs. + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Install the cosign tool except on PR + # https://github.com/sigstore/cosign-installer + - name: Install cosign + if: github.event_name != 'pull_request' + uses: sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 #v3.1.1 + with: + cosign-release: 'v2.1.1' + + # Set up BuildKit Docker container builder to be able to build + # multi-platform images and export cache + # https://github.com/docker/setup-buildx-action + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + + # Login against a Docker registry except on PR + # https://github.com/docker/login-action + - name: Log into registry ${{ env.REGISTRY }} + if: github.event_name != 'pull_request' + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Extract metadata (tags, labels) for Docker + # https://github.com/docker/metadata-action + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # Build and push Docker image with Buildx (don't push on PR) + # https://github.com/docker/build-push-action + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # Sign the resulting Docker image digest except on PRs. + # This will only write to the public Rekor transparency log when the Docker + # repository is public to avoid leaking data. If you would like to publish + # transparency data even for private images, pass --force to cosign below. + # https://github.com/sigstore/cosign + # - name: Sign the published Docker image + # if: ${{ github.event_name != 'pull_request' }} + # env: + # # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable + # TAGS: ${{ steps.meta.outputs.tags }} + # DIGEST: ${{ steps.build-and-push.outputs.digest }} + # # This step uses the identity token to provision an ephemeral certificate + # # against the sigstore community Fulcio instance. + # run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} diff --git a/.github/workflows/gradle-publish.yml b/.github/workflows/gradle-publish.yml index 8ac8cf6..789adfd 100644 --- a/.github/workflows/gradle-publish.yml +++ b/.github/workflows/gradle-publish.yml @@ -4,7 +4,7 @@ name: Gradle Package on: - + workflow_dispatch: release: types: [created] @@ -18,13 +18,13 @@ jobs: packages: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - name: Set up JDK 11 - uses: actions/setup-java@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v4 with: - java-version: '11' - distribution: 'adopt' + java-version: '17' + distribution: 'temurin' server-id: github # Value of the distributionManagement/repository/id field of the pom.xml settings-path: ${{ github.workspace }} # location for the settings.xml file @@ -38,7 +38,7 @@ jobs: ORG_GRADLE_PROJECT_RELEASE_STORE_FILE: ${{ secrets.BROUTER_KEYSTORE_FILE }} ORG_GRADLE_PROJECT_RELEASE_KEY_ALIAS: ${{ secrets.BROUTER_KEY_ALIAS }} ORG_GRADLE_PROJECT_RELEASE_KEY_PASSWORD: ${{ secrets.BROUTER_KEY_PASSWORD }} - ORG_GRADLE_PROJECT_RELEASE_STORE_PASSWORD: ${{ secrets.BROUTER_STORE_PASSWORD }} + ORG_GRADLE_PROJECT_RELEASE_STORE_PASSWORD: ${{ secrets.BROUTER_STORE_PASSWORD }} run: gradle build # The USERNAME and TOKEN need to correspond to the credentials environment variables used in diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index c2c9b86..952ae9d 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -15,12 +15,12 @@ jobs: runs-on: ubuntu-latest environment: BRouter steps: - - uses: actions/checkout@v3 - - name: Set up JDK 11 - uses: actions/setup-java@v3 + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 with: - java-version: '11' - distribution: 'zulu' + java-version: '17' + distribution: 'temurin' cache: gradle - name: Create local.properties run: touch local.properties @@ -37,7 +37,7 @@ jobs: ORG_GRADLE_PROJECT_RELEASE_STORE_PASSWORD: ${{ secrets.BROUTER_STORE_PASSWORD }} run: ./gradlew build - name: Upload ZIP - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ZIP path: brouter-server/build/distributions/brouter-*.zip diff --git a/.gitignore b/.gitignore index ddd96e8..4a88787 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.iml .gradle .idea/ +build /local.properties /.idea/caches /.idea/gradle.xml @@ -10,7 +11,6 @@ /.idea/navEditor.xml /.idea/assetWizardSettings.xml .DS_Store -/build /captures .externalNativeBuild .cxx diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2152dc8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +FROM gradle:jdk17-jammy as build + +RUN mkdir /tmp/brouter +WORKDIR /tmp/brouter +COPY . . +RUN ./gradlew clean build + +FROM openjdk:17.0.1-jdk-slim +COPY --from=build /tmp/brouter/brouter-server/build/libs/brouter-*-all.jar /brouter.jar +COPY --from=build /tmp/brouter/misc/scripts/standalone/server.sh /bin/ +COPY --from=build /tmp/brouter/misc/* /profiles2 + +CMD /bin/server.sh + diff --git a/README.md b/README.md index b1fcbc2..cecb125 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,58 @@ The API endpoints exposed by this HTTP server are documented in the [`brouter-server/src/main/java/btools/server/request/ServerHandler.java`](brouter-server/src/main/java/btools/server/request/ServerHandler.java) file. +The server emits log data for each routing request on stdout. For each routing +request a line with the following eight fields is printed. The fields are +separated by whitespace. + +- timestamp, in ISO8601 format, e.g. `2024-05-14T21:11:26.499+02:00` +- current server session count (integer number 1-999) or "new" when a new + IP address is detected +- IP address (IPv4 or IPv6), prefixed by `ip=` +- duration of routing request in ms, prefixed by `ms=` +- divider `->` +- HTTP request method +- HTTP request URL +- HTTP request version + +Example log output: + +``` +2024-05-14T21:11:26.499+02:00 new ip=127.0.0.1 ms=189 -> GET /brouter?lonlats=13.377485,52.516247%7C13.351221,52.515004&profile=trekking&alternativeidx=0&format=geojson HTTP/1.1 +2024-05-14T21:11:33.229+02:00 1 ip=127.0.0.1 ms=65 -> GET /brouter?lonlats=13.377485,52.516247%7C13.351221,52.515004&profile=trekking&alternativeidx=0&format=geojson HTTP/1.1 +``` + +## BRouter with Docker + +To build the Docker image run (in the project's top level directory): + +``` +docker build -t brouter . +``` + +Download the segment files as described in the previous chapter. The folder containing the +segment files can be mounted into the container. Run BRouter as follows: + +``` +docker run --rm \ + -v ./misc/scripts/segments4:/segments4 \ + -p 17777:17777 \ + --name brouter \ + brouter +``` + +This will start brouter with a set of default routing profiles. It will be accessible on port 17777. + +If you want to provide your own routing profiles, you can also mount the folder containing the custom profiles: + +``` +docker run --rm \ + -v ./misc/scripts/segments4:/segments4 \ + -v /path/to/custom/profiles:/profiles2 \ + -p 17777:17777 \ + --name brouter \ + brouter +``` ## Documentation diff --git a/brouter-codec/.gitignore b/brouter-codec/.gitignore deleted file mode 100644 index 84c048a..0000000 --- a/brouter-codec/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build/ diff --git a/brouter-codec/build.gradle b/brouter-codec/build.gradle index 22faffe..7224590 100644 --- a/brouter-codec/build.gradle +++ b/brouter-codec/build.gradle @@ -1,8 +1,7 @@ plugins { - id 'java-library' + id 'brouter.library-conventions' } dependencies { implementation project(':brouter-util') - testImplementation 'junit:junit:4.13.1' } diff --git a/brouter-codec/src/main/AndroidManifest.xml b/brouter-codec/src/main/AndroidManifest.xml deleted file mode 100644 index 152508c..0000000 --- a/brouter-codec/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/brouter-codec/src/main/java/btools/codec/MicroCache2.java b/brouter-codec/src/main/java/btools/codec/MicroCache2.java index c7b6878..a09d19e 100644 --- a/brouter-codec/src/main/java/btools/codec/MicroCache2.java +++ b/brouter-codec/src/main/java/btools/codec/MicroCache2.java @@ -1,6 +1,7 @@ package btools.codec; import java.util.HashMap; +import java.util.Map; import btools.util.ByteDataReader; import btools.util.IByteArrayUnifier; @@ -287,7 +288,7 @@ public final class MicroCache2 extends MicroCache { @Override public int encodeMicroCache(byte[] buffer) { - HashMap idMap = new HashMap<>(); + Map idMap = new HashMap<>(); for (int n = 0; n < size; n++) { // loop over nodes idMap.put(expandId(faid[n]), n); } @@ -418,7 +419,7 @@ public final class MicroCache2 extends MicroCache { nlinks++; if (isInternal) { - int nodeIdx = idx.intValue(); + int nodeIdx = idx; if (dodebug) System.out.println("*** target nodeIdx=" + nodeIdx); if (nodeIdx == n) throw new RuntimeException("ups: self ref?"); nodeIdxDiff.encodeSignedValue(nodeIdx - n); diff --git a/brouter-codec/src/main/java/btools/codec/TagValueCoder.java b/brouter-codec/src/main/java/btools/codec/TagValueCoder.java index 098ea06..f0a3a0f 100644 --- a/brouter-codec/src/main/java/btools/codec/TagValueCoder.java +++ b/brouter-codec/src/main/java/btools/codec/TagValueCoder.java @@ -4,6 +4,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.PriorityQueue; +import java.util.Queue; import btools.util.BitCoderContext; @@ -58,7 +59,7 @@ public final class TagValueCoder { TagValueSet dummy = new TagValueSet(nextTagValueSetId++); identityMap.put(dummy, dummy); } - PriorityQueue queue = new PriorityQueue<>(2 * identityMap.size(), new TagValueSet.FrequencyComparator()); + Queue queue = new PriorityQueue<>(2 * identityMap.size(), new TagValueSet.FrequencyComparator()); queue.addAll(identityMap.values()); while (queue.size() > 1) { TagValueSet node = new TagValueSet(nextTagValueSetId++); diff --git a/brouter-core/.gitignore b/brouter-core/.gitignore deleted file mode 100644 index 84c048a..0000000 --- a/brouter-core/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build/ diff --git a/brouter-core/build.gradle b/brouter-core/build.gradle index cb7dde5..e4c865f 100644 --- a/brouter-core/build.gradle +++ b/brouter-core/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'brouter.library-conventions' } dependencies { @@ -7,7 +7,6 @@ dependencies { implementation project(':brouter-util') implementation project(':brouter-expressions') implementation project(':brouter-codec') - testImplementation 'junit:junit:4.13.2' } // MapcreatorTest generates segments which are used in tests diff --git a/brouter-core/src/main/AndroidManifest.xml b/brouter-core/src/main/AndroidManifest.xml deleted file mode 100644 index 04180cf..0000000 --- a/brouter-core/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/brouter-core/src/main/java/btools/router/FormatCsv.java b/brouter-core/src/main/java/btools/router/FormatCsv.java new file mode 100644 index 0000000..37b7408 --- /dev/null +++ b/brouter-core/src/main/java/btools/router/FormatCsv.java @@ -0,0 +1,43 @@ +package btools.router; + +import java.io.BufferedWriter; +import java.io.StringWriter; + +public class FormatCsv extends Formatter { + + + public FormatCsv(RoutingContext rc) { + super(rc); + } + + @Override + public String format(OsmTrack t) { + try { + StringWriter sw = new StringWriter(); + BufferedWriter bw = new BufferedWriter(sw); + writeMessages(bw, t); + return sw.toString(); + } catch (Exception ex) { + return "Error: " + ex.getMessage(); + } + } + + public void writeMessages(BufferedWriter bw, OsmTrack t) throws Exception { + dumpLine(bw, MESSAGES_HEADER); + for (String m : t.aggregateMessages()) { + dumpLine(bw, m); + } + if (bw != null) + bw.close(); + } + + private void dumpLine(BufferedWriter bw, String s) throws Exception { + if (bw == null) { + System.out.println(s); + } else { + bw.write(s); + bw.write("\n"); + } + } + +} diff --git a/brouter-core/src/main/java/btools/router/FormatGpx.java b/brouter-core/src/main/java/btools/router/FormatGpx.java new file mode 100644 index 0000000..06f8564 --- /dev/null +++ b/brouter-core/src/main/java/btools/router/FormatGpx.java @@ -0,0 +1,534 @@ +package btools.router; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.util.Map; + +import btools.mapaccess.MatchedWaypoint; +import btools.util.StringUtils; + +public class FormatGpx extends Formatter { + public FormatGpx(RoutingContext rc) { + super(rc); + } + + @Override + public String format(OsmTrack t) { + try { + StringWriter sw = new StringWriter(8192); + BufferedWriter bw = new BufferedWriter(sw); + formatAsGpx(bw, t); + bw.close(); + return sw.toString(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public String formatAsGpx(BufferedWriter sb, OsmTrack t) throws IOException { + int turnInstructionMode = t.voiceHints != null ? t.voiceHints.turnInstructionMode : 0; + + sb.append("\n"); + if (turnInstructionMode != 9) { + for (int i = t.messageList.size() - 1; i >= 0; i--) { + String message = t.messageList.get(i); + if (i < t.messageList.size() - 1) + message = "(alt-index " + i + ": " + message + " )"; + if (message != null) + sb.append("\n"); + } + } + + if (turnInstructionMode == 4) { // comment style + sb.append("\n"); + sb.append("\n"); + sb.append("\n"); + } + sb.append("\n"); + } else { + sb.append(" creator=\"BRouter-" + t.version + "\" version=\"1.1\">\n"); + } + if (turnInstructionMode == 9) { + sb.append(" \n"); + sb.append(" ").append(t.name).append("\n"); + sb.append(" \n"); + sb.append(" ").append(t.messageList.get(0)).append("\n"); + if (t.params != null && t.params.size() > 0) { + sb.append(" e : t.params.entrySet()) { + if (i++ != 0) sb.append("&"); + sb.append(e.getKey()).append("=").append(e.getValue()); + } + sb.append("]]>\n"); + } + sb.append(" \n"); + sb.append(" \n"); + } + if (turnInstructionMode == 3 || turnInstructionMode == 8) { // osmand style, cruiser + float lastRteTime = 0; + + sb.append(" \n"); + + float rteTime = t.getVoiceHintTime(0); + StringBuffer first = new StringBuffer(); + // define start point + { + first.append(" \n") + .append(" start\n \n"); + if (rteTime != lastRteTime) { // add timing only if available + double ti = rteTime - lastRteTime; + first.append(" \n"); + lastRteTime = rteTime; + } + first.append(" 0\n \n \n"); + } + if (turnInstructionMode == 8) { + if (t.matchedWaypoints.get(0).direct && t.voiceHints.list.get(0).indexInTrack == 0) { + // has a voice hint do nothing, voice hint will do + } else { + sb.append(first.toString()); + } + } else { + sb.append(first.toString()); + } + + for (int i = 0; i < t.voiceHints.list.size(); i++) { + VoiceHint hint = t.voiceHints.list.get(i); + sb.append(" \n") + .append(" ") + .append(turnInstructionMode == 3 ? hint.getMessageString() : hint.getCruiserMessageString()) + .append("\n \n"); + + rteTime = t.getVoiceHintTime(i + 1); + + if (rteTime != lastRteTime) { // add timing only if available + double ti = rteTime - lastRteTime; + sb.append(" \n"); + lastRteTime = rteTime; + } + sb.append(" ") + .append(turnInstructionMode == 3 ? hint.getCommandString() : hint.getCruiserCommandString()) + .append("\n ").append("" + (int) hint.angle) + .append("\n ").append("" + hint.indexInTrack).append("\n \n \n"); + } + sb.append(" \n") + .append(" destination\n \n"); + sb.append(" \n"); + sb.append(" ").append("" + (t.nodes.size() - 1)).append("\n \n \n"); + + sb.append("\n"); + } + + if (turnInstructionMode == 7) { // old locus style + float lastRteTime = t.getVoiceHintTime(0); + + for (int i = 0; i < t.voiceHints.list.size(); i++) { + VoiceHint hint = t.voiceHints.list.get(i); + sb.append(" ") + .append(hint.selev == Short.MIN_VALUE ? "" : "" + (hint.selev / 4.) + "") + .append("") + .append(hint.getMessageString()) + .append("") + .append("").append("" + hint.distanceToNext).append(""); + float rteTime = t.getVoiceHintTime(i + 1); + if (rteTime != lastRteTime) { // add timing only if available + double ti = rteTime - lastRteTime; + double speed = hint.distanceToNext / ti; + sb.append("").append("" + ti).append("") + .append("").append("" + speed).append(""); + lastRteTime = rteTime; + } + sb.append("").append("" + hint.getLocusAction()).append("") + .append("\n"); + } + } + if (turnInstructionMode == 5) { // gpsies style + for (VoiceHint hint : t.voiceHints.list) { + sb.append(" ") + .append("").append(hint.getMessageString()).append("") + .append("").append(hint.getSymbolString().toLowerCase()).append("") + .append("").append(hint.getSymbolString()).append("") + .append("\n"); + } + } + + if (turnInstructionMode == 6) { // orux style + for (VoiceHint hint : t.voiceHints.list) { + sb.append(" ") + .append(hint.selev == Short.MIN_VALUE ? "" : "" + (hint.selev / 4.) + "") + .append("\n" + + " \n" + + " ").append("" + hint.getOruxAction()) + .append("\n" + + " \n" + + " \n" + + " \n"); + } + } + + for (int i = 0; i <= t.pois.size() - 1; i++) { + OsmNodeNamed poi = t.pois.get(i); + sb.append(" \n") + .append(" ").append(StringUtils.escapeXml10(poi.name)).append("\n") + .append(" \n"); + } + + if (t.exportWaypoints) { + for (int i = 0; i <= t.matchedWaypoints.size() - 1; i++) { + MatchedWaypoint wt = t.matchedWaypoints.get(i); + sb.append(" \n") + .append(" ").append(StringUtils.escapeXml10(wt.name)).append("\n"); + if (i == 0) { + sb.append(" from\n"); + } else if (i == t.matchedWaypoints.size() - 1) { + sb.append(" to\n"); + } else { + sb.append(" via\n"); + } + sb.append(" \n"); + } + } + sb.append(" \n"); + if (turnInstructionMode == 9 + || turnInstructionMode == 2 + || turnInstructionMode == 8 + || turnInstructionMode == 4) { // Locus, comment, cruise, brouter style + sb.append(" ").append(t.name).append("\n"); + sb.append(" ").append(t.voiceHints.getTransportMode()).append("\n"); + } else { + sb.append(" ").append(t.name).append("\n"); + } + + if (turnInstructionMode == 7) { + sb.append(" \n"); + sb.append(" ").append("" + t.voiceHints.getLocusRouteType()).append("\n"); + sb.append(" 1\n"); + sb.append(" \n"); + } + + + // all points + sb.append(" \n"); + String lastway = ""; + boolean bNextDirect = false; + OsmPathElement nn = null; + String aSpeed; + + for (int idx = 0; idx < t.nodes.size(); idx++) { + OsmPathElement n = t.nodes.get(idx); + String sele = n.getSElev() == Short.MIN_VALUE ? "" : "" + n.getElev() + ""; + VoiceHint hint = t.getVoiceHint(idx); + MatchedWaypoint mwpt = t.getMatchedWaypoint(idx); + + if (t.showTime) { + sele += ""; + } + if (turnInstructionMode == 8) { + if (mwpt != null && + !mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) { + sele += "" + mwpt.name + ""; + } + } + boolean bNeedHeader = false; + if (turnInstructionMode == 9) { // trkpt/sym style + + if (hint != null) { + + if (mwpt != null && + !mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) { + sele += "" + mwpt.name + ""; + } + sele += "" + hint.getCruiserMessageString() + ""; + sele += "" + hint.getCommandString(hint.cmd) + ""; + if (mwpt != null) { + sele += "Via"; + } + sele += ""; + if (t.showspeed) { + double speed = 0; + if (nn != null) { + int dist = n.calcDistance(nn); + float dt = n.getTime() - nn.getTime(); + if (dt != 0.f) { + speed = ((3.6f * dist) / dt + 0.5); + } + } + sele += "" + (((int) (speed * 10)) / 10.f) + ""; + } + + sele += "" + hint.getCommandString() + ";" + (int) (hint.distanceToNext) + "," + hint.formatGeometry() + ""; + if (n.message != null && n.message.wayKeyValues != null && !n.message.wayKeyValues.equals(lastway)) { + sele += "" + n.message.wayKeyValues + ""; + lastway = n.message.wayKeyValues; + } + if (n.message != null && n.message.nodeKeyValues != null) { + sele += "" + n.message.nodeKeyValues + ""; + } + sele += ""; + + } + if (idx == 0 && hint == null) { + if (mwpt != null && mwpt.direct) { + sele += "beeline"; + } else { + sele += "start"; + } + sele += "Via"; + + } else if (idx == t.nodes.size() - 1 && hint == null) { + + sele += "end"; + sele += "Via"; + + } else { + if (mwpt != null && hint == null) { + if (mwpt.direct) { + // bNextDirect = true; + sele += "beeline"; + } else { + sele += "" + mwpt.name + ""; + } + sele += "Via"; + bNextDirect = false; + } + } + + + if (hint == null) { + bNeedHeader = (t.showspeed || (n.message != null && n.message.wayKeyValues != null && !n.message.wayKeyValues.equals(lastway))) || + (n.message != null && n.message.nodeKeyValues != null); + if (bNeedHeader) { + sele += ""; + if (t.showspeed) { + double speed = 0; + if (nn != null) { + int dist = n.calcDistance(nn); + float dt = n.getTime() - nn.getTime(); + if (dt != 0.f) { + speed = ((3.6f * dist) / dt + 0.5); + } + } + sele += "" + (((int) (speed * 10)) / 10.f) + ""; + } + if (n.message != null && n.message.wayKeyValues != null && !n.message.wayKeyValues.equals(lastway)) { + sele += "" + n.message.wayKeyValues + ""; + lastway = n.message.wayKeyValues; + } + if (n.message != null && n.message.nodeKeyValues != null) { + sele += "" + n.message.nodeKeyValues + ""; + } + sele += ""; + } + } + } + + if (turnInstructionMode == 2) { // locus style new + if (hint != null) { + if (mwpt != null) { + if (!mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) { + sele += "" + mwpt.name + ""; + } + if (mwpt.direct && bNextDirect) { + sele += "" + hint.getLocusSymbolString() + "pass_placeShaping"; + // bNextDirect = false; + } else if (mwpt.direct) { + if (idx == 0) + sele += "pass_placeVia"; + else + sele += "pass_placeShaping"; + bNextDirect = true; + } else if (bNextDirect) { + sele += "beeline" + hint.getLocusSymbolString() + "Shaping"; + bNextDirect = false; + } else { + sele += "" + hint.getLocusSymbolString() + "Via"; + } + } else { + sele += "" + hint.getLocusSymbolString() + ""; + } + } else { + if (idx == 0 && hint == null) { + + int pos = sele.indexOf(""; + if (mwpt != null && mwpt.direct) { + bNextDirect = true; + } + sele += "pass_place"; + sele += "Via"; + + } else if (idx == t.nodes.size() - 1 && hint == null) { + + int pos = sele.indexOf(""; + if (bNextDirect) { + sele += "beeline"; + } + sele += "pass_place"; + sele += "Via"; + + } else { + if (mwpt != null) { + if (!mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) { + sele += "" + mwpt.name + ""; + } + if (mwpt.direct && bNextDirect) { + sele += "beelinepass_placeShaping"; + } else if (mwpt.direct) { + if (idx == 0) + sele += "pass_placeVia"; + else + sele += "pass_placeShaping"; + bNextDirect = true; + } else if (bNextDirect) { + sele += "beelinepass_placeShaping"; + bNextDirect = false; + } else if (mwpt.name.startsWith("via") || + mwpt.name.startsWith("from") || + mwpt.name.startsWith("to")) { + if (bNextDirect) { + sele += "beelinepass_placeShaping"; + } else { + sele += "pass_placeVia"; + } + bNextDirect = false; + } else { + sele += "" + mwpt.name + ""; + sele += "pass_placeVia"; + } + } + } + } + } + sb.append(" ").append(sele).append("\n"); + + nn = n; + } + + sb.append(" \n"); + sb.append(" \n"); + sb.append("\n"); + + return sb.toString(); + } + + public String formatAsWaypoint(OsmNodeNamed n) { + try { + StringWriter sw = new StringWriter(8192); + BufferedWriter bw = new BufferedWriter(sw); + formatGpxHeader(bw); + formatWaypointGpx(bw, n); + formatGpxFooter(bw); + bw.close(); + sw.close(); + return sw.toString(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public void formatGpxHeader(BufferedWriter sb) throws IOException { + sb.append("\n"); + sb.append("\n"); + } + + public void formatGpxFooter(BufferedWriter sb) throws IOException { + sb.append("\n"); + } + + public void formatWaypointGpx(BufferedWriter sb, OsmNodeNamed n) throws IOException { + sb.append(" "); + if (n.getSElev() != Short.MIN_VALUE) { + sb.append("").append("" + n.getElev()).append(""); + } + if (n.name != null) { + sb.append("").append(StringUtils.escapeXml10(n.name)).append(""); + } + if (n.nodeDescription != null && rc != null) { + sb.append("").append(rc.expctxWay.getKeyValueDescription(false, n.nodeDescription)).append(""); + } + sb.append("\n"); + } + + public static String getWaypoint(int ilon, int ilat, String name, String desc) { + return "" + name + "" + (desc != null ? "" + desc + "" : "") + ""; + } + + public OsmTrack read(String filename) throws Exception { + File f = new File(filename); + if (!f.exists()) { + return null; + } + OsmTrack track = new OsmTrack(); + BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(f))); + + for (; ; ) { + String line = br.readLine(); + if (line == null) + break; + + int idx0 = line.indexOf("= 0) { + idx0 = line.indexOf(" lon=\""); + idx0 += 6; + int idx1 = line.indexOf('"', idx0); + int ilon = (int) ((Double.parseDouble(line.substring(idx0, idx1)) + 180.) * 1000000. + 0.5); + int idx2 = line.indexOf(" lat=\""); + if (idx2 < 0) + continue; + idx2 += 6; + int idx3 = line.indexOf('"', idx2); + int ilat = (int) ((Double.parseDouble(line.substring(idx2, idx3)) + 90.) * 1000000. + 0.5); + track.nodes.add(OsmPathElement.create(ilon, ilat, (short) 0, null)); + } + } + br.close(); + return track; + } + +} diff --git a/brouter-core/src/main/java/btools/router/FormatJson.java b/brouter-core/src/main/java/btools/router/FormatJson.java new file mode 100644 index 0000000..a2c5b7a --- /dev/null +++ b/brouter-core/src/main/java/btools/router/FormatJson.java @@ -0,0 +1,246 @@ +package btools.router; + +import java.io.BufferedWriter; +import java.io.StringWriter; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.List; +import java.util.Locale; + +import btools.mapaccess.MatchedWaypoint; +import btools.util.StringUtils; + +public class FormatJson extends Formatter { + + public FormatJson(RoutingContext rc) { + super(rc); + } + + @Override + public String format(OsmTrack t) { + int turnInstructionMode = t.voiceHints != null ? t.voiceHints.turnInstructionMode : 0; + + StringBuilder sb = new StringBuilder(8192); + + sb.append("{\n"); + sb.append(" \"type\": \"FeatureCollection\",\n"); + sb.append(" \"features\": [\n"); + sb.append(" {\n"); + sb.append(" \"type\": \"Feature\",\n"); + sb.append(" \"properties\": {\n"); + sb.append(" \"creator\": \"BRouter-" + t.version + "\",\n"); + sb.append(" \"name\": \"").append(t.name).append("\",\n"); + sb.append(" \"track-length\": \"").append(t.distance).append("\",\n"); + sb.append(" \"filtered ascend\": \"").append(t.ascend).append("\",\n"); + sb.append(" \"plain-ascend\": \"").append(t.plainAscend).append("\",\n"); + sb.append(" \"total-time\": \"").append(t.getTotalSeconds()).append("\",\n"); + sb.append(" \"total-energy\": \"").append(t.energy).append("\",\n"); + sb.append(" \"cost\": \"").append(t.cost).append("\",\n"); + if (t.voiceHints != null && !t.voiceHints.list.isEmpty()) { + sb.append(" \"voicehints\": [\n"); + for (VoiceHint hint : t.voiceHints.list) { + sb.append(" ["); + sb.append(hint.indexInTrack); + sb.append(',').append(hint.getJsonCommandIndex()); + sb.append(',').append(hint.getExitNumber()); + sb.append(',').append(hint.distanceToNext); + sb.append(',').append((int) hint.angle); + + // not always include geometry because longer and only needed for comment style + if (turnInstructionMode == 4) { // comment style + sb.append(",\"").append(hint.formatGeometry()).append("\""); + } + + sb.append("],\n"); + } + sb.deleteCharAt(sb.lastIndexOf(",")); + sb.append(" ],\n"); + } + if (t.showSpeedProfile) { // set in profile + List sp = t.aggregateSpeedProfile(); + if (sp.size() > 0) { + sb.append(" \"speedprofile\": [\n"); + for (int i = sp.size() - 1; i >= 0; i--) { + sb.append(" [").append(sp.get(i)).append(i > 0 ? "],\n" : "]\n"); + } + sb.append(" ],\n"); + } + } + // ... traditional message list + { + sb.append(" \"messages\": [\n"); + sb.append(" [\"").append(MESSAGES_HEADER.replaceAll("\t", "\", \"")).append("\"],\n"); + for (String m : t.aggregateMessages()) { + sb.append(" [\"").append(m.replaceAll("\t", "\", \"")).append("\"],\n"); + } + sb.deleteCharAt(sb.lastIndexOf(",")); + sb.append(" ],\n"); + } + + if (t.getTotalSeconds() > 0) { + sb.append(" \"times\": ["); + DecimalFormat decimalFormat = (DecimalFormat) NumberFormat.getInstance(Locale.ENGLISH); + decimalFormat.applyPattern("0.###"); + for (OsmPathElement n : t.nodes) { + sb.append(decimalFormat.format(n.getTime())).append(","); + } + sb.deleteCharAt(sb.lastIndexOf(",")); + sb.append("]\n"); + } else { + sb.deleteCharAt(sb.lastIndexOf(",")); + } + + sb.append(" },\n"); + + if (t.iternity != null) { + sb.append(" \"iternity\": [\n"); + for (String s : t.iternity) { + sb.append(" \"").append(s).append("\",\n"); + } + sb.deleteCharAt(sb.lastIndexOf(",")); + sb.append(" ],\n"); + } + sb.append(" \"geometry\": {\n"); + sb.append(" \"type\": \"LineString\",\n"); + sb.append(" \"coordinates\": [\n"); + + OsmPathElement nn = null; + for (OsmPathElement n : t.nodes) { + String sele = n.getSElev() == Short.MIN_VALUE ? "" : ", " + n.getElev(); + if (t.showspeed) { // hack: show speed instead of elevation + double speed = 0; + if (nn != null) { + int dist = n.calcDistance(nn); + float dt = n.getTime() - nn.getTime(); + if (dt != 0.f) { + speed = ((3.6f * dist) / dt + 0.5); + } + } + sele = ", " + (((int) (speed * 10)) / 10.f); + } + sb.append(" [").append(formatILon(n.getILon())).append(", ").append(formatILat(n.getILat())) + .append(sele).append("],\n"); + nn = n; + } + sb.deleteCharAt(sb.lastIndexOf(",")); + + sb.append(" ]\n"); + sb.append(" }\n"); + if (t.exportWaypoints || !t.pois.isEmpty()) { + sb.append(" },\n"); + for (int i = 0; i <= t.pois.size() - 1; i++) { + OsmNodeNamed poi = t.pois.get(i); + addFeature(sb, "poi", poi.name, poi.ilat, poi.ilon); + if (i < t.matchedWaypoints.size() - 1) { + sb.append(","); + } + sb.append(" \n"); + } + if (t.exportWaypoints) { + for (int i = 0; i <= t.matchedWaypoints.size() - 1; i++) { + String type; + if (i == 0) { + type = "from"; + } else if (i == t.matchedWaypoints.size() - 1) { + type = "to"; + } else { + type = "via"; + } + + MatchedWaypoint wp = t.matchedWaypoints.get(i); + addFeature(sb, type, wp.name, wp.waypoint.ilat, wp.waypoint.ilon); + if (i < t.matchedWaypoints.size() - 1) { + sb.append(","); + } + sb.append(" \n"); + } + } + } else { + sb.append(" }\n"); + } + sb.append(" ]\n"); + sb.append("}\n"); + + return sb.toString(); + } + + private void addFeature(StringBuilder sb, String type, String name, int ilat, int ilon) { + sb.append(" {\n"); + sb.append(" \"type\": \"Feature\",\n"); + sb.append(" \"properties\": {\n"); + sb.append(" \"name\": \"" + StringUtils.escapeJson(name) + "\",\n"); + sb.append(" \"type\": \"" + type + "\"\n"); + sb.append(" },\n"); + sb.append(" \"geometry\": {\n"); + sb.append(" \"type\": \"Point\",\n"); + sb.append(" \"coordinates\": [\n"); + sb.append(" " + formatILon(ilon) + ",\n"); + sb.append(" " + formatILat(ilat) + "\n"); + sb.append(" ]\n"); + sb.append(" }\n"); + sb.append(" }"); + } + + public String formatAsWaypoint(OsmNodeNamed n) { + try { + StringWriter sw = new StringWriter(8192); + BufferedWriter bw = new BufferedWriter(sw); + addJsonHeader(bw); + addJsonFeature(bw, "info", "wpinfo", n.ilon, n.ilat, n.getElev(), (n.nodeDescription != null ? rc.expctxWay.getKeyValueDescription(false, n.nodeDescription) : null)); + addJsonFooter(bw); + bw.close(); + sw.close(); + return sw.toString(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void addJsonFeature(BufferedWriter sb, String type, String name, int ilon, int ilat, double elev, String desc) { + try { + sb.append(" {\n"); + sb.append(" \"type\": \"Feature\",\n"); + sb.append(" \"properties\": {\n"); + sb.append(" \"creator\": \"BRouter-" + OsmTrack.version + "\",\n"); + sb.append(" \"name\": \"" + StringUtils.escapeJson(name) + "\",\n"); + sb.append(" \"type\": \"" + type + "\""); + if (desc != null) { + sb.append(",\n \"message\": \"" + desc + "\"\n"); + } else { + sb.append("\n"); + } + sb.append(" },\n"); + sb.append(" \"geometry\": {\n"); + sb.append(" \"type\": \"Point\",\n"); + sb.append(" \"coordinates\": [\n"); + sb.append(" " + formatILon(ilon) + ",\n"); + sb.append(" " + formatILat(ilat) + ",\n"); + sb.append(" " + elev + "\n"); + sb.append(" ]\n"); + sb.append(" }\n"); + sb.append(" }\n"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static void addJsonHeader(BufferedWriter sb) { + try { + sb.append("{\n"); + sb.append(" \"type\": \"FeatureCollection\",\n"); + sb.append(" \"features\": [\n"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static void addJsonFooter(BufferedWriter sb) { + try { + sb.append(" ]\n"); + sb.append("}\n"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/brouter-core/src/main/java/btools/router/FormatKml.java b/brouter-core/src/main/java/btools/router/FormatKml.java new file mode 100644 index 0000000..5798c5c --- /dev/null +++ b/brouter-core/src/main/java/btools/router/FormatKml.java @@ -0,0 +1,91 @@ +package btools.router; + +import java.util.List; + +import btools.mapaccess.MatchedWaypoint; +import btools.util.StringUtils; + +public class FormatKml extends Formatter { + public FormatKml(RoutingContext rc) { + super(rc); + } + + @Override + public String format(OsmTrack t) { + StringBuilder sb = new StringBuilder(8192); + + sb.append("\n"); + + sb.append("\n"); + sb.append(" \n"); + sb.append(" KML Samples\n"); + sb.append(" 1\n"); + sb.append(" 3.497064\n"); + sb.append(" 872\n"); + sb.append(" To enable simple instructions add: 'instructions=1' as parameter to the URL\n"); + sb.append(" \n"); + sb.append(" Paths\n"); + sb.append(" 0\n"); + sb.append(" Examples of paths.\n"); + sb.append(" \n"); + sb.append(" Tessellated\n"); + sb.append(" 0\n"); + sb.append(" tag has a value of 1, the line will contour to the underlying terrain]]>\n"); + sb.append(" \n"); + sb.append(" 1\n"); + sb.append(" "); + + for (OsmPathElement n : t.nodes) { + sb.append(formatILon(n.getILon())).append(",").append(formatILat(n.getILat())).append("\n"); + } + + sb.append(" \n"); + sb.append(" \n"); + sb.append(" \n"); + sb.append(" \n"); + if (t.exportWaypoints || !t.pois.isEmpty()) { + if (!t.pois.isEmpty()) { + sb.append(" \n"); + sb.append(" poi\n"); + for (int i = 0; i < t.pois.size(); i++) { + OsmNodeNamed poi = t.pois.get(i); + createPlaceMark(sb, poi.name, poi.ilat, poi.ilon); + } + sb.append(" \n"); + } + + if (t.exportWaypoints) { + int size = t.matchedWaypoints.size(); + createFolder(sb, "start", t.matchedWaypoints.subList(0, 1)); + if (t.matchedWaypoints.size() > 2) { + createFolder(sb, "via", t.matchedWaypoints.subList(1, size - 1)); + } + createFolder(sb, "end", t.matchedWaypoints.subList(size - 1, size)); + } + } + sb.append(" \n"); + sb.append("\n"); + + return sb.toString(); + } + + private void createFolder(StringBuilder sb, String type, List waypoints) { + sb.append(" \n"); + sb.append(" " + type + "\n"); + for (int i = 0; i < waypoints.size(); i++) { + MatchedWaypoint wp = waypoints.get(i); + createPlaceMark(sb, wp.name, wp.waypoint.ilat, wp.waypoint.ilon); + } + sb.append(" \n"); + } + + private void createPlaceMark(StringBuilder sb, String name, int ilat, int ilon) { + sb.append(" \n"); + sb.append(" " + StringUtils.escapeXml10(name) + "\n"); + sb.append(" \n"); + sb.append(" " + formatILon(ilon) + "," + formatILat(ilat) + "\n"); + sb.append(" \n"); + sb.append(" \n"); + } + +} diff --git a/brouter-core/src/main/java/btools/router/Formatter.java b/brouter-core/src/main/java/btools/router/Formatter.java new file mode 100644 index 0000000..09bdb1d --- /dev/null +++ b/brouter-core/src/main/java/btools/router/Formatter.java @@ -0,0 +1,110 @@ +package btools.router; + +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +public abstract class Formatter { + + static final String MESSAGES_HEADER = "Longitude\tLatitude\tElevation\tDistance\tCostPerKm\tElevCost\tTurnCost\tNodeCost\tInitialCost\tWayTags\tNodeTags\tTime\tEnergy"; + + RoutingContext rc; + + Formatter() { + } + + Formatter(RoutingContext rc) { + this.rc = rc; + } + + /** + * writes the track in gpx-format to a file + * + * @param filename the filename to write to + * @param t the track to write + */ + public void write(String filename, OsmTrack t) throws Exception { + BufferedWriter bw = new BufferedWriter(new FileWriter(filename)); + bw.write(format(t)); + bw.close(); + } + + public OsmTrack read(String filename) throws Exception { + return null; + } + + /** + * writes the track in a selected output format to a string + * + * @param t the track to format + * @return the formatted string + */ + public abstract String format(OsmTrack t); + + + static String formatILon(int ilon) { + return formatPos(ilon - 180000000); + } + + static String formatILat(int ilat) { + return formatPos(ilat - 90000000); + } + + private static String formatPos(int p) { + boolean negative = p < 0; + if (negative) + p = -p; + char[] ac = new char[12]; + int i = 11; + while (p != 0 || i > 3) { + ac[i--] = (char) ('0' + (p % 10)); + p /= 10; + if (i == 5) + ac[i--] = '.'; + } + if (negative) + ac[i--] = '-'; + return new String(ac, i + 1, 11 - i); + } + + public static String getFormattedTime2(int s) { + int seconds = (int) (s + 0.5); + int hours = seconds / 3600; + int minutes = (seconds - hours * 3600) / 60; + seconds = seconds - hours * 3600 - minutes * 60; + String time = ""; + if (hours != 0) + time = "" + hours + "h "; + if (minutes != 0) + time = time + minutes + "m "; + if (seconds != 0) + time = time + seconds + "s"; + return time; + } + + static public String getFormattedEnergy(int energy) { + return format1(energy / 3600000.) + "kwh"; + } + + static private String format1(double n) { + String s = "" + (long) (n * 10 + 0.5); + int len = s.length(); + return s.substring(0, len - 1) + "." + s.charAt(len - 1); + } + + + static final String dateformat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + + static public String getFormattedTime3(float time) { + SimpleDateFormat TIMESTAMP_FORMAT = new SimpleDateFormat(dateformat, Locale.US); + TIMESTAMP_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); + // yyyy-mm-ddThh:mm:ss.SSSZ + Date d = new Date((long) (time * 1000f)); + return TIMESTAMP_FORMAT.format(d); + } + + +} diff --git a/brouter-core/src/main/java/btools/router/OsmNogoPolygon.java b/brouter-core/src/main/java/btools/router/OsmNogoPolygon.java index ea828fe..2809f27 100644 --- a/brouter-core/src/main/java/btools/router/OsmNogoPolygon.java +++ b/brouter-core/src/main/java/btools/router/OsmNogoPolygon.java @@ -168,7 +168,7 @@ public class OsmNogoPolygon extends OsmNodeNamed { Point p1 = points.get(0); for (int i = 1; i <= i_last; i++) { final Point p2 = points.get(i); - if (OsmNogoPolygon.isOnLine(px, py, p1.x, p1.y, p2.x, p2.y)) { + if (isOnLine(px, py, p1.x, p1.y, p2.x, p2.y)) { return true; } p1 = p2; @@ -234,7 +234,7 @@ public class OsmNogoPolygon extends OsmNodeNamed { final long p1x = p1.x; final long p1y = p1.y; - if (OsmNogoPolygon.isOnLine(px, py, p0x, p0y, p1x, p1y)) { + if (isOnLine(px, py, p0x, p0y, p1x, p1y)) { return true; } diff --git a/brouter-core/src/main/java/btools/router/OsmPath.java b/brouter-core/src/main/java/btools/router/OsmPath.java index 1bf318b..94549c9 100644 --- a/brouter-core/src/main/java/btools/router/OsmPath.java +++ b/brouter-core/src/main/java/btools/router/OsmPath.java @@ -5,8 +5,6 @@ */ package btools.router; -import java.io.IOException; - import btools.mapaccess.OsmLink; import btools.mapaccess.OsmLinkHolder; import btools.mapaccess.OsmNode; @@ -33,8 +31,6 @@ abstract class OsmPath implements OsmLinkHolder { public OsmPathElement originElement; public OsmPathElement myElement; - protected float traffic; - private OsmLinkHolder nextForLink = null; public int treedepth = 0; @@ -72,25 +68,6 @@ abstract class OsmPath implements OsmLinkHolder { public MessageData message; - public void unregisterUpTree(RoutingContext rc) { - try { - OsmPathElement pe = originElement; - while (pe instanceof OsmPathElementWithTraffic && ((OsmPathElementWithTraffic) pe).unregister(rc)) { - pe = pe.origin; - } - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } - } - - public void registerUpTree() { - if (originElement instanceof OsmPathElementWithTraffic) { - OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic) originElement; - ot.register(); - ot.addTraffic(traffic); - } - } - public void init(OsmLink link) { this.link = link; targetNode = link.getTarget(null); @@ -102,7 +79,7 @@ abstract class OsmPath implements OsmLinkHolder { public void init(OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc) { if (origin.myElement == null) { - origin.myElement = OsmPathElement.create(origin, rc.countTraffic); + origin.myElement = OsmPathElement.create(origin); } this.originElement = origin.myElement; this.link = link; @@ -143,7 +120,7 @@ abstract class OsmPath implements OsmLinkHolder { return; } - boolean recordTransferNodes = detailMode || rc.countTraffic; + boolean recordTransferNodes = detailMode; rc.nogoCost = 0.; @@ -272,7 +249,7 @@ abstract class OsmPath implements OsmLinkHolder { if (recordTransferNodes) { if (rc.wayfraction > 0.) { ele1 = interpolateEle(ele1, ele2, 1. - rc.wayfraction); - originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, ele1, null, rc.countTraffic); + originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, ele1, null); } else { originElement = null; // prevent duplicate point } @@ -296,7 +273,7 @@ abstract class OsmPath implements OsmLinkHolder { // apply a start-direction if appropriate (by faking the origin position) if (isStartpoint) { if (rc.startDirectionValid) { - double dir = rc.startDirection.intValue() * CheapRuler.DEG_TO_RAD; + double dir = rc.startDirection * CheapRuler.DEG_TO_RAD; double[] lonlat2m = CheapRuler.getLonLatToMeterScales((lon0 + lat1) >> 1); lon0 = lon1 - (int) (1000. * Math.sin(dir) / lonlat2m[0]); lat0 = lat1 - (int) (1000. * Math.cos(dir) / lonlat2m[1]); @@ -333,13 +310,6 @@ abstract class OsmPath implements OsmLinkHolder { cost += (int) sectionCost; - // calculate traffic - if (rc.countTraffic) { - int minDist = (int) rc.trafficSourceMinDist; - int cost2 = cost < minDist ? minDist : cost; - traffic += dist * rc.expctxWay.getTrafficSourceDensity() * Math.pow(cost2 / 10000.f, rc.trafficSourceExponent); - } - // compute kinematic computeKinematic(rc, dist, delta_h, detailMode); @@ -357,7 +327,7 @@ abstract class OsmPath implements OsmLinkHolder { if (stopAtEndpoint) { if (recordTransferNodes) { - originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, originEle2, originElement, rc.countTraffic); + originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, originEle2, originElement); originElement.cost = cost; if (message != null) { originElement.message = message; @@ -383,10 +353,8 @@ abstract class OsmPath implements OsmLinkHolder { transferNode = transferNode.next; if (recordTransferNodes) { - originElement = OsmPathElement.create(lon2, lat2, originEle2, originElement, rc.countTraffic); + originElement = OsmPathElement.create(lon2, lat2, originEle2, originElement); originElement.cost = cost; - originElement.addTraffic(traffic); - traffic = 0; } lon0 = lon1; lat0 = lat1; diff --git a/brouter-core/src/main/java/btools/router/OsmPathElement.java b/brouter-core/src/main/java/btools/router/OsmPathElement.java index d721ad8..08a8500 100644 --- a/brouter-core/src/main/java/btools/router/OsmPathElement.java +++ b/brouter-core/src/main/java/btools/router/OsmPathElement.java @@ -81,16 +81,16 @@ public class OsmPathElement implements OsmPos { public OsmPathElement origin; // construct a path element from a path - public static final OsmPathElement create(OsmPath path, boolean countTraffic) { + public static final OsmPathElement create(OsmPath path) { OsmNode n = path.getTargetNode(); - OsmPathElement pe = create(n.getILon(), n.getILat(), n.getSElev(), path.originElement, countTraffic); + OsmPathElement pe = create(n.getILon(), n.getILat(), n.getSElev(), path.originElement); pe.cost = path.cost; pe.message = path.message; return pe; } - public static final OsmPathElement create(int ilon, int ilat, short selev, OsmPathElement origin, boolean countTraffic) { - OsmPathElement pe = countTraffic ? new OsmPathElementWithTraffic() : new OsmPathElement(); + public static final OsmPathElement create(int ilon, int ilat, short selev, OsmPathElement origin) { + OsmPathElement pe = new OsmPathElement(); pe.ilon = ilon; pe.ilat = ilat; pe.selev = selev; @@ -101,9 +101,6 @@ public class OsmPathElement implements OsmPos { protected OsmPathElement() { } - public void addTraffic(float traffic) { - } - public String toString() { return ilon + "_" + ilat; } diff --git a/brouter-core/src/main/java/btools/router/OsmPathElementWithTraffic.java b/brouter-core/src/main/java/btools/router/OsmPathElementWithTraffic.java deleted file mode 100644 index 18496f4..0000000 --- a/brouter-core/src/main/java/btools/router/OsmPathElementWithTraffic.java +++ /dev/null @@ -1,68 +0,0 @@ -package btools.router; - -import java.io.IOException; - - -/** - * Extension to OsmPathElement to count traffic load - * - * @author ab - */ - -public final class OsmPathElementWithTraffic extends OsmPathElement { - private int registerCount; - private float farTraffic; - private float nearTraffic; - - public void register() { - if (registerCount++ == 0) { - if (origin instanceof OsmPathElementWithTraffic) { - OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic) origin; - ot.register(); - ot.farTraffic += farTraffic; - ot.nearTraffic += nearTraffic; - farTraffic = 0; - nearTraffic = 0; - } - } - } - - @Override - public void addTraffic(float traffic) { - this.farTraffic += traffic; - this.nearTraffic += traffic; - } - - // unregister from origin if our registercount is 0, else do nothing - - public static double maxtraffic = 0.; - - public boolean unregister(RoutingContext rc) throws IOException { - if (--registerCount == 0) { - if (origin instanceof OsmPathElementWithTraffic) { - OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic) origin; - - int costdelta = cost - ot.cost; - ot.farTraffic += farTraffic * Math.exp(-costdelta / rc.farTrafficDecayLength); - ot.nearTraffic += nearTraffic * Math.exp(-costdelta / rc.nearTrafficDecayLength); - - if (costdelta > 0 && farTraffic > maxtraffic) maxtraffic = farTraffic; - - int t2 = cost == ot.cost ? -1 : (int) (rc.farTrafficWeight * farTraffic + rc.nearTrafficWeight * nearTraffic); - - if (t2 > 4000 || t2 == -1) { - // System.out.println( "unregistered: " + this + " origin=" + ot + " farTraffic =" + farTraffic + " nearTraffic =" + nearTraffic + " cost=" + cost ); - if (rc.trafficOutputStream != null) { - rc.trafficOutputStream.writeLong(getIdFromPos()); - rc.trafficOutputStream.writeLong(ot.getIdFromPos()); - rc.trafficOutputStream.writeInt(t2); - } - } - farTraffic = 0; - nearTraffic = 0; - } - return true; - } - return false; - } -} diff --git a/brouter-core/src/main/java/btools/router/OsmTrack.java b/brouter-core/src/main/java/btools/router/OsmTrack.java index 89ffdac..859fdc8 100644 --- a/brouter-core/src/main/java/btools/router/OsmTrack.java +++ b/brouter-core/src/main/java/btools/router/OsmTrack.java @@ -7,37 +7,24 @@ package btools.router; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.BufferedWriter; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.StringWriter; -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Date; import java.util.List; -import java.util.Locale; import java.util.Map; -import java.util.TimeZone; import btools.mapaccess.MatchedWaypoint; import btools.mapaccess.OsmPos; import btools.util.CompactLongMap; import btools.util.FrozenLongMap; -import btools.util.StringUtils; public final class OsmTrack { - final public static String version = "1.7.1"; - final public static String versionDate = "12072023"; + final public static String version = "1.7.7"; + final public static String versionDate = "23072024"; // csv-header-line private static final String MESSAGES_HEADER = "Longitude\tLatitude\tElevation\tDistance\tCostPerKm\tElevCost\tTurnCost\tNodeCost\tInitialCost\tWayTags\tNodeTags\tTime\tEnergy"; @@ -66,7 +53,7 @@ public final class OsmTrack { private CompactLongMap detourMap; - private VoiceHintList voiceHints; + public VoiceHintList voiceHints; public String message = null; public List messageList = null; @@ -178,8 +165,8 @@ public final class OsmTrack { nodesMap = new FrozenLongMap<>(nodesMap); } - private List aggregateMessages() { - ArrayList res = new ArrayList<>(); + public List aggregateMessages() { + List res = new ArrayList<>(); MessageData current = null; for (OsmPathElement n : nodes) { if (n.message != null && n.message.wayKeyValues != null) { @@ -200,8 +187,8 @@ public final class OsmTrack { return res; } - private List aggregateSpeedProfile() { - ArrayList res = new ArrayList<>(); + public List aggregateSpeedProfile() { + List res = new ArrayList<>(); int vmax = -1; int vmaxe = -1; int vmin = -1; @@ -298,7 +285,9 @@ public final class OsmTrack { } dis.close(); } catch (Exception e) { - throw new RuntimeException("Exception reading rawTrack: " + e); + if (debugInfo != null) { + debugInfo.append("Error reading rawTrack: " + e); + } } } } @@ -356,6 +345,12 @@ public final class OsmTrack { if (i > 0 || ourSize == 0) { e.setTime(e.getTime() + t0); e.setEnergy(e.getEnergy() + e0); + if (e.message != null){ + if (!(e.message.lon == e.getILon() && e.message.lat == e.getILat())) { + e.message.lon = e.getILon(); + e.message.lat = e.getILat(); + } + } nodes.add(e); } } @@ -395,752 +390,9 @@ public final class OsmTrack { public int plainAscend; public int cost; public int energy; - - /** - * writes the track in gpx-format to a file - * - * @param filename the filename to write to - */ - public void writeGpx(String filename) throws Exception { - BufferedWriter bw = new BufferedWriter(new FileWriter(filename)); - formatAsGpx(bw); - bw.close(); - } - - public String formatAsGpx() { - try { - StringWriter sw = new StringWriter(8192); - BufferedWriter bw = new BufferedWriter(sw); - formatAsGpx(bw); - bw.close(); - return sw.toString(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public String formatAsGpx(BufferedWriter sb) throws IOException { - int turnInstructionMode = voiceHints != null ? voiceHints.turnInstructionMode : 0; - - sb.append("\n"); - if (turnInstructionMode != 9) { - for (int i = messageList.size() - 1; i >= 0; i--) { - String message = messageList.get(i); - if (i < messageList.size() - 1) - message = "(alt-index " + i + ": " + message + " )"; - if (message != null) - sb.append("\n"); - } - } - - if (turnInstructionMode == 4) { // comment style - sb.append("\n"); - sb.append("\n"); - sb.append("\n"); - } - sb.append("\n"); - } else { - sb.append(" creator=\"BRouter-" + version + "\" version=\"1.1\">\n"); - } - if (turnInstructionMode == 9) { - sb.append(" \n"); - sb.append(" ").append(name).append("\n"); - sb.append(" \n"); - sb.append(" ").append(messageList.get(0)).append("\n"); - if (params != null && params.size() > 0) { - sb.append(" e : params.entrySet()) { - if (i++ != 0) sb.append("&"); - sb.append(e.getKey()).append("=").append(e.getValue()); - } - sb.append("]]>\n"); - } - sb.append(" \n"); - sb.append(" \n"); - } - if (turnInstructionMode == 3 || turnInstructionMode == 8) { // osmand style, cruiser - float lastRteTime = 0; - - sb.append(" \n"); - - float rteTime = getVoiceHintTime(0); - StringBuffer first = new StringBuffer(); - // define start point - { - first.append(" \n") - .append(" start\n \n"); - if (rteTime != lastRteTime) { // add timing only if available - double t = rteTime - lastRteTime; - first.append(" \n"); - lastRteTime = rteTime; - } - first.append(" 0\n \n \n"); - } - if (turnInstructionMode == 8) { - if (matchedWaypoints.get(0).direct && voiceHints.list.get(0).indexInTrack == 0) { - // has a voice hint do nothing, voice hint will do - } else { - sb.append(first.toString()); - } - } else { - sb.append(first.toString()); - } - - for (int i = 0; i < voiceHints.list.size(); i++) { - VoiceHint hint = voiceHints.list.get(i); - sb.append(" \n") - .append(" ") - .append(turnInstructionMode == 3 ? hint.getMessageString() : hint.getCruiserMessageString()) - .append("\n \n"); - - rteTime = getVoiceHintTime(i + 1); - - if (rteTime != lastRteTime) { // add timing only if available - double t = rteTime - lastRteTime; - sb.append(" \n"); - lastRteTime = rteTime; - } - sb.append(" ") - .append(turnInstructionMode == 3 ? hint.getCommandString() : hint.getCruiserCommandString()) - .append("\n ").append("" + (int) hint.angle) - .append("\n ").append("" + hint.indexInTrack).append("\n \n \n"); - } - sb.append(" \n") - .append(" destination\n \n"); - sb.append(" \n"); - sb.append(" ").append("" + (nodes.size() - 1)).append("\n \n \n"); - - sb.append("\n"); - } - - if (turnInstructionMode == 7) { // old locus style - float lastRteTime = getVoiceHintTime(0); - - for (int i = 0; i < voiceHints.list.size(); i++) { - VoiceHint hint = voiceHints.list.get(i); - sb.append(" ") - .append(hint.selev == Short.MIN_VALUE ? "" : "" + (hint.selev / 4.) + "") - .append("").append(hint.getMessageString()).append("") - .append("").append("" + hint.distanceToNext).append(""); - float rteTime = getVoiceHintTime(i + 1); - if (rteTime != lastRteTime) { // add timing only if available - double t = rteTime - lastRteTime; - double speed = hint.distanceToNext / t; - sb.append("").append("" + t).append("") - .append("").append("" + speed).append(""); - lastRteTime = rteTime; - } - sb.append("").append("" + hint.getLocusAction()).append("") - .append("\n"); - } - } - if (turnInstructionMode == 5) { // gpsies style - for (VoiceHint hint : voiceHints.list) { - sb.append(" ") - .append("").append(hint.getMessageString()).append("") - .append("").append(hint.getSymbolString().toLowerCase()).append("") - .append("").append(hint.getSymbolString()).append("") - .append("\n"); - } - } - - if (turnInstructionMode == 6) { // orux style - for (VoiceHint hint : voiceHints.list) { - sb.append(" ") - .append(hint.selev == Short.MIN_VALUE ? "" : "" + (hint.selev / 4.) + "") - .append("\n" + - " \n" + - " ").append("" + hint.getOruxAction()) - .append("\n" + - " \n" + - " \n" + - " \n"); - } - } - - for (int i = 0; i <= pois.size() - 1; i++) { - OsmNodeNamed poi = pois.get(i); - sb.append(" \n") - .append(" ").append(StringUtils.escapeXml10(poi.name)).append("\n") - .append(" \n"); - } - - if (exportWaypoints) { - for (int i = 0; i <= matchedWaypoints.size() - 1; i++) { - MatchedWaypoint wt = matchedWaypoints.get(i); - sb.append(" \n") - .append(" ").append(StringUtils.escapeXml10(wt.name)).append("\n"); - if (i == 0) { - sb.append(" from\n"); - } else if (i == matchedWaypoints.size() - 1) { - sb.append(" to\n"); - } else { - sb.append(" via\n"); - } - sb.append(" \n"); - } - } - sb.append(" \n"); - if (turnInstructionMode == 9 - || turnInstructionMode == 2 - || turnInstructionMode == 8 - || turnInstructionMode == 4) { // Locus, comment, cruise, brouter style - sb.append(" ").append(name).append("\n"); - sb.append(" ").append(voiceHints.getTransportMode()).append("\n"); - } else { - sb.append(" ").append(name).append("\n"); - } - - if (turnInstructionMode == 7) { - sb.append(" \n"); - sb.append(" ").append("" + voiceHints.getLocusRouteType()).append("\n"); - sb.append(" 1\n"); - sb.append(" \n"); - } - - - // all points - sb.append(" \n"); - String lastway = ""; - boolean bNextDirect = false; - OsmPathElement nn = null; - String aSpeed; - - for (int idx = 0; idx < nodes.size(); idx++) { - OsmPathElement n = nodes.get(idx); - String sele = n.getSElev() == Short.MIN_VALUE ? "" : "" + n.getElev() + ""; - VoiceHint hint = getVoiceHint(idx); - MatchedWaypoint mwpt = getMatchedWaypoint(idx); - - if (showTime) { - sele += ""; - } - if (turnInstructionMode == 8) { - if (mwpt != null && - !mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) { - sele += "" + mwpt.name + ""; - } - } - boolean bNeedHeader = false; - if (turnInstructionMode == 9) { // trkpt/sym style - - if (hint != null) { - - if (mwpt != null && - !mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) { - sele += "" + mwpt.name + ""; - } - sele += "" + hint.getCruiserMessageString() + ""; - sele += "" + hint.getCommandString(hint.cmd) + ""; - if (mwpt != null) { - sele += "Via"; - } - sele += ""; - if (showspeed) { - double speed = 0; - if (nn != null) { - int dist = n.calcDistance(nn); - float dt = n.getTime() - nn.getTime(); - if (dt != 0.f) { - speed = ((3.6f * dist) / dt + 0.5); - } - } - sele += "" + (((int) (speed * 10)) / 10.f) + ""; - } - - sele += "" + hint.getCommandString() + ";" + (int) (hint.distanceToNext) + "," + hint.formatGeometry() + ""; - if (n.message != null && n.message.wayKeyValues != null && !n.message.wayKeyValues.equals(lastway)) { - sele += "" + n.message.wayKeyValues + ""; - lastway = n.message.wayKeyValues; - } - if (n.message != null && n.message.nodeKeyValues != null) { - sele += "" + n.message.nodeKeyValues + ""; - } - sele += ""; - - } - if (idx == 0 && hint == null) { - if (mwpt != null && mwpt.direct) { - sele += "beeline"; - } else { - sele += "start"; - } - sele += "Via"; - - } else if (idx == nodes.size() - 1 && hint == null) { - - sele += "end"; - sele += "Via"; - - } else { - if (mwpt != null && hint == null) { - if (mwpt.direct) { - // bNextDirect = true; - sele += "beeline"; - } else { - sele += "" + mwpt.name + ""; - } - sele += "Via"; - bNextDirect = false; - } - } - - - if (hint == null) { - bNeedHeader = (showspeed || (n.message != null && n.message.wayKeyValues != null && !n.message.wayKeyValues.equals(lastway))) || - (n.message != null && n.message.nodeKeyValues != null); - if (bNeedHeader) { - sele += ""; - if (showspeed) { - double speed = 0; - if (nn != null) { - int dist = n.calcDistance(nn); - float dt = n.getTime() - nn.getTime(); - if (dt != 0.f) { - speed = ((3.6f * dist) / dt + 0.5); - } - } - sele += "" + (((int) (speed * 10)) / 10.f) + ""; - } - if (n.message != null && n.message.wayKeyValues != null && !n.message.wayKeyValues.equals(lastway)) { - sele += "" + n.message.wayKeyValues + ""; - lastway = n.message.wayKeyValues; - } - if (n.message != null && n.message.nodeKeyValues != null) { - sele += "" + n.message.nodeKeyValues + ""; - } - sele += ""; - } - } - } - - if (turnInstructionMode == 2) { // locus style new - if (hint != null) { - if (mwpt != null) { - if (!mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) { - sele += "" + mwpt.name + ""; - } - if (mwpt.direct && bNextDirect) { - sele += "" + hint.getLocusSymbolString() + "pass_placeShaping"; - // bNextDirect = false; - } else if (mwpt.direct) { - if (idx == 0) - sele += "pass_placeVia"; - else - sele += "pass_placeShaping"; - bNextDirect = true; - } else if (bNextDirect) { - sele += "beeline" + hint.getLocusSymbolString() + "Shaping"; - bNextDirect = false; - } else { - sele += "" + hint.getLocusSymbolString() + "Via"; - } - } else { - sele += "" + hint.getLocusSymbolString() + ""; - } - } else { - if (idx == 0 && hint == null) { - - int pos = sele.indexOf(""; - if (mwpt != null && mwpt.direct) { - bNextDirect = true; - } - sele += "pass_place"; - sele += "Via"; - - } else if (idx == nodes.size() - 1 && hint == null) { - - int pos = sele.indexOf(""; - if (bNextDirect) { - sele += "beeline"; - } - sele += "pass_place"; - sele += "Via"; - - } else { - if (mwpt != null) { - if (!mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) { - sele += "" + mwpt.name + ""; - } - if (mwpt.direct && bNextDirect) { - sele += "beelinepass_placeShaping"; - } else if (mwpt.direct) { - if (idx == 0) - sele += "pass_placeVia"; - else - sele += "pass_placeShaping"; - bNextDirect = true; - } else if (bNextDirect) { - sele += "beelinepass_placeShaping"; - bNextDirect = false; - } else if (mwpt.name.startsWith("via") || - mwpt.name.startsWith("from") || - mwpt.name.startsWith("to")) { - if (bNextDirect) { - sele += "beelinepass_placeShaping"; - } else { - sele += "pass_placeVia"; - } - bNextDirect = false; - } else { - sele += "" + mwpt.name + ""; - sele += "pass_placeVia"; - } - } - } - } - } - sb.append(" ").append(sele).append("\n"); - - nn = n; - } - - sb.append(" \n"); - sb.append(" \n"); - sb.append("\n"); - - return sb.toString(); - } - - static public String formatAsGpxWaypoint(OsmNodeNamed n) { - try { - StringWriter sw = new StringWriter(8192); - BufferedWriter bw = new BufferedWriter(sw); - formatGpxHeader(bw); - formatWaypointGpx(bw, n); - formatGpxFooter(bw); - bw.close(); - sw.close(); - return sw.toString(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - static public void formatGpxHeader(BufferedWriter sb) throws IOException { - sb.append("\n"); - sb.append("\n"); - } - - static public void formatGpxFooter(BufferedWriter sb) throws IOException { - sb.append("\n"); - } - - static public void formatWaypointGpx(BufferedWriter sb, OsmNodeNamed n) throws IOException { - sb.append(" "); - if (n.getSElev() != Short.MIN_VALUE) { - sb.append("").append("" + n.getElev()).append(""); - } - if (n.name != null) { - sb.append("").append(StringUtils.escapeXml10(n.name)).append(""); - } - if (n.nodeDescription != null) { - sb.append("").append("hat desc").append(""); - } - sb.append("\n"); - } - - public void writeKml(String filename) throws Exception { - BufferedWriter bw = new BufferedWriter(new FileWriter(filename)); - - bw.write(formatAsKml()); - bw.close(); - } - - public String formatAsKml() { - StringBuilder sb = new StringBuilder(8192); - - sb.append("\n"); - - sb.append("\n"); - sb.append(" \n"); - sb.append(" KML Samples\n"); - sb.append(" 1\n"); - sb.append(" 3.497064\n"); - sb.append(" 872\n"); - sb.append(" To enable simple instructions add: 'instructions=1' as parameter to the URL\n"); - sb.append(" \n"); - sb.append(" Paths\n"); - sb.append(" 0\n"); - sb.append(" Examples of paths.\n"); - sb.append(" \n"); - sb.append(" Tessellated\n"); - sb.append(" 0\n"); - sb.append(" tag has a value of 1, the line will contour to the underlying terrain]]>\n"); - sb.append(" \n"); - sb.append(" 1\n"); - sb.append(" "); - - for (OsmPathElement n : nodes) { - sb.append(formatILon(n.getILon())).append(",").append(formatILat(n.getILat())).append("\n"); - } - - sb.append(" \n"); - sb.append(" \n"); - sb.append(" \n"); - sb.append(" \n"); - if (exportWaypoints || !pois.isEmpty()) { - if (!pois.isEmpty()) { - sb.append(" \n"); - sb.append(" poi\n"); - for (int i = 0; i < pois.size(); i++) { - OsmNodeNamed poi = pois.get(i); - createPlaceMark(sb, poi.name, poi.ilat, poi.ilon); - } - sb.append(" \n"); - } - - if (exportWaypoints) { - int size = matchedWaypoints.size(); - createFolder(sb, "start", matchedWaypoints.subList(0, 1)); - if (matchedWaypoints.size() > 2) { - createFolder(sb, "via", matchedWaypoints.subList(1, size - 1)); - } - createFolder(sb, "end", matchedWaypoints.subList(size - 1, size)); - } - } - sb.append(" \n"); - sb.append("\n"); - - return sb.toString(); - } - - private void createFolder(StringBuilder sb, String type, List waypoints) { - sb.append(" \n"); - sb.append(" " + type + "\n"); - for (int i = 0; i < waypoints.size(); i++) { - MatchedWaypoint wp = waypoints.get(i); - createPlaceMark(sb, wp.name, wp.waypoint.ilat, wp.waypoint.ilon); - } - sb.append(" \n"); - } - - private void createPlaceMark(StringBuilder sb, String name, int ilat, int ilon) { - sb.append(" \n"); - sb.append(" " + StringUtils.escapeXml10(name) + "\n"); - sb.append(" \n"); - sb.append(" " + formatILon(ilon) + "," + formatILat(ilat) + "\n"); - sb.append(" \n"); - sb.append(" \n"); - } - public List iternity; - public void writeJson(String filename) throws Exception { - BufferedWriter bw = new BufferedWriter(new FileWriter(filename)); - - bw.write(formatAsGeoJson()); - bw.close(); - } - - - public String formatAsGeoJson() { - int turnInstructionMode = voiceHints != null ? voiceHints.turnInstructionMode : 0; - - StringBuilder sb = new StringBuilder(8192); - - sb.append("{\n"); - sb.append(" \"type\": \"FeatureCollection\",\n"); - sb.append(" \"features\": [\n"); - sb.append(" {\n"); - sb.append(" \"type\": \"Feature\",\n"); - sb.append(" \"properties\": {\n"); - sb.append(" \"creator\": \"BRouter-" + version + "\",\n"); - sb.append(" \"name\": \"").append(name).append("\",\n"); - sb.append(" \"track-length\": \"").append(distance).append("\",\n"); - sb.append(" \"filtered ascend\": \"").append(ascend).append("\",\n"); - sb.append(" \"plain-ascend\": \"").append(plainAscend).append("\",\n"); - sb.append(" \"total-time\": \"").append(getTotalSeconds()).append("\",\n"); - sb.append(" \"total-energy\": \"").append(energy).append("\",\n"); - sb.append(" \"cost\": \"").append(cost).append("\",\n"); - if (voiceHints != null && !voiceHints.list.isEmpty()) { - sb.append(" \"voicehints\": [\n"); - for (VoiceHint hint : voiceHints.list) { - sb.append(" ["); - sb.append(hint.indexInTrack); - sb.append(',').append(hint.getCommand()); - sb.append(',').append(hint.getExitNumber()); - sb.append(',').append(hint.distanceToNext); - sb.append(',').append((int) hint.angle); - - // not always include geometry because longer and only needed for comment style - if (turnInstructionMode == 4) { // comment style - sb.append(",\"").append(hint.formatGeometry()).append("\""); - } - - sb.append("],\n"); - } - sb.deleteCharAt(sb.lastIndexOf(",")); - sb.append(" ],\n"); - } - if (showSpeedProfile) { // set in profile - List sp = aggregateSpeedProfile(); - if (sp.size() > 0) { - sb.append(" \"speedprofile\": [\n"); - for (int i = sp.size() - 1; i >= 0; i--) { - sb.append(" [").append(sp.get(i)).append(i > 0 ? "],\n" : "]\n"); - } - sb.append(" ],\n"); - } - } - // ... traditional message list - { - sb.append(" \"messages\": [\n"); - sb.append(" [\"").append(MESSAGES_HEADER.replaceAll("\t", "\", \"")).append("\"],\n"); - for (String m : aggregateMessages()) { - sb.append(" [\"").append(m.replaceAll("\t", "\", \"")).append("\"],\n"); - } - sb.deleteCharAt(sb.lastIndexOf(",")); - sb.append(" ],\n"); - } - - if (getTotalSeconds() > 0) { - sb.append(" \"times\": ["); - DecimalFormat decimalFormat = (DecimalFormat) NumberFormat.getInstance(Locale.ENGLISH); - decimalFormat.applyPattern("0.###"); - for (OsmPathElement n : nodes) { - sb.append(decimalFormat.format(n.getTime())).append(","); - } - sb.deleteCharAt(sb.lastIndexOf(",")); - sb.append("]\n"); - } else { - sb.deleteCharAt(sb.lastIndexOf(",")); - } - - sb.append(" },\n"); - - if (iternity != null) { - sb.append(" \"iternity\": [\n"); - for (String s : iternity) { - sb.append(" \"").append(s).append("\",\n"); - } - sb.deleteCharAt(sb.lastIndexOf(",")); - sb.append(" ],\n"); - } - sb.append(" \"geometry\": {\n"); - sb.append(" \"type\": \"LineString\",\n"); - sb.append(" \"coordinates\": [\n"); - - OsmPathElement nn = null; - for (OsmPathElement n : nodes) { - String sele = n.getSElev() == Short.MIN_VALUE ? "" : ", " + n.getElev(); - if (showspeed) { // hack: show speed instead of elevation - double speed = 0; - if (nn != null) { - int dist = n.calcDistance(nn); - float dt = n.getTime() - nn.getTime(); - if (dt != 0.f) { - speed = ((3.6f * dist) / dt + 0.5); - } - } - sele = ", " + (((int) (speed * 10)) / 10.f); - } - sb.append(" [").append(formatILon(n.getILon())).append(", ").append(formatILat(n.getILat())) - .append(sele).append("],\n"); - nn = n; - } - sb.deleteCharAt(sb.lastIndexOf(",")); - - sb.append(" ]\n"); - sb.append(" }\n"); - if (exportWaypoints || !pois.isEmpty()) { - sb.append(" },\n"); - for (int i = 0; i <= pois.size() - 1; i++) { - OsmNodeNamed poi = pois.get(i); - addFeature(sb, "poi", poi.name, poi.ilat, poi.ilon); - if (i < matchedWaypoints.size() - 1) { - sb.append(","); - } - sb.append(" \n"); - } - if (exportWaypoints) { - for (int i = 0; i <= matchedWaypoints.size() - 1; i++) { - String type; - if (i == 0) { - type = "from"; - } else if (i == matchedWaypoints.size() - 1) { - type = "to"; - } else { - type = "via"; - } - - MatchedWaypoint wp = matchedWaypoints.get(i); - addFeature(sb, type, wp.name, wp.waypoint.ilat, wp.waypoint.ilon); - if (i < matchedWaypoints.size() - 1) { - sb.append(","); - } - sb.append(" \n"); - } - } - } else { - sb.append(" }\n"); - } - sb.append(" ]\n"); - sb.append("}\n"); - - return sb.toString(); - } - - private void addFeature(StringBuilder sb, String type, String name, int ilat, int ilon) { - sb.append(" {\n"); - sb.append(" \"type\": \"Feature\",\n"); - sb.append(" \"properties\": {\n"); - sb.append(" \"name\": \"" + StringUtils.escapeJson(name) + "\",\n"); - sb.append(" \"type\": \"" + type + "\"\n"); - sb.append(" },\n"); - sb.append(" \"geometry\": {\n"); - sb.append(" \"type\": \"Point\",\n"); - sb.append(" \"coordinates\": [\n"); - sb.append(" " + formatILon(ilon) + ",\n"); - sb.append(" " + formatILat(ilat) + "\n"); - sb.append(" ]\n"); - sb.append(" }\n"); - sb.append(" }"); - } - - private VoiceHint getVoiceHint(int i) { + public VoiceHint getVoiceHint(int i) { if (voiceHints == null) return null; for (VoiceHint hint : voiceHints.list) { if (hint.indexInTrack == i) { @@ -1150,7 +402,7 @@ public final class OsmTrack { return null; } - private MatchedWaypoint getMatchedWaypoint(int idx) { + public MatchedWaypoint getMatchedWaypoint(int idx) { if (matchedWaypoints == null) return null; for (MatchedWaypoint wp : matchedWaypoints) { if (idx == wp.indexInTrack) { @@ -1168,128 +420,11 @@ public final class OsmTrack { return vnode0 < vnode1 ? vnode0 : vnode1; } - private int getTotalSeconds() { + public int getTotalSeconds() { float s = nodes.size() < 2 ? 0 : nodes.get(nodes.size() - 1).getTime() - nodes.get(0).getTime(); return (int) (s + 0.5); } - public String getFormattedTime() { - return format1(getTotalSeconds() / 60.) + "m"; - } - - public String getFormattedTime2() { - int seconds = (int) (getTotalSeconds() + 0.5); - int hours = seconds / 3600; - int minutes = (seconds - hours * 3600) / 60; - seconds = seconds - hours * 3600 - minutes * 60; - String time = ""; - if (hours != 0) - time = "" + hours + "h "; - if (minutes != 0) - time = time + minutes + "m "; - if (seconds != 0) - time = time + seconds + "s"; - return time; - } - - SimpleDateFormat TIMESTAMP_FORMAT; - - public String getFormattedTime3(float time) { - if (TIMESTAMP_FORMAT == null) { - TIMESTAMP_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US); - TIMESTAMP_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); - } - // yyyy-mm-ddThh:mm:ss.SSSZ - Date d = new Date((long) (time * 1000f)); - return TIMESTAMP_FORMAT.format(d); - } - - public String getFormattedEnergy() { - return format1(energy / 3600000.) + "kwh"; - } - - private static String formatILon(int ilon) { - return formatPos(ilon - 180000000); - } - - private static String formatILat(int ilat) { - return formatPos(ilat - 90000000); - } - - private static String formatPos(int p) { - boolean negative = p < 0; - if (negative) - p = -p; - char[] ac = new char[12]; - int i = 11; - while (p != 0 || i > 3) { - ac[i--] = (char) ('0' + (p % 10)); - p /= 10; - if (i == 5) - ac[i--] = '.'; - } - if (negative) - ac[i--] = '-'; - return new String(ac, i + 1, 11 - i); - } - - private String format1(double n) { - String s = "" + (long) (n * 10 + 0.5); - int len = s.length(); - return s.substring(0, len - 1) + "." + s.charAt(len - 1); - } - - public void dumpMessages(String filename, RoutingContext rc) throws Exception { - BufferedWriter bw = filename == null ? null : new BufferedWriter(new FileWriter(filename)); - writeMessages(bw, rc); - } - - public void writeMessages(BufferedWriter bw, RoutingContext rc) throws Exception { - dumpLine(bw, MESSAGES_HEADER); - for (String m : aggregateMessages()) { - dumpLine(bw, m); - } - if (bw != null) - bw.close(); - } - - private void dumpLine(BufferedWriter bw, String s) throws Exception { - if (bw == null) { - System.out.println(s); - } else { - bw.write(s); - bw.write("\n"); - } - } - - public void readGpx(String filename) throws Exception { - File f = new File(filename); - if (!f.exists()) - return; - BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(f))); - - for (; ; ) { - String line = br.readLine(); - if (line == null) - break; - - int idx0 = line.indexOf("= 0) { - idx0 += 12; - int idx1 = line.indexOf('"', idx0); - int ilon = (int) ((Double.parseDouble(line.substring(idx0, idx1)) + 180.) * 1000000. + 0.5); - int idx2 = line.indexOf(" lat=\""); - if (idx2 < 0) - continue; - idx2 += 6; - int idx3 = line.indexOf('"', idx2); - int ilat = (int) ((Double.parseDouble(line.substring(idx2, idx3)) + 90.) * 1000000. + 0.5); - nodes.add(OsmPathElement.create(ilon, ilat, (short) 0, null, false)); - } - } - br.close(); - } - public boolean equalsTrack(OsmTrack t) { if (nodes.size() != t.nodes.size()) return false; @@ -1371,7 +506,8 @@ public final class OsmTrack { node = node.origin; } - VoiceHintProcessor vproc = new VoiceHintProcessor(rc.turnInstructionCatchingRange, rc.turnInstructionRoundabouts); + int transportMode = voiceHints.transportMode(); + VoiceHintProcessor vproc = new VoiceHintProcessor(rc.turnInstructionCatchingRange, rc.turnInstructionRoundabouts, transportMode); List results = vproc.process(inputs); double minDistance = getMinDistance(); @@ -1384,13 +520,12 @@ public final class OsmTrack { int getMinDistance() { if (voiceHints != null) { - switch (voiceHints.getTransportMode()) { - case "car": + switch (voiceHints.transportMode()) { + case VoiceHintList.TRANS_MODE_CAR: return 20; - case "bike": - return 5; - case "foot": + case VoiceHintList.TRANS_MODE_FOOT: return 3; + case VoiceHintList.TRANS_MODE_BIKE: default: return 5; } @@ -1398,7 +533,7 @@ public final class OsmTrack { return 2; } - private float getVoiceHintTime(int i) { + public float getVoiceHintTime(int i) { if (voiceHints.list.isEmpty()) { return 0f; } diff --git a/brouter-core/src/main/java/btools/router/RoutingContext.java b/brouter-core/src/main/java/btools/router/RoutingContext.java index 43bd733..474c1c9 100644 --- a/brouter-core/src/main/java/btools/router/RoutingContext.java +++ b/brouter-core/src/main/java/btools/router/RoutingContext.java @@ -5,7 +5,6 @@ */ package btools.router; -import java.io.DataOutput; import java.io.File; import java.util.ArrayList; import java.util.List; @@ -141,14 +140,6 @@ public final class RoutingContext { starttimeoffset = expctxGlobal.getVariableValue("starttimeoffset", 0.f); transitonly = expctxGlobal.getVariableValue("transitonly", 0.f) != 0.f; - farTrafficWeight = expctxGlobal.getVariableValue("farTrafficWeight", 2.f); - nearTrafficWeight = expctxGlobal.getVariableValue("nearTrafficWeight", 2.f); - farTrafficDecayLength = expctxGlobal.getVariableValue("farTrafficDecayLength", 30000.f); - nearTrafficDecayLength = expctxGlobal.getVariableValue("nearTrafficDecayLength", 3000.f); - trafficDirectionFactor = expctxGlobal.getVariableValue("trafficDirectionFactor", 0.9f); - trafficSourceExponent = expctxGlobal.getVariableValue("trafficSourceExponent", -0.7f); - trafficSourceMinDist = expctxGlobal.getVariableValue("trafficSourceMinDist", 3000.f); - showspeed = 0.f != expctxGlobal.getVariableValue("showspeed", 0.f); showSpeedProfile = 0.f != expctxGlobal.getVariableValue("showSpeedProfile", 0.f); inverseRouting = 0.f != expctxGlobal.getVariableValue("inverseRouting", 0.f); @@ -199,23 +190,16 @@ public final class RoutingContext { public int ilatshortest; public int ilonshortest; - public boolean countTraffic; public boolean inverseDirection; - public DataOutput trafficOutputStream; - - public double farTrafficWeight; - public double nearTrafficWeight; - public double farTrafficDecayLength; - public double nearTrafficDecayLength; - public double trafficDirectionFactor; - public double trafficSourceExponent; - public double trafficSourceMinDist; public boolean showspeed; public boolean showSpeedProfile; 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/RoutingEngine.java b/brouter-core/src/main/java/btools/router/RoutingEngine.java index 59cb4f7..09113a8 100644 --- a/brouter-core/src/main/java/btools/router/RoutingEngine.java +++ b/brouter-core/src/main/java/btools/router/RoutingEngine.java @@ -20,6 +20,7 @@ import btools.mapaccess.OsmLink; import btools.mapaccess.OsmLinkHolder; import btools.mapaccess.OsmNode; import btools.mapaccess.OsmNodePairSet; +import btools.mapaccess.OsmPos; import btools.util.CompactLongMap; import btools.util.SortedHeap; import btools.util.StackSampler; @@ -158,16 +159,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"); } } @@ -181,37 +187,76 @@ public class RoutingEngine extends Thread { OsmTrack[] refTracks = new OsmTrack[nsections]; // used ways for alternatives OsmTrack[] lastTracks = new OsmTrack[nsections]; OsmTrack track = null; - ArrayList messageList = new ArrayList<>(); + List messageList = new ArrayList<>(); for (int i = 0; ; i++) { track = findTrack(refTracks, lastTracks); track.message = "track-length = " + track.distance + " filtered ascend = " + track.ascend + " plain-ascend = " + track.plainAscend + " cost=" + track.cost; if (track.energy != 0) { - track.message += " energy=" + track.getFormattedEnergy() + " time=" + track.getFormattedTime2(); + track.message += " energy=" + Formatter.getFormattedEnergy(track.energy) + " time=" + Formatter.getFormattedTime2(track.getTotalSeconds()); } track.name = "brouter_" + routingContext.getProfileName() + "_" + i; messageList.add(track.message); track.messageList = messageList; if (outfileBase != null) { - String filename = outfileBase + i + ".gpx"; - OsmTrack oldTrack = new OsmTrack(); - oldTrack.readGpx(filename); - if (track.equalsTrack(oldTrack)) { + String filename = outfileBase + i + "." + routingContext.outputFormat; + OsmTrack oldTrack = null; + switch (routingContext.outputFormat) { + case "gpx": + oldTrack = new FormatGpx(routingContext).read(filename); + break; + case "geojson": // read only gpx at the moment + case "json": + // oldTrack = new FormatJson(routingContext).read(filename); + break; + case "kml": + // oldTrack = new FormatJson(routingContext).read(filename); + break; + default: + break; + } + if (oldTrack != null && track.equalsTrack(oldTrack)) { continue; } oldTrack = null; - track.writeGpx(filename); + track.exportWaypoints = routingContext.exportWaypoints; + filename = outfileBase + i + "." + routingContext.outputFormat; + switch (routingContext.outputFormat) { + case "gpx": + outputMessage = new FormatGpx(routingContext).format(track); + break; + case "geojson": + case "json": + outputMessage = new FormatJson(routingContext).format(track); + break; + case "kml": + outputMessage = new FormatKml(routingContext).format(track); + break; + case "csv": + default: + outputMessage = null; + break; + } + if (outputMessage != null) { + File out = new File(filename); + FileWriter fw = new FileWriter(filename); + fw.write(outputMessage); + fw.close(); + outputMessage = null; + } + foundTrack = track; alternativeIndex = i; outfile = filename; } else { if (i == routingContext.getAlternativeIdx(0, 3)) { if ("CSV".equals(System.getProperty("reportFormat"))) { - track.dumpMessages(null, routingContext); + String filename = outfileBase + i + ".csv"; + new FormatCsv(routingContext).write(filename, track); } else { if (!quite) { - System.out.println(track.formatAsGpx()); + System.out.println(new FormatGpx(routingContext).format(track)); } } foundTrack = track; @@ -221,7 +266,7 @@ public class RoutingEngine extends Thread { } if (logfileBase != null) { String logfilename = logfileBase + i + ".csv"; - track.dumpMessages(logfilename, routingContext); + new FormatCsv(routingContext).write(logfilename, track); } break; } @@ -300,8 +345,32 @@ public class RoutingEngine extends Thread { OsmNodeNamed n = new OsmNodeNamed(listOne.get(0).crosspoint); n.selev = startNode != null ? startNode.getSElev() : Short.MIN_VALUE; - outputMessage = OsmTrack.formatAsGpxWaypoint(n); - + switch (routingContext.outputFormat) { + case "gpx": + outputMessage = new FormatGpx(routingContext).formatAsWaypoint(n); + break; + case "geojson": + case "json": + outputMessage = new FormatJson(routingContext).formatAsWaypoint(n); + break; + case "kml": + case "csv": + default: + outputMessage = null; + break; + } + if (outfileBase != null) { + String filename = outfileBase + "." + routingContext.outputFormat; + File out = new File(filename); + FileWriter fw = new FileWriter(filename); + fw.write(outputMessage); + fw.close(); + outputMessage = null; + } else { + if (!quite && outputMessage != null) { + System.out.println(outputMessage); + } + } long endTime = System.currentTimeMillis(); logInfo("execution time = " + (endTime - startTime) / 1000. + " seconds"); } catch (Exception e) { @@ -621,9 +690,9 @@ public class RoutingEngine extends Thread { return false; } } - ArrayList removeBackList = new ArrayList<>(); - ArrayList removeForeList = new ArrayList<>(); - ArrayList removeVoiceHintList = new ArrayList<>(); + List removeBackList = new ArrayList<>(); + List removeForeList = new ArrayList<>(); + List removeVoiceHintList = new ArrayList<>(); OsmPathElement last = null; OsmPathElement lastJunction = null; CompactLongMap lastJunctions = new CompactLongMap<>(); @@ -839,11 +908,12 @@ public class RoutingEngine extends Thread { if (ele_last != Short.MIN_VALUE) { ehb = ehb + (ele_last - ele) * eleFactor; } + double filter = elevationFilter(n); if (ehb > 0) { ascend += ehb; ehb = 0; - } else if (ehb < -10) { - ehb = -10; + } else if (ehb < filter) { + ehb = filter; } } @@ -880,6 +950,21 @@ public class RoutingEngine extends Thread { logInfo("filtered ascend = " + t.ascend); } + /** + * find the elevation type for position + * to determine the filter value + * + * @param n the point + * @return the filter value for 1sec / 3sec elevation source + */ + double elevationFilter(OsmPos n) { + if (nodesCache != null) { + int r = nodesCache.getElevationType(n.getILon(), n.getILat()); + if (r == 1) return -5.; + } + return -10.; + } + // geometric position matching finding the nearest routable way-section private void matchWaypointsToNodes(List unmatchedWaypoints) { resetCache(false); @@ -935,7 +1020,7 @@ public class RoutingEngine extends Thread { if (track == null) { for (int cfi = 0; cfi < airDistanceCostFactors.length; cfi++) { - if (cfi > 0) lastAirDistanceCostFactor = airDistanceCostFactors[cfi-1]; + if (cfi > 0) lastAirDistanceCostFactor = airDistanceCostFactors[cfi - 1]; airDistanceCostFactor = airDistanceCostFactors[cfi]; if (airDistanceCostFactor < 0.) { @@ -1161,7 +1246,7 @@ public class RoutingEngine extends Thread { addToOpenset(startPath1); addToOpenset(startPath2); } - ArrayList openBorderList = new ArrayList<>(4096); + List openBorderList = new ArrayList<>(4096); boolean memoryPanicMode = false; boolean needNonPanicProcessing = false; @@ -1194,7 +1279,6 @@ public class RoutingEngine extends Thread { } if (path.airdistance == -1) { - path.unregisterUpTree(routingContext); continue; } @@ -1262,7 +1346,6 @@ public class RoutingEngine extends Thread { OsmNode currentNode = path.getTargetNode(); if (currentLink.isLinkUnused()) { - path.unregisterUpTree(routingContext); continue; } @@ -1305,7 +1388,7 @@ public class RoutingEngine extends Thread { + path.elevationCorrection() + (costCuttingTrack.cost - pe.cost); if (costEstimate <= maxTotalCost) { - matchPath = OsmPathElement.create(path, routingContext.countTraffic); + matchPath = OsmPathElement.create(path); } if (costEstimate < maxTotalCost) { logInfo("maxcost " + maxTotalCost + " -> " + costEstimate); @@ -1315,7 +1398,6 @@ public class RoutingEngine extends Thread { } } - int keepPathAirdistance = path.airdistance; OsmLinkHolder firstLinkHolder = currentLink.getFirstLinkHolder(sourceNode); for (OsmLinkHolder linkHolder = firstLinkHolder; linkHolder != null; linkHolder = linkHolder.getNextForLink()) { ((OsmPath) linkHolder).airdistance = -1; // invalidate the entry in the open set; @@ -1334,7 +1416,6 @@ public class RoutingEngine extends Thread { // recheck cutoff before doing expensive stuff int addDiff = 100; if (path.cost + path.airdistance > maxTotalCost + addDiff) { - path.unregisterUpTree(routingContext); continue; } @@ -1389,7 +1470,7 @@ public class RoutingEngine extends Thread { if (routingContext.turnInstructionMode > 0) { OsmPath detour = routingContext.createPath(path, link, refTrack, true); if (detour.cost >= 0. && nextId != startNodeId1 && nextId != startNodeId2) { - guideTrack.registerDetourForId(currentNode.getIdFromPos(), OsmPathElement.create(detour, false)); + guideTrack.registerDetourForId(currentNode.getIdFromPos(), OsmPathElement.create(detour)); } } continue; @@ -1425,16 +1506,14 @@ public class RoutingEngine extends Thread { } } if (bestPath != null) { - boolean trafficSim = endPos == null; - - bestPath.airdistance = trafficSim ? keepPathAirdistance : (isFinalLink ? 0 : nextNode.calcDistance(endPos)); + bestPath.airdistance = isFinalLink ? 0 : nextNode.calcDistance(endPos); boolean inRadius = boundary == null || boundary.isInBoundary(nextNode, bestPath.cost); - if (inRadius && (isFinalLink || bestPath.cost + bestPath.airdistance <= (lastAirDistanceCostFactor != 0. ? maxTotalCost*lastAirDistanceCostFactor : maxTotalCost) + addDiff)) { + if (inRadius && (isFinalLink || bestPath.cost + bestPath.airdistance <= (lastAirDistanceCostFactor != 0. ? maxTotalCost * lastAirDistanceCostFactor : maxTotalCost) + addDiff)) { // add only if this may beat an existing path for that link OsmLinkHolder dominator = link.getFirstLinkHolder(currentNode); - while (!trafficSim && dominator != null) { + while (dominator != null) { OsmPath dp = (OsmPath) dominator; if (dp.airdistance != -1 && bestPath.definitlyWorseThan(dp)) { break; @@ -1443,9 +1522,6 @@ public class RoutingEngine extends Thread { } if (dominator == null) { - if (trafficSim && boundary != null && path.cost == 0 && bestPath.cost > 0) { - bestPath.airdistance += boundary.getBoundaryDistance(nextNode); - } bestPath.treedepth = path.treedepth + 1; link.addLinkHolder(bestPath, currentNode); addToOpenset(bestPath); @@ -1453,8 +1529,6 @@ public class RoutingEngine extends Thread { } } } - - path.unregisterUpTree(routingContext); } } @@ -1468,12 +1542,11 @@ public class RoutingEngine extends Thread { private void addToOpenset(OsmPath path) { if (path.cost >= 0) { openSet.add(path.cost + (int) (path.airdistance * airDistanceCostFactor), path); - path.registerUpTree(); } } private OsmTrack compileTrack(OsmPath path, boolean verbose) { - OsmPathElement element = OsmPathElement.create(path, false); + OsmPathElement element = OsmPathElement.create(path); // for final track, cut endnode if (guideTrack != null && element.origin != null) { @@ -1612,7 +1685,7 @@ public class RoutingEngine extends Thread { } public String getTime() { - return foundTrack.getFormattedTime2(); + return Formatter.getFormattedTime2(foundTrack.getTotalSeconds()); } public OsmTrack getFoundTrack() { 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..b8d7f6b --- /dev/null +++ b/brouter-core/src/main/java/btools/router/RoutingParamCollector.java @@ -0,0 +1,356 @@ +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 { + Map 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") && params.get("nogoLats").length() > 0) { + List nogoList = readNogos(params.get("nogoLons"), params.get("nogoLats"), params.get("nogoRadi")); + if (nogoList != null) { + RoutingContext.prepareNogoPoints(nogoList); + if (rctx.nogopoints == null) { + rctx.nogopoints = nogoList; + } else { + rctx.nogopoints.addAll(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); + if (rctx.nogopoints == null) { + rctx.nogopoints = nogoList; + } else { + rctx.nogopoints.addAll(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("turnInstructionFormat")) { + if ("osmand".equalsIgnoreCase(value)) { + rctx.turnInstructionMode = 3; + } else if ("locus".equalsIgnoreCase(value)) { + rctx.turnInstructionMode = 2; + } + } 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; + } + + +} diff --git a/brouter-core/src/main/java/btools/router/StdPath.java b/brouter-core/src/main/java/btools/router/StdPath.java index 925b06a..d7164a4 100644 --- a/brouter-core/src/main/java/btools/router/StdPath.java +++ b/brouter-core/src/main/java/btools/router/StdPath.java @@ -49,6 +49,8 @@ final class StdPath extends OsmPath { float turncostbase = rc.expctxWay.getTurncost(); float uphillcutoff = rc.expctxWay.getUphillcutoff() * 10000; float downhillcutoff = rc.expctxWay.getDownhillcutoff() * 10000; + float uphillmaxslope = rc.expctxWay.getUphillmaxslope() * 10000; + float downhillmaxslope = rc.expctxWay.getDownhillmaxslope() * 10000; float cfup = rc.expctxWay.getUphillCostfactor(); float cfdown = rc.expctxWay.getDownhillCostfactor(); float cf = rc.expctxWay.getCostfactor(); @@ -60,11 +62,27 @@ final class StdPath extends OsmPath { downhillcostdiv = 1000000 / downhillcostdiv; } + int downhillmaxslopecostdiv = (int) rc.expctxWay.getDownhillmaxslopecost(); + if (downhillmaxslopecostdiv > 0) { + downhillmaxslopecostdiv = 1000000 / downhillmaxslopecostdiv; + } else { + // if not given, use legacy behavior + downhillmaxslopecostdiv = downhillcostdiv; + } + uphillcostdiv = (int) rc.expctxWay.getUphillcost(); if (uphillcostdiv > 0) { uphillcostdiv = 1000000 / uphillcostdiv; } + int uphillmaxslopecostdiv = (int) rc.expctxWay.getUphillmaxslopecost(); + if (uphillmaxslopecostdiv > 0) { + uphillmaxslopecostdiv = 1000000 / uphillmaxslopecostdiv; + } else { + // if not given, use legacy behavior + uphillmaxslopecostdiv = uphillcostdiv; + } + int dist = (int) distance; // legacy arithmetics needs int // penalty for turning angle @@ -99,8 +117,14 @@ final class StdPath extends OsmPath { reduce = excess; } ehbd -= reduce; + float elevationCost = 0.f; if (downhillcostdiv > 0) { - int elevationCost = reduce / downhillcostdiv; + elevationCost += Math.min(reduce, dist * downhillmaxslope) / downhillcostdiv; + } + if (downhillmaxslopecostdiv > 0) { + elevationCost += Math.max(0, reduce - dist * downhillmaxslope) / downhillmaxslopecostdiv; + } + if (elevationCost > 0) { sectionCost += elevationCost; if (message != null) { message.linkelevationcost += elevationCost; @@ -125,8 +149,14 @@ final class StdPath extends OsmPath { reduce = excess; } ehbu -= reduce; + float elevationCost = 0.f; if (uphillcostdiv > 0) { - int elevationCost = reduce / uphillcostdiv; + elevationCost += Math.min(reduce, dist * uphillmaxslope) / uphillcostdiv; + } + if (uphillmaxslopecostdiv > 0) { + elevationCost += Math.max(0, reduce - dist * uphillmaxslope) / uphillmaxslopecostdiv; + } + if (elevationCost > 0) { sectionCost += elevationCost; if (message != null) { message.linkelevationcost += elevationCost; diff --git a/brouter-core/src/main/java/btools/router/VoiceHint.java b/brouter-core/src/main/java/btools/router/VoiceHint.java index 5fea2d7..3d0b425 100644 --- a/brouter-core/src/main/java/btools/router/VoiceHint.java +++ b/brouter-core/src/main/java/btools/router/VoiceHint.java @@ -20,11 +20,11 @@ public class VoiceHint { static final int KL = 8; // keep left static final int KR = 9; // keep right static final int TLU = 10; // U-turn - static final int TU = 11; // 180 degree u-turn - static final int TRU = 12; // Right U-turn - static final int OFFR = 13; // Off route - static final int RNDB = 14; // Roundabout - static final int RNLB = 15; // Roundabout left + static final int TRU = 11; // Right U-turn + static final int OFFR = 12; // Off route + static final int RNDB = 13; // Roundabout + static final int RNLB = 14; // Roundabout left + static final int TU = 15; // 180 degree u-turn static final int BL = 16; // Beeline routing int ilon; @@ -44,6 +44,7 @@ public class VoiceHint { float angle = Float.MAX_VALUE; boolean turnAngleConsumed; boolean needsRealTurn; + int maxBadPrio = -1; int roundaboutExit; @@ -61,8 +62,43 @@ public class VoiceHint { badWays.add(badWay); } - public int getCommand() { - return cmd; + public int getJsonCommandIndex() { + switch (cmd) { + case TLU: + return 10; + case TU: + return 15; + case TSHL: + return 4; + case TL: + return 2; + case TSLL: + return 3; + case KL: + return 8; + case C: + return 1; + case KR: + return 9; + case TSLR: + return 6; + case TR: + return 5; + case TSHR: + return 7; + case TRU: + return 11; + case RNDB: + return 13; + case RNLB: + return 14; + case BL: + return 16; + case OFFR: + return 12; + default: + throw new IllegalArgumentException("unknown command: " + cmd); + } } public int getExitNumber() { diff --git a/brouter-core/src/main/java/btools/router/VoiceHintList.java b/brouter-core/src/main/java/btools/router/VoiceHintList.java index 229f4ab..e377e24 100644 --- a/brouter-core/src/main/java/btools/router/VoiceHintList.java +++ b/brouter-core/src/main/java/btools/router/VoiceHintList.java @@ -10,23 +10,50 @@ import java.util.ArrayList; import java.util.List; public class VoiceHintList { - private String transportMode; + + static final int TRANS_MODE_NONE = 0; + static final int TRANS_MODE_FOOT = 1; + static final int TRANS_MODE_BIKE = 2; + static final int TRANS_MODE_CAR = 3; + + private int transportMode = TRANS_MODE_BIKE; int turnInstructionMode; List list = new ArrayList<>(); public void setTransportMode(boolean isCar, boolean isBike) { - transportMode = isCar ? "car" : (isBike ? "bike" : "foot"); + transportMode = isCar ? TRANS_MODE_CAR : (isBike ? TRANS_MODE_BIKE : TRANS_MODE_FOOT); + } + + public void setTransportMode(int mode) { + transportMode = mode; } public String getTransportMode() { + String ret; + switch (transportMode) { + case TRANS_MODE_FOOT: + ret = "foot"; + break; + case TRANS_MODE_CAR: + ret = "car"; + break; + case TRANS_MODE_BIKE: + default: + ret = "bike"; + break; + } + return ret; + } + + public int transportMode() { return transportMode; } public int getLocusRouteType() { - if ("car".equals(transportMode)) { + if (transportMode == TRANS_MODE_CAR) { return 0; } - if ("bike".equals(transportMode)) { + if (transportMode == TRANS_MODE_BIKE) { return 5; } return 3; // foot diff --git a/brouter-core/src/main/java/btools/router/VoiceHintProcessor.java b/brouter-core/src/main/java/btools/router/VoiceHintProcessor.java index 6b4f37e..cb60390 100644 --- a/brouter-core/src/main/java/btools/router/VoiceHintProcessor.java +++ b/brouter-core/src/main/java/btools/router/VoiceHintProcessor.java @@ -15,10 +15,12 @@ public final class VoiceHintProcessor { // private double catchingRange; // range to catch angles and merge turns private boolean explicitRoundabouts; + private int transportMode; - public VoiceHintProcessor(double catchingRange, boolean explicitRoundabouts) { + public VoiceHintProcessor(double catchingRange, boolean explicitRoundabouts, int transportMode) { // this.catchingRange = catchingRange; this.explicitRoundabouts = explicitRoundabouts; + this.transportMode = transportMode; } private float sumNonConsumedWithinCatchingRange(List inputs, int offset) { @@ -81,10 +83,21 @@ public final class VoiceHintProcessor { if (explicitRoundabouts && input.oldWay.isRoundabout()) { if (roundaboudStartIdx == -1) roundaboudStartIdx = hintIdx; roundAboutTurnAngle += sumNonConsumedWithinCatchingRange(inputs, hintIdx); + if (roundaboudStartIdx == hintIdx) { + if (input.badWays != null) { + // remove goodWay + roundAboutTurnAngle -= input.goodWay.turnangle; + // add a badWay + for (MessageData badWay : input.badWays) { + if (!badWay.isBadOneway()) roundAboutTurnAngle += badWay.turnangle; + } + } + } boolean isExit = roundaboutExit == 0; // exit point is always exit if (input.badWays != null) { for (MessageData badWay : input.badWays) { - if (!badWay.isBadOneway() && badWay.isGoodForCars() && Math.abs(badWay.turnangle) < 120.) { + if (!badWay.isBadOneway() && + badWay.isGoodForCars()) { isExit = true; } } @@ -95,12 +108,35 @@ public final class VoiceHintProcessor { continue; } if (roundaboutExit > 0) { - roundAboutTurnAngle += sumNonConsumedWithinCatchingRange(inputs, hintIdx); - double startTurn = (roundaboudStartIdx != -1 ? inputs.get(roundaboudStartIdx).goodWay.turnangle : turnAngle); + //roundAboutTurnAngle += sumNonConsumedWithinCatchingRange(inputs, hintIdx); + //double startTurn = (roundaboudStartIdx != -1 ? inputs.get(roundaboudStartIdx + 1).goodWay.turnangle : turnAngle); input.angle = roundAboutTurnAngle; + input.goodWay.turnangle = roundAboutTurnAngle; input.distanceToNext = distance; - input.roundaboutExit = startTurn < 0 ? -roundaboutExit : roundaboutExit; + //input.roundaboutExit = startTurn < 0 ? roundaboutExit : -roundaboutExit; + input.roundaboutExit = roundAboutTurnAngle < 0 ? roundaboutExit : -roundaboutExit; + float tmpangle = 0; + VoiceHint tmpRndAbt = new VoiceHint(); + tmpRndAbt.badWays = new ArrayList<>(); + for (int i = hintIdx-1; i > roundaboudStartIdx; i--) { + VoiceHint vh = inputs.get(i); + tmpangle += inputs.get(i).goodWay.turnangle; + if (vh.badWays != null) { + for (MessageData badWay : vh.badWays) { + if (!badWay.isBadOneway()) { + MessageData md = new MessageData(); + md.linkdist = vh.goodWay.linkdist; + md.priorityclassifier = vh.goodWay.priorityclassifier; + md.turnangle = tmpangle; + tmpRndAbt.badWays.add(md); + } + } + } + } distance = 0.; + + input.badWays = tmpRndAbt.badWays; + results.add(input); roundAboutTurnAngle = 0.f; roundaboutExit = 0; @@ -127,10 +163,7 @@ public final class VoiceHintProcessor { if (badPrio > maxPrioAll && !isBadHighway2Link) { maxPrioAll = badPrio; - } - - if (badWay.costfactor < 20.f && Math.abs(badTurn) < minAbsAngeRaw) { - minAbsAngeRaw = Math.abs(badTurn); + input.maxBadPrio = Math.max(input.maxBadPrio, badPrio); } if (badPrio < minPrio) { @@ -145,8 +178,13 @@ public final class VoiceHintProcessor { continue; // ways from the back should not trigger a slight turn } + if (badWay.costfactor < 20.f && Math.abs(badTurn) < minAbsAngeRaw) { + minAbsAngeRaw = Math.abs(badTurn); + } + if (badPrio > maxPrioCandidates) { maxPrioCandidates = badPrio; + input.maxBadPrio = Math.max(input.maxBadPrio, badPrio); } if (badTurn > maxAngle) { maxAngle = badTurn; @@ -157,7 +195,8 @@ public final class VoiceHintProcessor { } } - boolean hasSomethingMoreStraight = (Math.abs(turnAngle) - minAbsAngeRaw) > 20.; + // boolean hasSomethingMoreStraight = (Math.abs(turnAngle) - minAbsAngeRaw) > 20.; + boolean hasSomethingMoreStraight = (Math.abs(turnAngle - minAbsAngeRaw)) > 20. && input.badWays != null; // && !ignoreBadway; // unconditional triggers are all junctions with // - higher detour prios than the minimum route prio (except link->highway junctions) @@ -244,78 +283,132 @@ public final class VoiceHintProcessor { List results = new ArrayList<>(); double distance = 0; VoiceHint inputLast = null; - ArrayList tmpList = new ArrayList<>(); + VoiceHint inputLastSaved = null; for (int hintIdx = 0; hintIdx < inputs.size(); hintIdx++) { VoiceHint input = inputs.get(hintIdx); + VoiceHint nextInput = null; + if (hintIdx + 1 < inputs.size()) { + nextInput = inputs.get(hintIdx + 1); + } - if (input.cmd == VoiceHint.C && !input.goodWay.isLinktType()) { - int badWayPrio = 0; - for (MessageData md : input.badWays) { - badWayPrio = Math.max(badWayPrio, md.getPrio()); - } - if (input.goodWay.getPrio() < badWayPrio) { - results.add(input); - } else { - if (inputLast != null) { // when drop add distance to last - inputLast.distanceToNext += input.distanceToNext; + if (nextInput == null) { + if (input.cmd == VoiceHint.C && !input.goodWay.isLinktType()) { + if (input.goodWay.getPrio() < input.maxBadPrio && (inputLastSaved != null && inputLastSaved.distanceToNext > catchingRange)) { + results.add(input); + } else { + if (inputLast != null) { // when drop add distance to last + inputLast.distanceToNext += input.distanceToNext; + } + continue; } - continue; + } else { + results.add(input); } } else { - if (input.distanceToNext < catchingRange) { + if ((inputLastSaved != null && inputLastSaved.distanceToNext > catchingRange) || input.distanceToNext > catchingRange) { + if (input.cmd == VoiceHint.C && !input.goodWay.isLinktType()) { + if (input.goodWay.getPrio() < input.maxBadPrio + && (inputLastSaved != null && inputLastSaved.distanceToNext > minRange) + && (input.distanceToNext > minRange)) { + // add only on prio + results.add(input); + inputLastSaved = input; + } else { + if (inputLastSaved != null) { // when drop add distance to last + inputLastSaved.distanceToNext += input.distanceToNext; + } + } + } else { + // add all others + // ignore motorway / primary continue + if (((input.goodWay.getPrio() != 28) && + (input.goodWay.getPrio() != 30) && + (input.goodWay.getPrio() != 26)) + || input.isRoundabout() + || Math.abs(input.angle) > 21.f) { + results.add(input); + inputLastSaved = input; + } else { + if (inputLastSaved != null) { // when drop add distance to last + inputLastSaved.distanceToNext += input.distanceToNext; + } + } + } + } else if (input.distanceToNext < catchingRange) { double dist = input.distanceToNext; float angles = input.angle; int i = 1; - boolean save = true; - tmpList.clear(); - while (dist < catchingRange && hintIdx + i < inputs.size()) { - VoiceHint h2 = inputs.get(hintIdx + i); - dist += h2.distanceToNext; - angles += h2.angle; - if (VoiceHint.is180DegAngle(input.angle) || VoiceHint.is180DegAngle(h2.angle)) { // u-turn, 180 degree - save = true; - break; - } else if (Math.abs(angles) > 180 - SIGNIFICANT_ANGLE) { // u-turn, collects e.g. two left turns in range - input.angle = angles; - input.calcCommand(); - input.distanceToNext += h2.distanceToNext; - save = true; - hintIdx++; - break; - } else if (Math.abs(angles) < SIGNIFICANT_ANGLE && input.distanceToNext < minRange) { - input.angle = angles; - input.calcCommand(); - input.distanceToNext += h2.distanceToNext; - save = true; - hintIdx++; - break; - } else if (Math.abs(input.angle) > SIGNIFICANT_ANGLE) { - tmpList.add(h2); - hintIdx++; - } else if (dist > catchingRange) { // distance reached - break; - } else { - if (inputLast != null) { // when drop add distance to last - inputLast.distanceToNext += input.distanceToNext; + boolean save = false; + + dist += nextInput.distanceToNext; + angles += nextInput.angle; + + if (input.cmd == VoiceHint.C && !input.goodWay.isLinktType()) { + if (input.goodWay.getPrio() < input.maxBadPrio) { + if (inputLastSaved != null && inputLastSaved.cmd != VoiceHint.C + && (inputLastSaved != null && inputLastSaved.distanceToNext > minRange) + && transportMode != VoiceHintList.TRANS_MODE_CAR) { + // add when straight and not linktype + // and last vh not straight + save = true; + // remove when next straight and not linktype + if (nextInput != null && + nextInput.cmd == VoiceHint.C && + !nextInput.goodWay.isLinktType()) { + input.distanceToNext += nextInput.distanceToNext; + hintIdx++; + } + } + + } else { + if (inputLastSaved != null) { // when drop add distance to last + inputLastSaved.distanceToNext += input.distanceToNext; } - save = false; } - i++; + } else if (VoiceHint.is180DegAngle(input.angle)) { + // add u-turn, 180 degree + save = true; + } else if (transportMode == VoiceHintList.TRANS_MODE_CAR && Math.abs(angles) > 180 - SIGNIFICANT_ANGLE) { + // add when inc car mode and u-turn, collects e.g. two left turns in range + input.angle = angles; + input.calcCommand(); + input.distanceToNext += nextInput.distanceToNext; + save = true; + hintIdx++; + } else if (Math.abs(angles) < SIGNIFICANT_ANGLE && input.distanceToNext < minRange) { + input.angle = angles; + input.calcCommand(); + input.distanceToNext += nextInput.distanceToNext; + save = true; + hintIdx++; + } else if (Math.abs(input.angle) > SIGNIFICANT_ANGLE) { + // add when angle above 22.5 deg + save = true; + } else if (Math.abs(input.angle) < SIGNIFICANT_ANGLE) { + // add when angle below 22.5 deg ??? + // save = true; + } else { + // otherwise ignore but add distance to next + if (nextInput != null) { // when drop add distance to last + nextInput.distanceToNext += input.distanceToNext; + } + save = false; } + if (save) { results.add(input); // add when last - if (tmpList.size() > 0) { // add when something in stock - results.addAll(tmpList); - hintIdx += tmpList.size() - 1; - } + inputLastSaved = input; } } else { results.add(input); + inputLastSaved = input; } - inputLast = input; } + inputLast = input; } + return results; } + } 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); + } + +} diff --git a/brouter-expressions/.gitignore b/brouter-expressions/.gitignore deleted file mode 100644 index 84c048a..0000000 --- a/brouter-expressions/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build/ diff --git a/brouter-expressions/build.gradle b/brouter-expressions/build.gradle index ff146d6..fd71fbf 100644 --- a/brouter-expressions/build.gradle +++ b/brouter-expressions/build.gradle @@ -1,9 +1,8 @@ plugins { - id 'java-library' + id 'brouter.library-conventions' } dependencies { implementation project(':brouter-util') implementation project(':brouter-codec') - testImplementation 'junit:junit:4.13.1' } diff --git a/brouter-expressions/src/main/AndroidManifest.xml b/brouter-expressions/src/main/AndroidManifest.xml deleted file mode 100644 index 9110ced..0000000 --- a/brouter-expressions/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/brouter-expressions/src/main/java/btools/expressions/BExpression.java b/brouter-expressions/src/main/java/btools/expressions/BExpression.java index 45718ad..d266780 100644 --- a/brouter-expressions/src/main/java/btools/expressions/BExpression.java +++ b/brouter-expressions/src/main/java/btools/expressions/BExpression.java @@ -9,14 +9,15 @@ final class BExpression { private static final int ADD_EXP = 20; private static final int MULTIPLY_EXP = 21; - private static final int MAX_EXP = 22; - private static final int EQUAL_EXP = 23; - private static final int GREATER_EXP = 24; - private static final int MIN_EXP = 25; + private static final int DIVIDE_EXP = 22; + private static final int MAX_EXP = 23; + private static final int EQUAL_EXP = 24; + private static final int GREATER_EXP = 25; + private static final int MIN_EXP = 26; - private static final int SUB_EXP = 26; - private static final int LESSER_EXP = 27; - private static final int XOR_EXP = 28; + private static final int SUB_EXP = 27; + private static final int LESSER_EXP = 28; + private static final int XOR_EXP = 29; private static final int SWITCH_EXP = 30; private static final int ASSIGN_EXP = 31; @@ -66,11 +67,11 @@ final class BExpression { } else { BExpression eCollapsed = e.tryCollapse(); if (e != eCollapsed) { - e = eCollapsed; // allow breakspoint.. + e = eCollapsed; // allow breakpoint.. } BExpression eEvaluated = e.tryEvaluateConstant(); if (e != eEvaluated) { - e = eEvaluated; // allow breakspoint.. + e = eEvaluated; // allow breakpoint.. } } } @@ -144,6 +145,8 @@ final class BExpression { exp.typ = AND_EXP; } else if ("multiply".equals(operator)) { exp.typ = MULTIPLY_EXP; + } else if ("divide".equals(operator)) { + exp.typ = DIVIDE_EXP; } else if ("add".equals(operator)) { exp.typ = ADD_EXP; } else if ("max".equals(operator)) { @@ -239,15 +242,15 @@ final class BExpression { } // parse operands if (nops > 0) { - exp.op1 = BExpression.parse(ctx, level + 1, exp.typ == ASSIGN_EXP ? "=" : null); + exp.op1 = parse(ctx, level + 1, exp.typ == ASSIGN_EXP ? "=" : null); } if (nops > 1) { if (ifThenElse) checkExpectedToken(ctx, "then"); - exp.op2 = BExpression.parse(ctx, level + 1, null); + exp.op2 = parse(ctx, level + 1, null); } if (nops > 2) { if (ifThenElse) checkExpectedToken(ctx, "else"); - exp.op3 = BExpression.parse(ctx, level + 1, null); + exp.op3 = parse(ctx, level + 1, null); } if (brackets) { checkExpectedToken(ctx, ")"); @@ -277,6 +280,8 @@ final class BExpression { return op1.evaluate(ctx) - op2.evaluate(ctx); case MULTIPLY_EXP: return op1.evaluate(ctx) * op2.evaluate(ctx); + case DIVIDE_EXP: + return divide(op1.evaluate(ctx), op2.evaluate(ctx)); case MAX_EXP: return max(op1.evaluate(ctx), op2.evaluate(ctx)); case MIN_EXP: @@ -360,6 +365,11 @@ final class BExpression { return v1 < v2 ? v1 : v2; } + private float divide(float v1, float v2) { + if (v2 == 0f) throw new IllegalArgumentException("div by zero"); + return v1 / v2; + } + @Override public String toString() { if (typ == NUMBER_EXP) { diff --git a/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java b/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java index feb86a1..a459f38 100644 --- a/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java +++ b/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java @@ -14,6 +14,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.NavigableMap; import java.util.Random; import java.util.StringTokenizer; import java.util.TreeMap; @@ -227,7 +228,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier { } public List getKeyValueList(boolean inverseDirection, byte[] ab) { - ArrayList res = new ArrayList<>(); + List res = new ArrayList<>(); decode(lookupData, inverseDirection, ab); for (int inum = 0; inum < lookupValues.size(); inum++) { // loop over lookup names BExpressionLookupValue[] va = lookupValues.get(inum); @@ -245,7 +246,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier { public int getLookupKey(String name) { int res = -1; try { - res = lookupNumbers.get(name).intValue(); + res = lookupNumbers.get(name); } catch (Exception e) { } return res; @@ -433,11 +434,11 @@ public abstract class BExpressionContext implements IByteArrayUnifier { public void dumpStatistics() { - TreeMap counts = new TreeMap<>(); + NavigableMap counts = new TreeMap<>(); // first count for (String name : lookupNumbers.keySet()) { int cnt = 0; - int inum = lookupNumbers.get(name).intValue(); + int inum = lookupNumbers.get(name); int[] histo = lookupHistograms.get(inum); // if ( histo.length == 500 ) continue; for (int i = 2; i < histo.length; i++) { @@ -450,7 +451,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier { String key = counts.lastEntry().getKey(); String name = counts.get(key); counts.remove(key); - int inum = lookupNumbers.get(name).intValue(); + int inum = lookupNumbers.get(name); BExpressionLookupValue[] values = lookupValues.get(inum); int[] histo = lookupHistograms.get(inum); if (values.length == 1000) continue; @@ -507,7 +508,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier { public String variableName(int idx) { for (Map.Entry e : variableNumbers.entrySet()) { - if (e.getValue().intValue() == idx) { + if (e.getValue() == idx) { return e.getKey(); } } @@ -544,9 +545,8 @@ public abstract class BExpressionContext implements IByteArrayUnifier { } // look for that value - int inum = num.intValue(); - BExpressionLookupValue[] values = lookupValues.get(inum); - int[] histo = lookupHistograms.get(inum); + BExpressionLookupValue[] values = lookupValues.get(num); + int[] histo = lookupHistograms.get(num); int i = 0; boolean bFoundAsterix = false; for (; i < values.length; i++) { @@ -558,102 +558,105 @@ public abstract class BExpressionContext implements IByteArrayUnifier { if (lookupData2 != null) { // do not create unknown value for external data array, // record as 'unknown' instead - lookupData2[inum] = 1; // 1 == unknown + lookupData2[num] = 1; // 1 == unknown if (bFoundAsterix) { // found value for lookup * //System.out.println( "add unknown " + name + " " + value ); String org = value; try { // remove some unused characters - value = value.replace(",", "."); - value = value.replace(">", ""); - value = value.replace("_", ""); + value = value.replaceAll(",", "."); + value = value.replaceAll(">", ""); + value = value.replaceAll("_", ""); + value = value.replaceAll(" ", ""); + value = value.replaceAll("~", ""); + value = value.replace((char) 8217, '\''); + value = value.replace((char) 8221, '"'); if (value.indexOf("-") == 0) value = value.substring(1); - if (value.indexOf("~") == 0) value = value.substring(1); - if (value.contains("-")) { // replace eg. 1.4-1.6 m + if (value.contains("-")) { + // replace eg. 1.4-1.6 m to 1.4m + // but also 1'-6" to 1' + // keep the unit of measure String tmp = value.substring(value.indexOf("-") + 1).replaceAll("[0-9.,-]", ""); - value = value.substring(0, value.indexOf("-")) + tmp; + value = value.substring(0, value.indexOf("-")); + if (value.matches("\\d+(\\.\\d+)?")) value += tmp; } + value = value.toLowerCase(Locale.US); + // do some value conversion - if (value.toLowerCase().contains("ft")) { - float foot = 0f; + if (value.contains("ft")) { + float feet = 0f; int inch = 0; - String[] sa = value.toLowerCase().trim().split("ft"); - if (sa.length >= 1) foot = Float.parseFloat(sa[0].trim()); + String[] sa = value.split("ft"); + if (sa.length >= 1) feet = Float.parseFloat(sa[0]); if (sa.length == 2) { value = sa[1]; if (value.indexOf("in") > 0) value = value.substring(0, value.indexOf("in")); - inch = Integer.parseInt(value.trim()); - foot += inch / 12f; + inch = Integer.parseInt(value); + feet += inch / 12f; } - value = String.format(Locale.US, "%3.1f", foot * 0.3048f); - } - if (value.toLowerCase().contains("'")) { - float foot = 0f; + value = String.format(Locale.US, "%3.1f", feet * 0.3048f); + } else if (value.contains("'")) { + float feet = 0f; int inch = 0; - String[] sa = value.toLowerCase().trim().split("'"); - if (sa.length >= 1) foot = Float.parseFloat(sa[0].trim()); + String[] sa = value.split("'"); + if (sa.length >= 1) feet = Float.parseFloat(sa[0]); if (sa.length == 2) { value = sa[1]; if (value.indexOf("''") > 0) value = value.substring(0, value.indexOf("''")); if (value.indexOf("\"") > 0) value = value.substring(0, value.indexOf("\"")); - inch = Integer.parseInt(value.trim()); - foot += inch / 12f; + inch = Integer.parseInt(value); + feet += inch / 12f; } - value = String.format(Locale.US, "%3.1f", foot * 0.3048f); + value = String.format(Locale.US, "%3.1f", feet * 0.3048f); } else if (value.contains("in") || value.contains("\"")) { float inch = 0f; if (value.indexOf("in") > 0) value = value.substring(0, value.indexOf("in")); if (value.indexOf("\"") > 0) value = value.substring(0, value.indexOf("\"")); - inch = Float.parseFloat(value.trim()); + inch = Float.parseFloat(value); value = String.format(Locale.US, "%3.1f", inch * 0.0254f); - } else if (value.toLowerCase().contains("feet") || value.toLowerCase().contains("foot")) { + } else if (value.contains("feet") || value.contains("foot")) { float feet = 0f; - String s = value.substring(0, value.toLowerCase().indexOf("f")); - feet = Float.parseFloat(s.trim()); + String s = value.substring(0, value.indexOf("f")); + feet = Float.parseFloat(s); value = String.format(Locale.US, "%3.1f", feet * 0.3048f); - } else if (value.toLowerCase().contains("fathom") || value.toLowerCase().contains("fm")) { - float fathom = 0f; - String s = value.substring(0, value.toLowerCase().indexOf("f")); - fathom = Float.parseFloat(s.trim()); + } else if (value.contains("fathom") || value.contains("fm")) { + String s = value.substring(0, value.indexOf("f")); + float fathom = Float.parseFloat(s); value = String.format(Locale.US, "%3.1f", fathom * 1.8288f); } else if (value.contains("cm")) { - String[] sa = value.trim().split("cm"); - if (sa.length == 1) value = sa[0].trim(); - float cm = Float.parseFloat(value.trim()); - value = String.format(Locale.US, "%3.1f", cm * 100f); - } else if (value.toLowerCase().contains("meter")) { - String s = value.substring(0, value.toLowerCase().indexOf("m")); - value = s.trim(); - } else if (value.toLowerCase().contains("mph")) { - value = value.replace("_", ""); - String[] sa = value.trim().toLowerCase().split("mph"); - if (sa.length >= 1) value = sa[0].trim(); - float mph = Float.parseFloat(value.trim()); + String[] sa = value.split("cm"); + if (sa.length >= 1) value = sa[0]; + float cm = Float.parseFloat(value); + value = String.format(Locale.US, "%3.1f", cm / 100f); + } else if (value.contains("meter")) { + value = value.substring(0, value.indexOf("m")); + } else if (value.contains("mph")) { + String[] sa = value.split("mph"); + if (sa.length >= 1) value = sa[0]; + float mph = Float.parseFloat(value); value = String.format(Locale.US, "%3.1f", mph * 1.609344f); - } else if (value.toLowerCase().contains("knot")) { - String[] sa = value.trim().toLowerCase().split("knot"); - if (sa.length >= 1) value = sa[0].trim(); - float nm = Float.parseFloat(value.trim()); + } else if (value.contains("knot")) { + String[] sa = value.split("knot"); + if (sa.length >= 1) value = sa[0]; + float nm = Float.parseFloat(value); value = String.format(Locale.US, "%3.1f", nm * 1.852f); } else if (value.contains("kmh") || value.contains("km/h") || value.contains("kph")) { - String[] sa = value.trim().split("k"); - if (sa.length == 1) value = sa[0].trim(); + String[] sa = value.split("k"); + if (sa.length > 1) value = sa[0]; } else if (value.contains("m")) { - String s = value.substring(0, value.toLowerCase().indexOf("m")); - value = s.trim(); + value = value.substring(0, value.indexOf("m")); } else if (value.contains("(")) { - String s = value.substring(0, value.toLowerCase().indexOf("(")); - value = s.trim(); + value = value.substring(0, value.indexOf("(")); } // found negative maxdraft values // no negative values // values are float with 2 decimals - lookupData2[inum] = 1000 + (int) (Math.abs(Float.parseFloat(value)) * 100f); + lookupData2[num] = 1000 + (int) (Math.abs(Float.parseFloat(value)) * 100f); } catch (Exception e) { // ignore errors System.err.println("error for " + name + " " + org + " trans " + value + " " + e.getMessage()); - lookupData2[inum] = 0; + lookupData2[num] = 0; } } return newValue; @@ -674,15 +677,15 @@ public abstract class BExpressionContext implements IByteArrayUnifier { histo = nhisto; newValue = new BExpressionLookupValue(value); values[i] = newValue; - lookupHistograms.set(inum, histo); - lookupValues.set(inum, values); + lookupHistograms.set(num, histo); + lookupValues.set(num, values); } histo[i]++; // finally remember the actual data - if (lookupData2 != null) lookupData2[inum] = i; - else lookupData[inum] = i; + if (lookupData2 != null) lookupData2[num] = i; + else lookupData[num] = i; return newValue; } @@ -697,11 +700,10 @@ public abstract class BExpressionContext implements IByteArrayUnifier { } // look for that value - int inum = num.intValue(); - int nvalues = lookupValues.get(inum).length; + int nvalues = lookupValues.get(num).length; if (valueIndex < 0 || valueIndex >= nvalues) throw new IllegalArgumentException("value index out of range for name " + name + ": " + valueIndex); - lookupData[inum] = valueIndex; + lookupData[num] = valueIndex; } @@ -718,9 +720,8 @@ public abstract class BExpressionContext implements IByteArrayUnifier { } // look for that value - int inum = num.intValue(); - int nvalues = lookupValues.get(inum).length; - int oldValueIndex = lookupData[inum]; + int nvalues = lookupValues.get(num).length; + int oldValueIndex = lookupData[num]; if (oldValueIndex > 1 && oldValueIndex < valueIndex) { return; } @@ -729,12 +730,12 @@ public abstract class BExpressionContext implements IByteArrayUnifier { } if (valueIndex < 0) throw new IllegalArgumentException("value index out of range for name " + name + ": " + valueIndex); - lookupData[inum] = valueIndex; + lookupData[num] = valueIndex; } public boolean getBooleanLookupValue(String name) { Integer num = lookupNumbers.get(name); - return num != null && lookupData[num.intValue()] == 2; + return num != null && lookupData[num] == 2; } public int getOutputVariableIndex(String name, boolean mustExist) { @@ -846,7 +847,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier { public void setVariableValue(String name, float value, boolean create) { Integer num = variableNumbers.get(name); if (num != null) { - variableData[num.intValue()] = value; + variableData[num] = value; } else if (create) { num = getVariableIdx(name, create); float[] readOnlyData = variableData; @@ -855,13 +856,13 @@ public abstract class BExpressionContext implements IByteArrayUnifier { for (int i = 0; i < minWriteIdx; i++) { variableData[i] = readOnlyData[i]; } - variableData[num.intValue()] = value; + variableData[num] = value; } } public float getVariableValue(String name, float defaultValue) { Integer num = variableNumbers.get(name); - return num == null ? defaultValue : getVariableValue(num.intValue()); + return num == null ? defaultValue : getVariableValue(num); } float getVariableValue(int variableIdx) { @@ -879,7 +880,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier { return -1; } } - return num.intValue(); + return num; } int getMinWriteIdx() { @@ -897,7 +898,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier { public int getLookupNameIdx(String name) { Integer num = lookupNumbers.get(name); - return num == null ? -1 : num.intValue(); + return num == null ? -1 : num; } public final void markLookupIdxUsed(int idx) { diff --git a/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java b/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java index 5b37b7c..0a8338c 100644 --- a/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java +++ b/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java @@ -12,7 +12,7 @@ public final class BExpressionContextWay extends BExpressionContext implements T private boolean decodeForbidden = true; private static String[] buildInVariables = - {"costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "classifiermask", "maxspeed", "uphillcost", "downhillcost", "uphillcutoff", "downhillcutoff"}; + {"costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "classifiermask", "maxspeed", "uphillcost", "downhillcost", "uphillcutoff", "downhillcutoff", "uphillmaxslope", "downhillmaxslope", "uphillmaxslopecost", "downhillmaxslopecost"}; protected String[] getBuildInVariableNames() { return buildInVariables; @@ -82,6 +82,22 @@ public final class BExpressionContextWay extends BExpressionContext implements T return getBuildInVariable(15); } + public float getUphillmaxslope() { + return getBuildInVariable(16); + } + + public float getDownhillmaxslope() { + return getBuildInVariable(17); + } + + public float getUphillmaxslopecost() { + return getBuildInVariable(18); + } + + public float getDownhillmaxslopecost() { + return getBuildInVariable(19); + } + public BExpressionContextWay(BExpressionMetaData meta) { super("way", meta); } diff --git a/brouter-expressions/src/test/java/btools/expressions/EncodeDecodeTest.java b/brouter-expressions/src/test/java/btools/expressions/EncodeDecodeTest.java index 6979387..b731ea0 100644 --- a/brouter-expressions/src/test/java/btools/expressions/EncodeDecodeTest.java +++ b/brouter-expressions/src/test/java/btools/expressions/EncodeDecodeTest.java @@ -29,7 +29,7 @@ public class EncodeDecodeTest { "depth=1'6\"", // "depth=6 feet", "maxheight=5.1m", - "maxdraft=~3 mt", + "maxdraft=~3 m - 4 m", "reversedirection=yes" }; diff --git a/brouter-map-creator/.gitignore b/brouter-map-creator/.gitignore deleted file mode 100644 index 84c048a..0000000 --- a/brouter-map-creator/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build/ diff --git a/brouter-map-creator/build.gradle b/brouter-map-creator/build.gradle index 7c48259..3f8c263 100644 --- a/brouter-map-creator/build.gradle +++ b/brouter-map-creator/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'brouter.application-conventions' } dependencies { @@ -8,6 +8,4 @@ dependencies { implementation project(':brouter-expressions') implementation group: 'org.openstreetmap.osmosis', name: 'osmosis-osm-binary', version: '0.48.3' - - testImplementation('junit:junit:4.13.1') } diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/ConvertLidarTile.java b/brouter-map-creator/src/main/java/btools/mapcreator/ConvertLidarTile.java deleted file mode 100644 index 2a00e96..0000000 --- a/brouter-map-creator/src/main/java/btools/mapcreator/ConvertLidarTile.java +++ /dev/null @@ -1,250 +0,0 @@ -package btools.mapcreator; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.DataInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Arrays; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -public class ConvertLidarTile { - private static int NROWS; - private static int NCOLS; - - public static final short NODATA2 = -32767; // hgt-formats nodata - public static final short NODATA = Short.MIN_VALUE; - - private static final String HGT_FILE_EXT = ".hgt"; - private static final int HGT_BORDER_OVERLAP = 1; - private static final int HGT_3ASEC_ROWS = 1201; // 3 arc second resolution (90m) - private static final int HGT_3ASEC_FILE_SIZE = HGT_3ASEC_ROWS * HGT_3ASEC_ROWS * Short.BYTES; - private static final int HGT_1ASEC_ROWS = 3601; // 1 arc second resolution (30m) - - static short[] imagePixels; - - private static void readHgtZip(String filename, int rowOffset, int colOffset) throws Exception { - ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(filename))); - try { - for (; ; ) { - ZipEntry ze = zis.getNextEntry(); - if (ze == null) break; - if (ze.getName().toLowerCase().endsWith(HGT_FILE_EXT)) { - readHgtFromStream(zis, rowOffset, colOffset, HGT_3ASEC_ROWS); - return; - } - } - } finally { - zis.close(); - } - } - - private static void readHgtFromStream(InputStream is, int rowOffset, int colOffset, int rowLength) - throws Exception { - DataInputStream dis = new DataInputStream(new BufferedInputStream(is)); - for (int ir = 0; ir < rowLength; ir++) { - int row = rowOffset + ir; - - for (int ic = 0; ic < rowLength; ic++) { - int col = colOffset + ic; - - int i1 = dis.read(); // msb first! - int i0 = dis.read(); - - if (i0 == -1 || i1 == -1) - throw new RuntimeException("unexpected end of file reading hgt entry!"); - - short val = (short) ((i1 << 8) | i0); - - if (val == NODATA2) { - val = NODATA; - } - - setPixel(row, col, val); - } - } - } - - private static void setPixel(int row, int col, short val) { - if (row >= 0 && row < NROWS && col >= 0 && col < NCOLS) { - imagePixels[row * NCOLS + col] = val; - } - } - - private static short getPixel(int row, int col) { - if (row >= 0 && row < NROWS && col >= 0 && col < NCOLS) { - return imagePixels[row * NCOLS + col]; - } - return NODATA; - } - - - public static void doConvert(String inputDir, int lonDegreeStart, int latDegreeStart, String outputFile) throws Exception { - int extraBorder = 0; - - NROWS = 5 * 1200 + 1 + 2 * extraBorder; - NCOLS = 5 * 1200 + 1 + 2 * extraBorder; - - imagePixels = new short[NROWS * NCOLS]; // 650 MB ! - - // prefill as NODATA - for (int row = 0; row < NROWS; row++) { - for (int col = 0; col < NCOLS; col++) { - imagePixels[row * NCOLS + col] = NODATA; - } - } - - for (int latIdx = -1; latIdx <= 5; latIdx++) { - int latDegree = latDegreeStart + latIdx; - int rowOffset = extraBorder + (4 - latIdx) * 1200; - - for (int lonIdx = -1; lonIdx <= 5; lonIdx++) { - int lonDegree = lonDegreeStart + lonIdx; - int colOffset = extraBorder + lonIdx * 1200; - - String filename = inputDir + "/" + formatLat(latDegree) + formatLon(lonDegree) + ".zip"; - File f = new File(filename); - if (f.exists() && f.length() > 0) { - System.out.println("exist: " + filename); - readHgtZip(filename, rowOffset, colOffset); - } else { - System.out.println("none : " + filename); - } - } - } - - boolean halfCol5 = false; // no halfcol tiles in lidar data (?) - - - SrtmRaster raster = new SrtmRaster(); - raster.nrows = NROWS; - raster.ncols = NCOLS; - raster.halfcol = halfCol5; - raster.noDataValue = NODATA; - raster.cellsize = 1 / 1200.; - raster.xllcorner = lonDegreeStart - (0.5 + extraBorder) * raster.cellsize; - raster.yllcorner = latDegreeStart - (0.5 + extraBorder) * raster.cellsize; - raster.eval_array = imagePixels; - - // encode the raster - OutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile)); - new RasterCoder().encodeRaster(raster, os); - os.close(); - - // decode the raster - InputStream is = new BufferedInputStream(new FileInputStream(outputFile)); - SrtmRaster raster2 = new RasterCoder().decodeRaster(is); - is.close(); - - short[] pix2 = raster2.eval_array; - if (pix2.length != imagePixels.length) - throw new RuntimeException("length mismatch!"); - - // compare decoding result - for (int row = 0; row < NROWS; row++) { - int colstep = halfCol5 ? 2 : 1; - for (int col = 0; col < NCOLS; col += colstep) { - int idx = row * NCOLS + col; - short p2 = pix2[idx]; - if (p2 != imagePixels[idx]) { - throw new RuntimeException("content mismatch: p2=" + p2 + " p1=" + imagePixels[idx]); - } - } - } - } - - private static String formatLon(int lon) { - if (lon >= 180) - lon -= 180; // TODO: w180 oder E180 ? - - String s = "E"; - if (lon < 0) { - lon = -lon; - s = "W"; - } - String n = "000" + lon; - return s + n.substring(n.length() - 3); - } - - private static String formatLat(int lat) { - String s = "N"; - if (lat < 0) { - lat = -lat; - s = "S"; - } - String n = "00" + lat; - return s + n.substring(n.length() - 2); - } - - public static void main(String[] args) throws Exception { - String filename90 = args[0]; - String filename30 = filename90.substring(0, filename90.length() - 3) + "bef"; - - int srtmLonIdx = Integer.parseInt(filename90.substring(5, 7).toLowerCase()); - int srtmLatIdx = Integer.parseInt(filename90.substring(8, 10).toLowerCase()); - - int ilon_base = (srtmLonIdx - 1) * 5 - 180; - int ilat_base = 150 - srtmLatIdx * 5 - 90; - - doConvert(args[1], ilon_base, ilat_base, filename30); - } - - public SrtmRaster getRaster(File f, double lon, double lat) throws Exception { - long fileSize; - InputStream inputStream; - - if (f.getName().toLowerCase().endsWith(".zip")) { - ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(f))); - for (; ; ) { - ZipEntry ze = zis.getNextEntry(); - if (ze == null) { - throw new FileNotFoundException(f.getName() + " doesn't contain a " + HGT_FILE_EXT + " file."); - } - if (ze.getName().toLowerCase().endsWith(HGT_FILE_EXT)) { - fileSize = ze.getSize(); - inputStream = zis; - break; - } - } - } else { - fileSize = f.length(); - inputStream = new FileInputStream(f); - } - - int rowLength; - if (fileSize > HGT_3ASEC_FILE_SIZE) { - rowLength = HGT_1ASEC_ROWS; - } else { - rowLength = HGT_3ASEC_ROWS; - } - - // stay at 1 deg * 1 deg raster - NROWS = rowLength; - NCOLS = rowLength; - - imagePixels = new short[NROWS * NCOLS]; - - // prefill as NODATA - Arrays.fill(imagePixels, NODATA); - readHgtFromStream(inputStream, 0, 0, rowLength); - inputStream.close(); - - SrtmRaster raster = new SrtmRaster(); - raster.nrows = NROWS; - raster.ncols = NCOLS; - raster.halfcol = false; // assume full resolution - raster.noDataValue = NODATA; - raster.cellsize = 1. / (double) (rowLength - HGT_BORDER_OVERLAP); - raster.xllcorner = (int) (lon < 0 ? lon - 1 : lon); //onDegreeStart - raster.cellsize; - raster.yllcorner = (int) (lat < 0 ? lat - 1 : lat); //latDegreeStart - raster.cellsize; - raster.eval_array = imagePixels; - - return raster; - } - -} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/ConvertSrtmTile.java b/brouter-map-creator/src/main/java/btools/mapcreator/ConvertSrtmTile.java deleted file mode 100644 index e18c664..0000000 --- a/brouter-map-creator/src/main/java/btools/mapcreator/ConvertSrtmTile.java +++ /dev/null @@ -1,260 +0,0 @@ -package btools.mapcreator; - -import java.io.*; -import java.util.zip.*; - -public class ConvertSrtmTile { - public static int NROWS; - public static int NCOLS; - - public static final short SKIPDATA = -32766; // >50 degree skipped pixel - public static final short NODATA2 = -32767; // bil-formats nodata - public static final short NODATA = Short.MIN_VALUE; - - static short[] imagePixels; - - public static int[] diffs = new int[100]; - - private static void readBilZip(String filename, int rowOffset, int colOffset, boolean halfCols) throws Exception { - ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(filename))); - try { - for (; ; ) { - ZipEntry ze = zis.getNextEntry(); - if (ze.getName().endsWith(".bil")) { - readBilFromStream(zis, rowOffset, colOffset, halfCols); - return; - } - } - } finally { - zis.close(); - } - } - - private static void readBilFromStream(InputStream is, int rowOffset, int colOffset, boolean halfCols) - throws Exception { - DataInputStream dis = new DataInputStream(new BufferedInputStream(is)); - for (int ir = 0; ir < 3601; ir++) { - int row = rowOffset + ir; - - for (int ic = 0; ic < 3601; ic++) { - int col = colOffset + ic; - - if ((ic % 2) == 1 && halfCols) { - if (getPixel(row, col) == NODATA) { - setPixel(row, col, SKIPDATA); - } - continue; - } - - int i0 = dis.read(); - int i1 = dis.read(); - - if (i0 == -1 || i1 == -1) - throw new RuntimeException("unexcepted end of file reading bil entry!"); - - short val = (short) ((i1 << 8) | i0); - - if (val == NODATA2) { - val = NODATA; - } - - setPixel(row, col, val); - } - } - } - - - private static void setPixel(int row, int col, short val) { - if (row >= 0 && row < NROWS && col >= 0 && col < NCOLS) { - imagePixels[row * NCOLS + col] = val; - } - } - - private static short getPixel(int row, int col) { - if (row >= 0 && row < NROWS && col >= 0 && col < NCOLS) { - return imagePixels[row * NCOLS + col]; - } - return NODATA; - } - - - public static void doConvert(String inputDir, String v1Dir, int lonDegreeStart, int latDegreeStart, String outputFile, SrtmRaster raster90) throws Exception { - int extraBorder = 10; - int datacells = 0; - int mismatches = 0; - - NROWS = 5 * 3600 + 1 + 2 * extraBorder; - NCOLS = 5 * 3600 + 1 + 2 * extraBorder; - - imagePixels = new short[NROWS * NCOLS]; // 650 MB ! - - // prefill as NODATA - for (int row = 0; row < NROWS; row++) { - for (int col = 0; col < NCOLS; col++) { - imagePixels[row * NCOLS + col] = NODATA; - } - } - - for (int latIdx = -1; latIdx <= 5; latIdx++) { - int latDegree = latDegreeStart + latIdx; - int rowOffset = extraBorder + (4 - latIdx) * 3600; - - for (int lonIdx = -1; lonIdx <= 5; lonIdx++) { - int lonDegree = lonDegreeStart + lonIdx; - int colOffset = extraBorder + lonIdx * 3600; - - String filename = inputDir + "/" + formatLat(latDegree) + "_" + formatLon(lonDegree) + "_1arc_v3_bil.zip"; - File f = new File(filename); - if (f.exists() && f.length() > 0) { - System.out.println("exist: " + filename); - boolean halfCol = latDegree >= 50 || latDegree < -50; - readBilZip(filename, rowOffset, colOffset, halfCol); - } else { - System.out.println("none : " + filename); - } - } - } - - boolean halfCol5 = latDegreeStart >= 50 || latDegreeStart < -50; - - for (int row90 = 0; row90 < 6001; row90++) { - int crow = 3 * row90 + extraBorder; // center row of 3x3 - for (int col90 = 0; col90 < 6001; col90++) { - int ccol = 3 * col90 + extraBorder; // center col of 3x3 - - // evaluate 3x3 area - if (raster90 != null && (!halfCol5 || (col90 % 2) == 0)) { - short v90 = raster90.eval_array[row90 * 6001 + col90]; - - int sum = 0; - int nodatas = 0; - int datas = 0; - int colstep = halfCol5 ? 2 : 1; - for (int row = crow - 1; row <= crow + 1; row++) { - for (int col = ccol - colstep; col <= ccol + colstep; col += colstep) { - short v30 = imagePixels[row * NCOLS + col]; - if (v30 == NODATA) { - nodatas++; - } else if (v30 != SKIPDATA) { - sum += v30; - datas++; - } - } - } - boolean doReplace = nodatas > 0 || v90 == NODATA || datas < 7; - if (!doReplace) { - datacells++; - int diff = sum - datas * v90; - if (diff < -4 || diff > 4) { - doReplace = true; - mismatches++; - } - - if (diff > -50 && diff < 50 && (row90 % 1200) != 0 && (col90 % 1200) != 0) { - diffs[diff + 50]++; - } - } - if (doReplace) { - for (int row = crow - 1; row <= crow + 1; row++) { - for (int col = ccol - colstep; col <= ccol + colstep; col += colstep) { - imagePixels[row * NCOLS + col] = v90; - } - } - } - } - } - } - - SrtmRaster raster = new SrtmRaster(); - raster.nrows = NROWS; - raster.ncols = NCOLS; - raster.halfcol = halfCol5; - raster.noDataValue = NODATA; - raster.cellsize = 1 / 3600.; - raster.xllcorner = lonDegreeStart - (0.5 + extraBorder) * raster.cellsize; - raster.yllcorner = latDegreeStart - (0.5 + extraBorder) * raster.cellsize; - raster.eval_array = imagePixels; - - // encode the raster - OutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile)); - new RasterCoder().encodeRaster(raster, os); - os.close(); - - // decode the raster - InputStream is = new BufferedInputStream(new FileInputStream(outputFile)); - SrtmRaster raster2 = new RasterCoder().decodeRaster(is); - is.close(); - - short[] pix2 = raster2.eval_array; - if (pix2.length != imagePixels.length) - throw new RuntimeException("length mismatch!"); - - // compare decoding result - for (int row = 0; row < NROWS; row++) { - int colstep = halfCol5 ? 2 : 1; - for (int col = 0; col < NCOLS; col += colstep) { - int idx = row * NCOLS + col; - if (imagePixels[idx] == SKIPDATA) { - continue; - } - short p2 = pix2[idx]; - if (p2 > SKIPDATA) { - p2 /= 2; - } - if (p2 != imagePixels[idx]) { - throw new RuntimeException("content mismatch!"); - } - } - } - - for (int i = 1; i < 100; i++) System.out.println("diff[" + (i - 50) + "] = " + diffs[i]); - System.out.println("datacells=" + datacells + " mismatch%=" + (100. * mismatches) / datacells); - btools.util.MixCoderDataOutputStream.stats(); - // test( raster ); - // raster.calcWeights( 50. ); - // test( raster ); - // 39828330 &lon=3115280&layer=OpenStreetMap - } - - private static void test(SrtmRaster raster) { - int lat0 = 39828330; - int lon0 = 3115280; - - for (int iy = -9; iy <= 9; iy++) { - StringBuilder sb = new StringBuilder(); - for (int ix = -9; ix <= 9; ix++) { - int lat = lat0 + 90000000 - 100 * iy; - int lon = lon0 + 180000000 + 100 * ix; - int ival = (int) (raster.getElevation(lon, lat) / 4.); - String sval = " " + ival; - sb.append(sval.substring(sval.length() - 4)); - } - System.out.println(sb); - System.out.println(); - } - } - - private static String formatLon(int lon) { - if (lon >= 180) - lon -= 180; // TODO: w180 oder E180 ? - - String s = "e"; - if (lon < 0) { - lon = -lon; - s = "w"; - } - String n = "000" + lon; - return s + n.substring(n.length() - 3); - } - - private static String formatLat(int lat) { - String s = "n"; - if (lat < 0) { - lat = -lat; - s = "s"; - } - String n = "00" + lat; - return s + n.substring(n.length() - 2); - } - -} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/ConvertUrlList.java b/brouter-map-creator/src/main/java/btools/mapcreator/ConvertUrlList.java deleted file mode 100644 index ab5b8db..0000000 --- a/brouter-map-creator/src/main/java/btools/mapcreator/ConvertUrlList.java +++ /dev/null @@ -1,53 +0,0 @@ -package btools.mapcreator; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; - -public class ConvertUrlList { - public static final short NODATA = -32767; - - public static void main(String[] args) throws Exception { - BufferedReader br = new BufferedReader(new FileReader(args[0])); - - for (; ; ) { - String line = br.readLine(); - if (line == null) { - break; - } - int idx1 = line.indexOf("srtm_"); - if (idx1 < 0) { - continue; - } - - String filename90 = line.substring(idx1); - String filename30 = filename90.substring(0, filename90.length() - 3) + "bef"; - - if (new File(filename30).exists()) { - continue; - } - - // int srtmLonIdx = (ilon+5000000)/5000000; -> ilon = (srtmLonIdx-1)*5 - // int srtmLatIdx = (154999999-ilat)/5000000; -> ilat = 155 - srtmLatIdx*5 - - int srtmLonIdx = Integer.parseInt(filename90.substring(5, 7).toLowerCase()); - int srtmLatIdx = Integer.parseInt(filename90.substring(8, 10).toLowerCase()); - - int ilon_base = (srtmLonIdx - 1) * 5 - 180; - int ilat_base = 150 - srtmLatIdx * 5 - 90; - - SrtmRaster raster90 = null; - - File file90 = new File(new File(args[1]), filename90); - if (file90.exists()) { - System.out.println("reading " + file90); - raster90 = new SrtmData(file90).getRaster(); - } - - ConvertSrtmTile.doConvert(args[2], args[3], ilon_base, ilat_base, filename30, raster90); - } - br.close(); - } - - -} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/CreateElevationRasterImage.java b/brouter-map-creator/src/main/java/btools/mapcreator/CreateElevationRasterImage.java new file mode 100644 index 0000000..4cbd3c5 --- /dev/null +++ b/brouter-map-creator/src/main/java/btools/mapcreator/CreateElevationRasterImage.java @@ -0,0 +1,335 @@ +package btools.mapcreator; + +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; + +import javax.imageio.ImageIO; + + +public class CreateElevationRasterImage { + + final static boolean DEBUG = false; + + int[] data; + ElevationRaster lastSrtmRaster; + Map srtmmap; + int lastSrtmLonIdx; + int lastSrtmLatIdx; + short maxElev = Short.MIN_VALUE; + short minElev = Short.MAX_VALUE; + String srtmdir; + boolean missingData; + Map colorMap; + + + private void createImage(double lon, double lat, String dir, String imageName, int maxX, int maxY, int downscale, String format, String colors) throws Exception { + srtmdir = dir; + if (colors != null) { + loadColors(colors); + } + if (format.equals("hgt")) { + createImageFromHgt(lon, lat, dir, imageName, maxX, maxY); + return; + } + if (!format.equals("bef")) { + System.out.println("wrong format (bef|hgt)"); + return; + } + srtmmap = new HashMap<>(); + lastSrtmLonIdx = -1; + lastSrtmLatIdx = -1; + lastSrtmRaster = null; + NodeData n = new NodeData(1, lon, lat); + ElevationRaster srtm = srtmForNode(n.ilon, n.ilat); + if (srtm == null) { + System.out.println("no data"); + return; + } + System.out.println("srtm " + srtm.toString()); + //System.out.println("srtm elev " + srtm.getElevation(n.ilon, n.ilat)); + double[] pos = getElevationPos(srtm, n.ilon, n.ilat); + //System.out.println("srtm pos " + Math.round(pos[0]) + " " + Math.round(pos[1])); + short[] raster = srtm.eval_array; + int rasterX = srtm.ncols; + int rasterY = srtm.nrows; + + int tileSize = 1000 / downscale; + int sizeX = (maxX); + int sizeY = (maxY); + int[] imgraster = new int[sizeX * sizeY]; + for (int y = 0; y < sizeY; y++) { + for (int x = 0; x < sizeX; x++) { + //short e = getElevationXY(srtm, pos[0] + (sizeY - y) * downscale, pos[1] + (x * downscale)); + short e = get(srtm, (int) Math.round(pos[0]) + (sizeY - y), x + (int) Math.round(pos[1])); + if (e != Short.MIN_VALUE && e < minElev) minElev = e; + if (e != Short.MIN_VALUE && e > maxElev) maxElev = e; + + if (e == Short.MIN_VALUE) { + imgraster[sizeY * y + x] = 0xffff; + } else { + //imgraster[sizeY * y + x] = getColorForHeight((short)(e/4)); //(int)(e/4.); + imgraster[sizeY * y + x] = getColorForHeight(e); + } + } + } + System.out.println("srtm target " + sizeX + " " + sizeY + " (" + rasterX + " " + rasterY + ")" + " min " + minElev + " max " + maxElev); + + if (DEBUG) { + String out = "short "; + for (int i = 0; i < 100; i++) { + out += " " + get(srtm, sizeY - 0, i); + } + System.out.println(out); + } + + BufferedImage argbImage = new BufferedImage(sizeX, sizeY, BufferedImage.TYPE_INT_ARGB); + data = ((DataBufferInt) argbImage.getRaster().getDataBuffer()).getData(); + + for (int y = 0; y < sizeY; y++) { + for (int x = 0; x < sizeX; x++) { + int v0 = imgraster[sizeX * y + x]; + + int rgb; + if (v0 != 0xffff) + rgb = 0xff000000 | v0; //(v0 << 8); + else + rgb = 0xff000000; + data[y * sizeX + x] = rgb; + } + } + + ImageIO.write(argbImage, "png", new FileOutputStream(imageName)); + } + + private void createImageFromHgt(double lon, double lat, String dir, String imageName, int maxX, int maxY) throws Exception { + HgtReader rdr = new HgtReader(dir); + short[] data = rdr.getElevationDataFromHgt(lat, lon); + if (data == null) { + System.out.println("no data"); + return; + } + + int size = (data != null ? data.length : 0); + int rowlen = (int) Math.sqrt(size); + int sizeX = (maxX); + int sizeY = (maxY); + int[] imgraster = new int[sizeX * sizeY]; + + for (int y = 0; y < sizeY; y++) { + for (int x = 0; x < sizeX; x++) { + short e = data[(rowlen * y) + x]; + if (e != HgtReader.HGT_VOID && e < minElev) minElev = e; + if (e != HgtReader.HGT_VOID && e > maxElev) maxElev = e; + + if (e == HgtReader.HGT_VOID) { + imgraster[sizeY * y + x] = 0xffff; + } else if (e == 0) { + imgraster[sizeY * y + x] = 0xffff; + } else { + imgraster[sizeY * y + x] = getColorForHeight((short) (e)); + } + } + } + System.out.println("hgt size " + rowlen + " x " + rowlen + " min " + minElev + " max " + maxElev); + if (DEBUG) { + String out = "short "; + for (int i = 0; i < 100; i++) { + out += " " + data[i]; + } + System.out.println(out); + } + BufferedImage argbImage = new BufferedImage(sizeX, sizeY, BufferedImage.TYPE_INT_ARGB); + int[] idata = ((DataBufferInt) argbImage.getRaster().getDataBuffer()).getData(); + + for (int y = 0; y < sizeY; y++) { + for (int x = 0; x < sizeX; x++) { + int v0 = imgraster[sizeX * y + x]; + + int rgb; + if (v0 != 0xffff) + rgb = 0xff000000 | v0; //(v0 << 8); + else + rgb = 0xff000000; + idata[y * sizeX + x] = rgb; + } + } + + ImageIO.write(argbImage, "png", new FileOutputStream(imageName)); + + } + + private void loadColors(String colors) { + if (DEBUG) System.out.println("colors=" + colors); + File colFile = new File(colors); + if (colFile.exists()) { + BufferedReader reader = null; + colorMap = new TreeMap<>(); + try { + reader = new BufferedReader(new FileReader(colors)); + String line = reader.readLine(); + + while (line != null) { + if (DEBUG) System.out.println(line); + String[] sa = line.split(","); + if (!line.startsWith("#") && sa.length == 4) { + short e = Short.parseShort(sa[0].trim()); + short r = Short.parseShort(sa[1].trim()); + short g = Short.parseShort(sa[2].trim()); + short b = Short.parseShort(sa[3].trim()); + colorMap.put(e, new Color(r, g, b)); + } + // read next line + line = reader.readLine(); + } + + } catch (Exception e) { + e.printStackTrace(); + colorMap = null; + } finally { + if (reader != null) { + try { + reader.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } else { + System.out.println("color file " + colors + " not found"); + } + } + + public double[] getElevationPos(ElevationRaster srtm, int ilon, int ilat) { + double lon = ilon / 1000000. - 180.; + double lat = ilat / 1000000. - 90.; + + double dcol = (lon - srtm.xllcorner) / srtm.cellsize - 0.5; + double drow = (lat - srtm.yllcorner) / srtm.cellsize - 0.5; + int row = (int) drow; + int col = (int) dcol; + if (col < 0) col = 0; + if (row < 0) row = 0; + + return new double[]{drow, dcol}; + } + + private short get(ElevationRaster srtm, int r, int c) { + short e = srtm.eval_array[(srtm.nrows - 1 - r) * srtm.ncols + c]; + if (e == Short.MIN_VALUE) missingData = true; + return e; + } + + public short getElevationXY(ElevationRaster srtm, double drow, double dcol) { + int row = (int) drow; + int col = (int) dcol; + if (col < 0) col = 0; + if (col >= srtm.ncols - 1) col = srtm.ncols - 2; + if (row < 0) row = 0; + if (row >= srtm.nrows - 1) row = srtm.nrows - 2; + double wrow = drow - row; + double wcol = dcol - col; + missingData = false; + + double eval = (1. - wrow) * (1. - wcol) * get(srtm, row, col) + + (wrow) * (1. - wcol) * get(srtm, row + 1, col) + + (1. - wrow) * (wcol) * get(srtm, row, col + 1) + + (wrow) * (wcol) * get(srtm, row + 1, col + 1); + + return missingData ? Short.MIN_VALUE : (short) (eval * 4); + } + + + int getColorForHeight(short h) { + if (colorMap == null) { + colorMap = new TreeMap<>(); + colorMap.put((short) 0, new Color(102, 153, 153)); + colorMap.put((short) 1, new Color(0, 102, 0)); + colorMap.put((short) 500, new Color(251, 255, 128)); + colorMap.put((short) 1200, new Color(224, 108, 31)); + colorMap.put((short) 2500, new Color(200, 55, 55)); + colorMap.put((short) 4000, new Color(215, 244, 244)); + colorMap.put((short) 8000, new Color(255, 244, 244)); + } + Color lastColor = null; + short lastKey = 0; + for (Entry entry : colorMap.entrySet()) { + short key = entry.getKey(); + Color value = entry.getValue(); + if (key == h) return value.getRGB(); + if (lastColor != null && lastKey < h && key > h) { + double between = (double) (h - lastKey) / (key - lastKey); + return mixColors(value, lastColor, between); + } + lastColor = value; + lastKey = key; + } + return 0; + } + + public int mixColors(Color color1, Color color2, double percent) { + double inverse_percent = 1.0 - percent; + int redPart = (int) (color1.getRed() * percent + color2.getRed() * inverse_percent); + int greenPart = (int) (color1.getGreen() * percent + color2.getGreen() * inverse_percent); + int bluePart = (int) (color1.getBlue() * percent + color2.getBlue() * inverse_percent); + return new Color(redPart, greenPart, bluePart).getRGB(); + } + + private ElevationRaster srtmForNode(int ilon, int ilat) throws Exception { + int srtmLonIdx = (ilon + 5000000) / 5000000; + int srtmLatIdx = (654999999 - ilat) / 5000000 - 100; // ugly negative rounding... + + if (srtmLonIdx == lastSrtmLonIdx && srtmLatIdx == lastSrtmLatIdx) { + return lastSrtmRaster; + } + lastSrtmLonIdx = srtmLonIdx; + lastSrtmLatIdx = srtmLatIdx; + + String slonidx = "0" + srtmLonIdx; + String slatidx = "0" + srtmLatIdx; + String filename = "srtm_" + slonidx.substring(slonidx.length() - 2) + "_" + slatidx.substring(slatidx.length() - 2); + + lastSrtmRaster = srtmmap.get(filename); + if (lastSrtmRaster == null && !srtmmap.containsKey(filename)) { + File f = new File(new File(srtmdir), filename + ".bef"); + if (f.exists()) { + System.out.println("*** reading: " + f); + try { + InputStream isc = new BufferedInputStream(new FileInputStream(f)); + lastSrtmRaster = new ElevationRasterCoder().decodeRaster(isc); + isc.close(); + } catch (Exception e) { + System.out.println("**** ERROR reading " + f + " ****"); + } + srtmmap.put(filename, lastSrtmRaster); + return lastSrtmRaster; + } + + srtmmap.put(filename, lastSrtmRaster); + } + return lastSrtmRaster; + } + + public static void main(String[] args) throws Exception { + if (args.length < 6) { + System.out.println("usage: java CreateLidarImage [type] [color_file]"); + System.out.println("\nwhere: type = [bef|hgt] downscale = [1|2|4|..]"); + return; + } + String format = args.length >= 8 ? args[7] : "bef"; + String colors = args.length == 9 ? args[8] : null; + new CreateElevationRasterImage().createImage(Double.parseDouble(args[0]), Double.parseDouble(args[1]), args[2], args[3], + Integer.parseInt(args[4]), Integer.parseInt(args[5]), Integer.parseInt(args[6]), format, colors); + } +} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/SrtmRaster.java b/brouter-map-creator/src/main/java/btools/mapcreator/ElevationRaster.java similarity index 98% rename from brouter-map-creator/src/main/java/btools/mapcreator/SrtmRaster.java rename to brouter-map-creator/src/main/java/btools/mapcreator/ElevationRaster.java index 3a307bf..478811c 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/SrtmRaster.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/ElevationRaster.java @@ -3,11 +3,11 @@ package btools.mapcreator; import btools.util.ReducedMedianFilter; /** - * Container for a srtm-raster + it's meta-data + * Container for a elevation raster + it's meta-data * * @author ab */ -public class SrtmRaster { +public class ElevationRaster { public int ncols; public int nrows; public boolean halfcol; diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/RasterCoder.java b/brouter-map-creator/src/main/java/btools/mapcreator/ElevationRasterCoder.java similarity index 80% rename from brouter-map-creator/src/main/java/btools/mapcreator/RasterCoder.java rename to brouter-map-creator/src/main/java/btools/mapcreator/ElevationRasterCoder.java index 233e3ea..02a841d 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/RasterCoder.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/ElevationRasterCoder.java @@ -1,15 +1,20 @@ package btools.mapcreator; -import java.io.*; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; -import btools.util.*; +import btools.util.MixCoderDataInputStream; +import btools.util.MixCoderDataOutputStream; // // Encode/decode a raster // -public class RasterCoder { - public void encodeRaster(SrtmRaster raster, OutputStream os) throws IOException { +public class ElevationRasterCoder { + public void encodeRaster(ElevationRaster raster, OutputStream os) throws IOException { DataOutputStream dos = new DataOutputStream(os); long t0 = System.currentTimeMillis(); @@ -28,12 +33,12 @@ public class RasterCoder { System.out.println("finished encoding in " + (t1 - t0) + " ms"); } - public SrtmRaster decodeRaster(InputStream is) throws IOException { + public ElevationRaster decodeRaster(InputStream is) throws IOException { DataInputStream dis = new DataInputStream(is); long t0 = System.currentTimeMillis(); - SrtmRaster raster = new SrtmRaster(); + ElevationRaster raster = new ElevationRaster(); raster.ncols = dis.readInt(); raster.nrows = dis.readInt(); raster.halfcol = dis.readBoolean(); @@ -45,7 +50,7 @@ public class RasterCoder { _decodeRaster(raster, is); - raster.usingWeights = raster.ncols > 6001; + raster.usingWeights = false; // raster.ncols > 6001; long t1 = System.currentTimeMillis(); System.out.println("finished decoding in " + (t1 - t0) + " ms ncols=" + raster.ncols + " nrows=" + raster.nrows); @@ -53,7 +58,7 @@ public class RasterCoder { } - private void _encodeRaster(SrtmRaster raster, OutputStream os) throws IOException { + private void _encodeRaster(ElevationRaster raster, OutputStream os) throws IOException { MixCoderDataOutputStream mco = new MixCoderDataOutputStream(os); int nrows = raster.nrows; int ncols = raster.ncols; @@ -78,7 +83,7 @@ public class RasterCoder { mco.flush(); } - private void _decodeRaster(SrtmRaster raster, InputStream is) throws IOException { + private void _decodeRaster(ElevationRaster raster, InputStream is) throws IOException { MixCoderDataInputStream mci = new MixCoderDataInputStream(is); int nrows = raster.nrows; int ncols = raster.ncols; diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/ElevationRasterTileConverter.java b/brouter-map-creator/src/main/java/btools/mapcreator/ElevationRasterTileConverter.java new file mode 100644 index 0000000..229dcfd --- /dev/null +++ b/brouter-map-creator/src/main/java/btools/mapcreator/ElevationRasterTileConverter.java @@ -0,0 +1,545 @@ +package btools.mapcreator; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Locale; +import java.util.StringTokenizer; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +public class ElevationRasterTileConverter { + + public static final boolean DEBUG = false; + + public static final short NODATA2 = -32767; // hgt-formats nodata + public static final short NODATA = Short.MIN_VALUE; + + private static final String HGT_FILE_EXT = ".hgt"; + private static final int HGT_BORDER_OVERLAP = 1; + private static final int HGT_3ASEC_ROWS = 1201; // 3 arc second resolution (90m) + private static final int HGT_3ASEC_FILE_SIZE = HGT_3ASEC_ROWS * HGT_3ASEC_ROWS * Short.BYTES; + private static final int HGT_1ASEC_ROWS = 3601; // 1 arc second resolution (30m) + private static final int SRTM3_ROW_LENGTH = 1200; // number of elevation values per line + private static final int SRTM1_ROW_LENGTH = 3600; + private static final boolean SRTM_NO_ZERO = true; + + private int NROWS; + private int NCOLS; + private int ROW_LENGTH; + private short[] imagePixels; + + /** + * This generates elevation raster files with a 5x5 degree scope + * The output can be for 1sec (18000x18000 points) + * or for 3sec (6000x6000 points) + * When using 1sec input files a not found area can be called from 3sec pool + * The input can be 1x1 degree 1sec/3sec hgt files (also packed as zip) + * or 5x5 degree 3sec asc files (delivered as zip) + * Arguments for single file generation: + * ElevationRasterTileConverter [arc seconds (1 or 3,default=3)] [hgt-fallback-data-dir] + * Samples + * $ ... ElevationRasterTileConverter srtm_34_-1 ./srtm/hgt3sec ./srtm/srtm3_bef + * $ ... ElevationRasterTileConverter srtm_34_-1 ./srtm/hgt1sec ./srtm/srtm1_bef 1 + * $ ... ElevationRasterTileConverter srtm_34_-1 ./srtm/hgt1sec ./srtm/srtm1_bef 1 ./srtm/hgt3sec + *

+ * Arguments for multi file generation (world wide): + * $ ... ElevationRasterTileConverter all ./srtm/hgt3sec ./srtm/srtm3_bef + * $ ... ElevationRasterTileConverter all ./srtm/hgt1sec ./srtm/srtm1_bef 1 + * $ ... ElevationRasterTileConverter all ./srtm/hgt1sec ./srtm/srtm1_bef 1 ./srtm/hgt3sec + * + * @param args + * @throws Exception + */ + public static void main(String[] args) throws Exception { + if (args.length == 3 || args.length == 4 || args.length == 5) { + String filename90 = args[0]; + if ("all".equals(filename90)) { + //if (DEBUG) + System.out.println("raster convert all "); + new ElevationRasterTileConverter().doConvertAll(args[1], args[2], (args.length > 3 ? args[3] : null), (args.length == 5 ? args[4] : null)); + return; + } + // old filenames only + String filename30 = filename90 + ".bef"; //filename90.substring(0, filename90.length() - 3) + "bef"; + + int srtmLonIdx = Integer.parseInt(filename90.substring(5, 7).toLowerCase()); + int srtmLatIdx = Integer.parseInt(filename90.substring(8, 10).toLowerCase()); + + int ilon_base = (srtmLonIdx - 1) * 5 - 180; + int ilat_base = 150 - srtmLatIdx * 5 - 90; + int row_length = SRTM3_ROW_LENGTH; + String fallbackdir = null; + if (args.length > 3) { + row_length = (Integer.parseInt(args[3]) == 1 ? SRTM1_ROW_LENGTH : SRTM3_ROW_LENGTH); + fallbackdir = (args.length == 5 ? args[4] : null); + } + //if (DEBUG) + System.out.println("raster convert " + ilon_base + " " + ilat_base + " from " + srtmLonIdx + " " + srtmLatIdx + " f: " + filename90 + " rowl " + row_length); + + new ElevationRasterTileConverter().doConvert(args[1], ilon_base, ilat_base, args[2] + "/" + filename30, row_length, fallbackdir); + } else { + System.out.println("usage: java [arc seconds (1 or 3,default=3)] [hgt-fallback-data-dir]"); + System.out.println("or java all [arc seconds (1 or 3, default=3)] [hgt-fallback-data-dir]"); + } + } + + private void doConvertAll(String hgtdata, String outdir, String rlen, String hgtfallbackdata) throws Exception { + int row_length = SRTM3_ROW_LENGTH; + if (rlen != null) { + row_length = (Integer.parseInt(rlen) == 1 ? SRTM1_ROW_LENGTH : SRTM3_ROW_LENGTH); + } + String filename30; + for (int ilon_base = -180; ilon_base < 180; ilon_base += 5) { + for (int ilat_base = 85; ilat_base > -90; ilat_base -= 5) { + if (PosUnifier.UseRasterRd5FileName) { + filename30 = genFilenameRd5(ilon_base, ilat_base); + } else { + filename30 = genFilenameOld(ilon_base, ilat_base); + } + if (DEBUG) + System.out.println("lidar convert all: " + filename30); + doConvert(hgtdata, ilon_base, ilat_base, outdir + "/" + filename30, row_length, hgtfallbackdata); + } + } + } + + static String genFilenameOld(int ilon_base, int ilat_base) { + int srtmLonIdx = ((ilon_base + 180) / 5) + 1; + int srtmLatIdx = (60 - ilat_base) / 5; + return String.format(Locale.US, "srtm_%02d_%02d.bef", srtmLonIdx, srtmLatIdx); + } + + static String genFilenameRd5(int ilon_base, int ilat_base) { + return String.format("srtm_%s_%s.bef", ilon_base < 0 ? "W" + (-ilon_base) : "E" + ilon_base, + ilat_base < 0 ? "S" + (-ilat_base) : "N" + ilat_base); + } + + private void readHgtZip(String filename, int rowOffset, int colOffset, int row_length, int scale) throws Exception { + ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(filename))); + try { + for (; ; ) { + ZipEntry ze = zis.getNextEntry(); + if (ze == null) break; + if (ze.getName().toLowerCase().endsWith(HGT_FILE_EXT)) { + readHgtFromStream(zis, rowOffset, colOffset, row_length, scale); + return; + } + } + } finally { + zis.close(); + } + } + + private void readHgtFromStream(InputStream is, int rowOffset, int colOffset, int rowLength, int scale) + throws Exception { + DataInputStream dis = new DataInputStream(new BufferedInputStream(is)); + for (int ir = 0; ir < rowLength; ir++) { + int row = rowOffset + ir * scale; + + for (int ic = 0; ic < rowLength; ic++) { + int col = colOffset + ic * scale; + + int i1 = dis.read(); // msb first! + int i0 = dis.read(); + + if (i0 == -1 || i1 == -1) + throw new RuntimeException("unexpected end of file reading hgt entry!"); + + short val = (short) ((i1 << 8) | i0); + + if (val == NODATA2) { + val = NODATA; + } + if (scale == 3) { + setPixel(row, col, val); + setPixel(row + 1, col, val); + setPixel(row + 2, col, val); + setPixel(row, col + 1, val); + setPixel(row + 1, col + 1, val); + setPixel(row + 2, col + 1, val); + setPixel(row, col + 2, val); + setPixel(row + 1, col + 2, val); + setPixel(row + 2, col + 2, val); + } else { + setPixel(row, col, val); + } + } + } + } + + private void readHgtFile(File file, int rowOffset, int colOffset, int row_length, int scale) + throws Exception { + + if (DEBUG) + System.out.println("read: " + file + " " + row_length); + + FileInputStream fis = new FileInputStream(file); + try { + readHgtFromStream(fis, rowOffset, colOffset, row_length, scale); + } finally { + fis.close(); + } + } + + /* + private void readFallbackFile(File file, int rowOffset, int colOffset, int row_length) + throws Exception { + int rowLength; + int scale; + if (file.length() > HGT_3ASEC_FILE_SIZE) { + rowLength = HGT_1ASEC_ROWS; + scale = 1; + } else { + rowLength = HGT_3ASEC_ROWS; + scale = 3; + } + if (DEBUG) + System.out.println("read fallback: " + file + " " + rowLength); + + FileInputStream fis = new FileInputStream(file); + try { + readHgtFromStream(fis, rowOffset, colOffset, rowLength, scale); + } finally { + fis.close(); + } + } + */ + + private void readAscZip(File file, ElevationRaster raster) throws Exception { + + ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(file))); + try { + for (; ; ) { + ZipEntry ze = zis.getNextEntry(); + if (ze.getName().endsWith(".asc")) { + readAscFromStream(zis, raster); + return; + } + } + } finally { + zis.close(); + } + } + + private String secondToken(String s) { + StringTokenizer tk = new StringTokenizer(s, " "); + tk.nextToken(); + return tk.nextToken(); + } + + private void readAscFromStream(InputStream is, ElevationRaster raster) throws Exception { + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + int linenr = 0; + for (; ; ) { + linenr++; + if (linenr <= 6) { + String line = br.readLine(); + if (linenr == 1) + raster.ncols = Integer.parseInt(secondToken(line)); + else if (linenr == 2) + raster.nrows = Integer.parseInt(secondToken(line)); + else if (linenr == 3) + raster.xllcorner = Double.parseDouble(secondToken(line)); + else if (linenr == 4) + raster.yllcorner = Double.parseDouble(secondToken(line)); + else if (linenr == 5) + raster.cellsize = Double.parseDouble(secondToken(line)); + else if (linenr == 6) { + // nodata ignored here ( < -250 assumed nodata... ) + // raster.noDataValue = Short.parseShort( secondToken( line ) ); + raster.eval_array = new short[raster.ncols * raster.nrows]; + } + } else { + int row = 0; + int col = 0; + int n = 0; + boolean negative = false; + for (; ; ) { + int c = br.read(); + if (c < 0) + break; + if (c == ' ') { + if (negative) + n = -n; + short val = n < -250 ? Short.MIN_VALUE : (short) (n); + + raster.eval_array[row * raster.ncols + col] = val; + if (++col == raster.ncols) { + col = 0; + ++row; + } + n = 0; + negative = false; + } else if (c >= '0' && c <= '9') { + n = 10 * n + (c - '0'); + } else if (c == '-') { + negative = true; + } + } + break; + } + } + br.close(); + } + + + private void setPixel(int row, int col, short val) { + if (row >= 0 && row < NROWS && col >= 0 && col < NCOLS) { + imagePixels[row * NCOLS + col] = val; + } + } + + private short getPixel(int row, int col) { + if (row >= 0 && row < NROWS && col >= 0 && col < NCOLS) { + return imagePixels[row * NCOLS + col]; + } + return NODATA; + } + + + public void doConvert(String inputDir, int lonDegreeStart, int latDegreeStart, String outputFile, int row_length, String hgtfallbackdata) throws Exception { + int extraBorder = 0; + + //List foundList = new ArrayList<>(); + //List notfoundList = new ArrayList<>(); + + boolean hgtfound = false; + boolean ascfound = false; + String filename = null; + //if (row_length == SRTM1_ROW_LENGTH) + { + // check for sources w/o border + for (int latIdx = 0; latIdx < 5; latIdx++) { + int latDegree = latDegreeStart + latIdx; + + for (int lonIdx = 0; lonIdx < 5; lonIdx++) { + int lonDegree = lonDegreeStart + lonIdx; + + filename = inputDir + "/" + formatLat(latDegree) + formatLon(lonDegree) + ".zip"; + File f = new File(filename); + if (f.exists() && f.length() > 0) { + hgtfound = true; + break; + } + filename = filename.substring(0, filename.length() - 4) + ".hgt"; + f = new File(filename); + if (f.exists() && f.length() > 0) { + hgtfound = true; + break; + } + } + } + if (!hgtfound) { + filename = inputDir + "/" + genFilenameOld(lonDegreeStart, latDegreeStart).substring(0, 10) + ".zip"; + File f = new File(filename); + if (f.exists() && f.length() > 0) { + ascfound = true; + } + } + } + if (hgtfound) { // init when found + NROWS = 5 * row_length + 1 + 2 * extraBorder; + NCOLS = 5 * row_length + 1 + 2 * extraBorder; + imagePixels = new short[NROWS * NCOLS]; // 650 MB ! + + // prefill as NODATA + Arrays.fill(imagePixels, NODATA); + } else if (!ascfound) { + if (DEBUG) + System.out.println("none data: " + lonDegreeStart + " " + latDegreeStart); + return; + } + + if (hgtfound) { + for (int latIdx = -1; latIdx <= 5; latIdx++) { + int latDegree = latDegreeStart + latIdx; + int rowOffset = extraBorder + (4 - latIdx) * row_length; + + for (int lonIdx = -1; lonIdx <= 5; lonIdx++) { + int lonDegree = lonDegreeStart + lonIdx; + int colOffset = extraBorder + lonIdx * row_length; + + filename = inputDir + "/" + formatLat(latDegree) + formatLon(lonDegree) + ".zip"; + File f = new File(filename); + if (f.exists() && f.length() > 0) { + if (DEBUG) + System.out.println("exist: " + filename); + readHgtZip(filename, rowOffset, colOffset, row_length + 1, 1); + continue; + } + filename = filename.substring(0, filename.length() - 4) + ".hgt"; + f = new File(filename); + if (f.exists() && f.length() > 0) { + if (DEBUG) + System.out.println("exist: " + filename); + readHgtFile(f, rowOffset, colOffset, row_length + 1, 1); + continue; + } else { + if (hgtfallbackdata != null) { + filename = hgtfallbackdata + "/" + formatLat(latDegree) + formatLon(lonDegree) + ".hgt"; + f = new File(filename); + if (f.exists() && f.length() > 0) { + readHgtFile(f, rowOffset, colOffset, SRTM3_ROW_LENGTH + 1, 3); + continue; + } + filename = filename.substring(0, filename.length() - 4) + ".zip"; + f = new File(filename); + if (f.exists() && f.length() > 0) { + readHgtZip(filename, rowOffset, colOffset, SRTM3_ROW_LENGTH + 1, 3); + } else { + if (DEBUG) + System.out.println("none : " + filename); + } + } + + } + } + } + + // post fill zero + if (SRTM_NO_ZERO) { + for (int row = 0; row < NROWS; row++) { + for (int col = 0; col < NCOLS; col++) { + if (imagePixels[row * NCOLS + col] == 0) imagePixels[row * NCOLS + col] = NODATA; + } + } + } + } + + boolean halfCol5 = false; // no halfcol tiles in lidar data (?) + + + ElevationRaster raster = new ElevationRaster(); + if (hgtfound) { + raster.nrows = NROWS; + raster.ncols = NCOLS; + raster.halfcol = halfCol5; + raster.noDataValue = NODATA; + raster.cellsize = 1. / row_length; + raster.xllcorner = lonDegreeStart - (0.5 + extraBorder) * raster.cellsize; + raster.yllcorner = latDegreeStart - (0.5 + extraBorder) * raster.cellsize; + raster.eval_array = imagePixels; + } + + if (ascfound) { + File f = new File(filename); + readAscZip(f, raster); + } + + // encode the raster + OutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile)); + new ElevationRasterCoder().encodeRaster(raster, os); + os.close(); + + // decode the raster + InputStream is = new BufferedInputStream(new FileInputStream(outputFile)); + ElevationRaster raster2 = new ElevationRasterCoder().decodeRaster(is); + is.close(); + + short[] pix2 = raster2.eval_array; + if (pix2.length != raster.eval_array.length) + throw new RuntimeException("length mismatch!"); + + // compare decoding result + for (int row = 0; row < raster.nrows; row++) { + int colstep = halfCol5 ? 2 : 1; + for (int col = 0; col < raster.ncols; col += colstep) { + int idx = row * raster.ncols + col; + short p2 = pix2[idx]; + if (p2 != raster.eval_array[idx]) { + throw new RuntimeException("content mismatch: p2=" + p2 + " p1=" + raster.eval_array[idx]); + } + } + } + imagePixels = null; + } + + private static String formatLon(int lon) { + if (lon >= 180) + lon -= 180; // TODO: w180 oder E180 ? + + String s = "E"; + if (lon < 0) { + lon = -lon; + s = "W"; + } + String n = "000" + lon; + return s + n.substring(n.length() - 3); + } + + private static String formatLat(int lat) { + String s = "N"; + if (lat < 0) { + lat = -lat; + s = "S"; + } + String n = "00" + lat; + return s + n.substring(n.length() - 2); + } + + + public ElevationRaster getRaster(File f, double lon, double lat) throws Exception { + long fileSize; + InputStream inputStream; + + if (f.getName().toLowerCase().endsWith(".zip")) { + ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(f))); + for (; ; ) { + ZipEntry ze = zis.getNextEntry(); + if (ze == null) { + throw new FileNotFoundException(f.getName() + " doesn't contain a " + HGT_FILE_EXT + " file."); + } + if (ze.getName().toLowerCase().endsWith(HGT_FILE_EXT)) { + fileSize = ze.getSize(); + inputStream = zis; + break; + } + } + } else { + fileSize = f.length(); + inputStream = new FileInputStream(f); + } + + int rowLength; + if (fileSize > HGT_3ASEC_FILE_SIZE) { + rowLength = HGT_1ASEC_ROWS; + } else { + rowLength = HGT_3ASEC_ROWS; + } + + // stay at 1 deg * 1 deg raster + NROWS = rowLength; + NCOLS = rowLength; + + imagePixels = new short[NROWS * NCOLS]; + + // prefill as NODATA + Arrays.fill(imagePixels, NODATA); + readHgtFromStream(inputStream, 0, 0, rowLength, 1); + inputStream.close(); + + ElevationRaster raster = new ElevationRaster(); + raster.nrows = NROWS; + raster.ncols = NCOLS; + raster.halfcol = false; // assume full resolution + raster.noDataValue = NODATA; + raster.cellsize = 1. / (double) (rowLength - HGT_BORDER_OVERLAP); + raster.xllcorner = (int) (lon < 0 ? lon - 1 : lon); //onDegreeStart - raster.cellsize; + raster.yllcorner = (int) (lat < 0 ? lat - 1 : lat); //latDegreeStart - raster.cellsize; + raster.eval_array = imagePixels; + + return raster; + } + + +} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/HgtReader.java b/brouter-map-creator/src/main/java/btools/mapcreator/HgtReader.java new file mode 100644 index 0000000..f161d7d --- /dev/null +++ b/brouter-map-creator/src/main/java/btools/mapcreator/HgtReader.java @@ -0,0 +1,342 @@ +// License: GPL. For details, see LICENSE file. +package btools.mapcreator; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.ShortBuffer; +import java.nio.channels.FileChannel; +import java.util.HashMap; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * adapted from https://github.com/JOSM/josm-plugins/blob/master/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/HgtReader.java + *

+ * Class HgtReader reads data from SRTM HGT files. Currently this class is restricted to a resolution of 3 arc seconds. + *

+ * SRTM data files are available at the NASA SRTM site + * + * @author Oliver Wieland <oliver.wieland@online.de> + */ +public class HgtReader { + final static boolean DEBUG = false; + + private static final int SECONDS_PER_MINUTE = 60; + + public static final String HGT_EXT = ".hgt"; + public static final String ZIP_EXT = ".zip"; + + // alter these values for different SRTM resolutions + public static final int HGT3_RES = 3; // resolution in arc seconds + public static final int HGT3_ROW_LENGTH = 1201; // number of elevation values per line + public static final int HGT_VOID = -32768; // magic number which indicates 'void data' in HGT file + public static final int HGT1_RES = 1; // <<- The new SRTM is 1-ARCSEC + public static final int HGT1_ROW_LENGTH = 3601; //-- New file resolution is 3601x3601 + /** + * The 'no elevation' data magic. + */ + public static double NO_ELEVATION = Double.NaN; + + private static String srtmFolder = ""; + + private static final Map cache = new HashMap<>(); + + public HgtReader(String folder) { + srtmFolder = folder; + } + + public static double getElevationFromHgt(double lat, double lon) { + try { + String file = getHgtFileName(lat, lon); + if (DEBUG) System.out.println("HGT buffer " + file + " for " + lat + " " + lon); + + // given area in cache? + if (!cache.containsKey(file)) { + + // fill initial cache value. If no file is found, then + // we use it as a marker to indicate 'file has been searched + // but is not there' + cache.put(file, null); + // Try all resource directories + //for (String location : Main.pref.getAllPossiblePreferenceDirs()) + { + String fullPath = new File(srtmFolder, file + HGT_EXT).getPath(); + File f = new File(fullPath); + if (f.exists()) { + // found something: read HGT file... + ShortBuffer data = readHgtFile(fullPath); + // ... and store result in cache + cache.put(file, data); + //break; + } else { + fullPath = new File(srtmFolder, file + ZIP_EXT).getPath(); + f = new File(fullPath); + if (f.exists()) { + ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(f))); + try { + for (; ; ) { + ZipEntry ze = zis.getNextEntry(); + if (ze == null) break; + if (ze.getName().toLowerCase().endsWith(HGT_EXT)) { + // System.out.println("read zip " + ze.getName()); + ShortBuffer data = readHgtStream(zis); + // ... and store result in cache + cache.put(file, data); + break; + } + zis.closeEntry(); + } + } finally { + zis.close(); + } + } + } + System.out.println("*** reading: " + f.getName() + " " + cache.get(file)); + } + } + + // read elevation value + return readElevation(lat, lon); + } catch (FileNotFoundException e) { + System.err.println("HGT Get elevation " + lat + ", " + lon + " failed: => " + e.getMessage()); + // no problem... file not there + return NO_ELEVATION; + } catch (Exception ioe) { + // oops... + ioe.printStackTrace(System.err); + // fallback + return NO_ELEVATION; + } + } + + public static short[] getElevationDataFromHgt(double lat, double lon) { + try { + if (lon < 0) lon += 1; + if (lat < 0) lat += 1; + String file = getHgtFileName(lat, lon); + if (DEBUG) System.out.println("HGT buffer " + file + " for " + lat + " " + lon); + + ShortBuffer data = null; + + // Try all resource directories + //for (String location : Main.pref.getAllPossiblePreferenceDirs()) + + String fullPath = new File(srtmFolder, file + HGT_EXT).getPath(); + File f = new File(fullPath); + if (f.exists()) { + // found something: read HGT file... + data = readHgtFile(fullPath); + } else { + fullPath = new File(srtmFolder, file + ZIP_EXT).getPath(); + f = new File(fullPath); + if (f.exists()) { + ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(f))); + try { + for (; ; ) { + ZipEntry ze = zis.getNextEntry(); + if (ze == null) break; + if (ze.getName().toLowerCase().endsWith(HGT_EXT)) { + // System.out.println("read zip " + ze.getName()); + data = readHgtStream(zis); + break; + } + zis.closeEntry(); + } + } finally { + zis.close(); + } + } + } + + + System.out.println("*** reading: " + f.getName() + " " + (data != null ? data.limit() : -1)); + if (data != null) { + short[] array = new short[data.limit()]; + data.get(array); + return array; + } + return null; + } catch (FileNotFoundException e) { + System.err.println("HGT Get elevation " + lat + ", " + lon + " failed: => " + e.getMessage()); + // no problem... file not there + return null; + } catch (Exception ioe) { + // oops... + ioe.printStackTrace(System.err); + // fallback + return null; + } + } + + @SuppressWarnings("resource") + private static ShortBuffer readHgtFile(String file) throws Exception { + if (file == null) throw new Exception("no hgt file " + file); + + FileChannel fc = null; + ShortBuffer sb = null; + try { + // Eclipse complains here about resource leak on 'fc' - even with 'finally' clause??? + fc = new FileInputStream(file).getChannel(); + // choose the right endianness + + ByteBuffer bb = ByteBuffer.allocateDirect((int) fc.size()); + while (bb.remaining() > 0) fc.read(bb); + + bb.flip(); + //sb = bb.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer(); + sb = bb.order(ByteOrder.BIG_ENDIAN).asShortBuffer(); + } finally { + if (fc != null) fc.close(); + } + + return sb; + } + + // @SuppressWarnings("resource") + private static ShortBuffer readHgtStream(InputStream zis) throws Exception { + if (zis == null) throw new Exception("no hgt stream "); + + ShortBuffer sb = null; + try { + // choose the right endianness + + byte[] bytes = zis.readAllBytes(); + ByteBuffer bb = ByteBuffer.allocate(bytes.length); + bb.put(bytes, 0, bytes.length); + //while (bb.remaining() > 0) zis.read(bb, 0, size); + + //ByteBuffer bb = ByteBuffer.allocate(zis.available()); + //Channels.newChannel(zis).read(bb); + bb.flip(); + //sb = bb.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer(); + sb = bb.order(ByteOrder.BIG_ENDIAN).asShortBuffer(); + } finally { + + } + + return sb; + } + + /** + * Reads the elevation value for the given coordinate. + *

+ * See also stackexchange.com + * + * @param lat, lon the coordinate to get the elevation data for + * @return the elevation value or Double.NaN, if no value is present + */ + public static double readElevation(double lat, double lon) { + String tag = getHgtFileName(lat, lon); + + ShortBuffer sb = cache.get(tag); + + if (sb == null) { + return NO_ELEVATION; + } + + if (DEBUG) System.out.println("HGT buffer size " + sb.capacity() + " limit " + sb.limit()); + try { + int rowLength = HGT3_ROW_LENGTH; + int resolution = HGT3_RES; + if (sb.capacity() > (HGT3_ROW_LENGTH * HGT3_ROW_LENGTH)) { + rowLength = HGT1_ROW_LENGTH; + resolution = HGT1_RES; + } + // see http://gis.stackexchange.com/questions/43743/how-to-extract-elevation-from-hgt-file + double fLat = frac(lat) * SECONDS_PER_MINUTE; + double fLon = frac(lon) * SECONDS_PER_MINUTE; + + // compute offset within HGT file + int row = (int) Math.round((fLat) * SECONDS_PER_MINUTE / resolution); + int col = (int) Math.round((fLon) * SECONDS_PER_MINUTE / resolution); + if (lon < 0) col = rowLength - col - 1; + if (lat > 0) row = rowLength - row - 1; + + + //row = rowLength - row; + int cell = (rowLength * (row)) + col; + //int cell = ((rowLength * (latitude)) + longitude); + + if (DEBUG) + System.out.println("Read HGT elevation data from row/col/cell " + row + "," + col + ", " + cell + ", " + sb.limit()); + + // valid position in buffer? + if (cell < sb.limit()) { + short ele = sb.get(cell); + // check for data voids + if (ele == HGT_VOID) { + return NO_ELEVATION; + } else { + return ele; + } + } else { + return NO_ELEVATION; + } + } catch (Exception e) { + System.err.println("error at " + lon + " " + lat + " "); + e.printStackTrace(); + } + return NO_ELEVATION; + } + + /** + * Gets the associated HGT file name for the given way point. Usually the + * format is [N|S]nn[W|E]mmm.hgt where nn is the integral latitude + * without decimals and mmm is the longitude. + * + * @param llat,llon the coordinate to get the filename for + * @return the file name of the HGT file + */ + public static String getHgtFileName(double llat, double llon) { + int lat = (int) llat; + int lon = (int) llon; + + String latPref = "N"; + if (lat < 0) { + latPref = "S"; + lat = -lat + 1; + } + String lonPref = "E"; + if (lon < 0) { + lonPref = "W"; + lon = -lon + 1; + } + + return String.format("%s%02d%s%03d", latPref, lat, lonPref, lon); + } + + public static double frac(double d) { + long iPart; + double fPart; + + // Get user input + iPart = (long) d; + fPart = d - iPart; + return Math.abs(fPart); + } + + public static void clear() { + if (cache != null) { + cache.clear(); + } + } + + public static void main(String[] args) throws Exception { + System.out.println("*** HGT position values and enhance elevation"); + if (args.length == 3) { + HgtReader elevReader = new HgtReader(args[0]); + double lon = Double.parseDouble(args[1]); + double lat = Double.parseDouble(args[2]); + // check hgt direct + double elev = elevReader.getElevationFromHgt(lat, lon); + System.out.println("-----> elv for hgt " + lat + ", " + lon + " = " + elev); + + } + } +} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/OsmNodeP.java b/brouter-map-creator/src/main/java/btools/mapcreator/OsmNodeP.java index 572554e..3c796ee 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/OsmNodeP.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/OsmNodeP.java @@ -8,6 +8,8 @@ package btools.mapcreator; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; +import java.util.Map; import btools.codec.MicroCache; import btools.codec.MicroCache2; @@ -91,10 +93,10 @@ public class OsmNodeP extends OsmLinkP { return null; } - public void writeNodeData(MicroCache mc, OsmTrafficMap trafficMap) throws IOException { + public void writeNodeData(MicroCache mc) throws IOException { boolean valid = true; if (mc instanceof MicroCache2) { - valid = writeNodeData2((MicroCache2) mc, trafficMap); + valid = writeNodeData2((MicroCache2) mc); } else throw new IllegalArgumentException("unknown cache version: " + mc.getClass()); if (valid) { @@ -105,7 +107,7 @@ public class OsmNodeP extends OsmLinkP { } public void checkDuplicateTargets() { - HashMap targets = new HashMap<>(); + Map targets = new HashMap<>(); for (OsmLinkP link0 = getFirstLink(); link0 != null; link0 = link0.getNext(this)) { OsmLinkP link = link0; @@ -142,7 +144,7 @@ public class OsmNodeP extends OsmLinkP { } } - public boolean writeNodeData2(MicroCache2 mc, OsmTrafficMap trafficMap) throws IOException { + public boolean writeNodeData2(MicroCache2 mc) throws IOException { boolean hasLinks = false; // write turn restrictions @@ -165,14 +167,14 @@ public class OsmNodeP extends OsmLinkP { mc.writeVarBytes(getNodeDecsription()); // buffer internal reverse links - ArrayList internalReverse = new ArrayList<>(); + List internalReverse = new ArrayList<>(); for (OsmLinkP link0 = getFirstLink(); link0 != null; link0 = link0.getNext(this)) { OsmLinkP link = link0; OsmNodeP origin = this; OsmNodeP target = null; - ArrayList linkNodes = new ArrayList<>(); + List linkNodes = new ArrayList<>(); linkNodes.add(this); // first pass just to see if that link is consistent @@ -210,11 +212,7 @@ public class OsmNodeP extends OsmLinkP { } } - // add traffic simulation, if present byte[] description = link0.descriptionBitmap; - if (trafficMap != null) { - description = trafficMap.addTrafficClass(linkNodes, description); - } // write link data int sizeoffset = mc.writeSizePlaceHolder(); @@ -226,7 +224,7 @@ public class OsmNodeP extends OsmLinkP { origin = this; for (int i = 1; i < linkNodes.size() - 1; i++) { OsmNodeP tranferNode = linkNodes.get(i); - if ((tranferNode.bits & OsmNodeP.DP_SURVIVOR_BIT) != 0) { + if ((tranferNode.bits & DP_SURVIVOR_BIT) != 0) { mc.writeVarLengthSigned(tranferNode.ilon - origin.ilon); mc.writeVarLengthSigned(tranferNode.ilat - origin.ilat); mc.writeVarLengthSigned(tranferNode.getSElev() - origin.getSElev()); diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/OsmTrafficMap.java b/brouter-map-creator/src/main/java/btools/mapcreator/OsmTrafficMap.java deleted file mode 100644 index ca6d119..0000000 --- a/brouter-map-creator/src/main/java/btools/mapcreator/OsmTrafficMap.java +++ /dev/null @@ -1,245 +0,0 @@ -/** - * Container for link between two Osm nodes (pre-pocessor version) - * - * @author ab - */ -package btools.mapcreator; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.EOFException; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.List; - -import btools.expressions.BExpressionContextWay; -import btools.util.CheapRuler; -import btools.util.CompactLongMap; -import btools.util.FrozenLongMap; - - -public class OsmTrafficMap { - int minLon; - int minLat; - int maxLon; - int maxLat; - - private BExpressionContextWay expctxWay; - - private OsmTrafficMap oldTrafficClasses; - private DataOutputStream newTrafficDos; - private File oldTrafficFile; - private File newTrafficFile; - - private int totalChanges = 0; - private int supressedChanges = 0; - - private boolean doNotAdd = false; - private boolean debug = false; - - public OsmTrafficMap(BExpressionContextWay expctxWay) { - this.expctxWay = expctxWay; - debug = Boolean.getBoolean("debugTrafficMap"); - } - - public static class OsmTrafficElement { - public long node2; - public int traffic; - public OsmTrafficElement next; - } - - private CompactLongMap map = new CompactLongMap<>(); - - public void loadAll(File file, int minLon, int minLat, int maxLon, int maxLat, boolean includeMotorways) throws Exception { - load(file, minLon, minLat, maxLon, maxLat, includeMotorways); - - // check for old traffic data - oldTrafficFile = new File(file.getParentFile(), file.getName() + "_old"); - if (oldTrafficFile.exists()) { - oldTrafficClasses = new OsmTrafficMap(null); - oldTrafficClasses.doNotAdd = true; - oldTrafficClasses.load(oldTrafficFile, minLon, minLat, maxLon, maxLat, false); - } - - // check for old traffic data - newTrafficFile = new File(file.getParentFile(), file.getName() + "_new"); - newTrafficDos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(newTrafficFile))); - } - - public void finish() throws Exception { - if (newTrafficDos != null) { - newTrafficDos.close(); - newTrafficDos = null; - oldTrafficFile.delete(); - newTrafficFile.renameTo(oldTrafficFile); - System.out.println("TrafficMap: changes total=" + totalChanges + " supressed=" + supressedChanges); - } - } - - public void load(File file, int minLon, int minLat, int maxLon, int maxLat, boolean includeMotorways) throws Exception { - this.minLon = minLon; - this.minLat = minLat; - this.maxLon = maxLon; - this.maxLat = maxLat; - - int trafficElements = 0; - DataInputStream is = new DataInputStream(new BufferedInputStream(new FileInputStream(file))); - try { - for (; ; ) { - long n1 = is.readLong(); - long n2 = is.readLong(); - int traffic = is.readInt(); - if (traffic == -1 && !includeMotorways) { - continue; - } - if (isInsideBounds(n1) || isInsideBounds(n2)) { - if (addElement(n1, n2, traffic)) { - trafficElements++; - } - } - } - } catch (EOFException eof) { - } finally { - is.close(); - } - - map = new FrozenLongMap<>(map); - System.out.println("read traffic-elements: " + trafficElements); - } - - - public boolean addElement(long n1, long n2, int traffic) { - OsmTrafficElement e = getElement(n1, n2); - if (e == null) { - e = new OsmTrafficElement(); - e.node2 = n2; - e.traffic = traffic; - - OsmTrafficElement e0 = map.get(n1); - if (e0 != null) { - while (e0.next != null) { - e0 = e0.next; - } - e0.next = e; - } else { - map.fastPut(n1, e); - } - return true; - } - if (doNotAdd) { - e.traffic = Math.max(e.traffic, traffic); - } else { - e.traffic = e.traffic == -1 || traffic == -1 ? -1 : e.traffic + traffic; - } - return false; - } - - private boolean isInsideBounds(long id) { - int ilon = (int) (id >> 32); - int ilat = (int) (id & 0xffffffff); - - return ilon >= minLon && ilon < maxLon && ilat >= minLat && ilat < maxLat; - } - - public int getTrafficClass(long n1, long n2) { - // used for the old data, where we stpre traffic-classes, not volumes - OsmTrafficElement e = getElement(n1, n2); - return e == null ? 0 : e.traffic; - } - - public int getTrafficClassForTraffic(int traffic) { - if (traffic < 0) return -1; - if (traffic < 40000) return 0; - if (traffic < 80000) return 2; - if (traffic < 160000) return 3; - if (traffic < 320000) return 4; - if (traffic < 640000) return 5; - if (traffic < 1280000) return 6; - return 7; - } - - private int getTraffic(long n1, long n2) { - OsmTrafficElement e1 = getElement(n1, n2); - int traffic1 = e1 == null ? 0 : e1.traffic; - OsmTrafficElement e2 = getElement(n2, n1); - int traffic2 = e2 == null ? 0 : e2.traffic; - return traffic1 == -1 || traffic2 == -1 ? -1 : traffic1 > traffic2 ? traffic1 : traffic2; - } - - public void freeze() { - } - - private OsmTrafficElement getElement(long n1, long n2) { - OsmTrafficElement e = map.get(n1); - while (e != null) { - if (e.node2 == n2) { - return e; - } - e = e.next; - } - return null; - } - - public OsmTrafficElement getElement(long n) { - return map.get(n); - } - - public byte[] addTrafficClass(List linkNodes, byte[] description) throws IOException { - double distance = 0.; - double sum = 0.; - - for (int i = 0; i < linkNodes.size() - 1; i++) { - OsmNodeP n1 = linkNodes.get(i); - OsmNodeP n2 = linkNodes.get(i + 1); - int traffic = getTraffic(n1.getIdFromPos(), n2.getIdFromPos()); - double dist = CheapRuler.distance(n1.ilon, n1.ilat, n2.ilon, n2.ilat); - distance += dist; - sum += dist * traffic; - } - - if (distance == 0.) { - return description; - } - int traffic = (int) (sum / distance + 0.5); - - long id0 = linkNodes.get(0).getIdFromPos(); - long id1 = linkNodes.get(linkNodes.size() - 1).getIdFromPos(); - - int trafficClass = getTrafficClassForTraffic(traffic); - - // delta suppression: keep old traffic classes within some buffer range - if (oldTrafficClasses != null) { - int oldTrafficClass = oldTrafficClasses.getTrafficClass(id0, id1); - if (oldTrafficClass != trafficClass) { - totalChanges++; - boolean supressChange = - oldTrafficClass == getTrafficClassForTraffic((int) (traffic * 1.3)) - || oldTrafficClass == getTrafficClassForTraffic((int) (traffic * 0.77)); - - if (debug) { - System.out.println("traffic class change " + oldTrafficClass + "->" + trafficClass + " supress=" + supressChange); - } - if (supressChange) { - trafficClass = oldTrafficClass; - supressedChanges++; - } - } - } - - if (trafficClass > 0) { - newTrafficDos.writeLong(id0); - newTrafficDos.writeLong(id1); - newTrafficDos.writeInt(trafficClass); - - expctxWay.decode(description); - expctxWay.addLookupValue("estimated_traffic_class", trafficClass + 1); - return expctxWay.encode(); - } - return description; - } - -} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/PosUnifier.java b/brouter-map-creator/src/main/java/btools/mapcreator/PosUnifier.java index bccbf70..cea8cb5 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/PosUnifier.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/PosUnifier.java @@ -7,7 +7,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.HashMap; -import java.util.Locale; import java.util.Map; import btools.util.CompactLongSet; @@ -23,16 +22,21 @@ import btools.util.FrozenLongSet; * @author ab */ public class PosUnifier extends MapCreatorBase { + + public static final boolean UseRasterRd5FileName = false; + private DiffCoderDataOutputStream nodesOutStream; private DiffCoderDataOutputStream borderNodesOut; private File nodeTilesOut; + private File outNodeFile; private CompactLongSet[] positionSets; - private Map srtmmap; + private Map srtmmap; private int lastSrtmLonIdx; private int lastSrtmLatIdx; - private SrtmRaster lastSrtmRaster; + private ElevationRaster lastSrtmRaster; private String srtmdir; + private String srtmfallbackdir; private CompactLongSet borderNids; @@ -46,25 +50,40 @@ public class PosUnifier extends MapCreatorBase { double lat = Double.parseDouble(args[2]); NodeData n = new NodeData(1, lon, lat); - SrtmRaster srtm = posu.hgtForNode(n.ilon, n.ilat); short selev = Short.MIN_VALUE; + ElevationRaster srtm = null; + /* + // check hgt direct + srtm = posu.hgtForNode(n.ilon, n.ilat); + if (srtm != null) { + selev = srtm.getElevation(n.ilon, n.ilat); + } else { + System.out.println("hgtForNode no data"); + } + posu.resetElevationRaster(); + System.out.println("-----> selv for hgt " + lat + ", " + lon + " = " + selev + " = " + (selev / 4.)); + srtm = null; + selev = Short.MIN_VALUE; + */ if (srtm == null) { srtm = posu.srtmForNode(n.ilon, n.ilat); } if (srtm != null) selev = srtm.getElevation(n.ilon, n.ilat); - posu.resetSrtm(); - System.out.println("-----> selv for " + lat + ", " + lon + " = " + selev + " = " + (selev / 4.)); + posu.resetElevationRaster(); + System.out.println("-----> selv for bef " + lat + ", " + lon + " = " + selev + " = " + (selev / 4.)); return; - } else if (args.length != 5) { - System.out.println("usage: java PosUnifier "); + } else if (args.length != 5 && args.length != 6) { + System.out.println("usage: java PosUnifier [srtm-fallback-data-dir]"); + System.out.println("or java PosUnifier "); return; } - new PosUnifier().process(new File(args[0]), new File(args[1]), new File(args[2]), new File(args[3]), args[4]); + new PosUnifier().process(new File(args[0]), new File(args[1]), new File(args[2]), new File(args[3]), args[4], (args.length == 6 ? args[5] : null)); } - public void process(File nodeTilesIn, File nodeTilesOut, File bordernidsinfile, File bordernodesoutfile, String srtmdir) throws Exception { + public void process(File nodeTilesIn, File nodeTilesOut, File bordernidsinfile, File bordernodesoutfile, String srtmdir, String srtmfallbackdir) throws Exception { this.nodeTilesOut = nodeTilesOut; this.srtmdir = srtmdir; + this.srtmfallbackdir = srtmfallbackdir; // read border nids set DataInputStream dis = createInStream(bordernidsinfile); @@ -88,9 +107,9 @@ public class PosUnifier extends MapCreatorBase { @Override public void nodeFileStart(File nodefile) throws Exception { - resetSrtm(); - - nodesOutStream = createOutStream(fileFromTemplate(nodefile, nodeTilesOut, "u5d")); + resetElevationRaster(); + outNodeFile = fileFromTemplate(nodefile, nodeTilesOut, "u5d"); + nodesOutStream = createOutStream(outNodeFile); positionSets = new CompactLongSet[2500]; } @@ -106,7 +125,7 @@ public class PosUnifier extends MapCreatorBase { srtm = srtmForNode(n.ilon, n.ilat); } */ - SrtmRaster srtm = srtmForNode(n.ilon, n.ilat); + ElevationRaster srtm = srtmForNode(n.ilon, n.ilat); if (srtm != null) n.selev = srtm.getElevation(n.ilon, n.ilat); findUniquePos(n); @@ -120,7 +139,13 @@ public class PosUnifier extends MapCreatorBase { @Override public void nodeFileEnd(File nodeFile) throws Exception { nodesOutStream.close(); - resetSrtm(); + if (outNodeFile != null) { + if (lastSrtmRaster != null) { + String newName = outNodeFile.getAbsolutePath() + (lastSrtmRaster.nrows > 6001 ? "_1": "_3"); + outNodeFile.renameTo(new File(newName)); + } + } + resetElevationRaster(); } private boolean checkAdd(int lon, int lat) { @@ -168,7 +193,7 @@ public class PosUnifier extends MapCreatorBase { * get the srtm data set for a position srtm coords are * srtm__ where srtmLon = 180 + lon, srtmLat = 60 - lat */ - private SrtmRaster srtmForNode(int ilon, int ilat) throws Exception { + private ElevationRaster srtmForNode(int ilon, int ilat) throws Exception { int srtmLonIdx = (ilon + 5000000) / 5000000; int srtmLatIdx = (654999999 - ilat) / 5000000 - 100; // ugly negative rounding... @@ -178,35 +203,42 @@ public class PosUnifier extends MapCreatorBase { lastSrtmLonIdx = srtmLonIdx; lastSrtmLatIdx = srtmLatIdx; - String slonidx = "0" + srtmLonIdx; - String slatidx = "0" + srtmLatIdx; - String filename = "srtm_" + slonidx.substring(slonidx.length() - 2) + "_" + slatidx.substring(slatidx.length() - 2); + String filename; + if (UseRasterRd5FileName) { + filename = genFilenameRd5(ilon, ilat); + } else { + filename = genFilenameXY(srtmLonIdx, srtmLatIdx); + } lastSrtmRaster = srtmmap.get(filename); if (lastSrtmRaster == null && !srtmmap.containsKey(filename)) { File f = new File(new File(srtmdir), filename + ".bef"); if (f.exists()) { - System.out.println("*** reading: " + f); try { InputStream isc = new BufferedInputStream(new FileInputStream(f)); - lastSrtmRaster = new RasterCoder().decodeRaster(isc); + lastSrtmRaster = new ElevationRasterCoder().decodeRaster(isc); isc.close(); } catch (Exception e) { System.out.println("**** ERROR reading " + f + " ****"); } + System.out.println("*** reading: " + f + " " + lastSrtmRaster.ncols); srtmmap.put(filename, lastSrtmRaster); return lastSrtmRaster; } - - f = new File(new File(srtmdir), filename + ".zip"); - // System.out.println("reading: " + f + " ilon=" + ilon + " ilat=" + ilat); - if (f.exists()) { - try { - lastSrtmRaster = new SrtmData(f).getRaster(); + if (srtmfallbackdir != null) { + f = new File(new File(srtmfallbackdir), filename + ".bef"); + if (f.exists()) { + try { + InputStream isc = new BufferedInputStream(new FileInputStream(f)); + //lastSrtmRaster = new StatRasterCoder().decodeRaster(isc); + lastSrtmRaster = new ElevationRasterCoder().decodeRaster(isc); + isc.close(); + } catch (Exception e) { + System.out.println("**** ERROR reading " + f + " ****"); + } + System.out.println("*** reading: " + f + " " + lastSrtmRaster.cellsize); srtmmap.put(filename, lastSrtmRaster); return lastSrtmRaster; - } catch (Exception e) { - System.out.println("**** ERROR reading " + f + " ****"); } } srtmmap.put(filename, lastSrtmRaster); @@ -214,23 +246,41 @@ public class PosUnifier extends MapCreatorBase { return lastSrtmRaster; } - private SrtmRaster hgtForNode(int ilon, int ilat) throws Exception { + static String genFilenameXY(int srtmLonIdx, int srtmLatIdx) { + String slonidx = "0" + srtmLonIdx; + String slatidx = "0" + srtmLatIdx; + return "srtm_" + slonidx.substring(slonidx.length() - 2) + "_" + slatidx.substring(slatidx.length() - 2); + } + + static String genFilenameRd5(int ilon, int ilat) { + int lonDegree = ilon / 1000000; + int latDegree = ilat / 1000000; + int lonMod5 = lonDegree % 5; + int latMod5 = latDegree % 5; + lonDegree = lonDegree - 180 - lonMod5; + latDegree = latDegree - 90 - latMod5; + return String.format("srtm_%s_%s", lonDegree < 0 ? "W" + (-lonDegree) : "E" + lonDegree, + latDegree < 0 ? "S" + (-latDegree) : "N" + latDegree); + } + + + private ElevationRaster hgtForNode(int ilon, int ilat) throws Exception { double lon = (ilon - 180000000) / 1000000.; double lat = (ilat - 90000000) / 1000000.; String filename = buildHgtFilename(lat, lon); // don't block lastSrtmRaster - SrtmRaster srtm = srtmmap.get(filename); + ElevationRaster srtm = srtmmap.get(filename); if (srtm == null) { - File f = new File(new File(srtmdir), filename + ".hgt"); + File f = new File(new File(srtmdir), filename + ".zip"); if (f.exists()) { - srtm = new ConvertLidarTile().getRaster(f, lon, lat); + srtm = new ElevationRasterTileConverter().getRaster(f, lon, lat); srtmmap.put(filename, srtm); return srtm; } - f = new File(new File(srtmdir), filename + ".zip"); + f = new File(new File(srtmdir), filename + ".hgt"); if (f.exists()) { - srtm = new ConvertLidarTile().getRaster(f, lon, lat); + srtm = new ElevationRasterTileConverter().getRaster(f, lon, lat); srtmmap.put(filename, srtm); return srtm; } @@ -238,6 +288,7 @@ public class PosUnifier extends MapCreatorBase { return srtm; } + private String buildHgtFilename(double llat, double llon) { int lat = (int) llat; int lon = (int) llon; @@ -253,10 +304,10 @@ public class PosUnifier extends MapCreatorBase { lon = -lon + 1; } - return String.format(Locale.US, "%s%02d%s%03d", latPref, lat, lonPref, lon); + return String.format("%s%02d%s%03d", latPref, lat, lonPref, lon); } - private void resetSrtm() { + private void resetElevationRaster() { srtmmap = new HashMap<>(); lastSrtmLonIdx = -1; lastSrtmLatIdx = -1; diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/RelationStatistics.java b/brouter-map-creator/src/main/java/btools/mapcreator/RelationStatistics.java index a6d5e04..e239d89 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/RelationStatistics.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/RelationStatistics.java @@ -4,6 +4,7 @@ import java.io.DataInputStream; import java.io.EOFException; import java.io.File; import java.util.HashMap; +import java.util.Map; /** * WayCutter does 2 step in map-processing: @@ -25,7 +26,7 @@ public class RelationStatistics extends MapCreatorBase { } public void process(File relationFileIn) throws Exception { - HashMap relstats = new HashMap<>(); + Map relstats = new HashMap<>(); DataInputStream dis = createInStream(relationFileIn); try { diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/SrtmData.java b/brouter-map-creator/src/main/java/btools/mapcreator/SrtmData.java deleted file mode 100644 index b101cd8..0000000 --- a/brouter-map-creator/src/main/java/btools/mapcreator/SrtmData.java +++ /dev/null @@ -1,166 +0,0 @@ -package btools.mapcreator; - -/** - * This is a wrapper for a 5*5 degree srtm file in ascii/zip-format - *

- * - filter out unused nodes according to the way file - * - enhance with SRTM elevation data - * - split further in smaller (5*5 degree) tiles - * - * @author ab - */ - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.util.StringTokenizer; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -public class SrtmData { - private SrtmRaster raster; - - public SrtmData(File file) throws Exception { - raster = new SrtmRaster(); - - ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(file))); - try { - for (; ; ) { - ZipEntry ze = zis.getNextEntry(); - if (ze.getName().endsWith(".asc")) { - readFromStream(zis); - return; - } - } - } finally { - zis.close(); - } - } - - public SrtmRaster getRaster() { - return raster; - } - - private String secondToken(String s) { - StringTokenizer tk = new StringTokenizer(s, " "); - tk.nextToken(); - return tk.nextToken(); - } - - public void readFromStream(InputStream is) throws Exception { - BufferedReader br = new BufferedReader(new InputStreamReader(is)); - int linenr = 0; - for (; ; ) { - linenr++; - if (linenr <= 6) { - String line = br.readLine(); - if (linenr == 1) - raster.ncols = Integer.parseInt(secondToken(line)); - else if (linenr == 2) - raster.nrows = Integer.parseInt(secondToken(line)); - else if (linenr == 3) - raster.xllcorner = Double.parseDouble(secondToken(line)); - else if (linenr == 4) - raster.yllcorner = Double.parseDouble(secondToken(line)); - else if (linenr == 5) - raster.cellsize = Double.parseDouble(secondToken(line)); - else if (linenr == 6) { - // nodata ignored here ( < -250 assumed nodata... ) - // raster.noDataValue = Short.parseShort( secondToken( line ) ); - raster.eval_array = new short[raster.ncols * raster.nrows]; - } - } else { - int row = 0; - int col = 0; - int n = 0; - boolean negative = false; - for (; ; ) { - int c = br.read(); - if (c < 0) - break; - if (c == ' ') { - if (negative) - n = -n; - short val = n < -250 ? Short.MIN_VALUE : (short) (n); - - raster.eval_array[row * raster.ncols + col] = val; - if (++col == raster.ncols) { - col = 0; - ++row; - } - n = 0; - negative = false; - } else if (c >= '0' && c <= '9') { - n = 10 * n + (c - '0'); - } else if (c == '-') { - negative = true; - } - } - break; - } - } - br.close(); - } - - public static void main(String[] args) throws Exception { - String fromDir = args[0]; - String toDir = args[1]; - - File[] files = new File(fromDir).listFiles(); - for (File f : files) { - if (!f.getName().endsWith(".zip")) { - continue; - } - System.out.println("*** reading: " + f); - long t0 = System.currentTimeMillis(); - SrtmRaster raster = new SrtmData(f).getRaster(); - long t1 = System.currentTimeMillis(); - String name = f.getName(); - - long zipTime = t1 - t0; - - File fbef = new File(new File(toDir), name.substring(0, name.length() - 3) + "bef"); - System.out.println("recoding: " + f + " to " + fbef); - OutputStream osbef = new BufferedOutputStream(new FileOutputStream(fbef)); - new RasterCoder().encodeRaster(raster, osbef); - osbef.close(); - - System.out.println("*** re-reading: " + fbef); - - long t2 = System.currentTimeMillis(); - InputStream isc = new BufferedInputStream(new FileInputStream(fbef)); - SrtmRaster raster2 = new RasterCoder().decodeRaster(isc); - isc.close(); - long t3 = System.currentTimeMillis(); - - long befTime = t3 - t2; - - System.out.println("*** zip-time: " + zipTime + "*** bef-time: " + befTime); - - String s1 = raster.toString(); - String s2 = raster2.toString(); - - if (!s1.equals(s2)) { - throw new IllegalArgumentException("missmatch: " + s1 + "<--->" + s2); - } - - int cols = raster.ncols; - int rows = raster.nrows; - for (int c = 0; c < cols; c++) { - for (int r = 0; r < rows; r++) { - int idx = r * cols + c; - - if (raster.eval_array[idx] != raster2.eval_array[idx]) { - throw new IllegalArgumentException("missmatch: at " + c + "," + r + ": " + raster.eval_array[idx] + "<--->" + raster2.eval_array[idx]); - } - } - } - } - } -} diff --git a/brouter-map-creator/src/main/java/btools/mapcreator/WayLinker.java b/brouter-map-creator/src/main/java/btools/mapcreator/WayLinker.java index 91c70a9..2592143 100644 --- a/brouter-map-creator/src/main/java/btools/mapcreator/WayLinker.java +++ b/brouter-map-creator/src/main/java/btools/mapcreator/WayLinker.java @@ -9,6 +9,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.RandomAccessFile; import java.util.List; +import java.util.Map; import java.util.TreeMap; import btools.codec.DataBuffers; @@ -35,7 +36,6 @@ import btools.util.LazyArrayOfLists; public class WayLinker extends MapCreatorBase implements Runnable { private File nodeTilesIn; private File wayTilesIn; - private File trafficTilesIn; private File dataTilesOut; private File borderFileIn; @@ -44,13 +44,13 @@ public class WayLinker extends MapCreatorBase implements Runnable { private boolean readingBorder; private CompactLongMap nodesMap; - private OsmTrafficMap trafficMap; private List nodesList; private CompactLongSet borderSet; private short lookupVersion; private short lookupMinorVersion; private long creationTimeStamp; + private byte elevationType; private BExpressionContextWay expctxWay; @@ -153,7 +153,6 @@ public class WayLinker extends MapCreatorBase implements Runnable { String dataTilesSuffix) throws Exception { this.nodeTilesIn = nodeTilesIn; this.wayTilesIn = wayTilesIn; - this.trafficTilesIn = new File("../traffic"); this.dataTilesOut = dataTilesOut; this.borderFileIn = borderFileIn; this.dataTilesSuffix = dataTilesSuffix; @@ -214,10 +213,15 @@ public class WayLinker extends MapCreatorBase implements Runnable { } - File trafficFile = fileFromTemplate(wayfile, trafficTilesIn, "trf"); - // process corresponding node-file, if any - File nodeFile = fileFromTemplate(wayfile, nodeTilesIn, "u5d"); + elevationType = 3; + File nodeFile = fileFromTemplate(wayfile, nodeTilesIn, "u5d_1"); + if (nodeFile.exists()) { + elevationType = 1; + } else { + nodeFile = fileFromTemplate(wayfile, nodeTilesIn, "u5d_3"); + if (!nodeFile.exists()) nodeFile = fileFromTemplate(wayfile, nodeTilesIn, "u5d"); + } if (nodeFile.exists()) { reset(); @@ -265,11 +269,6 @@ public class WayLinker extends MapCreatorBase implements Runnable { nodesList = nodesMapFrozen.getValueList(); } - // read a traffic-file, if any - if (trafficFile.exists()) { - trafficMap = new OsmTrafficMap(expctxWay); - trafficMap.loadAll(trafficFile, minLon, minLat, minLon + 5000000, minLat + 5000000, false); - } return true; } @@ -473,7 +472,7 @@ public class WayLinker extends MapCreatorBase implements Runnable { MicroCache mc = new MicroCache2(size, abBuf2, lonIdxDiv, latIdxDiv, divisor); // sort via treemap - TreeMap sortedList = new TreeMap<>(); + Map sortedList = new TreeMap<>(); for (OsmNodeP n : subList) { long longId = n.getIdFromPos(); int shrinkid = mc.shrinkId(longId); @@ -484,7 +483,7 @@ public class WayLinker extends MapCreatorBase implements Runnable { } for (OsmNodeP n : sortedList.values()) { - n.writeNodeData(mc, trafficMap); + n.writeNodeData(mc); } if (mc.getSize() > 0) { byte[] subBytes; @@ -539,6 +538,7 @@ public class WayLinker extends MapCreatorBase implements Runnable { for (int i55 = 0; i55 < 25; i55++) { os.writeInt(fileHeaderCrcs[i55]); } + os.writeByte(elevationType); os.close(); @@ -547,10 +547,6 @@ public class WayLinker extends MapCreatorBase implements Runnable { ra.write(abFileIndex, 0, abFileIndex.length); ra.close(); } - if (trafficMap != null) { - trafficMap.finish(); - trafficMap = null; - } System.out.println("**** codec stats: *******\n" + StatCoderContext.getBitReport()); } diff --git a/brouter-map-creator/src/test/java/btools/mapcreator/MapcreatorTest.java b/brouter-map-creator/src/test/java/btools/mapcreator/MapcreatorTest.java index beb4cbd..b509bff 100644 --- a/brouter-map-creator/src/test/java/btools/mapcreator/MapcreatorTest.java +++ b/brouter-map-creator/src/test/java/btools/mapcreator/MapcreatorTest.java @@ -42,7 +42,7 @@ public class MapcreatorTest { File unodes55 = new File(tmpdir, "unodes55"); File bordernodes = new File(tmpdir, "bordernodes.dat"); unodes55.mkdir(); - new PosUnifier().process(nodes55, unodes55, borderFile, bordernodes, workingDir.getAbsolutePath()); + new PosUnifier().process(nodes55, unodes55, borderFile, bordernodes, workingDir.getAbsolutePath(), null); // run WayLinker File segments = new File(tmpdir, "segments"); diff --git a/brouter-mapaccess/.gitignore b/brouter-mapaccess/.gitignore deleted file mode 100644 index 84c048a..0000000 --- a/brouter-mapaccess/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build/ diff --git a/brouter-mapaccess/build.gradle b/brouter-mapaccess/build.gradle index 40189cb..b17e589 100644 --- a/brouter-mapaccess/build.gradle +++ b/brouter-mapaccess/build.gradle @@ -1,12 +1,11 @@ plugins { - id 'java-library' + id 'brouter.library-conventions' } dependencies { implementation project(':brouter-util') implementation project(':brouter-codec') implementation project(':brouter-expressions') - testImplementation 'junit:junit:4.13.2' } // MapcreatorTest generates segments which are used in tests diff --git a/brouter-mapaccess/src/main/AndroidManifest.xml b/brouter-mapaccess/src/main/AndroidManifest.xml deleted file mode 100644 index 46f6fa6..0000000 --- a/brouter-mapaccess/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/NodesCache.java b/brouter-mapaccess/src/main/java/btools/mapaccess/NodesCache.java index f2a6316..e16b8d2 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/NodesCache.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/NodesCache.java @@ -385,4 +385,20 @@ public final class NodesCache { } } } + + public int getElevationType(int ilon, int ilat) { + int lonDegree = ilon / 1000000; + int latDegree = ilat / 1000000; + OsmFile[] fileRow = fileRows[latDegree]; + int ndegrees = fileRow == null ? 0 : fileRow.length; + for (int i = 0; i < ndegrees; i++) { + if (fileRow[i].lonDegree == lonDegree) { + OsmFile osmf = fileRow[i]; + if (osmf != null) return osmf.elevationType; + break; + } + } + return 3; + } + } diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java index 0f773cf..9b5724b 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java @@ -31,8 +31,8 @@ final class OsmFile { private int divisor; private int cellsize; - private int ncaches; private int indexsize; + protected byte elevationType = 3; public OsmFile(PhysicalFile rafile, int lonDegree, int latDegree, DataBuffers dataBuffers) throws IOException { this.lonDegree = lonDegree; @@ -43,9 +43,10 @@ final class OsmFile { if (rafile != null) { divisor = rafile.divisor; + elevationType = rafile.elevationType; cellsize = 1000000 / divisor; - ncaches = divisor * divisor; + int ncaches = divisor * divisor; indexsize = ncaches * 4; byte[] iobuffer = dataBuffers.iobuffer; @@ -141,7 +142,7 @@ final class OsmFile { new DirectWeaver(bc, dataBuffers, lonIdx, latIdx, divisor, wayValidator, waypointMatcher, hollowNodes); return MicroCache.emptyNonVirgin; } finally { - // crc check only if the buffer has not been fully read + // crc check only if the buffer has not been fully read int readBytes = (bc.getReadingBitPosition() + 7) >> 3; if (readBytes != asize - 4) { int crcData = Crc32.crc(ab, 0, asize - 4); diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNodePairSet.java b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNodePairSet.java index 3fe874e..19aec72 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNodePairSet.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmNodePairSet.java @@ -21,7 +21,7 @@ public class OsmNodePairSet { n2a = new long[maxTempNodes]; } - private static class OsmNodePair { + private static final class OsmNodePair { public long node2; public OsmNodePair next; } diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java b/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java index 7bfada5..e61f469 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java @@ -24,6 +24,7 @@ final public class PhysicalFile { String fileName; public int divisor = 80; + public byte elevationType = 3; public static void main(String[] args) { MicroCache.debug = true; @@ -69,7 +70,7 @@ final public class PhysicalFile { DataBuffers dataBuffers = new DataBuffers(); pf = new PhysicalFile(f, dataBuffers, -1, -1); int div = pf.divisor; - for (int lonDegree = 0; lonDegree < 5; lonDegree++) { // does'nt really matter.. + for (int lonDegree = 0; lonDegree < 5; lonDegree++) { // doesn't really matter.. for (int latDegree = 0; latDegree < 5; latDegree++) { // ..where on earth we are OsmFile osmf = new OsmFile(pf, lonDegree, latDegree, dataBuffers); if (osmf.hasData()) @@ -113,6 +114,10 @@ final public class PhysicalFile { if (len == pos) return; // old format o.k. + if ((len-pos) > extraLen) { + extraLen++; + } + if (len < pos + extraLen) { // > is o.k. for future extensions! throw new IOException("file of size " + len + " too short, should be " + (pos + extraLen)); } @@ -134,5 +139,8 @@ final public class PhysicalFile { for (int i = 0; i < 25; i++) { fileHeaderCrcs[i] = dis.readInt(); } + try { + elevationType = dis.readByte(); + } catch (Exception e) {} } } diff --git a/brouter-routing-app/.gitignore b/brouter-routing-app/.gitignore deleted file mode 100644 index 84c048a..0000000 --- a/brouter-routing-app/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build/ diff --git a/brouter-routing-app/build.gradle b/brouter-routing-app/build.gradle index d0f9f2a..ebcc34b 100644 --- a/brouter-routing-app/build.gradle +++ b/brouter-routing-app/build.gradle @@ -1,26 +1,30 @@ import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform plugins { + id 'base' id 'com.android.application' + id 'checkstyle' + id 'brouter.version-conventions' } android { - compileSdkVersion 33 + compileSdk 34 + + base { + archivesName = "BRouterApp." + project.version + } defaultConfig { namespace 'btools.routingapp' applicationId "btools.routingapp" - versionCode 48 + versionCode 54 versionName project.version resValue('string', 'app_version', defaultConfig.versionName) - setProperty("archivesBaseName", "BRouterApp." + defaultConfig.versionName) - minSdkVersion 14 - targetSdkVersion 33 - - resConfigs "en" + minSdkVersion 21 + targetSdkVersion 34 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -53,7 +57,7 @@ android { buildTypes { release { - minifyEnabled false + minifyEnabled true debuggable false if (project.hasProperty("RELEASE_STORE_FILE") && RELEASE_STORE_FILE.length() > 0) { signingConfig signingConfigs.release @@ -76,6 +80,10 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + buildFeatures { + aidl true + buildConfig true + } applicationVariants.all { variant -> @@ -86,23 +94,28 @@ android { } } +repositories { + mavenCentral() + google() +} + dependencies { - implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'androidx.appcompat:appcompat:1.7.0' implementation "androidx.constraintlayout:constraintlayout:2.1.4" - implementation 'androidx.work:work-runtime:2.8.1' - implementation 'com.google.android.material:material:1.8.0' + implementation 'androidx.work:work-runtime:2.9.0' + implementation 'com.google.android.material:material:1.12.0' implementation project(':brouter-mapaccess') implementation project(':brouter-core') implementation project(':brouter-expressions') implementation project(':brouter-util') - implementation 'androidx.preference:preference:1.2.0' + implementation 'androidx.preference:preference:1.2.1' testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.5' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - androidTestImplementation 'androidx.work:work-testing:2.8.1' + androidTestImplementation 'androidx.test.ext:junit:1.2.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' + androidTestImplementation 'androidx.work:work-testing:2.9.0' } gradle.projectsEvaluated { @@ -150,3 +163,8 @@ task generateReadmesZip(type: Zip) { } destinationDirectory = layout.buildDirectory.dir("assets") } + +tasks.withType(JavaCompile).configureEach { + options.compilerArgs += ['-Xlint:unchecked'] + options.compilerArgs += ['-Xlint:deprecation'] +} diff --git a/brouter-routing-app/proguard-rules.pro b/brouter-routing-app/proguard-rules.pro index f1b4245..95f0278 100644 --- a/brouter-routing-app/proguard-rules.pro +++ b/brouter-routing-app/proguard-rules.pro @@ -19,3 +19,11 @@ # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile + +-keep class btools.codec.** { *; } +-keep class btools.router.** { *; } +-keep class btools.expressions.** { *; } +-keep class btools.mapaccess.** { *; } +-keep class btools.server.** { *; } +-keep class btools.util.** { *; } +-keep class btools.routingapp.** { *; } diff --git a/brouter-routing-app/src/androidTest/java/btools/routingapp/BRouterActivityTest.java b/brouter-routing-app/src/androidTest/java/btools/routingapp/BRouterActivityTest.java index 6a6a9cd..43b89f7 100644 --- a/brouter-routing-app/src/androidTest/java/btools/routingapp/BRouterActivityTest.java +++ b/brouter-routing-app/src/androidTest/java/btools/routingapp/BRouterActivityTest.java @@ -1,9 +1,9 @@ package btools.routingapp; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.not; -import static org.junit.Assert.assertThat; import android.os.Build; import android.os.Environment; diff --git a/brouter-routing-app/src/androidTest/java/btools/routingapp/DownloadWorkerTest.java b/brouter-routing-app/src/androidTest/java/btools/routingapp/DownloadWorkerTest.java index a9004c0..c0ce44d 100644 --- a/brouter-routing-app/src/androidTest/java/btools/routingapp/DownloadWorkerTest.java +++ b/brouter-routing-app/src/androidTest/java/btools/routingapp/DownloadWorkerTest.java @@ -1,7 +1,7 @@ package btools.routingapp; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; import android.content.Context; diff --git a/brouter-routing-app/src/main/AndroidManifest.xml b/brouter-routing-app/src/main/AndroidManifest.xml index a2f9dfa..831deea 100644 --- a/brouter-routing-app/src/main/AndroidManifest.xml +++ b/brouter-routing-app/src/main/AndroidManifest.xml @@ -10,6 +10,7 @@ + + String with a number of seconds for the routing timeout, default = 60 // "turnInstructionFormat"-->String selecting the format for turn-instructions values: osmand, locus // "trackFormat"-->[kml|gpx|json] default = gpx + // "acceptCompressedResult"-->[true] sends a compressed result when output format is gpx // "lats"-->double[] array of latitudes; 2 values at least. // "lons"-->double[] array of longitudes; 2 values at least. // "nogoLats"-->double[] array of nogo latitudes; may be null. diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BImportActivity.java b/brouter-routing-app/src/main/java/btools/routingapp/BImportActivity.java index 2550bf9..0cd981e 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/BImportActivity.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/BImportActivity.java @@ -73,13 +73,19 @@ public class BImportActivity extends AppCompatActivity { // URI example ==> dat=content://me.bluemail.mail.attachmentprovider/a2939069-76b5-44e4-8cbd-94485d0fd4ff/cc32b61d-97a6-4871-b67f-945d1d1d43c8/VIEW String filename = null; long filesize = 0L; + try (Cursor cursor = this.getContentResolver().query(intent.getData(), new String[]{ OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}, null, null, null)) { if (cursor != null && cursor.moveToFirst()) { filename = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)); filesize = cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE)); } + } catch (Exception e) { + resultMessage.append("ERROR: File not accessible\n"); + displayMessage(resultMessage.toString()); + return; } + // is the file extention ".brf" in the file name if (filename == null || isInvalidProfileFilename(filename)) { resultMessage.append("ERROR: File extention must be \".brf\"\n"); diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java b/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java index c49a6be..6dcc259 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java @@ -10,7 +10,6 @@ import android.annotation.SuppressLint; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; -import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.Resources; import android.os.Build; @@ -47,6 +46,8 @@ import btools.router.RoutingHelper; public class BInstallerActivity extends AppCompatActivity { + private static final String TAG = "BInstallerActivity"; + private static final int DIALOG_CONFIRM_DELETE_ID = 1; private static final int DIALOG_CONFIRM_NEXTSTEPS_ID = 2; private static final int DIALOG_CONFIRM_GETDIFFS_ID = 3; @@ -171,7 +172,7 @@ public class BInstallerActivity extends AppCompatActivity { } mButtonDownload.setText(getString(R.string.action_download, getSegmentsPlural(selectedTilesDownload.size()))); mButtonDownload.setEnabled(true); - mSummaryInfo.setText(getString(R.string.summary_segments, Formatter.formatFileSize(this, tileSize), Formatter.formatFileSize(this, getAvailableSpace(mBaseDir.getAbsolutePath())))); + mSummaryInfo.setText(String.format(getString(R.string.summary_segments), Formatter.formatFileSize(this, tileSize), Formatter.formatFileSize(this, getAvailableSpace(mBaseDir.getAbsolutePath())))); } else if (selectedTilesUpdate.size() > 0) { mButtonDownload.setText(getString(R.string.action_update, getSegmentsPlural(selectedTilesUpdate.size()))); mButtonDownload.setEnabled(true); @@ -214,9 +215,10 @@ public class BInstallerActivity extends AppCompatActivity { .build(); } catch (IllegalStateException e) { - Toast.makeText(this, "Too much data for download. Please reduce.", Toast.LENGTH_LONG).show(); + Object data; + Toast.makeText(this, R.string.msg_too_much_data, Toast.LENGTH_LONG).show(); - e.printStackTrace(); + Log.e(TAG, Log.getStackTraceString(e)); return; } @@ -242,10 +244,9 @@ public class BInstallerActivity extends AppCompatActivity { //WorkManager.getInstance(getApplicationContext()).cancelWorkById(downloadWorkRequest.getId()); } } catch (ExecutionException e) { - e.printStackTrace(); + Log.e(TAG, Log.getStackTraceString(e)); } catch (InterruptedException e) { - Log.d("worker", "canceled " + e.getMessage()); - //e.printStackTrace(); + Log.d(TAG, "canceled " + e.getMessage()); } workManager @@ -264,7 +265,7 @@ public class BInstallerActivity extends AppCompatActivity { } if (workInfo.getState() == WorkInfo.State.ENQUEUED) { - Toast.makeText(this, "Download scheduled. Check internet connection if it doesn't start.", Toast.LENGTH_LONG).show(); + Toast.makeText(this, R.string.msg_download_start, Toast.LENGTH_LONG).show(); mProgressIndicator.hide(); mProgressIndicator.setIndeterminate(true); mProgressIndicator.show(); @@ -279,7 +280,7 @@ public class BInstallerActivity extends AppCompatActivity { String segmentName = progress.getString(DownloadWorker.PROGRESS_SEGMENT_NAME); int percent = progress.getInt(DownloadWorker.PROGRESS_SEGMENT_PERCENT, 0); if (percent > 0) { - mDownloadSummaryInfo.setText("Downloading .. " + segmentName); + mDownloadSummaryInfo.setText(getString(R.string.msg_download_started) + segmentName); } if (percent > 0) { mProgressIndicator.setIndeterminate(false); @@ -295,13 +296,13 @@ public class BInstallerActivity extends AppCompatActivity { String result; switch (workInfo.getState()) { case FAILED: - result = "Download failed"; + result = getString(R.string.msg_download_failed); break; case CANCELLED: - result = "Download cancelled"; + result = getString(R.string.msg_download_cancel); break; case SUCCEEDED: - result = "Download succeeded"; + result = getString(R.string.msg_download_succeed); break; default: result = ""; @@ -349,12 +350,12 @@ public class BInstallerActivity extends AppCompatActivity { switch (id) { case DIALOG_CONFIRM_DELETE_ID: builder - .setTitle("Confirm Delete") - .setMessage("Really delete?").setPositiveButton("Yes", new DialogInterface.OnClickListener() { + .setTitle(R.string.title_delete) + .setMessage(R.string.summary_delete).setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { deleteSelectedTiles(); } - }).setNegativeButton("No", new DialogInterface.OnClickListener() { + }).setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { } }); @@ -362,9 +363,9 @@ public class BInstallerActivity extends AppCompatActivity { case DIALOG_CONFIRM_NEXTSTEPS_ID: builder - .setTitle("Version Problem") - .setMessage("The base version for tiles has changed. What to do?") - .setPositiveButton("Continue with current download, delete other old data", new DialogInterface.OnClickListener() { + .setTitle(R.string.title_version) + .setMessage(R.string.summary_version) + .setPositiveButton(R.string.action_version1, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { ArrayList allTiles = mBInstallerView.getSelectedTiles(MASK_INSTALLED_RD5); @@ -376,11 +377,11 @@ public class BInstallerActivity extends AppCompatActivity { } downloadSelectedTiles(); } - }).setNegativeButton("Select all for download and start", new DialogInterface.OnClickListener() { + }).setNegativeButton(R.string.action_version2, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { downloadInstalledTiles(); } - }).setNeutralButton("Cancel now, complete on an other day", new DialogInterface.OnClickListener() { + }).setNeutralButton(R.string.action_version3, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { File tmplookupFile = new File(mBaseDir, "brouter/profiles2/lookups.dat.tmp"); tmplookupFile.delete(); @@ -391,17 +392,17 @@ public class BInstallerActivity extends AppCompatActivity { case DIALOG_CONFIRM_GETDIFFS_ID: builder - .setTitle("Version Differences") - .setMessage("The base version for some tiles is different. What to do?") - .setPositiveButton("Download all different tiles", new DialogInterface.OnClickListener() { + .setTitle(R.string.title_version_diff) + .setMessage(R.string.summary_version_diff) + .setPositiveButton(R.string.action_version_diff1, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { downloadDiffVersionTiles(); } - }).setNegativeButton("Drop all different tiles", new DialogInterface.OnClickListener() { + }).setNegativeButton(R.string.action_version_diff2, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dropDiffVersionTiles(); } - }).setNeutralButton("Cancel now, complete on an other day", new DialogInterface.OnClickListener() { + }).setNeutralButton(R.string.action_version_diff3, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { finish(); } @@ -409,9 +410,9 @@ public class BInstallerActivity extends AppCompatActivity { return builder.create(); case DIALOG_NEW_APP_NEEDED_ID: builder - .setTitle("App Version") - .setMessage("The new data version needs a new app. Please update BRouter first") - .setPositiveButton("Ok", new DialogInterface.OnClickListener() { + .setTitle(R.string.title_version) + .setMessage(R.string.summary_new_version) + .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { finish(); } @@ -516,10 +517,10 @@ public class BInstallerActivity extends AppCompatActivity { } return running; } catch (ExecutionException e) { - e.printStackTrace(); + Log.e(TAG, Log.getStackTraceString(e)); return false; } catch (InterruptedException e) { - e.printStackTrace(); + Log.e(TAG, Log.getStackTraceString(e)); return false; } } diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BInstallerView.java b/brouter-routing-app/src/main/java/btools/routingapp/BInstallerView.java index 4eaddac..8fb8949 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/BInstallerView.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/BInstallerView.java @@ -175,7 +175,7 @@ public class BInstallerView extends View { int tidx = gridPos2Tileindex(ix, iy); int tilesize = BInstallerSizes.getRd5Size(tidx); if (tilesize > 0) { - canvas.drawRect(fw * ix, fh * (iy + 1), fw * (ix + 1), fh * iy, paintGrid); + canvas.drawRect(fw * ix, fh * iy, fw * (ix + 1), fh * (iy + 1), paintGrid); } } } @@ -214,7 +214,7 @@ public class BInstallerView extends View { canvas.drawLine(fw * ix, fh * (iy + 1), fw * (ix + 1), fh * iy, pnt); // draw frame - canvas.drawRect(fw * ix, fh * (iy + 1), fw * (ix + 1), fh * iy, pnt); + canvas.drawRect(fw * ix, fh * iy, fw * (ix + 1), fh * (iy + 1), pnt); } } } diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BRouterActivity.java b/brouter-routing-app/src/main/java/btools/routingapp/BRouterActivity.java index 1dca201..f633b47 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/BRouterActivity.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/BRouterActivity.java @@ -58,7 +58,7 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat private static final int DIALOG_PICKWAYPOINT_ID = 10; private static final int DIALOG_SELECTBASEDIR_ID = 11; private static final int DIALOG_MAINACTION_ID = 12; - private static final int DIALOG_OLDDATAHINT_ID = 13; + //private static final int DIALOG_OLDDATAHINT_ID = 13; private static final int DIALOG_SHOW_REPEAT_TIMEOUT_HELP_ID = 16; private final Set dialogIds = new HashSet<>(); private BRouterView mBRouterView; @@ -133,7 +133,7 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat switch (id) { case DIALOG_SELECTPROFILE_ID: - builder.setTitle("Select a routing profile"); + builder.setTitle(R.string.action_select_profile); builder.setItems(availableProfiles, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { selectedProfile = availableProfiles[item]; @@ -142,9 +142,9 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat }); return builder.create(); case DIALOG_MAINACTION_ID: - builder.setTitle("Select Main Action"); + builder.setTitle(R.string.main_action); builder.setItems( - new String[]{"Download Manager", "BRouter App"}, + new String[]{getString(R.string.main_action_1), getString(R.string.main_action_2)}, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { if (item == 0) @@ -153,7 +153,7 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat showADialog(DIALOG_SELECTPROFILE_ID); } }) - .setNegativeButton("Close", new DialogInterface.OnClickListener() { + .setNegativeButton(getString(R.string.close), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { finish(); } @@ -161,19 +161,15 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat return builder.create(); case DIALOG_SHOW_DM_INFO_ID: builder - .setTitle("BRouter Download Manager") - .setMessage( - "*** Attention: ***\n\n" + "The Download Manager is used to download routing-data " - + "files which can be up to 170MB each. Do not start the Download Manager " - + "on a cellular data connection without a data plan! " - + "Download speed is restricted to 16 MBit/s.") - .setPositiveButton("I know", new DialogInterface.OnClickListener() { + .setTitle(R.string.title_download) + .setMessage(R.string.summary_download) + .setPositiveButton(R.string.i_know, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { Intent intent = new Intent(BRouterActivity.this, BInstallerActivity.class); startActivity(intent); showNewDialog(DIALOG_MAINACTION_ID); } - }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() { + }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { finish(); } @@ -181,18 +177,15 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat return builder.create(); case DIALOG_SHOW_REPEAT_TIMEOUT_HELP_ID: builder - .setTitle("Successfully prepared a timeout-free calculation") - .setMessage( - "You successfully repeated a calculation that previously run into a timeout " - + "when started from your map-tool. If you repeat the same request from your " - + "maptool, with the exact same destination point and a close-by starting point, " - + "this request is guaranteed not to time out.") - .setNegativeButton("Exit", new DialogInterface.OnClickListener() { + .setTitle(R.string.title_timeoutfree) + .setMessage(R.string.summary_timeoutfree) + .setNegativeButton(R.string.exit, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { finish(); } }); return builder.create(); + /* case DIALOG_OLDDATAHINT_ID: builder .setTitle("Local setup needs reset") @@ -202,12 +195,13 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat + "Before downloading new datafiles made for the new table, " + "you have to reset your local setup by 'moving away' (or deleting) " + "your /brouter directory and start a new setup by calling the " + "BRouter App again.") - .setPositiveButton("OK", new DialogInterface.OnClickListener() { + .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { finish(); } }); return builder.create(); + */ case DIALOG_ROUTINGMODES_ID: builder.setTitle(message); builder.setMultiChoiceItems(routingModes, routingModesChecked, @@ -217,7 +211,7 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat routingModesChecked[which] = isChecked; } }); - builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() { + builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { mBRouterView.configureService(routingModes, routingModesChecked); } @@ -225,9 +219,9 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat return builder.create(); case DIALOG_EXCEPTION_ID: builder - .setTitle("An Error occured") + .setTitle(R.string.error) .setMessage(errorMessage) - .setPositiveButton("OK", + .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { mBRouterView.continueProcessing(); @@ -235,12 +229,12 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat }); return builder.create(); case DIALOG_TEXTENTRY_ID: - builder.setTitle("Enter SDCARD base dir:"); + builder.setTitle(R.string.title_sdcard); builder.setMessage(message); final EditText input = new EditText(this); // input.setText(defaultbasedir); builder.setView(input); - builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() { + builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { String basedir = input.getText().toString(); mBRouterView.startSetup(new File(basedir), true, false); @@ -248,7 +242,7 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat }); return builder.create(); case DIALOG_SELECTBASEDIR_ID: - builder.setTitle("Choose brouter data base dir:"); + builder.setTitle(getString(R.string.action_choose_folder)); // builder.setMessage( message ); builder.setSingleChoiceItems(basedirOptions, 0, new DialogInterface.OnClickListener() { @Override @@ -256,7 +250,7 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat selectedBasedir = item; } }); - builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() { + builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { if (selectedBasedir < availableBasedirs.size()) { mBRouterView.startSetup(availableBasedirs.get(selectedBasedir), true, false); @@ -267,7 +261,7 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat }); return builder.create(); case DIALOG_VIASELECT_ID: - builder.setTitle("Check VIA Selection:"); + builder.setTitle(R.string.action_via_select); builder.setMultiChoiceItems(availableVias, getCheckedBooleanArray(availableVias.length), new DialogInterface.OnMultiChoiceClickListener() { @Override @@ -279,7 +273,7 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat } } }); - builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() { + builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { mBRouterView.updateViaList(selectedVias); mBRouterView.startProcessing(selectedProfile); @@ -287,7 +281,7 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat }); return builder.create(); case DIALOG_NOGOSELECT_ID: - builder.setTitle("Check NoGo Selection:"); + builder.setTitle(R.string.action_nogo_select); String[] nogoNames = new String[nogoList.size()]; for (int i = 0; i < nogoList.size(); i++) nogoNames[i] = nogoList.get(i).name; @@ -299,7 +293,7 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat nogoEnabled[which] = isChecked; } }); - builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() { + builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { mBRouterView.updateNogoList(nogoEnabled); mBRouterView.startProcessing(selectedProfile); @@ -325,21 +319,21 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat List slist = new ArrayList<>(); // Neutral button if (wpCount == 0) { - slist.add("Server-Mode"); + slist.add(getString(R.string.action_servermode)); } else if (wpCount == -3) { - slist.add("Info"); + slist.add(getString(R.string.action_info)); } else if (wpCount >= 2) { - slist.add("Calc Route"); + slist.add(getString(R.string.action_calc_route)); } if (wpCount == 0) { - slist.add("Profile Settings"); + slist.add(getString(R.string.action_profile_settings)); } // Positive button if (wpCount == -3 || wpCount == -1) { - slist.add("Share GPX"); + slist.add(getString(R.string.action_share)); } else if (wpCount >= 0) { - String selectLabel = wpCount == 0 ? "Select from" : "Select to/via"; + String selectLabel = wpCount == 0 ? getString(R.string.action_select_from) : getString(R.string.action_select_to); slist.add(selectLabel); } @@ -407,16 +401,16 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat */ // Negative button - builder.setNegativeButton("Exit", (dialog, which) -> { + builder.setNegativeButton(R.string.exit, (dialog, which) -> { finish(); }); return builder.create(); case DIALOG_MODECONFIGOVERVIEW_ID: builder - .setTitle("Success") + .setTitle(R.string.success) .setMessage(message) - .setPositiveButton("Exit", + .setPositiveButton(R.string.exit, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { finish(); @@ -424,7 +418,7 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat }); return builder.create(); case DIALOG_PICKWAYPOINT_ID: - builder.setTitle(wpCount > 0 ? "Select to/via" : "Select from"); + builder.setTitle(wpCount == 0 ? getString(R.string.action_select_from) : getString(R.string.action_select_to)); builder.setItems(availableWaypoints, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { mBRouterView.updateWaypointList(availableWaypoints[item]); @@ -472,11 +466,11 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat //startActivityForResult(i, 100); someActivityResultLauncher.launch(i); } else { - Toast.makeText(this, "no profile data", Toast.LENGTH_LONG).show(); + Toast.makeText(this, R.string.msg_no_profile, Toast.LENGTH_LONG).show(); finish(); } } else { - Toast.makeText(this, selectedProfile + ", no used profile", Toast.LENGTH_LONG).show(); + Toast.makeText(this, selectedProfile + getString(R.string.msg_no_used_profile), Toast.LENGTH_LONG).show(); finish(); } } diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BRouterService.java b/brouter-routing-app/src/main/java/btools/routingapp/BRouterService.java index 95dbadd..a1e8963 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/BRouterService.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/BRouterService.java @@ -19,6 +19,7 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.zip.GZIPOutputStream; @@ -39,10 +40,6 @@ public class BRouterService extends Service { BRouterWorker worker = new BRouterWorker(); - for (String key : params.keySet()) { - // Log.d("BS", "income " + key + " = " + params.get(key)); - } - int engineMode = 0; if (params.containsKey("engineMode")) { engineMode = params.getInt("engineMode", 0); @@ -99,11 +96,13 @@ public class BRouterService extends Service { if (errMsg != null) { return errMsg; } + // profile is already done + params.remove("profile"); boolean canCompress = "true".equals(params.getString("acceptCompressedResult")); try { String gpxMessage = worker.getTrackFromParams(params); - if (canCompress && gpxMessage.startsWith("<")) { + if (canCompress && (gpxMessage.startsWith("<") || gpxMessage.startsWith("{"))) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write("z64".getBytes(Charset.forName("UTF-8"))); // marker prefix @@ -274,11 +273,17 @@ public class BRouterService extends Service { } } + @SuppressWarnings("deprecation") private void logBundle(Bundle params) { if (AppLogger.isLogging()) { for (String k : params.keySet()) { - Object val = "remoteProfile".equals(k) ? "<..cut..>" : params.getString(k); - String desc = "key=" + k + (val == null ? "" : " class=" + val.getClass() + " val=" + val.toString()); + Object val = "remoteProfile".equals(k) ? "<..cut..>" : params.get(k); + String desc = "key=" + k + (val == null ? "" : " class=" + val.getClass() + " val="); + if (val instanceof double[]) { + desc += Arrays.toString(params.getDoubleArray(k)); + } else { + desc += val.toString(); + } AppLogger.log(desc); } } diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java b/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java index e2ea815..efdbdfb 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java @@ -48,6 +48,8 @@ import btools.util.CheapRuler; public class BRouterView extends View { + private static final String TAG = "BRouterView"; + private final int memoryClass; RoutingEngine cr; private int imgw; @@ -148,8 +150,8 @@ public class BRouterView extends View { try { td.mkdirs(); } catch (Exception e) { - Log.d("BRouterView", "Error creating base directory: " + e.getMessage()); - e.printStackTrace(); + Log.d(TAG, "Error creating base directory: " + e.getMessage()); + Log.e(TAG, Log.getStackTraceString(e)); } if (!td.isDirectory()) { @@ -173,7 +175,7 @@ public class BRouterView extends View { // new init is done move old files if (waitingForMigration) { - Log.d("BR", "path " + oldMigrationPath + " " + basedir); + Log.d(TAG, "path " + oldMigrationPath + " " + basedir); Thread t = new Thread(new Runnable() { @Override public void run() { @@ -184,7 +186,7 @@ public class BRouterView extends View { try { t.join(500); } catch (InterruptedException e) { - e.printStackTrace(); + Log.e(TAG, Log.getStackTraceString(e)); } waitingForMigration = false; } @@ -224,7 +226,9 @@ public class BRouterView extends View { // add a "last timeout" dummy profile File lastTimeoutFile = new File(modesDir + "/timeoutdata.txt"); long lastTimeoutTime = lastTimeoutFile.lastModified(); - if (lastTimeoutTime > 0 && System.currentTimeMillis() - lastTimeoutTime < 1800000) { + if (lastTimeoutTime > 0 && + lastTimeoutFile.length() > 0 && + System.currentTimeMillis() - lastTimeoutTime < 1800000) { BufferedReader br = new BufferedReader(new FileReader(lastTimeoutFile)); String repeatProfile = br.readLine(); br.close(); @@ -331,9 +335,9 @@ public class BRouterView extends View { out.close(); } catch (FileNotFoundException fileNotFoundException) { - Log.e("tag", fileNotFoundException.getMessage()); + Log.e(TAG, fileNotFoundException.getMessage()); } catch (Exception e) { - Log.e("tag", e.getMessage()); + Log.e(TAG, e.getMessage()); } } @@ -380,14 +384,14 @@ public class BRouterView extends View { try { cor.readAllPoints(); } catch (Exception e) { - msg = "Error reading waypoints: " + e; + msg = getContext().getString(R.string.msg_read_wpt_error)+ ": " + e; } int size = cor.allpoints.size(); if (size < 1) - msg = "coordinate source does not contain any waypoints!"; + msg = getContext().getString(R.string.msg_no_wpt); if (size > 1000) - msg = "coordinate source contains too much waypoints: " + size + "(please use from/to/via names)"; + msg = String.format(getContext().getString(R.string.msg_too_much_wpts), size); } if (msg != null) { @@ -471,13 +475,13 @@ public class BRouterView extends View { if (needsWaypointSelection) { StringBuilder msg; if (wpList.size() == 0) { - msg = new StringBuilder("Expecting waypoint selection\n" + "(coordinate-source: " + cor.basedir + cor.rootdir + ")"); + msg = new StringBuilder(getContext().getString(R.string.msg_no_wpt_selection) + "(coordinate-source: " + cor.basedir + cor.rootdir + ")"); } else { - msg = new StringBuilder("current waypoint selection:\n"); + msg = new StringBuilder(getContext().getString(R.string.msg_wpt_selection)); for (int i = 0; i < wpList.size(); i++) msg.append(i > 0 ? "->" : "").append(wpList.get(i).name); } - ((BRouterActivity) getContext()).showResultMessage("Select Action", msg.toString(), wpList.size()); + ((BRouterActivity) getContext()).showResultMessage(getContext().getString(R.string.title_action), msg.toString(), wpList.size()); return; } @@ -718,8 +722,13 @@ public class BRouterView extends View { ((BRouterActivity) getContext()).showErrorMessage(cr.getErrorMessage()); } else { String memstat = memoryClass + "mb pathPeak " + ((cr.getPathPeak() + 500) / 1000) + "k"; - String result = "version = BRouter-" + getContext().getString(R.string.app_version) + "\n" + "mem = " + memstat + "\ndistance = " + cr.getDistance() / 1000. + " km\n" + "filtered ascend = " + cr.getAscend() - + " m\n" + "plain ascend = " + cr.getPlainAscend() + " m\n" + "estimated time = " + cr.getTime(); + String result = String.format(getContext().getString(R.string.msg_status_result), + getContext().getString(R.string.app_version), + memstat, + Double.toString(cr.getDistance() / 1000.), + Integer.toString(cr.getAscend()), + Integer.toString(cr.getPlainAscend()), + cr.getTime()); rawTrack = cr.getFoundRawTrack(); @@ -728,9 +737,9 @@ public class BRouterView extends View { writeRawTrackToPath(rawTrackPath); } - String title = "Success"; + String title = getContext().getString(R.string.success); if (cr.getAlternativeIndex() > 0) - title += " / " + cr.getAlternativeIndex() + ". Alternative"; + title += " / " + cr.getAlternativeIndex() + ". " + getContext().getString(R.string.msg_alternative); ((BRouterActivity) getContext()).showResultMessage(title, result, rawTrackPath == null ? -1 : -3); trackOutfile = cr.getOutfile(); @@ -845,8 +854,13 @@ public class BRouterView extends View { for (int i = 0; i < 6; i++) { if (checkedModes[i]) { writeRawTrackToMode(routingModes[i]); - String s = map.get(routingModes[i]).params; - String p = map.get(routingModes[i]).profile; + ServiceModeConfig sm = map.get(routingModes[i]); + String s = null; + String p = null; + if (sm != null) { + s = sm.params; + p = sm.profile; + } if (s == null || !p.equals(profileName)) s = "noparams"; ServiceModeConfig smc = new ServiceModeConfig(routingModes[i], profileName, s); for (OsmNodeNamed nogo : nogoVetoList) { diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BRouterWorker.java b/brouter-routing-app/src/main/java/btools/routingapp/BRouterWorker.java index e140561..1c733ba 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/BRouterWorker.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/BRouterWorker.java @@ -6,16 +6,20 @@ import android.os.Bundle; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; -import java.util.ArrayList; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; import java.util.HashMap; import java.util.List; -import java.util.StringTokenizer; +import java.util.Map; +import btools.router.FormatGpx; +import btools.router.FormatJson; +import btools.router.FormatKml; import btools.router.OsmNodeNamed; -import btools.router.OsmNogoPolygon; import btools.router.OsmTrack; import btools.router.RoutingContext; import btools.router.RoutingEngine; +import btools.router.RoutingParamCollector; public class BRouterWorker { private static final int OUTPUT_FORMAT_GPX = 0; @@ -32,6 +36,7 @@ public class BRouterWorker { public List nogoPolygonsList; public String profileParams; + @SuppressWarnings("deprecation") public String getTrackFromParams(Bundle params) { int engineMode = 0; @@ -39,6 +44,72 @@ public class BRouterWorker { engineMode = params.getInt("engineMode", 0); } + RoutingContext rc = new RoutingContext(); + rc.rawTrackPath = rawTrackPath; + rc.localFunction = profilePath; + + RoutingParamCollector routingParamCollector = new RoutingParamCollector(); + + // parameter pre control + if (params.containsKey("lonlats")) { + waypoints = routingParamCollector.getWayPointList(params.getString("lonlats")); + params.remove("lonlats"); + } + if (params.containsKey("lats")) { + double[] lats = params.getDoubleArray("lats"); + double[] lons = params.getDoubleArray("lons"); + waypoints = routingParamCollector.readPositions(lons, lats); + params.remove("lons"); + params.remove("lats"); + } + + if (waypoints == null) { + throw new IllegalArgumentException("no points!"); + } + if (engineMode == 0) { + if (waypoints.size() < 2) { + throw new IllegalArgumentException("we need two lat/lon points at least!"); + } + } else { + if (waypoints.size() < 1) { + throw new IllegalArgumentException("we need two lat/lon points at least!"); + } + } + + if (nogoList != null && nogoList.size() > 0) { + // forward already read nogos from filesystem + if (rc.nogopoints == null) { + rc.nogopoints = nogoList; + } else { + rc.nogopoints.addAll(nogoList); + } + + } + + Map theParams = new HashMap<>(); + for (String key : params.keySet()) { + Object value = params.get(key); + if (value instanceof double[]) { + String s = Arrays.toString(params.getDoubleArray(key)); + s = s.replace("[", "").replace("]", ""); + theParams.put(key, s); + } else { + theParams.put(key, value.toString()); + } + } + routingParamCollector.setParams(rc, waypoints, theParams); + + if (params.containsKey("extraParams")) { + Map profileparams = null; + try { + profileparams = routingParamCollector.getUrlParams(params.getString("extraParams")); + routingParamCollector.setProfileParams(rc, profileparams); + } catch (UnsupportedEncodingException e) { + // ignore + } + } + + String pathToFileResult = params.getString("pathToFileResult"); if (pathToFileResult != null) { @@ -52,100 +123,9 @@ public class BRouterWorker { long maxRunningTime = 60000; String sMaxRunningTime = params.getString("maxRunningTime"); if (sMaxRunningTime != null) { - maxRunningTime = Integer.parseInt(sMaxRunningTime) * 1000; + maxRunningTime = Integer.parseInt(sMaxRunningTime) * 1000L; } - RoutingContext rc = new RoutingContext(); - rc.rawTrackPath = rawTrackPath; - rc.localFunction = profilePath; - - String tiFormat = params.getString("turnInstructionFormat"); - if (tiFormat != null) { - if ("osmand".equalsIgnoreCase(tiFormat)) { - rc.turnInstructionMode = 3; - } else if ("locus".equalsIgnoreCase(tiFormat)) { - rc.turnInstructionMode = 7; - } - } - if (params.containsKey("timode")) { - rc.turnInstructionMode = params.getInt("timode"); - } - - if (params.containsKey("direction")) { - rc.startDirection = params.getInt("direction"); - } - if (params.containsKey("heading")) { - rc.startDirection = params.getInt("heading"); - rc.forceUseStartDirection = true; - } - if (params.containsKey("alternativeidx")) { - rc.alternativeIdx = params.getInt("alternativeidx"); - } - - readNogos(params); // add interface provided nogos - if (nogoList != null) { - RoutingContext.prepareNogoPoints(nogoList); - if (rc.nogopoints == null) { - rc.nogopoints = nogoList; - } else { - rc.nogopoints.addAll(nogoList); - } - } - if (rc.nogopoints == null) { - rc.nogopoints = nogoPolygonsList; - } else if (nogoPolygonsList != null) { - rc.nogopoints.addAll(nogoPolygonsList); - } - List poisList = readPoisList(params); - rc.poipoints = poisList; - - if (params.containsKey("lats")) { - waypoints = readPositions(params); - } - if (params.containsKey("lonlats")) { - waypoints = readLonlats(params, engineMode); - } - - if (waypoints == null) return "no pts "; - - if (params.containsKey("straight")) { - try { - String straight = params.getString("straight"); - String[] sa = straight.split(","); - for (int i = 0; i < sa.length; i++) { - int v = Integer.parseInt(sa[i]); - if (waypoints.size() > v) waypoints.get(v).direct = true; - } - } catch (NumberFormatException e) { - } - } - - String extraParams = null; - if (params.containsKey("extraParams")) { // add user params - extraParams = params.getString("extraParams"); - } - if (extraParams != null && this.profileParams != null) { - // don't overwrite incoming values - extraParams = this.profileParams + "&" + extraParams; - } else if (this.profileParams != null) { - extraParams = this.profileParams; - } - - if (params.containsKey("extraParams")) { // add user params - if (rc.keyValues == null) rc.keyValues = new HashMap<>(); - StringTokenizer tk = new StringTokenizer(extraParams, "?&"); - 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(); - rc.keyValues.put(key, value); - } - } - } - } try { writeTimeoutData(rc); @@ -170,50 +150,46 @@ public class BRouterWorker { return cr.getErrorMessage(); } - String format = params.getString("trackFormat"); int writeFromat = OUTPUT_FORMAT_GPX; - if (format != null) { - if ("kml".equals(format)) writeFromat = OUTPUT_FORMAT_KML; - if ("json".equals(format)) writeFromat = OUTPUT_FORMAT_JSON; + if (rc.outputFormat != null) { + if ("kml".equals(rc.outputFormat)) writeFromat = OUTPUT_FORMAT_KML; + if ("json".equals(rc.outputFormat)) writeFromat = OUTPUT_FORMAT_JSON; } - - OsmTrack track = cr.getFoundTrack(); + OsmTrack track = null; + track = cr.getFoundTrack(); if (track != null) { - if (params.containsKey("exportWaypoints")) { - track.exportWaypoints = (params.getInt("exportWaypoints", 0) == 1); - } + track.exportWaypoints = rc.exportWaypoints; if (pathToFileResult == null) { switch (writeFromat) { - case OUTPUT_FORMAT_GPX: - return track.formatAsGpx(); case OUTPUT_FORMAT_KML: - return track.formatAsKml(); + return new FormatKml(rc).format(track); case OUTPUT_FORMAT_JSON: - return track.formatAsGeoJson(); + return new FormatJson(rc).format(track); + case OUTPUT_FORMAT_GPX: default: - return track.formatAsGpx(); + return new FormatGpx(rc).format(track); } } - try { - switch (writeFromat) { - case OUTPUT_FORMAT_GPX: - track.writeGpx(pathToFileResult); - break; - case OUTPUT_FORMAT_KML: - track.writeKml(pathToFileResult); - break; - case OUTPUT_FORMAT_JSON: - track.writeJson(pathToFileResult); - break; - default: - track.writeGpx(pathToFileResult); - break; - } - } catch (Exception e) { - return "error writing file: " + e; - } + } + try { + switch (writeFromat) { + case OUTPUT_FORMAT_KML: + new FormatKml(rc).write(pathToFileResult, track); + break; + case OUTPUT_FORMAT_JSON: + new FormatJson(rc).write(pathToFileResult, track); + break; + case OUTPUT_FORMAT_GPX: + default: + new FormatGpx(rc).write(pathToFileResult, track); + break; + } + } catch (Exception e) { + return "error writing file: " + e; + } + } else { // get other infos if (cr.getErrorMessage() != null) { return cr.getErrorMessage(); @@ -223,204 +199,6 @@ public class BRouterWorker { return null; } - private List readPositions(Bundle params) { - List wplist = new ArrayList<>(); - - double[] lats = params.getDoubleArray("lats"); - double[] lons = params.getDoubleArray("lons"); - - if (lats == null || lats.length < 2 || lons == null || lons.length < 2) { - throw new IllegalArgumentException("we need two lat/lon points at least!"); - } - - 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 List readLonlats(Bundle params, int mode) { - List wplist = new ArrayList<>(); - - String lonLats = params.getString("lonlats"); - if (lonLats == null) throw new IllegalArgumentException("lonlats parameter not set"); - - String[] coords; - if (mode == 0) { - coords = lonLats.split("\\|"); - if (coords.length < 2) - throw new IllegalArgumentException("we need two lat/lon points at least!"); - } else { - coords = new String[1]; - coords[0] = lonLats; - } - for (int i = 0; i < coords.length; i++) { - String[] lonLat = coords[i].split(","); - if (lonLat.length < 2) - throw new IllegalArgumentException("we need a lat and 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; - } - - private static 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 static 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; - } - - - private void readNogos(Bundle params) { - if (params.containsKey("nogoLats")) { - double[] lats = params.getDoubleArray("nogoLats"); - double[] lons = params.getDoubleArray("nogoLons"); - double[] radi = params.getDoubleArray("nogoRadi"); - - if (lats == null || lons == null || radi == null) return; - - for (int i = 0; i < lats.length && i < lons.length && i < radi.length; i++) { - OsmNodeNamed n = new OsmNodeNamed(); - n.name = "nogo" + (int) radi[i]; - n.ilon = (int) ((lons[i] + 180.) * 1000000. + 0.5); - n.ilat = (int) ((lats[i] + 90.) * 1000000. + 0.5); - n.isNogo = true; - n.nogoWeight = Double.NaN; - AppLogger.log("added interface provided nogo: " + n); - nogoList.add(n); - } - } - if (params.containsKey("nogos")) { - nogoList = readNogoList(params); - } - if (params.containsKey("polylines") || - params.containsKey("polygons")) { - nogoPolygonsList = readNogoPolygons(params); - } - } - - private List readNogoList(Bundle params) { - // lon,lat,radius|... - String nogos = params.getString("nogos"); - 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; - } - - private static 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), Integer.parseInt(radius), weight); - } - - private static 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; - } - - private List readNogoPolygons(Bundle params) { - List result = new ArrayList<>(); - parseNogoPolygons(params.getString("polylines"), result, false); - parseNogoPolygons(params.getString("polygons"), result, true); - return result.size() > 0 ? result : null; - } - - private static 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); - polygon.name = "nogo" + i; - 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); - } - } - } - } - } - - private List readPoisList(Bundle params) { - // lon,lat,name|... - String pois = params.getString("pois"); - 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(","); - - 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; - } private void writeTimeoutData(RoutingContext rc) throws Exception { String timeoutFile = baseDir + "/brouter/modes/timeoutdata.txt"; @@ -431,15 +209,19 @@ public class BRouterWorker { bw.write(rc.rawTrackPath); bw.write("\n"); writeWPList(bw, waypoints); - writeWPList(bw, nogoList); + writeWPList(bw, rc.nogopoints); bw.close(); } private void writeWPList(BufferedWriter bw, List wps) throws Exception { - bw.write(wps.size() + "\n"); - for (OsmNodeNamed wp : wps) { - bw.write(wp.toString()); - bw.write("\n"); + if (wps == null) { + bw.write("0\n"); + } else { + bw.write(wps.size() + "\n"); + for (OsmNodeNamed wp : wps) { + bw.write(wp.toString()); + bw.write("\n"); + } } } } diff --git a/brouter-routing-app/src/main/java/btools/routingapp/DownloadWorker.java b/brouter-routing-app/src/main/java/btools/routingapp/DownloadWorker.java index 233d416..1aa7416 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/DownloadWorker.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/DownloadWorker.java @@ -4,6 +4,7 @@ import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; +import android.content.pm.ServiceInfo; import android.os.Build; import android.util.Log; @@ -72,6 +73,7 @@ public class DownloadWorker extends Worker { int version = -1; int appversion = -1; String errorCode = null; + private boolean bHttpDownloadProblem; public DownloadWorker( @NonNull Context context, @@ -169,7 +171,10 @@ public class DownloadWorker extends Worker { } notificationBuilder.setContentText("Starting Download"); // Mark the Worker as important - setForegroundAsync(new ForegroundInfo(NOTIFICATION_ID, notificationBuilder.build())); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) + setForegroundAsync(new ForegroundInfo(NOTIFICATION_ID, notificationBuilder.build(), ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)); + else + setForegroundAsync(new ForegroundInfo(NOTIFICATION_ID, notificationBuilder.build())); try { if (DEBUG) Log.d(LOG_TAG, "Download lookup & profiles"); if (!downloadLookup()) { @@ -251,6 +256,7 @@ public class DownloadWorker extends Worker { newappversion = meta.minAppVersion; } else { String lookupLocation = mServerConfig.getLookupUrl() + fileName; + if (bHttpDownloadProblem) lookupLocation = lookupLocation.replace("https://", "http://"); URL lookupUrl = new URL(lookupLocation); downloadProgressListener.onDownloadStart(fileName, DownloadType.LOOKUP); changed = downloadFile(lookupUrl, tmplookupFile, size, false, DownloadType.LOOKUP); @@ -301,6 +307,7 @@ public class DownloadWorker extends Worker { //if (profileFile.exists()) { String profileLocation = mServerConfig.getProfilesUrl() + fileName; + if (bHttpDownloadProblem) profileLocation = profileLocation.replace("https://", "http://"); URL profileUrl = new URL(profileLocation); int size = (int) (profileFile.exists() ? profileFile.length() : 0); @@ -322,6 +329,8 @@ public class DownloadWorker extends Worker { private void downloadSegment(String segmentBaseUrl, String segmentName) throws IOException, InterruptedException { File segmentFile = new File(baseDir, SEGMENTS_DIR + segmentName); File segmentFileTemp = new File(segmentFile.getAbsolutePath() + "_tmp"); + if (bHttpDownloadProblem) segmentBaseUrl = segmentBaseUrl.replace("https://", "http://"); + if (DEBUG) Log.d(LOG_TAG, "Download " + segmentName + " " + version + " " + versionChanged); try { if (segmentFile.exists()) { @@ -329,6 +338,7 @@ public class DownloadWorker extends Worker { String md5 = Rd5DiffManager.getMD5(segmentFile); if (DEBUG) Log.d(LOG_TAG, "Calculating local checksum " + md5); String segmentDeltaLocation = segmentBaseUrl + "diff/" + segmentName.replace(SEGMENT_SUFFIX, "/" + md5 + SEGMENT_DIFF_SUFFIX); + if (bHttpDownloadProblem) segmentDeltaLocation = segmentDeltaLocation.replace("https://", "http://"); URL segmentDeltaUrl = new URL(segmentDeltaLocation); if (httpFileExists(segmentDeltaUrl)) { File segmentDeltaFile = new File(segmentFile.getAbsolutePath() + "_diff"); @@ -372,13 +382,28 @@ public class DownloadWorker extends Worker { } private boolean httpFileExists(URL downloadUrl) throws IOException { - HttpURLConnection connection = (HttpURLConnection) downloadUrl.openConnection(); - connection.setConnectTimeout(5000); - connection.setRequestMethod("HEAD"); - connection.setDoInput(false); + HttpURLConnection connection = null; try { + connection = (HttpURLConnection) downloadUrl.openConnection(); + connection.setConnectTimeout(5000); + connection.setRequestMethod("HEAD"); + connection.setDoInput(false); connection.connect(); return connection.getResponseCode() == HttpURLConnection.HTTP_OK; + } catch (javax.net.ssl.SSLHandshakeException e) { + String url = downloadUrl.toString().replace("https://", "http://"); + downloadUrl = new URL(url); + try { + connection = (HttpURLConnection) downloadUrl.openConnection(); + connection.setConnectTimeout(5000); + connection.setRequestMethod("HEAD"); + connection.setDoInput(false); + connection.connect(); + bHttpDownloadProblem = true; + return connection.getResponseCode() == HttpURLConnection.HTTP_OK; + } finally { + connection.disconnect(); + } } finally { connection.disconnect(); } @@ -387,14 +412,24 @@ public class DownloadWorker extends Worker { private boolean downloadFile(URL downloadUrl, File outputFile, int fileSize, boolean limitDownloadSpeed, DownloadType type) throws IOException, InterruptedException { if (DEBUG) Log.d(LOG_TAG, "download " + outputFile.getAbsolutePath()); - HttpURLConnection connection = (HttpURLConnection) downloadUrl.openConnection(); - connection.setConnectTimeout(5000); - connection.setDefaultUseCaches(false); - + HttpURLConnection connection = null; InputStream input = null; OutputStream output = null; try { - connection.connect(); + try { + connection = (HttpURLConnection) downloadUrl.openConnection(); + connection.setConnectTimeout(5000); + connection.setDefaultUseCaches(false); + connection.connect(); + } catch (javax.net.ssl.SSLHandshakeException e) { + String url = downloadUrl.toString().replace("https://", "http://"); + downloadUrl = new URL(url); + connection = (HttpURLConnection) downloadUrl.openConnection(); + connection.setConnectTimeout(5000); + connection.setDefaultUseCaches(false); + connection.connect(); + bHttpDownloadProblem = true; + } if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { throw new IOException("HTTP Request failed: " + downloadUrl + " returned " + connection.getResponseCode()); diff --git a/brouter-routing-app/src/main/java/btools/routingapp/RoutingParameterDialog.java b/brouter-routing-app/src/main/java/btools/routingapp/RoutingParameterDialog.java index 4f892c6..d69a037 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/RoutingParameterDialog.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/RoutingParameterDialog.java @@ -4,11 +4,14 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; +import android.util.Log; import android.window.OnBackInvokedCallback; import android.window.OnBackInvokedDispatcher; +import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; @@ -144,7 +147,7 @@ public class RoutingParameterDialog extends AppCompatActivity { list.add(p); } } catch (Exception e) { - e.printStackTrace(); + Log.e(TAG, Log.getStackTraceString(e)); } } } while (line != null); @@ -167,41 +170,57 @@ public class RoutingParameterDialog extends AppCompatActivity { new OnBackInvokedCallback() { @Override public void onBackInvoked() { - StringBuilder sb = null; - if (sharedValues != null) { - // fill preference with used params - // for direct use in the BRouter interface "extraParams" - sb = new StringBuilder(); - for (Map.Entry entry : sharedValues.getAll().entrySet()) { - if (!entry.getKey().equals("params")) { - sb.append(sb.length() > 0 ? "&" : "") - .append(entry.getKey()) - .append("="); - String s = entry.getValue().toString(); - if (s.equals("true")) s = "1"; - else if (s.equals("false")) s = "0"; - sb.append(s); - } - } - } - // and return the array - // one should be enough - Intent i = new Intent(); - // i.putExtra("PARAMS", listParams); - i.putExtra("PROFILE", profile); - i.putExtra("PROFILE_HASH", profile_hash); - if (sb != null) i.putExtra("PARAMS_VALUES", sb.toString()); - - setResult(Activity.RESULT_OK, i); - finish(); + handleBackPressed(); } } ); - - + } else { + OnBackPressedCallback callback = new OnBackPressedCallback(true) { + @Override + public void handleOnBackPressed() { + handleBackPressed(); + } + }; + getOnBackPressedDispatcher().addCallback(this, callback); } } + private void handleBackPressed() { + StringBuilder sb = null; + if (sharedValues != null) { + // fill preference with used params + // for direct use in the BRouter interface "extraParams" + sb = new StringBuilder(); + for (Map.Entry entry : sharedValues.getAll().entrySet()) { + if (!entry.getKey().equals("params")) { + sb.append(sb.length() > 0 ? "&" : "") + .append(entry.getKey()) + .append("="); + String s = entry.getValue().toString(); + if (s.equals("true")) s = "1"; + else if (s.equals("false")) s = "0"; + sb.append(s); + } + } + } + // and return the array + // one should be enough + Intent i = new Intent(); + // i.putExtra("PARAMS", listParams); + i.putExtra("PROFILE", profile); + i.putExtra("PROFILE_HASH", profile_hash); + if (sb != null) i.putExtra("PARAMS_VALUES", sb.toString()); + + setResult(Activity.RESULT_OK, i); + finish(); + + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + } + public static class MyPreferenceFragment extends PreferenceFragmentCompat { @@ -222,6 +241,7 @@ public class RoutingParameterDialog extends AppCompatActivity { } @Override + @SuppressWarnings("deprecation") public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -244,10 +264,10 @@ public class RoutingParameterDialog extends AppCompatActivity { if (i.hasExtra("PARAMS")) { List result; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - result = (List) i.getExtras().getSerializable("PARAMS", ArrayList.class); - } else { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { result = (List) i.getExtras().getSerializable("PARAMS"); + } else { + result = (List) i.getExtras().getSerializable("PARAMS", ArrayList.class); } if (result instanceof ArrayList) { for (Object o : result) { @@ -259,7 +279,7 @@ public class RoutingParameterDialog extends AppCompatActivity { sparams = i.getExtras().getString("PARAMS_VALUES", ""); } } catch (Exception e) { - e.printStackTrace(); + Log.e(TAG, Log.getStackTraceString(e)); } getPreferenceManager().setSharedPreferencesName("prefs_profile_" + profile_hash); diff --git a/brouter-routing-app/src/main/java/btools/routingapp/ServerConfig.java b/brouter-routing-app/src/main/java/btools/routingapp/ServerConfig.java index 0d83d49..a059a19 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/ServerConfig.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/ServerConfig.java @@ -2,6 +2,7 @@ package btools.routingapp; import android.content.Context; import android.content.res.AssetManager; +import android.util.Log; import java.io.BufferedReader; import java.io.File; @@ -13,6 +14,9 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; public class ServerConfig { + + private static final String TAG = "ServerConfig"; + private static String mServerConfigName = "serverconfig.txt"; private String mSegmentUrl = "https://brouter.de/brouter/segments4/"; @@ -52,7 +56,7 @@ public class ServerConfig { } } } catch (IOException e) { - e.printStackTrace(); + Log.e(TAG, Log.getStackTraceString(e)); } finally { try { if (br != null) br.close(); diff --git a/brouter-routing-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/brouter-routing-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index bbd3e02..93542a7 100644 --- a/brouter-routing-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/brouter-routing-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -2,4 +2,5 @@ - \ No newline at end of file + + diff --git a/brouter-routing-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/brouter-routing-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index bbd3e02..93542a7 100644 --- a/brouter-routing-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/brouter-routing-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -2,4 +2,5 @@ - \ No newline at end of file + + diff --git a/brouter-routing-app/src/main/res/values-ar/strings.xml b/brouter-routing-app/src/main/res/values-ar/strings.xml new file mode 100644 index 0000000..8759f4a --- /dev/null +++ b/brouter-routing-app/src/main/res/values-ar/strings.xml @@ -0,0 +1,94 @@ + + + + %d قطعة + %d قطع + + إلغاء التنزيل + استيراد ملف تعريف + تنزيل %s + حذف %s + تحديث %s + حدد القطع + إيقاف التنزيل + الحجم=%1$s\nالحجم المتوفر=%2$s + تحميل القطع + + اختر مكان قاعدة بيانات brouter: + اختر ملف تعريف التوجيه + حدد الإجراء الرئيسي + مدير التنزيلات + تطبيق BRouter + + إلغاء + أعرف ذلك + إغلاق + خروج + موافق + تم بنجاح + حدث خطأ ما + + مدير تنزيلات BRouter + *** تحذير:*** + \n\nيتم استخدام مدير التنزيل لتنزيل ملفات بيانات التوجيه عبر الطرق + والتي يمكن أن يصل حجمها إلى 170 ميجابايت، لذلك لا تقم بتشغيل + "مدير التنزيلات" حينما تكون متصلاً عبر البيانات الخلوية ومحدوداً بحجم التنزيلات! + سرعة التنزيل محدودة على 16 ميجابايت/بالثانية. + أنجزت عملية الحساب بنجاح وبدون تأخير + لقد نجحت في تكرار عملية حسابية كانت قد انتهت في السابق + عند بدايتها من خلال أداة الخريطة، إذا قمت بتكرار نفس الطلب من + أداة الخريطة الخاصة بك ومع نفس الوجهة بالضبط وبنقطة بداية + قريبة، فسيضمن لك التطبيق عدم انتهاء مهلة هذا الطلب. + أدخل مسار الذاكرة الخارجية: + اختر الإجراء + + التحقق من النقاط الوسيطة: + التحقق من النقاط المسبتعدة: + وضع-الخادم + معلومات + حساب الطريق + إعدادات ملف التعريف + مشاركة ملف GPX + اختر من + اختر إلى/عبر + + لا توجد بيانات ملف التعريف + ، لا يوجد ملف تعريف مستخدم + هناك الكثير من البيانات للتنزيل، من فضلك قم بتقليلها. + تمت جدولة التنزيل، وتحقق من اتصالك بالإنترنت إذا لم يبدأ التنزيل. + نقاط الطريق الحالية المحددة:\n + نقاط الطريق المتوقعة المحددة\n + البديل + الإصدار = BRouter-%1$s \n + الذاكرة = %2$s \n + المسافة = %3$s كم\n + التصاعد المرشح = %4$s م\n + التصاعد العادي = %5$s م\n + الوقت المقدر = %6$s + خطأ في قراءة نقاط الطريق + لا يحتوي مصدر الإحداثيات على أي نقاط الطريق! + مصدر الإحداثيات يحتوي على عدد كبير جدًا من نقاط الطريق: %1$d (يرجى استخدام أسماء النقاط من/إلى/عبر from/to/via) + + لا + نعم + + تأكيد الحذف + هل ترغب بالحذف؟ + مشكلة في الإصدار + لقد تغير الإصدار الأساسي للصور المتجانبة، ما الإجراء المناسب لفعله؟ + متابعة التنزيل الحالي، وحذف البيانات الأخرى القديمة + تحديد الكل للتنزيل والبدء + قم بالإلغاء في الوقت الحالي، واستكمله لاحقاً + اختلافات الإصدارات + الإصدار الأساسي لبعض الصور المتجانبة مختلف. ما الإجراء المناسب لفعله؟ + تحميل المختلف من الصور المتجانبة + حذف كل الصور المتجانبة المختلفة عن الأخرى + قم بالإلغاء في الوقت الحالي، واستكمله لاحقاً + يحتاج إصدار البيانات الجديد إلى وجود التطبيق الجديد، لذا نرجوا منك تحديث BRouter أولاً + + فشل التنزيل + تم إلغاء التنزيل + تم التنزيل بنجاح + جار التنزيل… + + diff --git a/brouter-routing-app/src/main/res/values-ca/strings.xml b/brouter-routing-app/src/main/res/values-ca/strings.xml new file mode 100644 index 0000000..9df7107 --- /dev/null +++ b/brouter-routing-app/src/main/res/values-ca/strings.xml @@ -0,0 +1,94 @@ + + + + %d segment + %d segments + + Cancel·lar descàrrega + Importar perfil + Descàrrega %s + Eliminar %s + Actualitzar %s + Seleccionar segments + Aturar Descàrrega + Espai=%1$s\nLliure=%2$s + Descarregar segments + + Escollir directori base brouter: + Seleccionar un perfil d\'enrutament + Seleccionar Acció Principal + Gestor de Baixades + App BRouter + + Cancel·lar + Ho sé + Tancar + Sortir + D\'acord + Èxit + Ha aparegut un error + + Gestor de baixades BRouter + *** Atenció:*** + \n\nEl Gestor de Baixades es fa servir per descarregar fitxers de dades + d\'enrutament, que poden arribar als 170 Mb cadascun. No inicies el Gestor + des d\'un mòbil sense un pla de dades! + La velocitat de baixada està limitada a 16 Mbit/s. + S\'ha preparat correctament un càlcul sense límit de temps + S\'ha repetit amb èxit un càlcul a través + de l\'aplicació de cartografia que prèviament havia superat el temps d\'espera. + Si repeteixes la mateixa petició amb exactament el mateix destí i un punt de partida proper, + es garanteix que la petició no esgotarà el límit de temps. + Introdueix el directori base de la tarja SD: + Seleccionar Acció + + Comprovar Selecció VIA: + Comprovar Selecció NoGo: + Mode-Servidor + Info + Calc Ruta + Ajustaments del perfil + Compartir GPX + Seleccionar des de + Seleccionar a/via + + no hi ha dades al perfil + , cap perfil utilitzat + Massa dades per baixar. Si us plau, redueix-les. + Baixada programada. Comprovar la connexió a internet si no comença. + selecció actual de fita:\n + Esperant selecció de fita\n + Alternativa + versió = BRouter-%1$s \n + memòria = %2$s \n + distància = %3$s km\n + ascensió filtrada = %4$s m\n + ascens en pla = %5$s m\n + temps estimat = %6$s + Error llegint fites + la font de coordenades no conté cap fita! + la font de coordenades conté massa fites: %1$d (si us plau, fes servir noms des de/a/via) + + No + Si + + Confirmar Eliminar + Voleu realment eliminar? + Problema de versió + La versió base per les tesel·les ha canviat. Què vols fer? + Continuar amb la baixada, eliminar altres dades antigues + Seleccionar-ho tot per baixar i iniciar + Cancel·lar ara, completar un altre dia + Diferències de Versió + La versió base de diverses tesel·les és diferent. Què vols fer? + Baixar totes les tesel·les diferents + Descartar les tesel·les diferents + Cancel·lar ara, completar un altre dia + La nova versió de dades necessita una aplicació nova. Sisplau, actualitza BRouter primer + + Baixada fallida + Baixada cancel·lada + Baixada exitosa + Baixant… + + diff --git a/brouter-routing-app/src/main/res/values-de/strings.xml b/brouter-routing-app/src/main/res/values-de/strings.xml new file mode 100644 index 0000000..902aa4d --- /dev/null +++ b/brouter-routing-app/src/main/res/values-de/strings.xml @@ -0,0 +1,94 @@ + + + + %d Segment + %d Segmente + + Download abbrechen + Profil importieren + %s downloaden + %s löschen + %s aktualisieren + Segmente auswählen + Download stoppen + Größe=%1$s\nFrei=%2$s + Segmente herunterladen + + BRouter-Datenbankverzeichnis wählen: + Routenprofil wählen + Standardaktion wählen + Downloadmanager + BRouter-App + + Abbrechen + Ich weiß + Schließen + Verlassen + OK + Erfolg + Ein Fehler ist aufgetreten + + BRouter Downloadmanager + *** Achtung:*** + \n\nDer Downloadmanager wird zum Herunterladen von Routingdaten verwendet + die jeweils bis zu 170MB groß sein können. Starten Sie den Downloadmanager + nicht auf Mobiltelefonen ohne Mobilfunktdatentarif! + Die Downloadgeschwindigkeit ist auf 16 MBit/s begrenzt. + Berechnung ohne Zeitüberschreitung erfolgreich + Sie haben eine Berechnung erfolgreich wiederholt, bei + der zuvor beim Start von Ihrem Karten-Tool aus eine Zeitüberschreitung auftrat. + Wenn Sie dieselbe Anfrage von Ihrem Karten-Tool aus wiederholen, mit genau demselben + Zielpunkt und einem nahe gelegenen Startpunkt, wird diese Anfrage garantiert nicht abbrechen. + SD-Karten Verzeichnis wählen: + Aktion wählen + + Via-Auswahl prüfen: + NoGo-Auswahl prüfen: + Server-Modus + Info + Route berechnen + Profileinstellungen + GPX teilen + "Von" wählen + "Nach"/Via wählen + + Keine Profildaten + , kein verwendetes Profil + Zu viele Daten für den Download. Bitte reduzieren. + Download geplant. Überprüfen Sie die Internetverbindung, wenn der Download nicht startet. + aktuelle Wegpunktauswahl:\n + Erwarte Wegpunktauswahl\n + Alternative + Version = BRouter-%1$s\n + Speicher = %2$s \n + Abstand = %3$s km\n + gefilterter Aufstieg = %4$s m\n + flacher Aufstieg = %5$s m\n + geschätzte Zeit = %6$s + Fehler beim Lesen von Wegpunkten + Die Koordinatenquelle enthält keine Wegpunkte! + Die Koordinatenquelle enthält zu viele Wegpunkte: %1$d (bitte von/nach/via-Namen verwenden) + + Nein + Ja + + Löschen bestätigen + Wirklich löschen? + Versionsproblem + Die Datenversion der Kacheln hat sich geändert. Bitte auswählen: + Download fortsetzen und alte Daten löschen? + Alle auswählen und Download starten + Abbrechen und später fortsetzen + Versionskonflikt + Die Datenversion für einige Kacheln ist unterschiedlich. Bitte auswählen: + Download alle unterschiedlichen Kacheln + Überspringe alle unterschiedlichen Kacheln + Abbrechen und später fortsetzen + Die neue Datenversion verlangt ein BRouter-Update. Bitte aktualisieren + + Download fehlgeschlagen + Download abgebrochen + Download erfolgreich + Download läuft… + + diff --git a/brouter-routing-app/src/main/res/values-el/strings.xml b/brouter-routing-app/src/main/res/values-el/strings.xml new file mode 100644 index 0000000..f5d098b --- /dev/null +++ b/brouter-routing-app/src/main/res/values-el/strings.xml @@ -0,0 +1,94 @@ + + + + %d τμήμα + %d τμήματα + + Ακύρωση λήψης + Εισαγωγή προφίλ + Λήψη %s + Διαγραφή %s + Ενημέρωση %s + Επιλογή τμημάτων + Διακοπή λήψης + Μέγεθος=%1$s\nΕλεύθερο=%2$s + Λήψη τμημάτων + + Επιλέξτε φάκελο δεδομένων brouter: + Επιλέξτε ένα προφίλ δρομολόγησης + Επιλέξτε ενέργεια + Διαχειριστής λήψεων + Εφαρμογή BRouter + + Ακύρωση + Γνωρίζω + Κλείσιμο + Έξοδος + OK + Επιτυχία + Παρουσιάστηκε σφάλμα + + BRouter διαχειριστής λήψεων + *** Προσοχή:*** + \n\nΟ διαχειριστής λήψεων χρησιμοποιείται για τη λήψη αρχείων δεδομένων δρομολόγησης + που μπορεί να είναι έως 170MB το καθένα. Μην ξεκινήσετε το διαχειριστή λήψεων + σε σύνδεση δεδομένων κινητής τηλεφωνίας χωρίς πρόγραμμα δεδομένων! + Η ταχύτητα λήψης περιορίζεται στα 16 MBit/s. + Ετοιμάστηκε επιτυχώς ένας υπολογισμός χωρίς χρονικό όριο + Επαναλάβατε με επιτυχία έναν υπολογισμό που πριν είχε χρονικό όριο + όταν ξεκίνησε από το εργαλείο χαρτών σας. Εάν επαναλάβετε το ίδιο αίτημα + από το εργαλείο χαρτών σας, με τον ίδιο ακριβώς προορισμό και μια κοντινή αφετηρία, + αυτό το αίτημα είναι εγγυημένο ότι δεν θα λήξει. + Εισάγετε φάκελο SDCARD: + Επιλέξτε ενέργεια + + Ελέγξτε την επιλογή VIA: + Ελέγξτε την επιλογή NoGo: + Λειτουργία διακομιστή + Πληροφορίες + Υπολογισμός διαδρομής + Ρυθμίσεις προφίλ + Κοινή χρήση GPX + Επιλέξτε από + Επιλέξτε προς/μέσω + + μη δεδομένα προφίλ + , μη χρησιμοποιημένο προφίλ + Πάρα πολλά δεδομένα για λήψη. Παρακαλώ μειώστε. + Προγραμματισμένη λήψη. Ελέγξτε τη σύνδεση στο διαδίκτυο εάν δεν ξεκινά. + τρέχουσα επιλογή σημείου:\n + Αναμένεται επιλογή σημείου\n + Εναλλακτική + έκδοση = BRouter-%1$s \n + μνήμη = %2$s \n + απόσταση = %3$s km\n + φιλτραρισμένη άνοδος = %4$s m\n + απλή άνοδος = %5$s m\n + εκτιμώμενος χρόνος = %6$s + Σφάλμα ανάγνωσης σημείων + η πηγή συντεταγμένων δεν περιέχει σημεία! + η πηγή συντεταγμένων περιέχει πάρα πολλά σημεία: %1$d (παρακαλώ χρησιμοποιήστε από/προς/μέσω ονόματα) + + Όχι + Ναι + + Επιβεβαίωση διαγραφής + Να γίνει διαγραφή; + Πρόβλημα έκδοσης + Η βασική έκδοση για τμήματα άλλαξε. Τι να κάνετε; + Συνέχεια με την τρέχουσα λήψη, διαγραφή άλλων παλαιών δεδομένων + Επιλέξτε όλα για λήψη και έναρξη + Ακύρωση τώρα, ολοκλήρωση άλλη μέρα + Διαφορές έκδοσης + Η βασική έκδοση για ορισμένα τμήματα είναι διαφορετική. Τι να κάνετε; + Λήψη όλων των διαφορετικών τμημάτων + Απόρριψη όλων των διαφορετικών τμημάτων + Ακύρωση τώρα, ολοκλήρωση άλλη μέρα + Η νέα έκδοση δεδομένων χρειάζεται νέα εφαρμογή. Ενημερώστε πρώτα το BRouter + + Η λήψη απέτυχε + Η λήψη ακυρώθηκε + Η λήψη ολοκληρώθηκε + Λήψη… + + diff --git a/brouter-routing-app/src/main/res/values-es/strings.xml b/brouter-routing-app/src/main/res/values-es/strings.xml new file mode 100644 index 0000000..37094da --- /dev/null +++ b/brouter-routing-app/src/main/res/values-es/strings.xml @@ -0,0 +1,94 @@ + + + + %d segmento + %d segmentos + + Cancelar descarga + Importar perfil + Descargar %s + Eliminar %s + Actualizar %s + Seleccionar segmentos + Detener descarga + Tamaño=%1$s\nGratis=%2$s + Descargar segmentos + + Seleccionar directorio base brouter: + Seleccionar un perfil de enrutamiento + Seleccionar Acción Principal + Gestor de Descargas + App BRouter + + Cancelar + Lo se + Cerrar + Salir + OK + Salir + Ha aparecido un error + + Gestor de descargas BRouter + *** Atención:*** + \n\nEl Gestor de Descargas se usa para descargar ficheros de datos + de ruta, que pueden llegar a los 170 Mb cada uno. No inicies el Gestor + desde un terminal sin plan de datos! + La velocidad de descarga está limitada a 16 Mbit/s. + Se ha preparado correctamente un cálculo sin límite de tiempo + Se ha repetido un cálculo a través + de la aplicación de mapas que previamente había superado el tiempo de espera. + Si repites la misma petición con exactamente el mismo destino y un punto de partida cercano, + se garantiza que la petición no agotará el límite de tiempo. + Introduce el directorio base de la tarjeta SD: + Seleccionar Acción + + Comprobar Selección VIA: + Comprobar Selección NoGo: + Modo-Servidor + Info + Calc Ruta + Ajustes del perfil + Compartir GPX + Seleccionar desde + Seleccionar a/vía + + no hay datos en el perfil + , ningún perfil utilizado + Demasiados datos a descargar. Por favor, reducidlos. + Descarga programada. Comprueba la conexión a internet si no se inicia. + selección actual de waypoint:\n + Esperando selección de waypoint\n + Alternativa + versión = BRouter-%1$s \n + memoria = %2$s \n + distancia = %3$s km\n + ascensión filtrada = %4$s m\n + ascensión en llano = %5$s m\n + tiempo estimado = %6$s + Error leyendo waypoints + la fuente de coordenadas no contiene ningún waypoint! + la fuente de coordenadas contiene demasiados waypoints: %1$d (por favor, usa nombres desde/a/vía) + + No + Si + + Confirmar Eliminar + ¿Quieres realmente eliminar? + Problema de versión + La versión base para as teselas ha cambiado. ¿Qué quieres hacer? + Continuar con la descarga, eliminar otros datos antiguos + Seleccionar todo para descargar e iniciar + Cancelar ahora, completar otro día + Diferencias de versión + La versión base de varias teselas es distinta. ¿Qué quieres hacer? + Descargar todas las teselas distintas + Descartar las teselas distintas + Cancelar ahora, completar otro día + La nueva versión de datos necesita un aplicación nueva. Por favor, actualiza BRouter primero + + Descarga fallida + Descarga cancelada + Descarga exitosa + Descargando… + + diff --git a/brouter-routing-app/src/main/res/values-fr/strings.xml b/brouter-routing-app/src/main/res/values-fr/strings.xml new file mode 100644 index 0000000..0b028f7 --- /dev/null +++ b/brouter-routing-app/src/main/res/values-fr/strings.xml @@ -0,0 +1,94 @@ + + + + %d segment + %d segments + + Annuler le téléchargement + Importer le profil + Télécharger %s + Supprimer %s + Mettre à jour %s + Sélectionner les segments + Arrêter le téléchargement + Taille=%1$s\nGratuit=%2$s + Télécharger les segments + + Choisissez le répertoire de la base de données brouter: + Sélectionnez un profil de routage + Sélectionner l\'action principale + Gestionnaire de téléchargement + Application BRouter + + Annuler + Je sais + Fermer + Quitter + OK + Succès + Une erreur s\'est produite + + Gestionnaire de téléchargement BRouter + *** Attention :*** + \n\nLe gestionnaire de téléchargement est utilisé pour télécharger les données de routage + fichiers pouvant atteindre 170MB chacun. Ne démarrez pas le gestionnaire de téléchargement + sur une connexion de données cellulaires sans forfait de données ! + La vitesse de téléchargement est limitée à 16 MBit/s. + Préparation réussie d\'un calcul sans délai d\'attente + Vous avez répété avec succès un calcul qui avait précédemment expiré + lorsqu\'il est démarré à partir de votre outil cartographique. Si vous répétez la même demande de votre + maptool, avec exactement le même point de destination et un point de départ à proximité, + il est garanti que cette requête n\'expirera pas. + Entrez le répertoire de base de la SDCARD: + Sélectionner une action + + Vérifier la sélection VIA: + Vérifier la sélection NoGo: + Mode serveur + Informations + Calculer l\'itinéraire + Paramètres du profil + Partager GPX + Sélectionner de + Sélectionner vers/via + + aucune donnée de profil + , aucun profil utilisé + Trop de données à télécharger. Veuillez réduire. + Téléchargement planifié. Vérifiez la connexion Internet si elle ne démarre pas. + sélection actuelle du waypoint:\n + Sélection de waypoint en attente\n + Alternative + version = BRouter-%1$s \n + mémoire = %2$s \n + distance = %3$s km\n + montée filtrée = %4$s m\n + montée simple = %5$s m\n + temps estimé = %6$s + Erreur lors de la lecture des waypoints + la source de coordonnées ne contient aucun waypoint! + la source de coordonnées contient trop de waypoints: %1$d (veuillez utiliser les noms de/vers/via) + + Non + Oui + + Confirmer la suppression + Vraiment supprimer? + Problème de version + La version de base des vignettes a changé. Que faire? + Continuer le téléchargement en cours, supprimer les autres anciennes données + Sélectionnez tout pour télécharger et démarrer + Annuler maintenant, terminer un autre jour + Différences de version + La version de base de certaines vignettes est différente. Que faire? + Télécharger toutes les différentes vignettes + Supprimez toutes les différentes vignettes + Annuler maintenant, terminer un autre jour + La nouvelle version des données nécessite une nouvelle application. Veuillez d\'abord mettre à jour BRouter + + Téléchargement échoué + Téléchargement annulé + Téléchargement réussi + Téléchargement… + + diff --git a/brouter-routing-app/src/main/res/values-it/strings.xml b/brouter-routing-app/src/main/res/values-it/strings.xml new file mode 100644 index 0000000..c82a6e2 --- /dev/null +++ b/brouter-routing-app/src/main/res/values-it/strings.xml @@ -0,0 +1,94 @@ + + + + %d segmento + %d segmenti + + Annulla download + Importa profilo + Scarica %s + Elimina %s + Aggiorna %s + Seleziona segmenti + Interrompi download + Taglia=%1$s\nGratis=%2$s + Scarica segmenti + + Seleziona la directory del database brouter: + Seleziona un profilo di instradamento + Seleziona azione principale + Gestore dei download + Applicazione BRouter + + Annulla + Lo so + Chiudi + Esci + OK + Successo + Si è verificato un errore + + Gestore download BRouter + *** Attenzione:*** + \n\nIl Download Manager viene utilizzato per scaricare i dati di routing + file che possono pesare fino a 170 MB ciascuno. Non avviare il Download Manager + su una connessione dati cellulare senza un piano dati! + La velocità di download è limitata a 16 MBit/s. + Preparato con successo un calcolo senza timeout + Hai ripetuto con successo un calcolo che in precedenza era terminato con un timeout + quando avviato dal tuo strumento mappa. Se ripeti la stessa richiesta dal tuo + maptool, con lo stesso identico punto di destinazione e un punto di partenza vicino, + è garantito che questa richiesta non vada in timeout. + Inserisci la directory base della SDCARD: + Seleziona azione + + Controlla la selezione VIA: + Controlla la selezione NoGo: + Modalità server + Informazioni + Calcola percorso + Impostazioni profilo + Condividi GPX + Seleziona da + Seleziona a/via + + nessun dato del profilo + , nessun profilo utilizzato + Troppi dati per il download. Per favore riduci. + Download programmato. Controlla la connessione Internet se non si avvia. + selezione del waypoint corrente:\n + Attesa selezione waypoint\n + Alternativa + versione = BRouter-%1$s \n + memoria = %2$s \n + distanza = %3$s km\n + salita filtrata = %4$s m\n + salita semplice = %5$s m\n + tempo stimato = %6$s + Errore durante la lettura dei waypoint + la fonte delle coordinate non contiene alcun waypoint! + la fonte delle coordinate contiene troppi waypoint: %1$d (usa i nomi da/a/via) + + No + + + Conferma eliminazione + Eliminare davvero? + Problema di versione + La versione base per i riquadri è cambiata. Cosa fare? + Continua con il download corrente, elimina altri vecchi dati + Seleziona tutto per il download e avvia + Annulla ora, completa un altro giorno + Differenze di versione + La versione base di alcuni riquadri è diversa. Cosa fare? + Scarica tutti i diversi riquadri + Rilascia tutti i diversi riquadri + Annulla ora, completa un altro giorno + La nuova versione dei dati necessita di una nuova app. Per favore aggiorna prima BRouter + + Download fallito + Download annullato + Download riuscito + Download in corso… + + diff --git a/brouter-routing-app/src/main/res/values-ko/strings.xml b/brouter-routing-app/src/main/res/values-ko/strings.xml new file mode 100644 index 0000000..1c316ed --- /dev/null +++ b/brouter-routing-app/src/main/res/values-ko/strings.xml @@ -0,0 +1,94 @@ + + + + %d 구간 + %d 구간 + + 다운로드 취소 + 프로필 가져오기 + 다운로드 %s + 삭제 %s + 업데이트 %s + 구간 선택 + 다운로드 중지 + 크기=%1$s\n여유=%2$s + 구간 다운로드 + + brouter 데이터 베이스 선택 dir: + 루트 프로필 선택 + 주 활동 선택 + 다운로드 관리자 + BRouter 앱 + + 취소 + 알고 있습니다 + 가까운 + 나가기 + OK + 성공 + 에러가 발생됐습니다 + + BRouter 다운로드 관리자 + *** 주의:*** + \n\n다운로드 관리자는 라우팅 데이터를 다운로드하는 데 사용됩니다. + 파일당 최대 170MB까지 가능합니다. 다운로드 관리자를 시작하지 마십시오 + 데이터 요금제 없이 셀룰러 데이터 연결로! + 다운로드 속도는 16 MBit/s로 제한됩니다. + 시간 초과 없는 계산을 성공적으로 준비했습니다 + 이전에 시간 초과가 발생한 계산을 성공적으로 반복하셨습니다. + 지도 도구에서 시작할 때. 귀하가 귀하로부터 동일한 요청을 반복하는 경우 + 정확히 동일한 목적지와 가까운 시작점이 있는 지도 도구, + 이 요청은 시간 초과되지 않음이 보장됩니다. + SDCARD 기본 디렉토리 입력: + 작업 선택 + + 경유 선택 체크: + NoGo 선택 체크: + 서버-모드 + 정보 + 루트 계산 + 프로필 설정 + GPX 공유 + ~로부터 선택 + 선택 ~로/경유하여 + + 프로필 데이터 없음 + , 사용된 프로필 없음 + 다운로드 하기에 너무 많은 데이터. 줄이도록 하세요. + 다운로드 계획되어 있음. 인터넷 연결 체크 그렇지 않다면 시작. + 현재 장소 선택:\n + 장소 선택 기대\n + 대체 가능한 + 버전 = BRouter-%1$s \n + 메모리 = %2$s \n + 거리 = %3$s km\n + 필터링된 오름차순 = %4$s m\n + 일반 오름차순 = %5$s m\n + 예상 시간 = %6$s + 장소 읽는데 에러 + 좌표정보는 어떤 장소도 포함하고 있지 않습니다! + 좌표정보는 너무 많은 장소를 포함하고 있습니다: %1$d (~로 부터/~로/경유 이름 사용하세요) + + 아니요 + + + 삭제 확인 + 정말 삭제하시겠습니까? + 버전 문제 + 타일을 위한 기본 버전이 바뀌었습니다. 무엇을 해야죠? + 현재 다운로드 계속, 다른 오래된 데이터 삭제 + 다운로드 위해 모두 선택하고 시작 + 지금 취소, 다른 날 완료 + 버전 차이 + 어떤 타일을 위한 기본 버전은 다릅니다, 무엇을 해야죠? + 모든 다른 타일 다운로드 + 모든 다른 타일 백지화 + 지금 취소, 다른 날 완료 + 새로운 데이터 버전은 새로운 앱을 필요로 합니다. 우선 BRouter를 업데이트 하세요 + + 다운로드 실패 + 다운로드 취소됨 + 다운로드 성공 + 다운로딩… + + diff --git a/brouter-routing-app/src/main/res/values-nl/strings.xml b/brouter-routing-app/src/main/res/values-nl/strings.xml new file mode 100644 index 0000000..fea694a --- /dev/null +++ b/brouter-routing-app/src/main/res/values-nl/strings.xml @@ -0,0 +1,94 @@ + + + + %d segment + %d segmenten + + Download annuleren + Profiel importeren + Downloaden %s + Verwijderen %s + Bijwerken %s + Segmenten selecteren + Download stoppen + Grootte=%1$s\nGratis=%2$s + Segmenten downloaden + + Kies de brouter database map: + Selecteer een routingprofiel + Selecteer de hoofdactie + Download beheerder + BRouter App + + Annuleer + Ik weet het + Sluiten + Verlaat + OK + Succes + Er is een fout opgetreden + + BRouter Download Manager + *** OPgelet:*** + \n\nDe Download Manager wordt gebruikt voor het downloaden van routing-gegevens + bestanden die elk maximaal 170 MB groot kunnen zijn. Start de Download Manager + niet op een mobiele dataverbinding zonder data-abonnement! + De downloadsnelheid is beperkt tot 16 MBit/s. + Een time-outvrije berekening is succesvol voorbereid + Je hebt met succes een berekening herhaald die eerder op een time-out + stuitte toen deze vanuit je map tool werd gestart. Als u hetzelfde verzoek herhaalt + vanuit uw map tool, met exact hetzelfde bestemmingspunt en een dichtbijgelegen beginpunt, + zal deze aanvraag gegarandeerd geen time-out krijgen. + De SDCARD basis directory invoeren: + Actie selecteren + + Controleer de VIA selectie: + Controleer de NoGo selectie: + Server-mode + Info + Bereken route + Profiel instellingen + GPX delen + Selecteer van + Selecteer naar/via + + geen profiel data + , geen gebruikt profiel + Te veel gegevens om te downloaden. Verminder a.u.b. + Download is gepland. Controleer de internetverbinding als deze niet start. + huidige waypointselectie:\n + Verwacht waypointselectie\n + Alternatief + versie = BRouter-%1$s \n + geheugen = %2$s \n + afstand = %3$s km\n + gefilterde opstijging = %4$s m\n + simpel opstijging = %5$s m\n + geschatte tijd = %6$s + Fout bij het lezen van waypoints + coördinaten bron bevat geen waypoints! + De coördinatenbron bevat te veel waypoints: %1$d (gebruik van/naar/via namen) + + Nee + Ja + + Verwijderen bevestigen + Echt verwijderen? + Versie probleem + De basisversie voor de tegels is gewijzigd. Wat moet er gebeuren? + Doorgaan met de huidige download, verwijder de oude gegevens. + Selecteer alles om te downloaden en start + Annuleer nu, voltooi op een andere dag + Versieverschillen + De basisversie voor sommige tiles is anders. Wat moet er gebeuren? + Download alle verschillende tegels + Laat alle verschillende tegels vallen + Annuleer nu, en voltooi op een andere dag + Voor de nieuwe gegevensversie is een nieuwe app nodig. Update eerst de BRouter + + Download is mislukt + Download is geannuleerd + Download is geslaagd + Downloaden… + + diff --git a/brouter-routing-app/src/main/res/values-pl/strings.xml b/brouter-routing-app/src/main/res/values-pl/strings.xml new file mode 100644 index 0000000..6901240 --- /dev/null +++ b/brouter-routing-app/src/main/res/values-pl/strings.xml @@ -0,0 +1,94 @@ + + + + %d segment + %d segmenty/ów + + Anuluj pobieranie + Importuj profil + Pobierz %s + Usuń %s + Zaktualizuj %s + Wybierz segmenty + Zatrzymaj pobieranie + Rozmiar=%1$s\nDostępne=%2$s + Pobieranie segmentów + + Wybierz katalog bazy danych BRoutera: + Wybierz profil routingu + Wybierz akcję główną + Menedżer pobierania + Aplikacja BRouter + + Anuluj + Rozumiem + Zamknij + Wyjdź + OK + Sukces + Wystąpił błąd + + Menedżer pobierania BRoutera + *** Uwaga: *** + \n\nMenedżer pobierania służy do pobierania plików danych routingu, + które mogą mieć do 170 MB każdy. Nie uruchamiaj Menedżera pobierania, + jeżeli korzystasz z komórkowej transmisji danych bez planu taryfowego! + Prędkość pobierania jest ograniczona do 16 MBit/s. + Pomyślnie przygotowano obliczenia nie przekraczając limitu czasu + Pomyślnie powtórzono obliczenia, które wcześniej przekroczyły limit czasu + po uruchomieniu z narzędzia mapowego. Jeśli powtórzysz to samo żądanie ze swojego + narzędzia mapowego, z dokładnie tym samym punktem docelowym i pobliskim punktem początkowym, + gwarantuję, że to żądanie nie przekroczy limitu czasu. + Wybierz katalog SDCARD dla bazy: + Wybierz akcję + + Sprawdź wybór VIA: + Sprawdź wybór NoGo: + Tryb serwera + Informacje + Oblicz trasę + Ustawienia profilu + Udostępnij GPX + Wybierz z + Wybierz do/przez + + brak danych profilu + , brak używanego profilu + Za dużo danych do pobrania. Proszę ogranicz. + Pobieranie zaplanowane. Jeśli się nie rozpocznie, sprawdź połączenie internetowe. + bieżący wybór punktu trasy:\n + Oczekuję na wybór punktu trasy\n + Alternatywa + wersja = BRouter-%1$s \n + pamięć = %2$s \n + odległość = %3$s km\n + filtrowane wznoszenie = %4$sm\n + zwykłe wznoszenie = %5$sm\n + szacowany czas = %6$s + Błąd podczas odczytu punktów trasy + Źródło współrzędnych nie zawiera żadnych punktów trasy! + Źródło współrzędnych zawiera zbyt dużo punktów trasy: %1$d (proszę używać nazw z/do/przez) + + Nie + Tak + + Potwierdź usunięcie + Naprawdę usunąć? + Problem z wersją + Zmieniła się podstawowa wersja kafelków. Co robić? + Kontynuuj bieżące pobieranie, usuń inne stare dane + Wybierz wszystko i rozpocznij pobieranie + Anuluj teraz, dokończ innego dnia + Różnice wersji + Wersja podstawowa dla niektórych kafelków jest inna. Co robić? + Pobierz różne kafelki + Opuść wszystkie różne kafelki + Anuluj teraz, dokończ innego dnia + Nowa wersja danych wymaga nowej aplikacji. Najpierw zaktualizuj BRoutera + + Pobieranie nie powiodło się + Pobieranie anulowane + Pobieranie powiodło się + Pobieranie… + + diff --git a/brouter-routing-app/src/main/res/values/donottranslate-strings.xml b/brouter-routing-app/src/main/res/values/donottranslate-strings.xml new file mode 100644 index 0000000..254dd8f --- /dev/null +++ b/brouter-routing-app/src/main/res/values/donottranslate-strings.xml @@ -0,0 +1,7 @@ + + + BRouter + filename.brf + brouter_download + Downloads + diff --git a/brouter-routing-app/src/main/res/values/strings.xml b/brouter-routing-app/src/main/res/values/strings.xml index 168a26a..fe274ab 100644 --- a/brouter-routing-app/src/main/res/values/strings.xml +++ b/brouter-routing-app/src/main/res/values/strings.xml @@ -1,37 +1,95 @@ - - - - + + %d segment %d segments - BRouter - Cancel Download - Import Profile - filename.brf - Starting download… - Cancelling… + Cancel download + Import profile Download %s Delete %s Update %s Select segments - Stop Download - Size=%s\nFree=%s - brouter_download - Download Segments - Downloads + Stop download + Size=%1$s\nFree=%2$s + Download segments + + Choose brouter data base dir: + Select a routing profile + Select Main Action + Download Manager + BRouter App + + Cancel + I know + Close + Exit + OK + Success + An Error occurred + + BRouter Download Manager + *** Attention:*** + \n\nThe Download Manager is used to download routing-data + files which can be up to 170MB each. Do not start the Download Manager + on a cellular data connection without a data plan! + Download speed is restricted to 16 MBit/s. + Successfully prepared a timeout-free calculation + You successfully repeated a calculation that previously run into a timeout + when started from your map tool. If you repeat the same request from your + map tool, with the exact same destination point and a close-by starting point, + this request is guaranteed not to time out. + Enter SDCARD base dir: + Select Action + + Check VIA Selection: + Check NoGo Selection: + Server-Mode + Info + Calc Route + Profile Settings + Share GPX + Select from + Select to/via + + no profile data + , no used profile + Too much data for download. Please reduce. + Download scheduled. Check internet connection if it doesn\'t start. + current waypoint selection:\n + Expecting waypoint selection\n + Alternative + version = BRouter-%1$s \n + mem = %2$s \n + distance = %3$s km\n + filtered ascend = %4$s m\n + plain ascend = %5$s m\n + estimated time = %6$s + Error reading waypoints + coordinate source does not contain any waypoints! + coordinate source contains too much waypoints: %1$d (please use from/to/via names) + + No + Yes + + Confirm Delete + Really delete? + Version Problem + The base version for tiles has changed. What to do? + Continue with current download, delete other old data + Select all for download and start + Cancel now, complete on another day + Version Differences + The base version for some tiles is different. What to do? + Download all different tiles + Drop all different tiles + Cancel now, complete on another day + The new data version needs a new app. Please update BRouter first + + Download failed + Download cancelled + Download succeeded + Downloading… + + diff --git a/brouter-server/.gitignore b/brouter-server/.gitignore deleted file mode 100644 index 84c048a..0000000 --- a/brouter-server/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build/ diff --git a/brouter-server/build.gradle b/brouter-server/build.gradle index 9dac2d4..49cfd98 100644 --- a/brouter-server/build.gradle +++ b/brouter-server/build.gradle @@ -1,13 +1,10 @@ plugins { - id 'application' + id 'brouter.application-conventions' } - application { mainClass.set('btools.server.BRouter') - distTar.enabled = false - jar { manifest { attributes "Main-Class": getMainClass(), "Implementation-Version": project.version @@ -35,6 +32,7 @@ application { distZip { dependsOn fatJar + if (file('../local.properties').exists()) dependsOn (':brouter-routing-app:assemble') archiveFileName = 'brouter-' + project.version + '.zip' } @@ -77,6 +75,5 @@ dependencies { implementation project(':brouter-mapaccess') implementation project(':brouter-util') - testImplementation 'junit:junit:4.13.2' testImplementation 'org.json:json:20180813' } diff --git a/brouter-server/src/main/java/btools/server/BRouter.java b/brouter-server/src/main/java/btools/server/BRouter.java index 90ded9c..a6dfe10 100644 --- a/brouter-server/src/main/java/btools/server/BRouter.java +++ b/brouter-server/src/main/java/btools/server/BRouter.java @@ -1,60 +1,48 @@ package btools.server; -import java.io.BufferedOutputStream; -import java.io.DataOutputStream; -import java.io.FileOutputStream; -import java.net.URLDecoder; -import java.util.ArrayList; -import java.util.List; import java.io.File; +import java.net.URLDecoder; +import java.util.List; +import java.util.Map; import btools.router.OsmNodeNamed; import btools.router.OsmTrack; import btools.router.RoutingContext; import btools.router.RoutingEngine; -import btools.router.SearchBoundary; +import btools.router.RoutingParamCollector; public class BRouter { public static void main(String[] args) throws Exception { - if (args.length == 2) { // cgi-input-mode + if (args.length == 3 || args.length == 4) { // 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); + int engineMode = 0; + if (params.containsKey("engineMode")) { + engineMode = Integer.parseInt(params.get("engineMode")); } + 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; - } + String exportName = null; + if (args.length == 4) { + exportName = args[3]; + } else { + // cgi-header + System.out.println("Content-type: text/plain"); + System.out.println(); } long maxRunningTime = 60000; // the cgi gets a 1 Minute timeout @@ -63,11 +51,7 @@ public class BRouter { 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(exportName, null, new File(args[0]), wplist, rc, engineMode); re.doRun(maxRunningTime); if (re.getErrorMessage() != null) { System.out.println(re.getErrorMessage()); @@ -78,66 +62,57 @@ 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 [output-filename]"); + System.exit(0); } - List wplist = new ArrayList<>(); - wplist.add(readPosition(args, 1, "from")); - RoutingEngine re = null; - if ("seed".equals(args[3])) { - int searchRadius = Integer.parseInt(args[4]); // if = 0 search a 5x5 square - String filename = SearchBoundary.getFileName(wplist.get(0)); - DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("traffic/" + filename))); + int engineMode = 0; + try { + engineMode = Integer.parseInt(args[2]); + } catch (NumberFormatException e) { + } - for (int direction = 0; direction < 8; direction++) { - RoutingContext rc = readRoutingContext(args); - SearchBoundary boundary = new SearchBoundary(wplist.get(0), searchRadius, direction / 2); - rc.trafficOutputStream = dos; - rc.inverseDirection = (direction & 1) != 0; - re = new RoutingEngine("mytrack", "mylog", new File(args[0]), wplist, rc); - re.boundary = boundary; - re.airDistanceCostFactor = rc.trafficDirectionFactor; - rc.countTraffic = true; - re.doSearch(); - if (re.getErrorMessage() != null) { - break; - } + 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 { + RoutingEngine re = null; + 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); } - 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); - - } - if (re.getErrorMessage() != null) { - System.out.println(re.getErrorMessage()); + } catch (Exception e) { + System.out.println(e.getMessage()); } + } - private static OsmNodeNamed readPosition(String[] args, int idx, String name) { - OsmNodeNamed n = new OsmNodeNamed(); - n.name = name; - n.ilon = (int) ((Double.parseDouble(args[idx]) + 180.) * 1000000. + 0.5); - n.ilat = (int) ((Double.parseDouble(args[idx + 1]) + 90.) * 1000000. + 0.5); - return n; - } - - private static RoutingContext readRoutingContext(String[] args) { - RoutingContext c = new RoutingContext(); - if (args.length > 5) { - c.localFunction = args[5]; - if (args.length > 6) { - c.setAlternativeIdx(Integer.parseInt(args[6])); - } - } - c.memoryclass = (int) (Runtime.getRuntime().maxMemory() / 1024 / 1024); - // c.startDirection= Integer.valueOf( 150 ); - return c; - } } diff --git a/brouter-server/src/main/java/btools/server/IpAccessMonitor.java b/brouter-server/src/main/java/btools/server/IpAccessMonitor.java index a9413e2..6545db7 100644 --- a/brouter-server/src/main/java/btools/server/IpAccessMonitor.java +++ b/brouter-server/src/main/java/btools/server/IpAccessMonitor.java @@ -15,7 +15,7 @@ public class IpAccessMonitor { synchronized (sync) { Long lastTime = ipAccess.get(ip); ipAccess.put(ip, t); - return lastTime == null || t - lastTime.longValue() > MAX_IDLE; + return lastTime == null || t - lastTime > MAX_IDLE; } } @@ -31,9 +31,9 @@ public class IpAccessMonitor { } private static void cleanup(long t) { - HashMap newMap = new HashMap<>(ipAccess.size()); + Map newMap = new HashMap<>(ipAccess.size()); for (Map.Entry e : ipAccess.entrySet()) { - if (t - e.getValue().longValue() <= MAX_IDLE) { + if (t - e.getValue() <= MAX_IDLE) { newMap.put(e.getKey(), e.getValue()); } } diff --git a/brouter-server/src/main/java/btools/server/Polygon.java b/brouter-server/src/main/java/btools/server/Polygon.java index 9115ef1..a711de5 100644 --- a/brouter-server/src/main/java/btools/server/Polygon.java +++ b/brouter-server/src/main/java/btools/server/Polygon.java @@ -3,6 +3,7 @@ package btools.server; import java.io.BufferedReader; import java.io.IOException; import java.util.ArrayList; +import java.util.List; import java.util.StringTokenizer; public class Polygon { @@ -15,7 +16,7 @@ public class Polygon { private int maxy = Integer.MIN_VALUE; public Polygon(BufferedReader br) throws IOException { - ArrayList lines = new ArrayList<>(); + List lines = new ArrayList<>(); for (; ; ) { String line = br.readLine(); diff --git a/brouter-server/src/main/java/btools/server/RouteServer.java b/brouter-server/src/main/java/btools/server/RouteServer.java index c0e5846..36145dc 100644 --- a/brouter-server/src/main/java/btools/server/RouteServer.java +++ b/brouter-server/src/main/java/btools/server/RouteServer.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.PriorityQueue; +import java.util.Queue; import java.util.StringTokenizer; import java.util.zip.GZIPOutputStream; @@ -29,6 +30,7 @@ import btools.router.OsmTrack; import btools.router.ProfileCache; import btools.router.RoutingContext; import btools.router.RoutingEngine; +import btools.router.RoutingParamCollector; import btools.server.request.ProfileUploadHandler; import btools.server.request.RequestHandler; import btools.server.request.ServerHandler; @@ -57,7 +59,7 @@ public class RouteServer extends Thread implements Comparable { if (e != null) e.terminate(); } - private static DateFormat tsFormat = new SimpleDateFormat("dd.MM.yy HH:mm", new Locale("en", "US")); + private static DateFormat tsFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", new Locale("en", "US")); private static String formattedTimeStamp(long t) { synchronized (tsFormat) { @@ -146,7 +148,9 @@ public class RouteServer extends Thread implements Comparable { } String url = getline.split(" ")[1]; - Map params = getUrlParams(url); + + RoutingParamCollector routingParamCollector = new RoutingParamCollector(); + Map params = routingParamCollector.getUrlParams(url); long maxRunningTime = getMaxRunningTime(); @@ -186,33 +190,21 @@ public class RouteServer extends Thread implements Comparable { return; } RoutingContext rc = handler.readRoutingContext(); - List wplist = handler.readWayPointList(); + List wplist = routingParamCollector.getWayPointList(params.get("lonlats")); if (wplist.size() < 10) { SuspectManager.nearRecentWps.add(wplist); } - int engineMode = 0; - for (Map.Entry e : params.entrySet()) { - if ("engineMode".equals(e.getKey())) { - engineMode = Integer.parseInt(e.getValue()); - } else if ("timode".equals(e.getKey())) { - rc.turnInstructionMode = Integer.parseInt(e.getValue()); - } else if ("heading".equals(e.getKey())) { - rc.startDirection = Integer.parseInt(e.getValue()); - rc.forceUseStartDirection = true; - } else if (e.getKey().startsWith("profile:")) { - if (rc.keyValues == null) { - rc.keyValues = new HashMap<>(); - } - rc.keyValues.put(e.getKey().substring(8), e.getValue()); - } else if (e.getKey().equals("straight")) { - String[] sa = e.getValue().split(","); - for (int i = 0; i < sa.length; i++) { - int v = Integer.parseInt(sa[i]); - if (wplist.size() > v) wplist.get(v).direct = true; - } - } + if (params.containsKey("profile")) { + // already handled in readRoutingContext + params.remove("profile"); } + int engineMode = 0; + if (params.containsKey("engineMode")) { + engineMode = Integer.parseInt(params.get("engineMode")); + } + routingParamCollector.setParams(rc, wplist, params); + cr = new RoutingEngine(null, null, serviceContext.segmentDir, wplist, rc, engineMode); cr.quite = true; cr.doRun(maxRunningTime); @@ -224,18 +216,29 @@ public class RouteServer extends Thread implements Comparable { } else { OsmTrack track = cr.getFoundTrack(); + if (engineMode == 2) { + // no zip for this engineMode + encodings = null; + } String headers = encodings == null || encodings.indexOf("gzip") < 0 ? null : "Content-Encoding: gzip\n"; writeHttpHeader(bw, handler.getMimeType(), handler.getFileName(), headers, HTTP_STATUS_OK); - if (track != null) { - if (headers != null) { // compressed - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Writer w = new OutputStreamWriter(new GZIPOutputStream(baos), "UTF-8"); - w.write(handler.formatTrack(track)); - w.close(); - bw.flush(); - clientSocket.getOutputStream().write(baos.toByteArray()); - } else { - bw.write(handler.formatTrack(track)); + if (engineMode == 0) { + if (track != null) { + if (headers != null) { // compressed + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Writer w = new OutputStreamWriter(new GZIPOutputStream(baos), "UTF-8"); + w.write(handler.formatTrack(track)); + w.close(); + bw.flush(); + clientSocket.getOutputStream().write(baos.toByteArray()); + } else { + bw.write(handler.formatTrack(track)); + } + } + } else if (engineMode == 2) { + String s = cr.getFoundInfo(); + if (s != null) { + bw.write(s); } } } @@ -297,7 +300,7 @@ public class RouteServer extends Thread implements Comparable { ProfileCache.setSize(2 * maxthreads); - PriorityQueue threadQueue = new PriorityQueue<>(); + Queue threadQueue = new PriorityQueue<>(); ServerSocket serverSocket = args.length > 5 ? new ServerSocket(Integer.parseInt(args[3]), 100, InetAddress.getByName(args[5])) : new ServerSocket(Integer.parseInt(args[3])); @@ -362,7 +365,7 @@ public class RouteServer extends Thread implements Comparable { private static Map getUrlParams(String url) throws UnsupportedEncodingException { - HashMap params = new HashMap<>(); + Map params = new HashMap<>(); String decoded = URLDecoder.decode(url, "UTF-8"); StringTokenizer tk = new StringTokenizer(decoded, "?&"); while (tk.hasMoreTokens()) { @@ -415,7 +418,7 @@ public class RouteServer extends Thread implements Comparable { bw.write("\n"); } - private static void cleanupThreadQueue(PriorityQueue threadQueue) { + private static void cleanupThreadQueue(Queue threadQueue) { for (; ; ) { boolean removedItem = false; for (RouteServer t : threadQueue) { diff --git a/brouter-server/src/main/java/btools/server/SuspectManager.java b/brouter-server/src/main/java/btools/server/SuspectManager.java index 303c699..8e67933 100644 --- a/brouter-server/src/main/java/btools/server/SuspectManager.java +++ b/brouter-server/src/main/java/btools/server/SuspectManager.java @@ -9,6 +9,7 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; +import java.util.Set; import java.util.StringTokenizer; import java.util.TreeSet; @@ -244,7 +245,7 @@ public class SuspectManager extends Thread { bw.write("\n"); File countryParent = new File("worldpolys" + country); File[] files = countryParent.listFiles(); - TreeSet names = new TreeSet<>(); + Set names = new TreeSet<>(); for (File f : files) { String name = f.getName(); if (name.endsWith(".poly")) { diff --git a/brouter-server/src/main/java/btools/server/request/ProfileUploadHandler.java b/brouter-server/src/main/java/btools/server/request/ProfileUploadHandler.java index 78a39cf..81d52b0 100644 --- a/brouter-server/src/main/java/btools/server/request/ProfileUploadHandler.java +++ b/brouter-server/src/main/java/btools/server/request/ProfileUploadHandler.java @@ -37,7 +37,7 @@ public class ProfileUploadHandler { String id; if (profileId != null) { // update existing file when id appended - id = profileId.substring(ProfileUploadHandler.CUSTOM_PREFIX.length()); + id = profileId.substring(CUSTOM_PREFIX.length()); } else { id = "" + System.currentTimeMillis(); } diff --git a/brouter-server/src/main/java/btools/server/request/RequestHandler.java b/brouter-server/src/main/java/btools/server/request/RequestHandler.java index 5364c5b..6fbb816 100644 --- a/brouter-server/src/main/java/btools/server/request/RequestHandler.java +++ b/brouter-server/src/main/java/btools/server/request/RequestHandler.java @@ -1,9 +1,7 @@ package btools.server.request; -import java.util.List; import java.util.Map; -import btools.router.OsmNodeNamed; import btools.router.OsmTrack; import btools.router.RoutingContext; import btools.server.ServiceContext; @@ -19,8 +17,6 @@ public abstract class RequestHandler { public abstract RoutingContext readRoutingContext(); - public abstract List readWayPointList(); - public abstract String formatTrack(OsmTrack track); public abstract String getMimeType(); diff --git a/brouter-server/src/main/java/btools/server/request/ServerHandler.java b/brouter-server/src/main/java/btools/server/request/ServerHandler.java index fb8f520..c459322 100644 --- a/brouter-server/src/main/java/btools/server/request/ServerHandler.java +++ b/brouter-server/src/main/java/btools/server/request/ServerHandler.java @@ -1,14 +1,12 @@ package btools.server.request; -import java.io.BufferedWriter; import java.io.File; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.List; import java.util.Map; -import btools.router.OsmNodeNamed; -import btools.router.OsmNogoPolygon; +import btools.router.FormatCsv; +import btools.router.FormatGpx; +import btools.router.FormatJson; +import btools.router.FormatKml; import btools.router.OsmTrack; import btools.router.RoutingContext; import btools.server.ServiceContext; @@ -62,59 +60,9 @@ public class ServerHandler extends RequestHandler { } rc.localFunction = profile; - rc.setAlternativeIdx(Integer.parseInt(params.get("alternativeidx"))); - - List poisList = readPoisList(); - rc.poipoints = poisList; - - List nogoList = readNogoList(); - List nogoPolygonsList = readNogoPolygons(); - - if (nogoList != null) { - RoutingContext.prepareNogoPoints(nogoList); - rc.nogopoints = nogoList; - } - - if (rc.nogopoints == null) { - rc.nogopoints = nogoPolygonsList; - } else if (nogoPolygonsList != null) { - rc.nogopoints.addAll(nogoPolygonsList); - } - return rc; } - @Override - public List readWayPointList() { - // lon,lat|... - String lonLats = params.get("lonlats"); - if (lonLats == null) throw new IllegalArgumentException("lonlats parameter not set"); - - String[] coords = lonLats.split("\\|"); - if (coords.length < 2) - throw new IllegalArgumentException("we need two lat/lon points at least!"); - - List wplist = new ArrayList<>(); - for (int i = 0; i < coords.length; i++) { - String[] lonLat = coords[i].split(","); - if (lonLat.length < 2) - throw new IllegalArgumentException("we need two lat/lon points 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; - } - @Override public String formatTrack(OsmTrack track) { String result; @@ -130,23 +78,17 @@ public class ServerHandler extends RequestHandler { } if (format == null || "gpx".equals(format)) { - result = track.formatAsGpx(); + result = new FormatGpx(rc).format(track); } else if ("kml".equals(format)) { - result = track.formatAsKml(); + result = new FormatKml(rc).format(track); } else if ("geojson".equals(format)) { - result = track.formatAsGeoJson(); + result = new FormatJson(rc).format(track); } else if ("csv".equals(format)) { - try { - StringWriter sw = new StringWriter(); - BufferedWriter bw = new BufferedWriter(sw); - track.writeMessages(bw, rc); - return sw.toString(); - } catch (Exception ex) { - return "Error: " + ex.getMessage(); - } + result = new FormatCsv(rc).format(track); } else { System.out.println("unknown track format '" + format + "', using default"); - result = track.formatAsGpx(); + //result = track.formatAsGpx(); + result = new FormatGpx(rc).format(track); } return result; @@ -165,7 +107,7 @@ public class ServerHandler extends RequestHandler { } else if ("kml".equals(format)) { result = "application/vnd.google-earth.kml+xml"; } else if ("geojson".equals(format)) { - result = "application/vnd.geo+json"; + result = "application/geo+json"; } else if ("csv".equals(format)) { result = "text/tab-separated-values"; } @@ -191,115 +133,4 @@ public class ServerHandler extends RequestHandler { return params.get("trackname") == null ? null : params.get("trackname").replaceAll("[^a-zA-Z0-9 \\._\\-]+", ""); } - private static 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 static 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; - } - - private List readPoisList() { - // lon,lat,name|... - String pois = params.get("pois"); - 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; - } - - private List readNogoList() { - // lon,lat,radius|... - String nogos = params.get("nogos"); - 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; - } - - private static 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), Integer.parseInt(radius), weight); - } - - private static 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; - } - - private List readNogoPolygons() { - List result = new ArrayList<>(); - parseNogoPolygons(params.get("polylines"), result, false); - parseNogoPolygons(params.get("polygons"), result, true); - return result.size() > 0 ? result : null; - } - - private static 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); - } - } - } - } - } } diff --git a/brouter-server/src/test/java/btools/server/RequestHandlerTest.java b/brouter-server/src/test/java/btools/server/RequestHandlerTest.java index 2d5f698..81d03cb 100644 --- a/brouter-server/src/test/java/btools/server/RequestHandlerTest.java +++ b/brouter-server/src/test/java/btools/server/RequestHandlerTest.java @@ -5,6 +5,7 @@ import org.junit.Ignore; import org.junit.Test; import java.util.HashMap; +import java.util.Map; import btools.router.RoutingContext; import btools.server.request.ServerHandler; @@ -13,7 +14,7 @@ public class RequestHandlerTest { @Test @Ignore("Parameters are currently handled by RouteServer, not RequestHandler") public void parseParameters() { - HashMap params = new HashMap<>(); + Map params = new HashMap<>(); params.put("lonlats", "8.799297,49.565883|8.811764,49.563606"); params.put("profile", "trekking"); params.put("alternativeidx", "0"); diff --git a/brouter-util/.gitignore b/brouter-util/.gitignore deleted file mode 100644 index 84c048a..0000000 --- a/brouter-util/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build/ diff --git a/brouter-util/build.gradle b/brouter-util/build.gradle index 3dc5565..80c7d8c 100644 --- a/brouter-util/build.gradle +++ b/brouter-util/build.gradle @@ -1,7 +1,3 @@ plugins { - id 'java-library' -} - -dependencies { - testImplementation('junit:junit:4.13.1') + id 'brouter.library-conventions' } diff --git a/brouter-util/pom.xml b/brouter-util/pom.xml deleted file mode 100644 index 23b3925..0000000 --- a/brouter-util/pom.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - 4.0.0 - - org.btools - brouter - 1.6.1 - ../pom.xml - - brouter-util - jar - - - - junit - junit - test - - - diff --git a/brouter-util/src/main/AndroidManifest.xml b/brouter-util/src/main/AndroidManifest.xml deleted file mode 100644 index d28b2cc..0000000 --- a/brouter-util/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/brouter-util/src/main/java/btools/util/CompactLongMap.java b/brouter-util/src/main/java/btools/util/CompactLongMap.java index b29d83a..49efc08 100644 --- a/brouter-util/src/main/java/btools/util/CompactLongMap.java +++ b/brouter-util/src/main/java/btools/util/CompactLongMap.java @@ -224,6 +224,7 @@ public class CompactLongMap { // does sorted array "a" contain "id" ? + @SuppressWarnings("unchecked") private boolean contains(int idx, long id, boolean doPut) { long[] a = al[idx]; int offset = a.length; @@ -243,6 +244,7 @@ public class CompactLongMap { return false; } + @SuppressWarnings("unchecked") protected void moveToFrozenArrays(long[] faid, List flv) { for (int i = 1; i < MAXLISTS; i++) { pa[i] = 0; diff --git a/brouter-util/src/main/java/btools/util/LazyArrayOfLists.java b/brouter-util/src/main/java/btools/util/LazyArrayOfLists.java index d8b05a5..b9f6d0c 100644 --- a/brouter-util/src/main/java/btools/util/LazyArrayOfLists.java +++ b/brouter-util/src/main/java/btools/util/LazyArrayOfLists.java @@ -9,6 +9,7 @@ import java.util.List; * * @author ab */ +@SuppressWarnings("PMD.LooseCoupling") public class LazyArrayOfLists { private List> lists; diff --git a/brouter-util/src/main/java/btools/util/SortedHeap.java b/brouter-util/src/main/java/btools/util/SortedHeap.java index 3f2c4f1..b350d63 100644 --- a/brouter-util/src/main/java/btools/util/SortedHeap.java +++ b/brouter-util/src/main/java/btools/util/SortedHeap.java @@ -19,6 +19,7 @@ public final class SortedHeap { /** * @return the lowest key value, or null if none */ + @SuppressWarnings("unchecked") public V popLowestKeyValue() { SortedBin bin = firstNonEmpty; if (firstNonEmpty == null) { diff --git a/brouter-util/src/main/java/btools/util/StackSampler.java b/brouter-util/src/main/java/btools/util/StackSampler.java index 6b2e304..753fec4 100644 --- a/brouter-util/src/main/java/btools/util/StackSampler.java +++ b/brouter-util/src/main/java/btools/util/StackSampler.java @@ -51,12 +51,12 @@ public class StackSampler extends Thread { try { int wait1 = rand.nextInt(interval); int wait2 = interval - wait1; - Thread.sleep(wait1); + sleep(wait1); StringBuilder sb = new StringBuilder(df.format(new Date()) + " THREADDUMP\n"); - Map allThreads = Thread.getAllStackTraces(); + Map allThreads = getAllStackTraces(); for (Map.Entry e : allThreads.entrySet()) { Thread t = e.getKey(); - if (t == Thread.currentThread()) { + if (t == currentThread()) { continue; // not me } @@ -76,7 +76,7 @@ public class StackSampler extends Thread { flushCnt = 0; bw.flush(); } - Thread.sleep(wait2); + sleep(wait2); } catch (Exception e) { // ignore } diff --git a/brouter-util/src/test/java/btools/util/CompactMapTest.java b/brouter-util/src/test/java/btools/util/CompactMapTest.java index 1217497..b2dc0af 100644 --- a/brouter-util/src/test/java/btools/util/CompactMapTest.java +++ b/brouter-util/src/test/java/btools/util/CompactMapTest.java @@ -4,6 +4,7 @@ import org.junit.Assert; import org.junit.Test; import java.util.HashMap; +import java.util.Map; import java.util.Random; public class CompactMapTest { @@ -22,7 +23,7 @@ public class CompactMapTest { private void hashMapComparison(int mapsize, int trycount) { Random rand = new Random(12345); - HashMap hmap = new HashMap<>(); + Map hmap = new HashMap<>(); CompactLongMap cmap_slow = new CompactLongMap<>(); CompactLongMap cmap_fast = new CompactLongMap<>(); diff --git a/brouter-util/src/test/java/btools/util/CompactSetTest.java b/brouter-util/src/test/java/btools/util/CompactSetTest.java index e0f5340..1a4f130 100644 --- a/brouter-util/src/test/java/btools/util/CompactSetTest.java +++ b/brouter-util/src/test/java/btools/util/CompactSetTest.java @@ -5,6 +5,7 @@ import org.junit.Test; import java.util.HashSet; import java.util.Random; +import java.util.Set; public class CompactSetTest { @Test @@ -22,7 +23,7 @@ public class CompactSetTest { private void hashSetComparison(int setsize, int trycount) { Random rand = new Random(12345); - HashSet hset = new HashSet<>(); + Set hset = new HashSet<>(); CompactLongSet cset_slow = new CompactLongSet(); CompactLongSet cset_fast = new CompactLongSet(); diff --git a/brouter-util/src/test/java/btools/util/DenseLongMapTest.java b/brouter-util/src/test/java/btools/util/DenseLongMapTest.java index 57e867a..85936d7 100644 --- a/brouter-util/src/test/java/btools/util/DenseLongMapTest.java +++ b/brouter-util/src/test/java/btools/util/DenseLongMapTest.java @@ -5,7 +5,9 @@ import org.junit.Test; import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Random; +import java.util.Set; public class DenseLongMapTest { @Test @@ -16,7 +18,7 @@ public class DenseLongMapTest { private void hashMapComparison(int mapsize, int trycount, long keyrange) { Random rand = new Random(12345); - HashMap hmap = new HashMap<>(); + Map hmap = new HashMap<>(); DenseLongMap dmap = new DenseLongMap(512); for (int i = 0; i < mapsize; i++) { @@ -32,7 +34,7 @@ public class DenseLongMapTest { long k = (long) (rand.nextDouble() * keyrange); Long KK = k; Integer VV = hmap.get(KK); - int hvalue = VV == null ? -1 : VV.intValue(); + int hvalue = VV == null ? -1 : VV; int dvalue = dmap.getInt(k); if (hvalue != dvalue) { @@ -48,7 +50,7 @@ public class DenseLongMapTest { int trycount = 100000; Random rand = new Random(12345); - HashSet hset = new HashSet<>(); + Set hset = new HashSet<>(); DenseLongMap dmap = new DenseLongMap(512); for (int i = 0; i < mapputs; i++) { diff --git a/build.gradle b/build.gradle index 657cd43..6c64c85 100644 --- a/build.gradle +++ b/build.gradle @@ -1,65 +1,18 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - repositories { mavenCentral() google() } dependencies { - classpath 'com.android.tools.build:gradle:7.4.2' - + classpath 'com.android.tools.build:gradle:8.2.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } -allprojects { - // NOTE: - // there are four places to change the version number - // this file - // app: build.gradle (versionCode only) - // OsmTrack (version and versionDate) - // docs revisions.md (version and versionDate) - project.version "1.7.1" - group 'org.btools' - - repositories { - mavenCentral() - google() - } - - apply plugin: "maven-publish" - publishing { - repositories { - maven { - name = "GitHubPackages" - url = uri("https://maven.pkg.github.com/$System.env.REPO") - credentials { - username = project.findProperty("gpr.user") ?: System.getenv("USERNAME") - password = project.findProperty("gpr.key") ?: System.getenv("TOKEN") - } - } - } - publications { - gpr(MavenPublication) - } - } - - apply plugin: "checkstyle" - apply plugin: "pmd" - - pmd { - consoleOutput = true - toolVersion = "6.51.0" - rulesMinimumPriority = 5 - ruleSetFiles = files("${rootProject.rootDir}/config/pmd/pmd-ruleset.xml") - ruleSets = [] - // ignoreFailures = true - } -} - task clean(type: Delete) { delete rootProject.buildDir } diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle new file mode 100644 index 0000000..6784052 --- /dev/null +++ b/buildSrc/build.gradle @@ -0,0 +1,3 @@ +plugins { + id 'groovy-gradle-plugin' +} diff --git a/buildSrc/src/main/groovy/brouter.application-conventions.gradle b/buildSrc/src/main/groovy/brouter.application-conventions.gradle new file mode 100644 index 0000000..da0466c --- /dev/null +++ b/buildSrc/src/main/groovy/brouter.application-conventions.gradle @@ -0,0 +1,8 @@ +plugins { + id 'application' + id 'brouter.java-conventions' +} + +application { + distTar.enabled = false +} diff --git a/buildSrc/src/main/groovy/brouter.java-conventions.gradle b/buildSrc/src/main/groovy/brouter.java-conventions.gradle new file mode 100644 index 0000000..4609537 --- /dev/null +++ b/buildSrc/src/main/groovy/brouter.java-conventions.gradle @@ -0,0 +1,29 @@ +plugins { + id 'java' + id 'checkstyle' + id 'pmd' + id 'brouter.version-conventions' +} + +group 'org.btools' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation 'junit:junit:4.13.2' +} + +compileJava { + options.release = 11 +} + +pmd { + consoleOutput = true + toolVersion = '7.0.0' + rulesMinimumPriority = 5 + ruleSetFiles = files("${rootProject.rootDir}/config/pmd/pmd-ruleset.xml") + ruleSets = [] + // ignoreFailures = true +} diff --git a/buildSrc/src/main/groovy/brouter.library-conventions.gradle b/buildSrc/src/main/groovy/brouter.library-conventions.gradle new file mode 100644 index 0000000..3fd5d46 --- /dev/null +++ b/buildSrc/src/main/groovy/brouter.library-conventions.gradle @@ -0,0 +1,21 @@ +plugins { + id 'java-library' + id 'maven-publish' + id 'brouter.java-conventions' +} + +publishing { + repositories { + maven { + name = 'GitHubPackages' + url = uri("https://maven.pkg.github.com/$System.env.REPO") + credentials { + username = project.findProperty('gpr.user') ?: System.getenv('USERNAME') + password = project.findProperty('gpr.key') ?: System.getenv('TOKEN') + } + } + } + publications { + gpr(MavenPublication) + } +} diff --git a/buildSrc/src/main/groovy/brouter.version-conventions.gradle b/buildSrc/src/main/groovy/brouter.version-conventions.gradle new file mode 100644 index 0000000..b99c641 --- /dev/null +++ b/buildSrc/src/main/groovy/brouter.version-conventions.gradle @@ -0,0 +1,7 @@ +// NOTE: +// there are four places to change the version number +// this file +// app: build.gradle (versionCode only) +// OsmTrack (version and versionDate) +// docs revisions.md (version and versionDate) +version '1.7.7' diff --git a/config/pmd/pmd-ruleset.xml b/config/pmd/pmd-ruleset.xml index 787e663..1837468 100644 --- a/config/pmd/pmd-ruleset.xml +++ b/config/pmd/pmd-ruleset.xml @@ -29,6 +29,7 @@ + @@ -51,7 +52,6 @@ - - + diff --git a/docs/Gemfile b/docs/Gemfile index 7bfae82..587276e 100644 --- a/docs/Gemfile +++ b/docs/Gemfile @@ -27,3 +27,5 @@ end # Performance-booster for watching directories on Windows gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin] + +gem "webrick", "~> 1.8" diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index e50be30..e51aaf0 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,46 +1,55 @@ GEM remote: https://rubygems.org/ specs: - activesupport (7.0.4.3) + activesupport (7.1.3.2) + base64 + bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) minitest (>= 5.1) + mutex_m tzinfo (~> 2.0) - addressable (2.8.4) + addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) + base64 (0.2.0) + bigdecimal (3.1.7) coffee-script (2.4.1) coffee-script-source execjs - coffee-script-source (1.11.1) + coffee-script-source (1.12.2) colorator (1.1.0) - commonmarker (0.23.9) - concurrent-ruby (1.2.2) - dnsruby (1.70.0) + commonmarker (0.23.10) + concurrent-ruby (1.2.3) + connection_pool (2.4.1) + dnsruby (1.72.0) simpleidn (~> 0.2.1) + drb (2.2.1) em-websocket (0.5.3) eventmachine (>= 0.12.9) http_parser.rb (~> 0) ethon (0.16.0) ffi (>= 1.15.0) eventmachine (1.2.7) - execjs (2.8.1) - faraday (2.7.4) - faraday-net_http (>= 2.0, < 3.1) - ruby2_keywords (>= 0.0.4) - faraday-net_http (3.0.2) - ffi (1.15.5) + execjs (2.9.1) + faraday (2.9.0) + faraday-net_http (>= 2.0, < 3.2) + faraday-net_http (3.1.0) + net-http + ffi (1.16.3) forwardable-extended (2.6.0) - gemoji (3.0.1) - github-pages (228) - github-pages-health-check (= 1.17.9) - jekyll (= 3.9.3) - jekyll-avatar (= 0.7.0) - jekyll-coffeescript (= 1.1.1) + gemoji (4.1.0) + github-pages (231) + github-pages-health-check (= 1.18.2) + jekyll (= 3.9.5) + jekyll-avatar (= 0.8.0) + jekyll-coffeescript (= 1.2.2) jekyll-commonmark-ghpages (= 0.4.0) - jekyll-default-layout (= 0.1.4) - jekyll-feed (= 0.15.1) + jekyll-default-layout (= 0.1.5) + jekyll-feed (= 0.17.0) jekyll-gist (= 1.5.0) - jekyll-github-metadata (= 2.13.0) + jekyll-github-metadata (= 2.16.1) jekyll-include-cache (= 0.2.1) jekyll-mentions (= 1.6.0) jekyll-optional-front-matter (= 0.3.2) @@ -67,28 +76,28 @@ GEM jekyll-theme-tactile (= 0.2.0) jekyll-theme-time-machine (= 0.2.0) jekyll-titles-from-headings (= 0.5.3) - jemoji (= 0.12.0) - kramdown (= 2.3.2) + jemoji (= 0.13.0) + kramdown (= 2.4.0) kramdown-parser-gfm (= 1.1.0) liquid (= 4.0.4) mercenary (~> 0.3) minima (= 2.5.1) nokogiri (>= 1.13.6, < 2.0) - rouge (= 3.26.0) + rouge (= 3.30.0) terminal-table (~> 1.4) - github-pages-health-check (1.17.9) + github-pages-health-check (1.18.2) addressable (~> 2.3) dnsruby (~> 1.60) - octokit (~> 4.0) - public_suffix (>= 3.0, < 5.0) + octokit (>= 4, < 8) + public_suffix (>= 3.0, < 6.0) typhoeus (~> 1.3) html-pipeline (2.14.3) activesupport (>= 2) nokogiri (>= 1.4) http_parser.rb (0.8.0) - i18n (1.13.0) + i18n (1.14.4) concurrent-ruby (~> 1.0) - jekyll (3.9.3) + jekyll (3.9.5) addressable (~> 2.4) colorator (~> 1.0) em-websocket (~> 0.5) @@ -101,11 +110,11 @@ GEM pathutil (~> 0.9) rouge (>= 1.7, < 4) safe_yaml (~> 1.0) - jekyll-avatar (0.7.0) + jekyll-avatar (0.8.0) jekyll (>= 3.0, < 5.0) - jekyll-coffeescript (1.1.1) + jekyll-coffeescript (1.2.2) coffee-script (~> 2.2) - coffee-script-source (~> 1.11.1) + coffee-script-source (~> 1.12) jekyll-commonmark (1.4.0) commonmarker (~> 0.22) jekyll-commonmark-ghpages (0.4.0) @@ -113,15 +122,15 @@ GEM jekyll (~> 3.9.0) jekyll-commonmark (~> 1.4.0) rouge (>= 2.0, < 5.0) - jekyll-default-layout (0.1.4) - jekyll (~> 3.0) - jekyll-feed (0.15.1) + jekyll-default-layout (0.1.5) + jekyll (>= 3.0, < 5.0) + jekyll-feed (0.17.0) jekyll (>= 3.7, < 5.0) jekyll-gist (1.5.0) octokit (~> 4.2) - jekyll-github-metadata (2.13.0) + jekyll-github-metadata (2.16.1) jekyll (>= 3.4, < 5.0) - octokit (~> 4.0, != 4.4.0) + octokit (>= 4, < 7, != 4.4.0) jekyll-include-cache (0.2.1) jekyll (>= 3.7, < 5.0) jekyll-mentions (1.6.0) @@ -192,16 +201,16 @@ GEM jekyll (>= 3.3, < 5.0) jekyll-watch (2.2.1) listen (~> 3.0) - jemoji (0.12.0) - gemoji (~> 3.0) + jemoji (0.13.0) + gemoji (>= 3, < 5) html-pipeline (~> 2.2) jekyll (>= 3.0, < 5.0) - kramdown (2.3.2) + kramdown (2.4.0) rexml kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) liquid (4.0.4) - listen (3.8.0) + listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) mercenary (0.3.6) @@ -209,22 +218,24 @@ GEM jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) - minitest (5.18.0) - nokogiri (1.15.0-x86_64-linux) + minitest (5.22.3) + mutex_m (0.2.0) + net-http (0.4.1) + uri + nokogiri (1.16.3-x86_64-linux) racc (~> 1.4) octokit (4.25.1) faraday (>= 1, < 3) sawyer (~> 0.9) pathutil (0.16.2) forwardable-extended (~> 2.6) - public_suffix (4.0.7) - racc (1.6.2) + public_suffix (5.0.5) + racc (1.7.3) rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) - rexml (3.2.5) - rouge (3.26.0) - ruby2_keywords (0.0.5) + rexml (3.2.6) + rouge (3.30.0) rubyzip (2.3.2) safe_yaml (1.0.5) sass (3.7.4) @@ -239,14 +250,16 @@ GEM unf (~> 0.1.4) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) - typhoeus (1.4.0) + typhoeus (1.4.1) ethon (>= 0.9.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unf (0.1.4) unf_ext - unf_ext (0.0.8.2) + unf_ext (0.0.9.1) unicode-display_width (1.8.0) + uri (0.13.0) + webrick (1.8.1) PLATFORMS x86_64-linux @@ -256,6 +269,7 @@ DEPENDENCIES tzinfo (~> 1.2) tzinfo-data wdm (~> 0.1.1) + webrick (~> 1.8) BUNDLED WITH - 2.4.12 + 2.5.4 diff --git a/docs/_README.md b/docs/_README.md new file mode 100644 index 0000000..cdda75c --- /dev/null +++ b/docs/_README.md @@ -0,0 +1,15 @@ +# BRouter Docs + +This documentation can be used to generate a static website using [jekyll](https://jekyllrb.com/). + +## Dependencies + +jekyll uses ruby and therefore installs dependencies using gem. It is recommended to use bundler to manage those dependencies. + +`bundle install` installs all dependencies. To update dependencies use `bundle update`. + +## Preview + +jekyll provides a built-in webserver which can be used for fast feedback during editing. + +`bundle exec jekyll serve` diff --git a/docs/developers/android_service.md b/docs/developers/android_service.md index 0feaddf..5bbc475 100644 --- a/docs/developers/android_service.md +++ b/docs/developers/android_service.md @@ -39,19 +39,52 @@ Please note: when they have a parameter 'weight' the result is not an absolute n This parameters are needed to tell BRouter what to do. +### using profiles + +For calulation BRouter uses a set of rules defined in a profile. See description of profile [rules](https://github.com/abrensch/brouter/blob/master/docs/developers/profile_developers_guide.md). + +Here we talk about how we let BRouter know witch profile to use. +There are three ways: + +1. use the parameter 'v' and 'fast' +``` + "v"-->[motorcar|bicycle|foot] + "fast"-->[0|1] + This enables BRouter to look into the file serviceconfig.dat. + In there BRouter find the profile associated for e.g bicyle_fast trekking + This could be changed by the user calling the BRouter app server-mode. +``` + +2. use the profile parameter +``` + profile=trekking + It needs an available file in the BRouter profile folder e.g. trekking.brf +``` + +3. use a remote profile +``` + remoteProfile=a long string with routing rules + This is saved in BRouter profile folder temporary with the file name 'remote.brf' +``` + + ### profile parameter Profile parameters affect the result of a profile. +The variables inside a profile predefine a value e.g. avoidsteps=1 +A parameter call gives the chance to change this start value without changing the profile e.g. avoidsteps=0 For the app it is a list of params concatenated by '&'. E.g. extraParams=avoidferry=1&avoidsteps=0 The server calls profile params by a prefix 'profile:'. E.g. ...&profile:avoidferry=1&profile:avoidsteps=0 +By using this parameter logic, there is no need to edit a profile before sending. + ### using profile parameter inside an app To be flexible it is possible to send a profile to BRouter - server or app. Another variant is to send parameters for an existing profile that are different from the original profile. -With the version 1.7.1 it is possible to collect parameters from the profile. +With the version 1.7.1 it is possible to collect parameters from the profile. The variable parameters are defined like this ``` assign avoid_path = false # %avoid_path% | Set to true to avoid pathes | boolean @@ -61,9 +94,9 @@ Now you could do that with an calling app. What to do to get it work? -- First copy the [RoutingParam](brouter-routing-app/src/main/java/btools/routingapp/RoutingParam.java) class to your source - use the same name and package name. +- First copy the [RoutingParam](brouter-routing-app/src/main/java/btools/routingapp/RoutingParam.java) class to your source - use the same name and package name. - Second analyze the profile for which you need the parameter. - This [BRouter routine](https://github.com/abrensch/brouter/blob/086503e529da7c044cc0f88f86c394fdb574d6cf/brouter-routing-app/src/main/java/btools/routingapp/RoutingParameterDialog.java#L103) can do that, just copy it to your source to use it in your app. + This [BRouter routine](https://github.com/abrensch/brouter/blob/086503e529da7c044cc0f88f86c394fdb574d6cf/brouter-routing-app/src/main/java/btools/routingapp/RoutingParameterDialog.java#L103) can do that, just copy it to your source to use it in your app. It builds a List you could send to BRouter app. - You find the call of BRouter app in comment at [RoutingParameterDialog](https://github.com/abrensch/brouter/blob/086503e529da7c044cc0f88f86c394fdb574d6cf/brouter-routing-app/src/main/java/btools/routingapp/RoutingParameterDialog.java#L33) @@ -79,7 +112,7 @@ intent.putExtra("runsilent", true); startActivity(intent); ``` -This suppress the first question after installation for the BRouter path, generates the BRouter folders in main space and starts the download dialog. +This suppress the first question after installation for the BRouter path, generates the BRouter folders in main space and starts the download dialog. ### silent app call @@ -92,7 +125,7 @@ intent.putExtra("runsilent", true); startActivity(intent); ``` -This suppress the first question after installation for the BRouter path, generates the BRouter folders in main space and starts the download dialog. +This suppress the first question after installation for the BRouter path, generates the BRouter folders in main space and starts the download dialog. ## other routing engine modes in app diff --git a/docs/developers/build_segments.md b/docs/developers/build_segments.md index 513fab5..79bd296 100644 --- a/docs/developers/build_segments.md +++ b/docs/developers/build_segments.md @@ -26,7 +26,7 @@ official BRouter segments files are the ones provided by [CGIAR](https://cgiarcsi.community/data/srtm-90m-digital-elevation-database-v4-1/). If you are working with rather small geographical extracts, you can download tiles manually using [this -interface](http://srtm.csi.cgiar.org/SELECTION/inputCoord.asp) (use the +interface](https://srtm.csi.cgiar.org/srtmdata/) (use the "ArcInfo ASCII" format), instead of having to ask for an access for bulk download of data. There is no need to unzip the downloaded files, the `process_pbf_planet.sh` script expects a folder with all the ZIP files inside @@ -48,3 +48,37 @@ and set the `PLANET_FILE` variable to point to it. _Note:_ It is possible that you encounter an error complaining about not being able to run `bash^M` on Linux/Mac OS. You can fix this one by running `sed -i -e 's/\r$//' process_pbf_planet.sh`. + + +## Run a generation for elevation data tiles + +To match the 5x5 OSM data grid (*.rd5) files from BRouter, there are elevation +data in a 5x5 degree format (*.bef). At the moment (end of 2023) the naming of +this elevation tiles follows the konvention used by srtm.csi.cgiar.org: srtm_x_y + +As the srtm files are only available between 60N and 60S the filenames above 60N +contains negative values. e.g. srtm_34_-1 as a tile above srtm_34_00. + +Please see OSM wiki for more info on [srtm](https://wiki.openstreetmap.org/wiki/SRTM). + +The converter generates bef tiles from `hgt` files, `zipped hgt` files and `zipped 'ESRI' asc` files. + +Converter call with arguments for a single tile generation: + +`ElevationRasterTileConverter [arc seconds (1 or 3,default=3)] [hgt-fallback-data-dir] +Samples: +$ ... ElevationRasterTileConverter srtm_34_-1 ./srtm/hgt3sec ./srtm/srtm3_bef +$ ... ElevationRasterTileConverter srtm_34_-1 ./srtm/hgt1sec ./srtm/srtm1_bef 1 +$ ... ElevationRasterTileConverter srtm_34_-1 ./srtm/hgt1sec ./srtm/srtm1_bef 1 ./srtm/hgt3sec +` +Arguments for multi file generation (world wide): + +`$ ... ElevationRasterTileConverter all ./srtm/hgt3sec ./srtm/srtm3_bef +$ ... ElevationRasterTileConverter all ./srtm/hgt1sec ./srtm/srtm1_bef 1 +$ ... ElevationRasterTileConverter all ./srtm/hgt1sec ./srtm/srtm1_bef 1 ./srtm/hgt3sec +` + +To use 1sec and 3sec bef tiles at rd5 generation time you need an extra parameter to the fallback folder. +E.g. +`$ ... PosUnifier nodes55 unodes55 bordernids.dat bordernodes.dat ../srtm/srtm1_bef ../srtm/srtm3_bef +` diff --git a/docs/developers/docker_guide.md b/docs/developers/docker_guide.md new file mode 100644 index 0000000..990d8e9 --- /dev/null +++ b/docs/developers/docker_guide.md @@ -0,0 +1,125 @@ +--- +parent: Developers +--- + +# Docker help + +In addition to the intro in readme.md about Docker, here are a few commands for daily work with the system. + +Build the Docker with a version based name +``` +$ docker build -t brouter-1.7.2 . +``` + +Start Docker with name additional to the Docker image name. +Please note: +The path for segments are on a Windows system. +Here the port used in server.sh is published. +``` +$ docker run --rm -v "I:/Data/test/segment4":/segments4 --publish 17777:17777 --name brouter-1.7.2 brouter-1.7.2 +``` + +and with a mount for profiles as well +``` +$ docker run --rm -v "I:/Data/test/segment4":/segments4 -v "I:/Data/test/profiles2":/profiles2 --name brouter-1.7.2 brouter-1.7.2 +``` + +Show the running Docker processes +``` +$ docker ps + +output: +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +b23518e8791d brouter-1.7.2 "/bin/sh -c /bin/ser…" 5 minutes ago Up 5 minutes 0.0.0.0:17777->17777/tcp brouter-1.7.2 +``` + +Fire some curl or wget commands to test if is realy useful running. + +Stop a running Docker image - please note, this only works when starts docker image with name, see above +``` +$ docker stop brouter-1.7.2 +``` + +Docker available images + +``` +$ docker images + +output: +REPOSITORY TAG IMAGE ID CREATED SIZE +brouter-1.7.2 latest e39703dec2fa 2 hours ago 410MB +brouter latest 728f122c7388 3 hours ago 410MB +``` + +Control +## Docker with docker-compose + +Use a git clone to build a local folder with last version. +Make a Docker container with version number inside your repository folder. +``` +$ docker build -t brouter:1.7.2 . + +$ docker images + +REPOSITORY TAG IMAGE ID CREATED SIZE +brouter-1.7.2 latest e39703dec2fa 3 hours ago 410MB +brouter 1.7.2 e39703dec2fa 3 hours ago 410MB +``` + +Start a container with composer +This needs a docker config file docker-compose.yml +Something like this: +``` +version: '2' +services: + brouter: + image: brouter:1.7.2 + restart: unless-stopped + ports: + - 17777:17777 + volumes: + - type: bind + source: "I:/Data/test/segment4" + target: /segments4 +# - type: bind +# source: "I:/Data/test/profiles2" +# target: /profiles2 +``` + +Start it +``` +$ docker-compose up -d +``` + +Have a look what is running +``` +$ docker-compose ps +or +$ docker-compose ls +or +$ docker ps +``` + + +Now update your repository (git pull) and build your Docker container with the new version tag +``` +$ docker build -t brouter:1.7.3 . + +$ docker images + +REPOSITORY TAG IMAGE ID CREATED SIZE +brouter 1.7.3 5edc998cb5ae 3 hours ago 410MB +brouter-1.7.2 latest e39703dec2fa 6 hours ago 410MB +``` + +Replace the version in Docker config file docker-compose.yml +``` + image: brouter:1.7.3 +``` + +Stop old running container and start the new one +``` +$ docker-compose down + +$ docker-compose up -d +``` diff --git a/docs/developers/environmental_considerations_and_pseudo_tags.md b/docs/developers/environmental_considerations_and_pseudo_tags.md new file mode 100644 index 0000000..aa4ef90 --- /dev/null +++ b/docs/developers/environmental_considerations_and_pseudo_tags.md @@ -0,0 +1,108 @@ +--- +parent: Developers +--- + +# Environmental considerations + +Environmental considerations (penalties for traffic, noise, town, no river, no forest) are possible due to the creation of pseudo tags during processing OSM data by spatial SQL queries in [brouter.sql](https://github.com/abrensch/brouter/blob/master/misc/scripts/mapcreation/brouter.sql). During this processing, roads are extended by a 32 m buffer creating 64 m wide lines. Then it is calculated what percentage of such line is at a specific distance to a noise source or within a forest, for example. The percentage is converted to a factor and the factor is assigned to a class. Ways that pass through different environments and are represented by a single OSM way can be problematic because the class is always based on the average environment along an entire OSM way. For traffic, calculations are on another level of complexity. + +### noise_class + +For proximity of noisy roads (secondary and higher). The noise factor represents the proportion of a road's buffer area that lies within the 64-meter buffer of noisy roads. This proportion is reduced: + +- for motorways and trunk roads with max speed < 105 by 1.5 +- for primary roads 2 times +- 3 times if maxspeed is 75 - 105 for primary and secondary +- other secondary roads 5 times + +`noise_class` is roughly proportional to the noise factor: + +| `noise_factor` | `noise_class` | +| -------------- | ------------- | +| < 0.1 | 1 | +| < 0.25 | 2 | +| < 0.4 | 3 | +| < 0.55 | 4 | +| < 0.8 | 5 | +| ELSE | 6 | + +To be classified as noise class 6, a way must be less than 13 m on average from the middle of the carriageway of a motorway with a maximum speed exceeding 105. For a class 5, the distance must be up to 35 meters. (1 - noise_factor) \* 64 m for a given class determines the distance + +| highway | maxspeed | max `noise_class` | +| -------------- | -------- | ----------------- | +| motorway,trunk | > 105 | 6 | +| motorway,trunk | 105 | 5 | +| motorway,trunk | 75 | 5 | +| primary | > 105 | 4 | +| primary | 105 | 4 | +| primary | 75 | 3 | +| secondary | > 105 | 3 | +| secondary | 105 | 3 | +| secondary | 75 | 2 | + +### river_class + +OSM data recognized as river: + +- waterway: river, canal +- natural: water (except wastewater) + +Waterways have 32 m wide buffers. Water areas have 77 m wide buffers. + +| `river_see` | `river_class` | +| ----------- | ------------- | +| < 0.1 | 1 | +| < 0.3 | 2 | +| < 0.5 | 3 | +| < 0.8 | 4 | +| < 0.9 | 5 | +| ELSE | 6 | + +### forest_class + +OSM data recognized as forest: + +- landuse: forest, allotments, flowerbed, orchard, vineyard, recreation_ground, village_green +- leisure: garden, park, nature_reserve + +No forest buffers are used. + +Imagine you trace the way with a pencil drawing lines 62 meters wide. Then estimated_forest_class=6 corresponds to the case that at least 98% of the line is in the woodland. This number is called a green factor. + +| `green_factor` | `forest_class` | +| -------------- | -------------- | +| < 0.1 | NULL | +| < 0.2 | 1 | +| < 0.4 | 2 | +| < 0.6 | 3 | +| < 0.8 | 4 | +| < 0.98 | 5 | +| ELSE | 6 | + +### town_class + +Town_class is determined by population data from OSM. + +| population | `town_class` | +| ------------------ | ------------ | +| < 80 k people | 1 | +| < 150 k people | 2 | +| < 400 k people | 3 | +| < 1 million people | 4 | +| < 2 million people | 5 | +| > 2 million people | 6 | + +### traffic_class + +(modified copy from the sql file). +OSM data used to estimate the traffic: + +- population of towns (+ distance from position to the towns) +- size of industrial areas (landuse=industrial) and distance to them. Not considered: solar & wind farms. +- airports international +- motorway and trunk road density - traffic on motorways decreases traffic on primary/secondary/tertiary. Calculated on a grid (100 km^2) +- density of highways (tertiary and higher) calculated on a grid (100 km^2). Traffic decreases when more such roads are available. Exceptions: near junctions between motorways and other roads the traffic increases on these roads. +- mountain-ranges calculated as density of peaks > 400 m traffic is generally on highways in such regions higher as only generated by the local population or industrial areas +- calculate traffic from the population (for each segment of type primary secondary tertiary) +- SUM of (population of each town < 100 km) / ( town-radius + 2500 + dist(segment-position to the town) \*\* 2 ) +- town-radius is calculated as sqrt(population) diff --git a/docs/developers/profile_developers_guide.md b/docs/developers/profile_developers_guide.md index 200732c..cbc6fc3 100644 --- a/docs/developers/profile_developers_guide.md +++ b/docs/developers/profile_developers_guide.md @@ -72,12 +72,16 @@ Some variable names are pre-defined and accessed by the routing engine: - for the global section these are: - - 7 elevation configuration parameters: + - 11 elevation configuration parameters: - `downhillcost` - `downhillcutoff` + - `downhillmaxslope` + - `downhillmaxslopecost` - `uphillcost` - `uphillcutoff` + - `uphillmaxslope` + - `uphillmaxslopecost` - `elevationpenaltybuffer` - `elevationmaxbuffer` - `elevationbufferreduce` @@ -172,6 +176,7 @@ All expressions have one of the following basic forms: - `and ` - `xor ` - `multiply ` + - `divide ` - `add ` - `sub ` - `max ` @@ -276,33 +281,37 @@ it climbed only 10 m on those 500 m, all 10 m would be *swallowed* by cutoff, together with up to 5 m from the buffer, if there were any. When elevation does not fit the buffer of size `elevationmaxbuffer`, it is -converted by up/downhillcost ratio to Elevationcost portion of Equivalentlength. -Up/downhillcostfactors are used, if defined, otherwise costfactor is used. +converted by `up/downhill[maxslope]cost` ratio to Elevationcost portion of Equivalentlength. +`up/downhillcostfactors` are used, if defined, otherwise `costfactor` is used. - `elevationpenaltybuffer` - default 5(m). The variable value is used for 2 purposes - with `buffer content > elevationpenaltybuffer`, it starts partially convert - the buffered elevation to ElevationCost by Up/downhillcost + the buffered elevation to ElevationCost by `up/downhillcost` - with `elevation taken = MIN (buffer content - elevationpenaltybuffer, WayLength[km] * elevationbufferreduce*10` - Up/downhillcost factor takes place instead of costfactor at the percentage + The `up/downhillcostfactor` takes place instead of `costfactor` at the percentage of how much is `WayLength[km] * elevationbufferreduce*10` is saturated by the buffer content above elevationpenaltybuffer. - `elevationmaxbuffer` - default 10(m) is the size of the buffer, above which all elevation is converted to - Elevationcost by Up/Downhillcost ratio, and - if defined - - Up/downhillcostfactor fully replaces costfactor in way cost calculation. + Elevationcost by `up/downhill[maxslope]cost` ratio, and - if defined - + `up/downhillcostfactor` fully replaces `costfactor` in way cost calculation. - `elevationbufferreduce` - default 0(slope%) is rate of conversion of the buffer content above elevationpenaltybuffer to ElevationCost. For a way of length L, the amount of converted elevation is L[km] * elevationbufferreduce[%] * 10. The elevation to Elevationcost - conversion ratio is given by Up/downhillcost. + conversion ratio is given by `up/downhill[maxslope]cost`. + +Whether `up/downhillmaxslope` or `up/downhillmaxslopecost` is used as conversion +ratio depends on whether the elevation was accumulated below or above the slope +threshold values defined in `up/downhillmaxslope`. Example: Let's examine steady slopes with `elevationmaxbuffer=10`, `elevationpenaltybuffer=5`, `elevationbufferreduce=0.5`, `cutoffs=1.5`, @@ -313,7 +322,7 @@ All slopes within 0 .. 1.5% are swallowed by the cutoff. - For slope 1.75%, there will remain 0.25%. That saturates the elevationbufferreduce 0.5% by 50%. That gives Way cost to - be calculated 50% from costfactor and 50% from Up/downhillcostfactor. + be calculated 50% from `costfactor` and 50% from `up/downhillcostfactor`. Additionally, 0.25% gives 2.5m per 1km, converted to 2.5*60 = 150m of Elevationcost. diff --git a/docs/revisions.md b/docs/revisions.md index 3c6e3cf..e58e2a9 100644 --- a/docs/revisions.md +++ b/docs/revisions.md @@ -2,7 +2,76 @@ (ZIP-Archives including APK, readme + profiles) -### [brouter-1.7.1.zip](../brouter_bin/brouter-1.7.1.zip) (current revision, 12.07.2023) +### [brouter-1.7.7.zip](../brouter_bin/brouter-1.7.7.zip) (current revision, 23.07.2024) + +- new Android API 34 + + +### [brouter-1.7.6.zip](../brouter_bin/brouter-1.7.6.zip) (20.06.2024) + +Android + +- bug fix for car profiles + +[Solved issues](https://github.com/abrensch/brouter/issues?q=is%3Aissue+milestone%3A%22Version+1.7.6%22+is%3Aclosed) + + +### [brouter-1.7.5.zip](../brouter_bin/brouter-1.7.5.zip) (05.06.2024) + +Android + +- bug fix for repeat last route + +Library + +- ISO8601 compatible timestamps in log +- Update MIME type for GeoJSON responses + +Profiles + +- update gravel profile + + +- Minor bug fixes + +[Solved issues](https://github.com/abrensch/brouter/issues?q=is%3Aissue+milestone%3A%22Version+1.7.5%22+is%3Aclosed) + + +### [brouter-1.7.4.zip](../brouter_bin/brouter-1.7.4.zip) (09.04.2024) + +Library + +- new "DIVIDE" command for profile calculation +- new "maxslope" and "maxslopecost" parameters +- new parameter collector +- new output logic +- rework on voice hints and roundabouts +- enabled elevation raster files with 1 asec + + +Android + +- BRouter translations +- fallback on certificate problems + + +- Minor bug fixes + +[Solved issues](https://github.com/abrensch/brouter/issues?q=is%3Aissue+milestone%3A%22Version+1.7.4%22+is%3Aclosed) + + +### [brouter-1.7.3.zip](../brouter_bin/brouter-1.7.3.zip) (19.08.2023) + +- Minor bug fixes + + +### [brouter-1.7.2.zip](../brouter_bin/brouter-1.7.2.zip) (19.07.2023) + +- Re-index Json output + Note: This is different to releases 1.7.0 and 1.7.1. It is recommended to use the current version to avoid breaks in voice hint output for GeoJson. + + +### [brouter-1.7.1.zip](../brouter_bin/brouter-1.7.1.zip) (12.07.2023) Android diff --git a/docs/users/osmand.md b/docs/users/osmand.md index db751f7..a1c2f03 100644 --- a/docs/users/osmand.md +++ b/docs/users/osmand.md @@ -67,3 +67,29 @@ application profiles"/> The BRouter app should be launched before OsmAnd for this specific entry to appear in OsmAnd. Therefore, if you cannot find "BRouter (offline)" navigation option, you should force quit OsmAnd and restart it. + + +## OsmAnd version 4.7.1 + +From version 4.7.1 upwards Osmand supports the profile parameter for mapping: +Since Osmand version 3, many profiles can be defined in Osmand and the user can easily switch between these profiles. +This allow now when using the service-interface to address different brouter-profiles in a more flexible and better comprehensive way. + +- If in Osmand a profile has "BRouter" defined as navigation service +- AND the profile-name looks like "Brouter[mysting] + +==> then the profile "mystring" will be used in the Brouter-app! +(this new mapping replaces in that case the basic mapping defined above and based on the file "serviceconfig.dat) + +### Examples: Osmand-profile name Brouter-app +``` +[Brouter[trekking] "trekking" profile will be used (file trekking.brf) +[Brouter[racebike] "racebike" profile will be used (file racebike.brf) +.... +``` +Remark: +Currently Osmand do not check the defined name (case sensitiv) for the Brouter-profile (mystring). +If no profile is found, the routing will fail with "Could not calculate route.."! + +BRouter configuration in OsmAnd
+application profiles diff --git a/docs/users/osmand/brouter-osmand-4.7.1.png b/docs/users/osmand/brouter-osmand-4.7.1.png new file mode 100644 index 0000000..42dfe80 Binary files /dev/null and b/docs/users/osmand/brouter-osmand-4.7.1.png differ diff --git a/gradle.properties b/gradle.properties index 147c25a..b94ae53 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,5 +17,5 @@ org.gradle.jvmargs=-Xmx1536m android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=false - - +android.nonTransitiveRClass=false +android.nonFinalResIds=false diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cdbfc5f..11a5bd0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip diff --git a/misc/profiles2/fastbike.brf b/misc/profiles2/fastbike.brf index 1c48994..d51a880 100644 --- a/misc/profiles2/fastbike.brf +++ b/misc/profiles2/fastbike.brf @@ -22,14 +22,10 @@ assign validForBikes = true assign allow_steps = true # %allow_steps% | Set to false to disallow steps | boolean assign allow_ferries = true # %allow_ferries% | set to false to disallow ferries | boolean assign allow_motorways = false # %allow_motorways% | Set to true to allow motorways (useful in Asia / Oceania for example) | boolean -assign ignore_cycleroutes = false # %ignore_cycleroutes% | Set to true for better elevation results | boolean -assign stick_to_cycleroutes = false # %stick_to_cycleroutes% | Set to true to just follow cycleroutes | boolean -assign avoid_unsafe = false # %avoid_unsafe% | Set to true to avoid standard highways | boolean -assign avoid_path = false # %avoid_path% | Set to true to avoid pathes | boolean assign consider_traffic = false # %consider_traffic% | Activate to avoid traffic | boolean assign consider_noise = false # %consider_noise% | Activate to prefer a low-noise route | boolean -assign consider_river = false # %consider_river% | Activate to prefer a route along rivers or sees | boolean +assign consider_river = false # %consider_river% | Activate to prefer a route along rivers, lakes, etc. | boolean assign consider_forest = false # %consider_forest% | Activate to prefer a route in forest or parks | boolean assign consider_town = false # %consider_town% | Activate to bypass cities / big towns as far as possible | boolean @@ -159,6 +155,8 @@ assign hascycleway = not and ( or cycleway= cycleway=no|none ) and ( or cycleway:left= cycleway:left=no ) ( or cycleway:right= cycleway:right=no ) assign trafficpenalty0 = + if consider_traffic then + ( if highway=primary|primary_link then ( if estimated_traffic_class=4 then 0.2 @@ -183,6 +181,8 @@ assign trafficpenalty0 = else 0 ) else 0 + ) + else 0 assign trafficpenalty = if consider_traffic then @@ -255,14 +255,13 @@ assign costfactor switch or highway=tertiary highway=tertiary_link 1.0 switch highway=unclassified switch isunpaved 10 1.1 switch highway=pedestrian 10 - switch highway=steps 1000 - switch route=ferry 5.67 + switch highway=steps switch allow_steps 1000 10000 + switch route=ferry switch allow_ferries 5.67 10000 switch highway=bridleway 5 switch highway=cycleway 1.3 switch isresidentialorliving switch isunpaved 10 1.2 switch highway=service switch isunpaved 10 1.2 - switch highway=path switch avoid_path 2.1 1.1 - switch or highway=track or highway=road highway=footway + switch or highway=track or highway=road or highway=path highway=footway switch tracktype=grade1 switch isunpaved 3 1.2 switch tracktype=grade2 switch isunpaved 10 3 switch tracktype=grade3 10.0 diff --git a/misc/profiles2/gravel.brf b/misc/profiles2/gravel.brf new file mode 100644 index 0000000..3468ae0 --- /dev/null +++ b/misc/profiles2/gravel.brf @@ -0,0 +1,340 @@ +# "gravel.brf" -- Version 28.04.2024 +# This customizeable profile, developed by quaelnix, is designed for gravel cyclists who want to avoid traffic as much +# as possible, but still get to their destination efficiently - taking into account the capabilities of a gravel bike. + +---context:global + +assign turnInstructionRoundabouts true # %turnInstructionRoundabouts% | Special turn-by-turn directions for roundabouts | boolean +assign considerTurnRestrictions true +assign turnInstructionMode 1 # %turnInstructionMode% | Mode for the generated turn-by-turn directions | [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=oruxmaps-style] + +#assign processUnusedTags true +assign pass1coefficient 4 +assign validForBikes true + +# +++ Kinematic model parameters (travel time computation) +assign totalMass = 90 # %totalMass% | Mass (kg) of the bike + biker | number +assign maxSpeed = 35 # %maxSpeed% | Absolute maximum speed (km/h) | number +assign S_C_x = 0.300 # %S_C_x% | Drag coefficient times reference area (m^2) times half air density (kg/m^3) +assign C_r = 0.005 # %C_r% | Rolling resistance coefficient (dimensionless) +assign bikerPower = 150 # %bikerPower% | Average power (W) provided by the biker | number + +assign consider_traffic_estimate false # %consider_traffic_estimate% | Enable to consider traffic estimates | boolean +assign assume_wet_conditions false # %assume_wet_conditions% | Enable to assume wet conditions | boolean +assign prefer_unpaved_paths false # %prefer_unpaved_paths% | Enable to prefer unpaved paths | boolean +assign avoid_steep_inclines false # %avoid_steep_inclines% | Enable to avoid steep inclines | boolean +assign prefer_cycle_routes false # %prefer_cycle_routes% | Enable to prefer cycle routes | boolean +assign consider_elevation false # %consider_elevation% | Enable to consider elevation | boolean +assign prefer_forests false # %prefer_forests% | Enable to prefer forest areas | boolean +assign prefer_rivers false # %prefer_rivers% | Enable to prefer river valleys | boolean +assign avoid_towns false # %avoid_towns% | Enable to avoid urban areas | boolean +assign avoid_noise false # %avoid_noise% | Enable to avoid noisy areas | boolean + +---context:way + +assign downhillcost switch consider_elevation 40 switch avoid_steep_inclines 80 0.0 +assign downhillcutoff switch and not consider_elevation avoid_steep_inclines 8 1.5 +assign uphillcost switch consider_elevation 80 switch avoid_steep_inclines 160 0.0 +assign uphillcutoff switch and not consider_elevation avoid_steep_inclines 8 1.5 + +assign has_decent_surface surface=asphalt|concrete|paved|paving_stones|fine_gravel|compacted +assign bad_when_steep ( and highway=track|path not ( or tracktype=grade1|grade2 has_decent_surface ) ) + +assign downhillcost switch bad_when_steep ( max 160 downhillcost ) downhillcost +assign downhillcutoff switch bad_when_steep ( min 1.5 downhillcutoff ) downhillcutoff +assign uphillcost switch bad_when_steep ( max 80 uphillcost ) uphillcost +assign uphillcutoff switch bad_when_steep ( min 1.5 uphillcutoff ) uphillcutoff + +assign any_cycleway or cycleway=track|lane|shared_lane|shared + or and cycleway:right=track|lane|shared_lane reversedirection= + and cycleway:left=track|lane|shared_lane reversedirection=yes + +assign any_cycleroute or route_bicycle_icn=yes or route_bicycle_ncn=yes or route_bicycle_rcn=yes route_bicycle_lcn=yes + +assign turncost switch junction=roundabout 15 65 + +assign is_main_road highway=primary|primary_link|secondary|secondary_link|tertiary|tertiary_link +assign initialclassifier switch route=ferry 4 switch is_main_road 3 switch footway=crossing 2 1 +assign initialcost switch route=ferry 20000 switch is_main_road 400 switch assume_wet_conditions 100 20 + +assign nobikeaccess switch bicycle= switch vehicle= access=no|private vehicle=no|private bicycle=no|private|dismount +assign nofootaccess switch foot= ( and access=no|private ( not bicycle=dismount ) ) foot=no|private + +assign badoneway + switch not reversedirection=yes oneway=-1 + switch oneway=yes|reversible not oneway:bicycle=no + or junction=roundabout oneway:bicycle=yes + +assign onewaypenalty + switch or ( not badoneway ) ( or cycleway=opposite|opposite_lane|opposite_track oneway:bicycle=no ) 1 + switch not junction=roundabout 5 + 20 + +assign smoothnesspenalty + switch smoothness=excellent switch not assume_wet_conditions 1.0 1.0 + switch smoothness=good switch not assume_wet_conditions 1.1 1.2 + switch smoothness=intermediate switch not assume_wet_conditions 1.3 1.3 + switch smoothness=bad switch not assume_wet_conditions 1.5 2.5 + switch smoothness=very_bad switch not assume_wet_conditions 3.0 4.0 + switch smoothness=horrible switch not assume_wet_conditions 8.0 9.8 + switch smoothness=very_horrible switch not assume_wet_conditions 9.4 100 + switch smoothness=impassable switch not assume_wet_conditions 100 200 + # estimate smoothness off surface + switch not cycleway:surface= + switch cycleway:surface=asphalt switch not assume_wet_conditions 1.1 1.1 + switch cycleway:surface=fine_gravel|compacted switch not assume_wet_conditions 1.2 1.2 + switch cycleway:surface=concrete|paving_stones switch not assume_wet_conditions 1.3 1.3 + switch cycleway:surface=paved switch not assume_wet_conditions 1.4 1.4 + switch cycleway:surface=gravel switch not assume_wet_conditions 1.5 1.5 + switch cycleway:surface=unpaved switch not assume_wet_conditions 1.6 2.0 + switch cycleway:surface=cobblestone|sett switch not assume_wet_conditions 1.7 2.4 + switch not assume_wet_conditions 1.6 1.8 + switch surface=asphalt switch not assume_wet_conditions 1.1 1.1 + switch concrete=plates|lanes switch not assume_wet_conditions 1.2 1.2 + switch surface=fine_gravel|compacted switch not assume_wet_conditions 1.2 1.2 + switch surface=concrete|paving_stones switch not assume_wet_conditions 1.3 1.3 + switch surface=paved switch not assume_wet_conditions 1.4 1.4 + switch surface=gravel switch not assume_wet_conditions 1.5 1.5 + switch surface=wood|metal switch not assume_wet_conditions 1.6 2.0 + switch surface=cobblestone|sett switch not assume_wet_conditions 1.7 2.5 + switch surface=grass_paver switch not assume_wet_conditions 1.8 2.2 + switch surface=pebblestone switch not assume_wet_conditions 1.9 2.8 + switch highway=track + # estimate smoothness off tracktype + switch tracktype=grade1 switch not assume_wet_conditions 1.2 1.2 + switch tracktype=grade2 switch not assume_wet_conditions 1.4 1.5 + switch tracktype=grade3 switch not assume_wet_conditions 1.6 2.6 + switch tracktype=grade4 switch not assume_wet_conditions 2.6 3.8 + switch tracktype=grade5 switch not assume_wet_conditions 2.8 4.4 + switch not assume_wet_conditions 3.6 6.2 # assume the worst + # estimate smoothness off highway type + switch highway=primary|primary_link|secondary|secondary_link|tertiary|tertiary_link 1.0 + switch highway=service|unclassified|living_street|residential 1.1 + switch highway=cycleway 1.2 + # handle non-specific tags last + switch surface=unpaved switch not assume_wet_conditions 1.6 3.0 + switch surface=ground|grass|dirt|earth|mud|clay|sand switch not assume_wet_conditions 2.6 4.0 + # default smoothness + switch not assume_wet_conditions 1.6 3.2 + +assign footwaypenalty + switch bicycle=yes|designated + switch segregated=yes 1.8 + 2.6 + switch not footway=sidewalk 3.2 + 4.9 + +assign cyclewaypenalty + switch not foot=yes|designated 1.0 + switch segregated=yes 1.2 + 2.4 + +assign pathpenalty + switch bicycle=yes|designated + switch not foot=yes|designated 1.1 + switch segregated=yes 1.8 + 2.4 + switch foot=designated 4.9 + # neither designated foot- nor cycle path + switch lesser smoothnesspenalty 1.6 1.1 + switch not and surface= smoothness= 3.0 + 4.9 + +assign stepspenalty + switch ramp:stroller=yes 75 + switch ramp:bicycle=yes 150 + switch surface=paving_stones 180 + switch not assume_wet_conditions 1000 + 1500 + +assign tracktypepenalty + switch tracktype=grade1 switch not prefer_unpaved_paths 1.0 1.1 + switch tracktype=grade2 switch not prefer_unpaved_paths 1.1 1.0 + switch tracktype=grade3 switch not assume_wet_conditions 1.2 3.0 + switch tracktype=grade4 switch not assume_wet_conditions 1.5 4.0 + switch tracktype=grade5 switch not assume_wet_conditions 1.8 5.0 + switch highway=track switch not assume_wet_conditions 1.3 3.5 + 1.0 + +assign is_paved + and not surface=ground|grass|dirt|earth|mud|clay|sand|pebblestone|gravel|fine_gravel|compacted + or highway=primary|primary_link|secondary|secondary_link|tertiary|tertiary_link + or surface=asphalt|concrete|paved|paving_stones|sett|cobblestone|metal + or highway=road|unclassified|residential|living_street|service + or smoothness=excellent|good + tracktype=grade1 + +assign initialcost + multiply initialcost ( switch and prefer_unpaved_paths is_paved 2 1 ) + +assign smoothnesspenalty + multiply smoothnesspenalty ( switch and prefer_unpaved_paths is_paved 2 1 ) + +assign smoothnesspenalty + max 1 multiply smoothnesspenalty ( switch and prefer_unpaved_paths ( lesser smoothnesspenalty 1.6 ) 0.84 1 ) + +assign hikingpenalty + if sac_scale= then 1.0 else if sac_scale=hiking then 2.5 else 10000 + +assign mtbpenalty + if mtb:scale= then 1.0 else if mtb:scale=0- then 1.2 else + if mtb:scale=0 then 1.5 else if mtb:scale=0+ then 3.0 else 9.0 + +assign notcycleroutepenalty switch any_cycleroute 1.0 switch not prefer_cycle_routes 1.03 1.85 +assign notcyclewaypenalty switch bicycle_road=yes 1.0 switch any_cycleway 1.02 1.06 + +assign maxspeedpenalty + switch maxspeed=50 1.0 + switch maxspeed=60 1.1 + switch maxspeed=70 1.3 + switch maxspeed=80 1.4 + switch maxspeed=90 1.6 + switch maxspeed=100 1.8 switch highway=primary|secondary 1.8 1.0 + +assign trafficpenalty + switch consider_traffic_estimate + switch estimated_traffic_class= 1.0 + switch estimated_traffic_class=1|2 1.1 + switch estimated_traffic_class=3 1.3 + switch estimated_traffic_class=4 2.0 + switch estimated_traffic_class=5 2.5 + switch estimated_traffic_class=6|7 3.0 1 1 + +assign noisepenalty + switch avoid_noise + switch estimated_noise_class= 1.00 + switch estimated_noise_class=1 1.10 + switch estimated_noise_class=2 1.15 + switch estimated_noise_class=3 1.20 + switch estimated_noise_class=4 1.60 + switch estimated_noise_class=5 1.80 + switch estimated_noise_class=6 2.20 1 1 + +assign noriverpenalty + switch prefer_rivers + switch estimated_river_class= 3.15 + switch estimated_river_class=1 3.00 + switch estimated_river_class=2 1.50 + switch estimated_river_class=3 1.15 + switch estimated_river_class=4 1.10 + switch estimated_river_class=5 1.05 + switch estimated_river_class=6 1.00 1 1 + +assign noforestpenalty + switch prefer_forests + switch estimated_forest_class= 2.5 + switch estimated_forest_class=1 2.2 + switch estimated_forest_class=2 2.1 + switch estimated_forest_class=3 2.0 + switch estimated_forest_class=4 1.8 + switch estimated_forest_class=5 1.4 + switch estimated_forest_class=6 1.0 1 1 + +assign townpenalty + switch avoid_towns + switch estimated_town_class= 1.0 + switch estimated_town_class=1 1.2 + switch estimated_town_class=2 1.4 + switch estimated_town_class=3 1.6 + switch estimated_town_class=4 1.8 + switch estimated_town_class=5 2.0 + switch estimated_town_class=6 2.2 1 1 + +assign costfactor + multiply notcycleroutepenalty multiply noriverpenalty + multiply notcyclewaypenalty multiply maxspeedpenalty + multiply smoothnesspenalty multiply onewaypenalty + multiply noforestpenalty multiply noisepenalty + multiply trafficpenalty multiply townpenalty + multiply hikingpenalty multiply mtbpenalty + multiply switch bicycle=use_sidepath 1.6 1 + multiply switch nobikeaccess 6 1 + multiply tracktypepenalty + switch or highway=motorway|motorway_link motorroad=yes 10000 + switch and nobikeaccess nofootaccess 10000 + switch highway= switch not route=ferry 10000 2 + switch highway=trunk|trunk_link switch any_cycleroute 20 80 + switch highway=bridleway|raceway switch not bicycle=yes 20 5 + switch highway=pedestrian switch not bicycle=yes 8 3 + switch highway=primary|primary_link 4.8 + switch highway=secondary|secondary_link 4.2 + switch highway=tertiary|tertiary_link 3.6 + switch highway=corridor 3.2 + switch highway=road 2.8 + switch highway=unclassified switch not any_cycleroute 2.6 1.4 + switch highway=living_street 2.5 + switch highway=service switch service= 2.4 3.0 + switch highway=residential switch not maxspeed=30 2.2 1.8 + switch highway=track 1.0 + switch highway=path pathpenalty + switch highway=steps stepspenalty + switch highway=footway footwaypenalty + switch highway=cycleway cyclewaypenalty + 10000 + +# way priorities used for voice hint generation + +assign priorityclassifier = + if ( highway=motorway ) then 30 + else if ( highway=motorway_link ) then 29 + else if ( highway=trunk ) then 28 + else if ( highway=trunk_link ) then 27 + else if ( highway=primary ) then 23 + else if ( highway=primary_link ) then 23 + else if ( highway=secondary ) then 22 + else if ( highway=secondary_link ) then 22 + else if ( highway=tertiary ) then 21 + else if ( highway=tertiary_link ) then 21 + else if ( highway=residential|living_street ) then 15 + else if ( highway=unclassified ) then 8 + else if ( highway=service ) then 8 + else if ( highway=cycleway ) then 8 + else if ( bicycle=designated ) then 8 + else if ( highway=track|road|path ) + then if or surface=asphalt|paved|concrete|wood|metal tracktype=grade1 then 8 else 6 + else if ( highway=steps ) then 2 + else if ( highway=pedestrian ) then 2 + else 0 + +# some more classifying bits used for voice hint generation... + +assign isbadoneway = not equal onewaypenalty 0 +assign isgoodoneway = if reversedirection=yes then oneway=-1 + else if oneway= then junction=roundabout else oneway=yes|true|1 +assign isroundabout = junction=roundabout +assign islinktype = highway=motorway_link|trunk_link|primary_link|secondary_link|tertiary_link +assign isgoodforcars = if greater priorityclassifier 6 then true + else if highway=residential|living_street|service then true + else if ( and highway=track tracktype=grade1 ) then true + else false + +# ... encoded into a bitmask + +assign classifiermask +# add isbadoneway # no voice hint if 1 of the 2 possibilities is badoneway + add multiply isgoodoneway 2 + add multiply isroundabout 4 + add multiply islinktype 8 + multiply isgoodforcars 16 + +---context:node + +assign nobikeaccess switch bicycle= switch vehicle= access=no|private vehicle=no|private bicycle=no|private|dismount +assign nofootaccess switch foot= ( and access=no|private ( not bicycle=dismount ) ) foot=no|private + +assign barrierpenalty + switch barrier= 0 + switch barrier=block|bollard 25 + switch barrier=gate|swing_gate 50 + switch barrier=cycle_barrier 87 + 139 + +assign initialcost + add barrierpenalty + switch and nobikeaccess nofootaccess 1000000 + switch railway=level_crossing 300 + switch nobikeaccess 200 + switch railway=crossing 35 + switch not traffic_calming= 25 + switch highway=traffic_signals|crossing 20 + 0 diff --git a/misc/profiles2/hiking-mountain.brf b/misc/profiles2/hiking-mountain.brf index fc61759..688da66 100644 --- a/misc/profiles2/hiking-mountain.brf +++ b/misc/profiles2/hiking-mountain.brf @@ -12,10 +12,9 @@ assign consider_elevation = false # %consider_elevation% | Activate to prefer a route with few elevation meters | boolean assign consider_noise = false # %consider_noise% | Activate to prefer a low-noise route | boolean -assign consider_river = false # %consider_river% | Activate to prefer a route along rivers or sees | boolean +assign consider_river = false # %consider_river% | Activate to prefer a route along rivers, lakes, etc. | boolean assign consider_forest = false # %consider_forest% | Activate to prefer a route in forest or green areas| boolean assign consider_town = false # %consider_town% | Activate to bypass cities / big towns as far as possible | boolean -assign avoid_path = false # %avoid_path% | Activate to avoid pathes | boolean assign consider_traffic = 1 # %consider_traffic% | how do you plan to drive the tour? | [1=as cyclist alone in the week, 0.5=as cyclist alone at weekend, 0.3 =with a group of cyclists, 0.1=with a group of cyclists at week-end] assign shortest_way 0 # 0 as default, duplicate shortest standard profile, SAC access limit ignored for now diff --git a/misc/profiles2/trekking.brf b/misc/profiles2/trekking.brf index 011ee43..b4aa6ed 100644 --- a/misc/profiles2/trekking.brf +++ b/misc/profiles2/trekking.brf @@ -14,10 +14,9 @@ assign allow_ferries = true # %allow_ferries% | Set false to disa assign ignore_cycleroutes = false # %ignore_cycleroutes% | Set true for better elevation results | boolean assign stick_to_cycleroutes = false # %stick_to_cycleroutes% | Set true to just follow cycleroutes | boolean assign avoid_unsafe = false # %avoid_unsafe% | Set true to avoid standard highways | boolean -assign avoid_path = false # %avoid_path% | Set true to avoid pathes | boolean assign consider_noise = false # %consider_noise% | Activate to prefer a low-noise route | boolean -assign consider_river = false # %consider_river% | Activate to prefer a route along rivers or sees | boolean +assign consider_river = false # %consider_river% | Activate to prefer a route along rivers, lakes, etc. | boolean assign consider_forest = false # %consider_forest% | Activate to prefer a route in forest or parks | boolean assign consider_town = false # %consider_town% | Activate to bypass cities / big towns as far as possible | boolean assign consider_traffic = false # %consider_traffic% | Activate to consider traffic estimates | boolean @@ -300,8 +299,7 @@ assign costfactor # else if ( highway=track|road|path|footway ) then ( - if ( and highway=path avoid_path ) then ( 10.0 ) - else if ( tracktype=grade1 ) then ( if probablyGood then 1.0 else 1.3 ) + if ( tracktype=grade1 ) then ( if probablyGood then 1.0 else 1.3 ) else if ( tracktype=grade2 ) then ( if probablyGood then 1.1 else 2.0 ) else if ( tracktype=grade3 ) then ( if probablyGood then 1.5 else 3.0 ) else if ( tracktype=grade4 ) then ( if probablyGood then 2.0 else 5.0 ) diff --git a/misc/scripts/mapcreation/brouter.sql b/misc/scripts/mapcreation/brouter.sql index 14aa658..e830078 100644 --- a/misc/scripts/mapcreation/brouter.sql +++ b/misc/scripts/mapcreation/brouter.sql @@ -44,7 +44,6 @@ SELECT -- "buffer radius" was initially created with 50 meters at a lat 50 degrees.... ==> ST_Buffer(way,50) -- but, using geometry "projection", to get same results by a calculation of the planet (latitude between -80, +85) this value should be adapted to the latitude of the highways... , - -- ST_Buffer (way, 32.15 * st_length (ST_Transform (way, 3857)) / st_length (ST_Transform (way, 4326)::geography)) AS way INTO TABLE osm_line_buf_50 FROM lines @@ -77,7 +76,7 @@ FROM polygons p WHERE -- do not consider small surfaces - st_area (p.way) > 1000 + st_area (st_transform (p.way, 4326)::geography) > 1000 AND p.natural IN ('water') OR (p.landuse IN ('forest', 'allotments', 'flowerbed', 'orchard', 'vineyard', 'recreation_ground', 'village_green') OR p.leisure IN ('garden', 'park', 'nature_reserve')); @@ -93,7 +92,7 @@ FROM polygons p WHERE -- do not consider small surfaces - st_area (p.way) > 1000 + st_area (st_transform (p.way, 4326)::geography) > 1000 AND p.natural IN ('water') OR (p.landuse IN ('forest', 'allotments', 'flowerbed', 'orchard', 'vineyard', 'recreation_ground', 'village_green') OR p.leisure IN ('garden', 'park', 'nature_reserve')); @@ -361,7 +360,7 @@ SELECT now(); -- create tags for noise --- create raw data +-- create raw data for noise coming from cars -- when several highways-segments are producing noise, aggregate the noises using the "ST_Union" of the segments! -- (better as using "sum" or "max" that do not deliver good factors) SELECT @@ -418,6 +417,47 @@ GROUP BY ORDER BY sum_noise_factor DESC; +-- noise coming from airports +SELECT + name, + st_buffer (way, (643 * st_length (ST_Transform (st_makeline (st_startpoint (way), st_centroid (way)), 3857)) / st_length (ST_Transform (st_makeline (st_startpoint (way), st_centroid (way)), 4326)::geography))) AS way INTO TABLE poly_airport +FROM + polygons +WHERE + aeroway = 'aerodrome' + AND aerodrome = 'international'; + +SELECT + m.osm_id losmid, + st_area (st_intersection (m.way, q.way)) / (st_area (m.way) * 1.5) AS dist_factor INTO TABLE noise_airport +FROM + osm_line_buf_50 AS m + INNER JOIN poly_airport AS q ON ST_intersects (m.way, q.way) +WHERE + m.highway IS NOT NULL + --GROUP BY losmid, m.way +ORDER BY + dist_factor DESC; + +-- add car & airport noises +SELECT + losmid, + sum(noise_factor) AS sum_noise_factor INTO TABLE noise_tmp3 +FROM (( + SELECT + losmid, + sum_noise_factor AS noise_factor + FROM + noise_tmp2 AS nois1) + UNION ( + SELECT + losmid, + dist_factor AS noise_factor + FROM + noise_airport AS nois2)) AS nois_sum +GROUP BY + losmid; + -- create the noise classes SELECT losmid, @@ -435,7 +475,7 @@ SELECT '6' END AS noise_class INTO TABLE noise_tags FROM - noise_tmp2 y + noise_tmp3 y WHERE y.sum_noise_factor > 0.01; @@ -707,7 +747,8 @@ SELECT ----------------------------------------- -- OSM data used to calculate/estimate the traffic: -- population of towns (+ distance from position to the towns) --- industrial areas (landuse=industrial) (+ surface of the areas and distance from position) +-- industrial& retail areas (landuse=industrial/retail) (consider surface of the areas and distance from position) +-- airports international -- motorway density (traffic on motorways decreases traffic on primary/secondary/tertiary) calculated on grid -- highway density (traffic decreases when more primary/secondary/tertiary highways are available) calculated on grid -- exceptions: near junctions between motorways and primary/secondary/tertiary the traffic increases on the primary/secondary/tertiary.. @@ -841,7 +882,8 @@ SELECT now(); -- --- traffic due to industrial parcs ... +-- traffic due to industrial or retail areas ... (exceptions/not considered: solar & wind parks!) +-- traffic due to aerodromes -- SELECT now(); @@ -852,8 +894,15 @@ SELECT st_length (ST_Transform (st_makeline (st_startpoint (way), st_centroid (way)), 3857)) / st_length (ST_Transform (st_makeline (st_startpoint (way), st_centroid (way)), 4326)::geography) AS merca_coef INTO TABLE poly_industri FROM polygons -WHERE - landuse = 'industrial'; +WHERE (landuse IN ('industrial', 'retail')) + OR (aeroway = 'aerodrome' + AND aerodrome = 'international') + --where landuse in ('industrial', 'retail') + --where landuse in ('industrial') + AND (plant_method IS NULL + OR plant_method NOT IN ('photovoltaic')) + AND (plant_source IS NULL + OR plant_source NOT IN ('solar', 'wind')); SELECT name, diff --git a/misc/scripts/mapcreation/brouter_cfg.lua b/misc/scripts/mapcreation/brouter_cfg.lua index 5ed6d53..b1a7979 100644 --- a/misc/scripts/mapcreation/brouter_cfg.lua +++ b/misc/scripts/mapcreation/brouter_cfg.lua @@ -2,8 +2,9 @@ local srid = 3857 --- 3857 SHOULD BE USED here for distance calculation ... (not srid = 4326 !) --- https://gis.stackexchange.com/questions/48949/epsg-3857-or-4326-for-web-mapping + +-- 3857 (projection) SHOULD BE USED here for distance calculation ... (not srid = 4326 !) +-- https://gis.stackexchange.com/questions/48949/epsg-3857-or-4326-for-web-mapping local tables = {} @@ -28,6 +29,11 @@ tables.polygons = osm2pgsql.define_area_table('polygons', { { column = 'leisure', type = 'text' }, { column = 'natural', type = 'text' }, { column = 'water', type = 'text' }, + { column = 'power', type = 'text' }, + { column = 'plant_method', type = 'text' }, + { column = 'plant_source', type = 'text' }, + { column = 'aeroway', type = 'text' }, + { column = 'aerodrome', type = 'text' }, { column = 'way', type = 'geometry', projection = srid, not_null = true }, }) @@ -73,7 +79,34 @@ function has_area_tags(tags) end return tags.place - or tags.population + or tags.population +end + +function get_plant_source(tags) + local source = nil + + for _, key in ipairs({'source'}) do + local a = tags['plant:' .. key] + if a then + source = a + end + end + + return source +end + + +function get_plant_method(tags) + local method = nil + + for _, key in ipairs({'method'}) do + local a = tags['plant:' .. key] + if a then + method = a + end + end + + return method end function osm2pgsql.process_node(object) @@ -87,7 +120,6 @@ function osm2pgsql.process_node(object) population = object.tags.population, way = object:as_point() }) - end if (object.tags.natural == 'peak') then @@ -115,8 +147,13 @@ function osm2pgsql.process_way(object) leisure = object.tags.leisure, natural = object.tags.natural, water = object.tags.water, + power = object.tags.power, + plant_source = get_plant_source(object.tags), + plant_method = get_plant_method(object.tags), + aeroway = object.tags.aeroway, + aerodrome = object.tags.aerodrome, way = object:as_polygon() - }) + }) end if ( object.tags.highway ~= nil) or ( object.tags.waterway ~= nil) then @@ -131,7 +168,6 @@ function osm2pgsql.process_way(object) }) end - end function osm2pgsql.process_relation(object) @@ -149,6 +185,11 @@ function osm2pgsql.process_relation(object) leisure = object.tags.leisure, natural = object.tags.natural, water = object.tags.water, + power = object.tags.power, + plant_source = get_plant_source(object.tags), + plant_method = get_plant_method(object.tags), + aeroway = object.tags.aeroway, + aerodrome = object.tags.aerodrome, way = object:as_multipolygon() }) @@ -166,4 +207,4 @@ function osm2pgsql.process_relation(object) }) end -end \ No newline at end of file +end diff --git a/misc/scripts/mapcreation/process_pbf_planet.sh b/misc/scripts/mapcreation/process_pbf_planet.sh index d3b190e..99021b1 100755 --- a/misc/scripts/mapcreation/process_pbf_planet.sh +++ b/misc/scripts/mapcreation/process_pbf_planet.sh @@ -62,22 +62,6 @@ ${JAVA} -cp ${BROUTER_JAR} -Ddeletetmpfiles=true -DuseDenseMaps=true btools.mapc mkdir segments ${JAVA} -cp ${BROUTER_JAR} -DuseDenseMaps=true btools.mapcreator.WayLinker unodes55 waytiles55 bordernodes.dat restrictions.dat ${BROUTER_PROFILES}/lookups.dat ${BROUTER_PROFILES}/all.brf segments rd5 -mkdir traffic - -${JAVA} -jar ${BROUTER_JAR} segments 8.593025 49.724868 seed 0 ${BROUTER_PROFILES}/car-traffic_analysis.brf - -${JAVA} -jar ${BROUTER_JAR} segments 8.609011 50.527861 seed 0 ${BROUTER_PROFILES}/car-traffic_analysis.brf - -${JAVA} -jar ${BROUTER_JAR} segments 12.867994 51.239889 seed 0 ${BROUTER_PROFILES}/car-traffic_analysis.brf - -${JAVA} -jar ${BROUTER_JAR} segments 11.128099 49.501845 seed 0 ${BROUTER_PROFILES}/car-traffic_analysis.brf - -${JAVA} -jar ${BROUTER_JAR} segments 16.532815 49.169541 seed 0 ${BROUTER_PROFILES}/car-traffic_analysis.brf - -${JAVA} -jar ${BROUTER_JAR} segments 16.917636 51.040949 seed 0 ${BROUTER_PROFILES}/car-traffic_analysis.brf - -${JAVA} -cp ${BROUTER_JAR} -DuseDenseMaps=true btools.mapcreator.WayLinker unodes55 waytiles55 bordernodes.dat restrictions.dat ${BROUTER_PROFILES}/lookups.dat ${BROUTER_PROFILES}/all.brf segments rd5 - cd .. rm -rf segments mv tmp/segments segments diff --git a/misc/scripts/mapcreation/readme_database.md b/misc/scripts/mapcreation/readme_database.md index 648d28f..737f702 100644 --- a/misc/scripts/mapcreation/readme_database.md +++ b/misc/scripts/mapcreation/readme_database.md @@ -12,29 +12,29 @@ Import new tags for noise, green and water feature - prepare database ``` -# postgres createdb --encoding=UTF8 -U postgres osm +# postgres createdb --encoding=UTF8 -U postgres osm -# postgres psql -U postgres osm --command='CREATE EXTENSION postgis;' +# postgres psql -U postgres osm --command='CREATE EXTENSION postgis;' ``` - import to database and create ``` -# osm2pgsql -c -s -d osm -U postgres -W -H localhost -P 5432 -O flex -S brouter_cfg.lua /path/to/file.pbf +# osm2pgsql -c -s -d osm -U postgres -W -H localhost -P 5432 -O flex -S brouter_cfg.lua /path/to/file.pbf ``` -- generate new tags inside the database +- generate new tags inside the database (use `<` if `-f` does not work) ``` -# psql -d osm -U postgres -H localhost -P 5432 -f brouter.sql +# postgres psql -d osm -U postgres -f brouter.sql ``` - prepare generation of pbf - when using database and new tagging an other lookups.dat is needed, use lookups_db.dat and rename - - script needs a jdbc in the classpath + - script needs a jdbc in the classpath (on UNIX and Linux use a colon `:` as delimiter) `... -cp ../postgresql-42.6.0.jar;../brouter_fc.jar ...` diff --git a/settings.gradle b/settings.gradle index eaa63a4..9bded48 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,7 +1,7 @@ rootProject.name='brouter' if (file('local.properties').exists()) { - include ':brouter-routing-app' + include ':brouter-routing-app' } else { println "Note: To include Android app add 'local.properties' with 'sdk.dir=...' " } -include ':brouter-mapaccess', ':brouter-core', ':brouter-util', ':brouter-expressions', ':brouter-codec', ':brouter-map-creator', ':brouter-server' \ No newline at end of file +include ':brouter-mapaccess', ':brouter-core', ':brouter-util', ':brouter-expressions', ':brouter-codec', ':brouter-map-creator', ':brouter-server'