Merge branch 'abrensch:master' into master

This commit is contained in:
vcoppe 2024-07-23 17:11:26 +02:00 committed by GitHub
commit c616bccabe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
145 changed files with 6174 additions and 3408 deletions

98
.github/workflows/docker-publish.yml vendored Normal file
View file

@ -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 <account>/<repo>
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}

View file

@ -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

View file

@ -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

2
.gitignore vendored
View file

@ -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

14
Dockerfile Normal file
View file

@ -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

View file

@ -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

View file

@ -1 +0,0 @@
/build/

View file

@ -1,8 +1,7 @@
plugins {
id 'java-library'
id 'brouter.library-conventions'
}
dependencies {
implementation project(':brouter-util')
testImplementation 'junit:junit:4.13.1'
}

View file

@ -1,3 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="btools.codec" />

View file

@ -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<Long, Integer> idMap = new HashMap<>();
Map<Long, Integer> 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);

View file

@ -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<TagValueSet> queue = new PriorityQueue<>(2 * identityMap.size(), new TagValueSet.FrequencyComparator());
Queue<TagValueSet> queue = new PriorityQueue<>(2 * identityMap.size(), new TagValueSet.FrequencyComparator());
queue.addAll(identityMap.values());
while (queue.size() > 1) {
TagValueSet node = new TagValueSet(nextTagValueSetId++);

View file

@ -1 +0,0 @@
/build/

View file

@ -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

View file

@ -1,3 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="btools.router" />

View file

@ -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");
}
}
}

View file

@ -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("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\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("<!-- ").append(message).append(" -->\n");
}
}
if (turnInstructionMode == 4) { // comment style
sb.append("<!-- $transport-mode$").append(t.voiceHints.getTransportMode()).append("$ -->\n");
sb.append("<!-- cmd idx lon lat d2next geometry -->\n");
sb.append("<!-- $turn-instruction-start$\n");
for (VoiceHint hint : t.voiceHints.list) {
sb.append(String.format(" $turn$%6s;%6d;%10s;%10s;%6d;%s$\n", hint.getCommandString(), hint.indexInTrack,
formatILon(hint.ilon), formatILat(hint.ilat), (int) (hint.distanceToNext), hint.formatGeometry()));
}
sb.append(" $turn-instruction-end$ -->\n");
}
sb.append("<gpx \n");
sb.append(" xmlns=\"http://www.topografix.com/GPX/1/1\" \n");
sb.append(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n");
if (turnInstructionMode == 9) { // BRouter style
sb.append(" xmlns:brouter=\"Not yet documented\" \n");
}
if (turnInstructionMode == 7) { // old locus style
sb.append(" xmlns:locus=\"http://www.locusmap.eu\" \n");
}
sb.append(" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\" \n");
if (turnInstructionMode == 3) {
sb.append(" creator=\"OsmAndRouter\" version=\"1.1\">\n");
} else {
sb.append(" creator=\"BRouter-" + t.version + "\" version=\"1.1\">\n");
}
if (turnInstructionMode == 9) {
sb.append(" <metadata>\n");
sb.append(" <name>").append(t.name).append("</name>\n");
sb.append(" <extensions>\n");
sb.append(" <brouter:info>").append(t.messageList.get(0)).append("</brouter:info>\n");
if (t.params != null && t.params.size() > 0) {
sb.append(" <brouter:params><![CDATA[");
int i = 0;
for (Map.Entry<String, String> e : t.params.entrySet()) {
if (i++ != 0) sb.append("&");
sb.append(e.getKey()).append("=").append(e.getValue());
}
sb.append("]]></brouter:params>\n");
}
sb.append(" </extensions>\n");
sb.append(" </metadata>\n");
}
if (turnInstructionMode == 3 || turnInstructionMode == 8) { // osmand style, cruiser
float lastRteTime = 0;
sb.append(" <rte>\n");
float rteTime = t.getVoiceHintTime(0);
StringBuffer first = new StringBuffer();
// define start point
{
first.append(" <rtept lat=\"").append(formatILat(t.nodes.get(0).getILat())).append("\" lon=\"")
.append(formatILon(t.nodes.get(0).getILon())).append("\">\n")
.append(" <desc>start</desc>\n <extensions>\n");
if (rteTime != lastRteTime) { // add timing only if available
double ti = rteTime - lastRteTime;
first.append(" <time>").append("" + (int) (ti + 0.5)).append("</time>\n");
lastRteTime = rteTime;
}
first.append(" <offset>0</offset>\n </extensions>\n </rtept>\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(" <rtept lat=\"").append(formatILat(hint.ilat)).append("\" lon=\"")
.append(formatILon(hint.ilon)).append("\">\n")
.append(" <desc>")
.append(turnInstructionMode == 3 ? hint.getMessageString() : hint.getCruiserMessageString())
.append("</desc>\n <extensions>\n");
rteTime = t.getVoiceHintTime(i + 1);
if (rteTime != lastRteTime) { // add timing only if available
double ti = rteTime - lastRteTime;
sb.append(" <time>").append("" + (int) (ti + 0.5)).append("</time>\n");
lastRteTime = rteTime;
}
sb.append(" <turn>")
.append(turnInstructionMode == 3 ? hint.getCommandString() : hint.getCruiserCommandString())
.append("</turn>\n <turn-angle>").append("" + (int) hint.angle)
.append("</turn-angle>\n <offset>").append("" + hint.indexInTrack).append("</offset>\n </extensions>\n </rtept>\n");
}
sb.append(" <rtept lat=\"").append(formatILat(t.nodes.get(t.nodes.size() - 1).getILat())).append("\" lon=\"")
.append(formatILon(t.nodes.get(t.nodes.size() - 1).getILon())).append("\">\n")
.append(" <desc>destination</desc>\n <extensions>\n");
sb.append(" <time>0</time>\n");
sb.append(" <offset>").append("" + (t.nodes.size() - 1)).append("</offset>\n </extensions>\n </rtept>\n");
sb.append("</rte>\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(" <wpt lon=\"").append(formatILon(hint.ilon)).append("\" lat=\"")
.append(formatILat(hint.ilat)).append("\">")
.append(hint.selev == Short.MIN_VALUE ? "" : "<ele>" + (hint.selev / 4.) + "</ele>")
.append("<name>")
.append(hint.getMessageString())
.append("</name>")
.append("<extensions><locus:rteDistance>").append("" + hint.distanceToNext).append("</locus:rteDistance>");
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("<locus:rteTime>").append("" + ti).append("</locus:rteTime>")
.append("<locus:rteSpeed>").append("" + speed).append("</locus:rteSpeed>");
lastRteTime = rteTime;
}
sb.append("<locus:rtePointAction>").append("" + hint.getLocusAction()).append("</locus:rtePointAction></extensions>")
.append("</wpt>\n");
}
}
if (turnInstructionMode == 5) { // gpsies style
for (VoiceHint hint : t.voiceHints.list) {
sb.append(" <wpt lon=\"").append(formatILon(hint.ilon)).append("\" lat=\"")
.append(formatILat(hint.ilat)).append("\">")
.append("<name>").append(hint.getMessageString()).append("</name>")
.append("<sym>").append(hint.getSymbolString().toLowerCase()).append("</sym>")
.append("<type>").append(hint.getSymbolString()).append("</type>")
.append("</wpt>\n");
}
}
if (turnInstructionMode == 6) { // orux style
for (VoiceHint hint : t.voiceHints.list) {
sb.append(" <wpt lat=\"").append(formatILat(hint.ilat)).append("\" lon=\"")
.append(formatILon(hint.ilon)).append("\">")
.append(hint.selev == Short.MIN_VALUE ? "" : "<ele>" + (hint.selev / 4.) + "</ele>")
.append("<extensions>\n" +
" <om:oruxmapsextensions xmlns:om=\"http://www.oruxmaps.com/oruxmapsextensions/1/0\">\n" +
" <om:ext type=\"ICON\" subtype=\"0\">").append("" + hint.getOruxAction())
.append("</om:ext>\n" +
" </om:oruxmapsextensions>\n" +
" </extensions>\n" +
" </wpt>\n");
}
}
for (int i = 0; i <= t.pois.size() - 1; i++) {
OsmNodeNamed poi = t.pois.get(i);
sb.append(" <wpt lon=\"").append(formatILon(poi.ilon)).append("\" lat=\"")
.append(formatILat(poi.ilat)).append("\">\n")
.append(" <name>").append(StringUtils.escapeXml10(poi.name)).append("</name>\n")
.append(" </wpt>\n");
}
if (t.exportWaypoints) {
for (int i = 0; i <= t.matchedWaypoints.size() - 1; i++) {
MatchedWaypoint wt = t.matchedWaypoints.get(i);
sb.append(" <wpt lon=\"").append(formatILon(wt.waypoint.ilon)).append("\" lat=\"")
.append(formatILat(wt.waypoint.ilat)).append("\">\n")
.append(" <name>").append(StringUtils.escapeXml10(wt.name)).append("</name>\n");
if (i == 0) {
sb.append(" <type>from</type>\n");
} else if (i == t.matchedWaypoints.size() - 1) {
sb.append(" <type>to</type>\n");
} else {
sb.append(" <type>via</type>\n");
}
sb.append(" </wpt>\n");
}
}
sb.append(" <trk>\n");
if (turnInstructionMode == 9
|| turnInstructionMode == 2
|| turnInstructionMode == 8
|| turnInstructionMode == 4) { // Locus, comment, cruise, brouter style
sb.append(" <src>").append(t.name).append("</src>\n");
sb.append(" <type>").append(t.voiceHints.getTransportMode()).append("</type>\n");
} else {
sb.append(" <name>").append(t.name).append("</name>\n");
}
if (turnInstructionMode == 7) {
sb.append(" <extensions>\n");
sb.append(" <locus:rteComputeType>").append("" + t.voiceHints.getLocusRouteType()).append("</locus:rteComputeType>\n");
sb.append(" <locus:rteSimpleRoundabouts>1</locus:rteSimpleRoundabouts>\n");
sb.append(" </extensions>\n");
}
// all points
sb.append(" <trkseg>\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 ? "" : "<ele>" + n.getElev() + "</ele>";
VoiceHint hint = t.getVoiceHint(idx);
MatchedWaypoint mwpt = t.getMatchedWaypoint(idx);
if (t.showTime) {
sele += "<time>" + getFormattedTime3(n.getTime()) + "</time>";
}
if (turnInstructionMode == 8) {
if (mwpt != null &&
!mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) {
sele += "<name>" + mwpt.name + "</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 += "<name>" + mwpt.name + "</name>";
}
sele += "<desc>" + hint.getCruiserMessageString() + "</desc>";
sele += "<sym>" + hint.getCommandString(hint.cmd) + "</sym>";
if (mwpt != null) {
sele += "<type>Via</type>";
}
sele += "<extensions>";
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 += "<brouter:speed>" + (((int) (speed * 10)) / 10.f) + "</brouter:speed>";
}
sele += "<brouter:voicehint>" + hint.getCommandString() + ";" + (int) (hint.distanceToNext) + "," + hint.formatGeometry() + "</brouter:voicehint>";
if (n.message != null && n.message.wayKeyValues != null && !n.message.wayKeyValues.equals(lastway)) {
sele += "<brouter:way>" + n.message.wayKeyValues + "</brouter:way>";
lastway = n.message.wayKeyValues;
}
if (n.message != null && n.message.nodeKeyValues != null) {
sele += "<brouter:node>" + n.message.nodeKeyValues + "</brouter:node>";
}
sele += "</extensions>";
}
if (idx == 0 && hint == null) {
if (mwpt != null && mwpt.direct) {
sele += "<desc>beeline</desc>";
} else {
sele += "<desc>start</desc>";
}
sele += "<type>Via</type>";
} else if (idx == t.nodes.size() - 1 && hint == null) {
sele += "<desc>end</desc>";
sele += "<type>Via</type>";
} else {
if (mwpt != null && hint == null) {
if (mwpt.direct) {
// bNextDirect = true;
sele += "<desc>beeline</desc>";
} else {
sele += "<desc>" + mwpt.name + "</desc>";
}
sele += "<type>Via</type>";
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 += "<extensions>";
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 += "<brouter:speed>" + (((int) (speed * 10)) / 10.f) + "</brouter:speed>";
}
if (n.message != null && n.message.wayKeyValues != null && !n.message.wayKeyValues.equals(lastway)) {
sele += "<brouter:way>" + n.message.wayKeyValues + "</brouter:way>";
lastway = n.message.wayKeyValues;
}
if (n.message != null && n.message.nodeKeyValues != null) {
sele += "<brouter:node>" + n.message.nodeKeyValues + "</brouter:node>";
}
sele += "</extensions>";
}
}
}
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 += "<name>" + mwpt.name + "</name>";
}
if (mwpt.direct && bNextDirect) {
sele += "<src>" + hint.getLocusSymbolString() + "</src><sym>pass_place</sym><type>Shaping</type>";
// bNextDirect = false;
} else if (mwpt.direct) {
if (idx == 0)
sele += "<sym>pass_place</sym><type>Via</type>";
else
sele += "<sym>pass_place</sym><type>Shaping</type>";
bNextDirect = true;
} else if (bNextDirect) {
sele += "<src>beeline</src><sym>" + hint.getLocusSymbolString() + "</sym><type>Shaping</type>";
bNextDirect = false;
} else {
sele += "<sym>" + hint.getLocusSymbolString() + "</sym><type>Via</type>";
}
} else {
sele += "<sym>" + hint.getLocusSymbolString() + "</sym>";
}
} else {
if (idx == 0 && hint == null) {
int pos = sele.indexOf("<sym");
if (pos != -1) {
sele = sele.substring(0, pos);
}
if (mwpt != null && !mwpt.name.startsWith("from"))
sele += "<name>" + mwpt.name + "</name>";
if (mwpt != null && mwpt.direct) {
bNextDirect = true;
}
sele += "<sym>pass_place</sym>";
sele += "<type>Via</type>";
} else if (idx == t.nodes.size() - 1 && hint == null) {
int pos = sele.indexOf("<sym");
if (pos != -1) {
sele = sele.substring(0, pos);
}
if (mwpt != null && mwpt.name != null && !mwpt.name.startsWith("to"))
sele += "<name>" + mwpt.name + "</name>";
if (bNextDirect) {
sele += "<src>beeline</src>";
}
sele += "<sym>pass_place</sym>";
sele += "<type>Via</type>";
} else {
if (mwpt != null) {
if (!mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) {
sele += "<name>" + mwpt.name + "</name>";
}
if (mwpt.direct && bNextDirect) {
sele += "<src>beeline</src><sym>pass_place</sym><type>Shaping</type>";
} else if (mwpt.direct) {
if (idx == 0)
sele += "<sym>pass_place</sym><type>Via</type>";
else
sele += "<sym>pass_place</sym><type>Shaping</type>";
bNextDirect = true;
} else if (bNextDirect) {
sele += "<src>beeline</src><sym>pass_place</sym><type>Shaping</type>";
bNextDirect = false;
} else if (mwpt.name.startsWith("via") ||
mwpt.name.startsWith("from") ||
mwpt.name.startsWith("to")) {
if (bNextDirect) {
sele += "<src>beeline</src><sym>pass_place</sym><type>Shaping</type>";
} else {
sele += "<sym>pass_place</sym><type>Via</type>";
}
bNextDirect = false;
} else {
sele += "<name>" + mwpt.name + "</name>";
sele += "<sym>pass_place</sym><type>Via</type>";
}
}
}
}
}
sb.append(" <trkpt lon=\"").append(formatILon(n.getILon())).append("\" lat=\"")
.append(formatILat(n.getILat())).append("\">").append(sele).append("</trkpt>\n");
nn = n;
}
sb.append(" </trkseg>\n");
sb.append(" </trk>\n");
sb.append("</gpx>\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("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
sb.append("<gpx \n");
sb.append(" xmlns=\"http://www.topografix.com/GPX/1/1\" \n");
sb.append(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n");
sb.append(" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\" \n");
sb.append(" creator=\"BRouter-" + OsmTrack.version + "\" version=\"1.1\">\n");
}
public void formatGpxFooter(BufferedWriter sb) throws IOException {
sb.append("</gpx>\n");
}
public void formatWaypointGpx(BufferedWriter sb, OsmNodeNamed n) throws IOException {
sb.append(" <wpt lon=\"").append(formatILon(n.ilon)).append("\" lat=\"")
.append(formatILat(n.ilat)).append("\">");
if (n.getSElev() != Short.MIN_VALUE) {
sb.append("<ele>").append("" + n.getElev()).append("</ele>");
}
if (n.name != null) {
sb.append("<name>").append(StringUtils.escapeXml10(n.name)).append("</name>");
}
if (n.nodeDescription != null && rc != null) {
sb.append("<desc>").append(rc.expctxWay.getKeyValueDescription(false, n.nodeDescription)).append("</desc>");
}
sb.append("</wpt>\n");
}
public static String getWaypoint(int ilon, int ilat, String name, String desc) {
return "<wpt lon=\"" + formatILon(ilon) + "\" lat=\"" + formatILat(ilat) + "\"><name>" + name + "</name>" + (desc != null ? "<desc>" + desc + "</desc>" : "") + "</wpt>";
}
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("<trkpt ");
if (idx0 >= 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;
}
}

View file

@ -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<String> 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);
}
}
}

View file

@ -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("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
sb.append("<kml xmlns=\"http://earth.google.com/kml/2.0\">\n");
sb.append(" <Document>\n");
sb.append(" <name>KML Samples</name>\n");
sb.append(" <open>1</open>\n");
sb.append(" <distance>3.497064</distance>\n");
sb.append(" <traveltime>872</traveltime>\n");
sb.append(" <description>To enable simple instructions add: 'instructions=1' as parameter to the URL</description>\n");
sb.append(" <Folder>\n");
sb.append(" <name>Paths</name>\n");
sb.append(" <visibility>0</visibility>\n");
sb.append(" <description>Examples of paths.</description>\n");
sb.append(" <Placemark>\n");
sb.append(" <name>Tessellated</name>\n");
sb.append(" <visibility>0</visibility>\n");
sb.append(" <description><![CDATA[If the <tessellate> tag has a value of 1, the line will contour to the underlying terrain]]></description>\n");
sb.append(" <LineString>\n");
sb.append(" <tessellate>1</tessellate>\n");
sb.append(" <coordinates>");
for (OsmPathElement n : t.nodes) {
sb.append(formatILon(n.getILon())).append(",").append(formatILat(n.getILat())).append("\n");
}
sb.append(" </coordinates>\n");
sb.append(" </LineString>\n");
sb.append(" </Placemark>\n");
sb.append(" </Folder>\n");
if (t.exportWaypoints || !t.pois.isEmpty()) {
if (!t.pois.isEmpty()) {
sb.append(" <Folder>\n");
sb.append(" <name>poi</name>\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(" </Folder>\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(" </Document>\n");
sb.append("</kml>\n");
return sb.toString();
}
private void createFolder(StringBuilder sb, String type, List<MatchedWaypoint> waypoints) {
sb.append(" <Folder>\n");
sb.append(" <name>" + type + "</name>\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(" </Folder>\n");
}
private void createPlaceMark(StringBuilder sb, String name, int ilat, int ilon) {
sb.append(" <Placemark>\n");
sb.append(" <name>" + StringUtils.escapeXml10(name) + "</name>\n");
sb.append(" <Point>\n");
sb.append(" <coordinates>" + formatILon(ilon) + "," + formatILat(ilat) + "</coordinates>\n");
sb.append(" </Point>\n");
sb.append(" </Placemark>\n");
}
}

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}
}

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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<String> messageList = new ArrayList<>();
List<String> 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<OsmPathElement> removeBackList = new ArrayList<>();
ArrayList<OsmPathElement> removeForeList = new ArrayList<>();
ArrayList<Integer> removeVoiceHintList = new ArrayList<>();
List<OsmPathElement> removeBackList = new ArrayList<>();
List<OsmPathElement> removeForeList = new ArrayList<>();
List<Integer> removeVoiceHintList = new ArrayList<>();
OsmPathElement last = null;
OsmPathElement lastJunction = null;
CompactLongMap<OsmTrack.OsmPathElementHolder> 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<MatchedWaypoint> 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<OsmPath> openBorderList = new ArrayList<>(4096);
List<OsmPath> 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() {

View file

@ -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<OsmNodeNamed> 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<OsmNodeNamed> 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<OsmNodeNamed> readPositions(double[] lons, double[] lats) {
List<OsmNodeNamed> 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<String, String> getUrlParams(String url) throws UnsupportedEncodingException {
Map<String, String> 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<OsmNodeNamed> wplist, Map<String, String> 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<OsmNodeNamed> 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<OsmNodeNamed> 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<OsmNodeNamed> 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<OsmNodeNamed> 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<String, String> 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<String, String> params) {
if (params != null) {
if (params.size() == 0) return;
if (rctx.keyValues == null) rctx.keyValues = new HashMap<>();
for (Map.Entry<String, String> 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<OsmNodeNamed> 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<OsmNodeNamed> readPoisList(String pois) {
// lon,lat,name|...
if (pois == null) return null;
String[] lonLatNameList = pois.split("\\|");
List<OsmNodeNamed> 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<OsmNodeNamed> readNogoList(String nogos) {
// lon,lat,radius[,weight]|...
if (nogos == null) return null;
String[] lonLatRadList = nogos.split("\\|");
List<OsmNodeNamed> 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<OsmNodeNamed> readNogos(String nogoLons, String nogoLats, String nogoRadi) {
if (nogoLons == null || nogoLats == null || nogoRadi == null) return null;
List<OsmNodeNamed> 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;
}
}

View file

@ -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;

View file

@ -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() {

View file

@ -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<VoiceHint> 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

View file

@ -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<VoiceHint> 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<VoiceHint> results = new ArrayList<>();
double distance = 0;
VoiceHint inputLast = null;
ArrayList<VoiceHint> 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;
}
}

View file

@ -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<OsmNodeNamed> 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<OsmNodeNamed> 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<String, String> map = rpc.getUrlParams(url);
Assert.assertEquals("result content ", 3, map.size());
}
@Test
public void readParamsFromList() throws UnsupportedEncodingException {
Map<String, String> 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);
}
}

View file

@ -1 +0,0 @@
/build/

View file

@ -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'
}

View file

@ -1,3 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="btools.expressions" />

View file

@ -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) {

View file

@ -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<String> getKeyValueList(boolean inverseDirection, byte[] ab) {
ArrayList<String> res = new ArrayList<>();
List<String> 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<String, String> counts = new TreeMap<>();
NavigableMap<String, String> 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<String, Integer> 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) {

View file

@ -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);
}

View file

@ -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"
};

View file

@ -1 +0,0 @@
/build/

View file

@ -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')
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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();
}
}

View file

@ -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<String, ElevationRaster> srtmmap;
int lastSrtmLonIdx;
int lastSrtmLatIdx;
short maxElev = Short.MIN_VALUE;
short minElev = Short.MAX_VALUE;
String srtmdir;
boolean missingData;
Map<Short, Color> 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<Short, Color> 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 <lon> <lat> <srtm-folder> <imageFileName> <maxX> <maxY> <downscale> [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);
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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 <srtm-filename | all> <hgt-data-dir> <srtm-output-dir> [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
* <p>
* 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 <srtm-filename> <hgt-data-dir> <srtm-output-dir> [arc seconds (1 or 3,default=3)] [hgt-fallback-data-dir]");
System.out.println("or java all <hgt-data-dir> <srtm-output-dir> [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<String> foundList = new ArrayList<>();
//List<String> 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;
}
}

View file

@ -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
* <p>
* Class HgtReader reads data from SRTM HGT files. Currently this class is restricted to a resolution of 3 arc seconds.
* <p>
* SRTM data files are available at the <a href="http://dds.cr.usgs.gov/srtm/version2_1/SRTM3">NASA SRTM site</a>
*
* @author Oliver Wieland &lt;oliver.wieland@online.de&gt;
*/
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<String, ShortBuffer> 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.
* <p>
* See also <a href="http://gis.stackexchange.com/questions/43743/how-to-extract-elevation-from-hgt-file">stackexchange.com</a>
*
* @param lat, lon the coordinate to get the elevation data for
* @return the elevation value or <code>Double.NaN</code>, 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 <tt>[N|S]nn[W|E]mmm.hgt</tt> where <i>nn</i> is the integral latitude
* without decimals and <i>mmm</i> 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);
}
}
}

View file

@ -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<OsmNodeP, OsmLinkP> targets = new HashMap<>();
Map<OsmNodeP, OsmLinkP> 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<OsmNodeP> internalReverse = new ArrayList<>();
List<OsmNodeP> internalReverse = new ArrayList<>();
for (OsmLinkP link0 = getFirstLink(); link0 != null; link0 = link0.getNext(this)) {
OsmLinkP link = link0;
OsmNodeP origin = this;
OsmNodeP target = null;
ArrayList<OsmNodeP> linkNodes = new ArrayList<>();
List<OsmNodeP> 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());

View file

@ -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<OsmTrafficElement> 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<OsmNodeP> 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;
}
}

View file

@ -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<String, SrtmRaster> srtmmap;
private Map<String, ElevationRaster> 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 <node-tiles-in> <node-tiles-out> <bordernids-in> <bordernodes-out> <srtm-data-dir>");
} else if (args.length != 5 && args.length != 6) {
System.out.println("usage: java PosUnifier <node-tiles-in> <node-tiles-out> <bordernids-in> <bordernodes-out> <srtm-data-dir> [srtm-fallback-data-dir]");
System.out.println("or java PosUnifier <srtm-data-dir> <lon> <lat>");
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_<srtmLon>_<srtmLat> 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;

View file

@ -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<String, long[]> relstats = new HashMap<>();
Map<String, long[]> relstats = new HashMap<>();
DataInputStream dis = createInStream(relationFileIn);
try {

View file

@ -1,166 +0,0 @@
package btools.mapcreator;
/**
* This is a wrapper for a 5*5 degree srtm file in ascii/zip-format
* <p>
* - 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]);
}
}
}
}
}
}

View file

@ -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<OsmNodeP> nodesMap;
private OsmTrafficMap trafficMap;
private List<OsmNodeP> 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<Integer, OsmNodeP> sortedList = new TreeMap<>();
Map<Integer, OsmNodeP> 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());
}

View file

@ -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");

View file

@ -1 +0,0 @@
/build/

View file

@ -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

View file

@ -1,3 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="btools.mapaccess" />

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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) {}
}
}

View file

@ -1 +0,0 @@
/build/

View file

@ -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']
}

View file

@ -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.** { *; }

View file

@ -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;

View file

@ -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;

View file

@ -10,6 +10,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<application
@ -92,6 +93,7 @@
android:name=".RoutingParameterDialog"
android:exported="true"
android:launchMode="singleTask"
android:configChanges="orientation|screenSize|keyboardHidden"
/>
<service
@ -99,6 +101,10 @@
android:enabled="true"
android:exported="true"
android:process=":brouter_service" />
<service
android:name="androidx.work.impl.foreground.SystemForegroundService"
android:foregroundServiceType="dataSync"
tools:node="merge" />
<provider
android:name="androidx.core.content.FileProvider"

View file

@ -10,6 +10,7 @@ interface IBRouterService {
// "maxRunningTime"-->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.

View file

@ -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");

View file

@ -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<Integer> 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;
}
}

View file

@ -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);
}
}
}

View file

@ -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<Integer> 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 <basedir>/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<String> 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();
}
}

View file

@ -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);
}
}

View file

@ -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) {

View file

@ -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<OsmNodeNamed> 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<String, String> 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<String, String> 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<OsmNodeNamed> 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<OsmNodeNamed> readPositions(Bundle params) {
List<OsmNodeNamed> 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<OsmNodeNamed> readLonlats(Bundle params, int mode) {
List<OsmNodeNamed> 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<OsmNodeNamed> readNogoList(Bundle params) {
// lon,lat,radius|...
String nogos = params.getString("nogos");
if (nogos == null) return null;
String[] lonLatRadList = nogos.split("\\|");
List<OsmNodeNamed> 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<OsmNodeNamed> readNogoPolygons(Bundle params) {
List<OsmNodeNamed> 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<OsmNodeNamed> 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<OsmNodeNamed> readPoisList(Bundle params) {
// lon,lat,name|...
String pois = params.getString("pois");
if (pois == null) return null;
String[] lonLatNameList = pois.split("\\|");
List<OsmNodeNamed> 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<OsmNodeNamed> 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");
}
}
}
}

View file

@ -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());

View file

@ -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<String, ?> 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<String, ?> 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);

View file

@ -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();

View file

@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View file

@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View file

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
<item quantity="one">%d قطعة</item>
<item quantity="other">%d قطع</item>
</plurals>
<string name="cancel_download">إلغاء التنزيل</string>
<string name="import_profile">استيراد ملف تعريف</string>
<string name="action_download">تنزيل %s</string>
<string name="action_delete">حذف %s</string>
<string name="action_update">تحديث %s</string>
<string name="action_select">حدد القطع</string>
<string name="action_cancel">إيقاف التنزيل</string>
<string name="summary_segments">الحجم=%1$s\nالحجم المتوفر=%2$s</string>
<string name="notification_title">تحميل القطع</string>
<string name="action_choose_folder">اختر مكان قاعدة بيانات brouter:</string>
<string name="action_select_profile">اختر ملف تعريف التوجيه</string>
<string name="main_action">حدد الإجراء الرئيسي</string>
<string name="main_action_1">مدير التنزيلات</string>
<string name="main_action_2">تطبيق BRouter</string>
<string name="cancel">إلغاء</string>
<string name="i_know">أعرف ذلك</string>
<string name="close">إغلاق</string>
<string name="exit">خروج</string>
<string name="ok">موافق</string>
<string name="success">تم بنجاح</string>
<string name="error">حدث خطأ ما</string>
<string name="title_download">مدير تنزيلات BRouter</string>
<string name="summary_download">*** تحذير:***
\n\nيتم استخدام مدير التنزيل لتنزيل ملفات بيانات التوجيه عبر الطرق
والتي يمكن أن يصل حجمها إلى 170 ميجابايت، لذلك لا تقم بتشغيل
"مدير التنزيلات" حينما تكون متصلاً عبر البيانات الخلوية ومحدوداً بحجم التنزيلات!
سرعة التنزيل محدودة على 16 ميجابايت/بالثانية.</string>
<string name="title_timeoutfree">أنجزت عملية الحساب بنجاح وبدون تأخير</string>
<string name="summary_timeoutfree">لقد نجحت في تكرار عملية حسابية كانت قد انتهت في السابق
عند بدايتها من خلال أداة الخريطة، إذا قمت بتكرار نفس الطلب من
أداة الخريطة الخاصة بك ومع نفس الوجهة بالضبط وبنقطة بداية
قريبة، فسيضمن لك التطبيق عدم انتهاء مهلة هذا الطلب.</string>
<string name="title_sdcard">أدخل مسار الذاكرة الخارجية:</string>
<string name="title_action">اختر الإجراء</string>
<string name="action_via_select">التحقق من النقاط الوسيطة:</string>
<string name="action_nogo_select">التحقق من النقاط المسبتعدة:</string>
<string name="action_servermode">وضع-الخادم</string>
<string name="action_info">معلومات</string>
<string name="action_calc_route">حساب الطريق</string>
<string name="action_profile_settings">إعدادات ملف التعريف</string>
<string name="action_share">مشاركة ملف GPX</string>
<string name="action_select_from">اختر من</string>
<string name="action_select_to">اختر إلى/عبر</string>
<string name="msg_no_profile">لا توجد بيانات ملف التعريف</string>
<string name="msg_no_used_profile">، لا يوجد ملف تعريف مستخدم</string>
<string name="msg_too_much_data">هناك الكثير من البيانات للتنزيل، من فضلك قم بتقليلها.</string>
<string name="msg_download_start">تمت جدولة التنزيل، وتحقق من اتصالك بالإنترنت إذا لم يبدأ التنزيل.</string>
<string name="msg_wpt_selection">نقاط الطريق الحالية المحددة:\n</string>
<string name="msg_no_wpt_selection">نقاط الطريق المتوقعة المحددة\n</string>
<string name="msg_alternative">البديل</string>
<string name="msg_status_result">الإصدار = BRouter-%1$s \n
الذاكرة = %2$s \n
المسافة = %3$s كم\n
التصاعد المرشح = %4$s م\n
التصاعد العادي = %5$s م\n
الوقت المقدر = %6$s</string>
<string name="msg_read_wpt_error">خطأ في قراءة نقاط الطريق</string>
<string name="msg_no_wpt">لا يحتوي مصدر الإحداثيات على أي نقاط الطريق!</string>
<string name="msg_too_much_wpts">مصدر الإحداثيات يحتوي على عدد كبير جدًا من نقاط الطريق: %1$d (يرجى استخدام أسماء النقاط من/إلى/عبر from/to/via)</string>
<string name="no">لا</string>
<string name="yes">نعم</string>
<string name="title_delete">تأكيد الحذف</string>
<string name="summary_delete">هل ترغب بالحذف؟</string>
<string name="title_version">مشكلة في الإصدار</string>
<string name="summary_version">لقد تغير الإصدار الأساسي للصور المتجانبة، ما الإجراء المناسب لفعله؟</string>
<string name="action_version1">متابعة التنزيل الحالي، وحذف البيانات الأخرى القديمة</string>
<string name="action_version2">تحديد الكل للتنزيل والبدء</string>
<string name="action_version3">قم بالإلغاء في الوقت الحالي، واستكمله لاحقاً</string>
<string name="title_version_diff">اختلافات الإصدارات</string>
<string name="summary_version_diff">الإصدار الأساسي لبعض الصور المتجانبة مختلف. ما الإجراء المناسب لفعله؟</string>
<string name="action_version_diff1">تحميل المختلف من الصور المتجانبة</string>
<string name="action_version_diff2">حذف كل الصور المتجانبة المختلفة عن الأخرى</string>
<string name="action_version_diff3">قم بالإلغاء في الوقت الحالي، واستكمله لاحقاً</string>
<string name="summary_new_version">يحتاج إصدار البيانات الجديد إلى وجود التطبيق الجديد، لذا نرجوا منك تحديث BRouter أولاً</string>
<string name="msg_download_failed">فشل التنزيل</string>
<string name="msg_download_cancel">تم إلغاء التنزيل</string>
<string name="msg_download_succeed">تم التنزيل بنجاح</string>
<string name="msg_download_started">جار التنزيل…</string>
</resources>

View file

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
<item quantity="one">%d segment</item>
<item quantity="other">%d segments</item>
</plurals>
<string name="cancel_download">Cancel·lar descàrrega</string>
<string name="import_profile">Importar perfil</string>
<string name="action_download">Descàrrega %s</string>
<string name="action_delete">Eliminar %s</string>
<string name="action_update">Actualitzar %s</string>
<string name="action_select">Seleccionar segments</string>
<string name="action_cancel">Aturar Descàrrega</string>
<string name="summary_segments">Espai=%1$s\nLliure=%2$s</string>
<string name="notification_title">Descarregar segments</string>
<string name="action_choose_folder">Escollir directori base brouter:</string>
<string name="action_select_profile">Seleccionar un perfil d\'enrutament</string>
<string name="main_action">Seleccionar Acció Principal</string>
<string name="main_action_1">Gestor de Baixades</string>
<string name="main_action_2">App BRouter</string>
<string name="cancel">Cancel·lar</string>
<string name="i_know">Ho sé</string>
<string name="close">Tancar</string>
<string name="exit">Sortir</string>
<string name="ok">D\'acord</string>
<string name="success">Èxit</string>
<string name="error">Ha aparegut un error</string>
<string name="title_download">Gestor de baixades BRouter</string>
<string name="summary_download">*** 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.</string>
<string name="title_timeoutfree">S\'ha preparat correctament un càlcul sense límit de temps</string>
<string name="summary_timeoutfree">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.</string>
<string name="title_sdcard">Introdueix el directori base de la tarja SD:</string>
<string name="title_action">Seleccionar Acció</string>
<string name="action_via_select">Comprovar Selecció VIA:</string>
<string name="action_nogo_select">Comprovar Selecció NoGo:</string>
<string name="action_servermode">Mode-Servidor</string>
<string name="action_info">Info</string>
<string name="action_calc_route">Calc Ruta</string>
<string name="action_profile_settings">Ajustaments del perfil</string>
<string name="action_share">Compartir GPX</string>
<string name="action_select_from">Seleccionar des de</string>
<string name="action_select_to">Seleccionar a/via</string>
<string name="msg_no_profile">no hi ha dades al perfil</string>
<string name="msg_no_used_profile">, cap perfil utilitzat</string>
<string name="msg_too_much_data">Massa dades per baixar. Si us plau, redueix-les.</string>
<string name="msg_download_start">Baixada programada. Comprovar la connexió a internet si no comença.</string>
<string name="msg_wpt_selection">selecció actual de fita:\n</string>
<string name="msg_no_wpt_selection">Esperant selecció de fita\n</string>
<string name="msg_alternative">Alternativa</string>
<string name="msg_status_result">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</string>
<string name="msg_read_wpt_error">Error llegint fites</string>
<string name="msg_no_wpt">la font de coordenades no conté cap fita!</string>
<string name="msg_too_much_wpts">la font de coordenades conté massa fites: %1$d (si us plau, fes servir noms des de/a/via)</string>
<string name="no">No</string>
<string name="yes">Si</string>
<string name="title_delete">Confirmar Eliminar</string>
<string name="summary_delete">Voleu realment eliminar?</string>
<string name="title_version">Problema de versió</string>
<string name="summary_version">La versió base per les tesel·les ha canviat. Què vols fer?</string>
<string name="action_version1">Continuar amb la baixada, eliminar altres dades antigues</string>
<string name="action_version2">Seleccionar-ho tot per baixar i iniciar</string>
<string name="action_version3">Cancel·lar ara, completar un altre dia</string>
<string name="title_version_diff">Diferències de Versió</string>
<string name="summary_version_diff">La versió base de diverses tesel·les és diferent. Què vols fer?</string>
<string name="action_version_diff1">Baixar totes les tesel·les diferents</string>
<string name="action_version_diff2">Descartar les tesel·les diferents</string>
<string name="action_version_diff3">Cancel·lar ara, completar un altre dia</string>
<string name="summary_new_version">La nova versió de dades necessita una aplicació nova. Sisplau, actualitza BRouter primer</string>
<string name="msg_download_failed">Baixada fallida</string>
<string name="msg_download_cancel">Baixada cancel·lada</string>
<string name="msg_download_succeed">Baixada exitosa</string>
<string name="msg_download_started">Baixant…</string>
</resources>

View file

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
<item quantity="one">%d Segment</item>
<item quantity="other">%d Segmente</item>
</plurals>
<string name="cancel_download">Download abbrechen</string>
<string name="import_profile">Profil importieren</string>
<string name="action_download">%s downloaden</string>
<string name="action_delete">%s löschen</string>
<string name="action_update">%s aktualisieren</string>
<string name="action_select">Segmente auswählen</string>
<string name="action_cancel">Download stoppen</string>
<string name="summary_segments">Größe=%1$s\nFrei=%2$s</string>
<string name="notification_title">Segmente herunterladen</string>
<string name="action_choose_folder">BRouter-Datenbankverzeichnis wählen:</string>
<string name="action_select_profile">Routenprofil wählen</string>
<string name="main_action">Standardaktion wählen</string>
<string name="main_action_1">Downloadmanager</string>
<string name="main_action_2">BRouter-App</string>
<string name="cancel">Abbrechen</string>
<string name="i_know">Ich weiß</string>
<string name="close">Schließen</string>
<string name="exit">Verlassen</string>
<string name="ok">OK</string>
<string name="success">Erfolg</string>
<string name="error">Ein Fehler ist aufgetreten</string>
<string name="title_download">BRouter Downloadmanager</string>
<string name="summary_download">*** 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.</string>
<string name="title_timeoutfree">Berechnung ohne Zeitüberschreitung erfolgreich</string>
<string name="summary_timeoutfree">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.</string>
<string name="title_sdcard">SD-Karten Verzeichnis wählen:</string>
<string name="title_action">Aktion wählen</string>
<string name="action_via_select">Via-Auswahl prüfen:</string>
<string name="action_nogo_select">NoGo-Auswahl prüfen:</string>
<string name="action_servermode">Server-Modus</string>
<string name="action_info">Info</string>
<string name="action_calc_route">Route berechnen</string>
<string name="action_profile_settings">Profileinstellungen</string>
<string name="action_share">GPX teilen</string>
<string name="action_select_from">"Von" wählen</string>
<string name="action_select_to">"Nach"/Via wählen</string>
<string name="msg_no_profile">Keine Profildaten</string>
<string name="msg_no_used_profile">, kein verwendetes Profil</string>
<string name="msg_too_much_data">Zu viele Daten für den Download. Bitte reduzieren.</string>
<string name="msg_download_start">Download geplant. Überprüfen Sie die Internetverbindung, wenn der Download nicht startet.</string>
<string name="msg_wpt_selection">aktuelle Wegpunktauswahl:\n</string>
<string name="msg_no_wpt_selection">Erwarte Wegpunktauswahl\n</string>
<string name="msg_alternative">Alternative</string>
<string name="msg_status_result">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</string>
<string name="msg_read_wpt_error">Fehler beim Lesen von Wegpunkten</string>
<string name="msg_no_wpt">Die Koordinatenquelle enthält keine Wegpunkte!</string>
<string name="msg_too_much_wpts">Die Koordinatenquelle enthält zu viele Wegpunkte: %1$d (bitte von/nach/via-Namen verwenden)</string>
<string name="no">Nein</string>
<string name="yes">Ja</string>
<string name="title_delete">Löschen bestätigen</string>
<string name="summary_delete">Wirklich löschen?</string>
<string name="title_version">Versionsproblem</string>
<string name="summary_version">Die Datenversion der Kacheln hat sich geändert. Bitte auswählen:</string>
<string name="action_version1">Download fortsetzen und alte Daten löschen?</string>
<string name="action_version2">Alle auswählen und Download starten</string>
<string name="action_version3">Abbrechen und später fortsetzen</string>
<string name="title_version_diff">Versionskonflikt</string>
<string name="summary_version_diff">Die Datenversion für einige Kacheln ist unterschiedlich. Bitte auswählen:</string>
<string name="action_version_diff1">Download alle unterschiedlichen Kacheln</string>
<string name="action_version_diff2">Überspringe alle unterschiedlichen Kacheln</string>
<string name="action_version_diff3">Abbrechen und später fortsetzen</string>
<string name="summary_new_version">Die neue Datenversion verlangt ein BRouter-Update. Bitte aktualisieren</string>
<string name="msg_download_failed">Download fehlgeschlagen</string>
<string name="msg_download_cancel">Download abgebrochen</string>
<string name="msg_download_succeed">Download erfolgreich</string>
<string name="msg_download_started">Download läuft…</string>
</resources>

View file

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
<item quantity="one">%d τμήμα</item>
<item quantity="other">%d τμήματα</item>
</plurals>
<string name="cancel_download">Ακύρωση λήψης</string>
<string name="import_profile">Εισαγωγή προφίλ</string>
<string name="action_download">Λήψη %s</string>
<string name="action_delete">Διαγραφή %s</string>
<string name="action_update">Ενημέρωση %s</string>
<string name="action_select">Επιλογή τμημάτων</string>
<string name="action_cancel">Διακοπή λήψης</string>
<string name="summary_segments">Μέγεθος=%1$s\nΕλεύθερο=%2$s</string>
<string name="notification_title">Λήψη τμημάτων</string>
<string name="action_choose_folder">Επιλέξτε φάκελο δεδομένων brouter:</string>
<string name="action_select_profile">Επιλέξτε ένα προφίλ δρομολόγησης</string>
<string name="main_action">Επιλέξτε ενέργεια</string>
<string name="main_action_1">Διαχειριστής λήψεων</string>
<string name="main_action_2">Εφαρμογή BRouter</string>
<string name="cancel">Ακύρωση</string>
<string name="i_know">Γνωρίζω</string>
<string name="close">Κλείσιμο</string>
<string name="exit">Έξοδος</string>
<string name="ok">OK</string>
<string name="success">Επιτυχία</string>
<string name="error">Παρουσιάστηκε σφάλμα</string>
<string name="title_download">BRouter διαχειριστής λήψεων</string>
<string name="summary_download">*** Προσοχή:***
\n\nΟ διαχειριστής λήψεων χρησιμοποιείται για τη λήψη αρχείων δεδομένων δρομολόγησης
που μπορεί να είναι έως 170MB το καθένα. Μην ξεκινήσετε το διαχειριστή λήψεων
σε σύνδεση δεδομένων κινητής τηλεφωνίας χωρίς πρόγραμμα δεδομένων!
Η ταχύτητα λήψης περιορίζεται στα 16 MBit/s.</string>
<string name="title_timeoutfree">Ετοιμάστηκε επιτυχώς ένας υπολογισμός χωρίς χρονικό όριο</string>
<string name="summary_timeoutfree">Επαναλάβατε με επιτυχία έναν υπολογισμό που πριν είχε χρονικό όριο
όταν ξεκίνησε από το εργαλείο χαρτών σας. Εάν επαναλάβετε το ίδιο αίτημα
από το εργαλείο χαρτών σας, με τον ίδιο ακριβώς προορισμό και μια κοντινή αφετηρία,
αυτό το αίτημα είναι εγγυημένο ότι δεν θα λήξει.</string>
<string name="title_sdcard">Εισάγετε φάκελο SDCARD:</string>
<string name="title_action">Επιλέξτε ενέργεια</string>
<string name="action_via_select">Ελέγξτε την επιλογή VIA:</string>
<string name="action_nogo_select">Ελέγξτε την επιλογή NoGo:</string>
<string name="action_servermode">Λειτουργία διακομιστή</string>
<string name="action_info">Πληροφορίες</string>
<string name="action_calc_route">Υπολογισμός διαδρομής</string>
<string name="action_profile_settings">Ρυθμίσεις προφίλ</string>
<string name="action_share">Κοινή χρήση GPX</string>
<string name="action_select_from">Επιλέξτε από</string>
<string name="action_select_to">Επιλέξτε προς/μέσω</string>
<string name="msg_no_profile">μη δεδομένα προφίλ</string>
<string name="msg_no_used_profile">, μη χρησιμοποιημένο προφίλ</string>
<string name="msg_too_much_data">Πάρα πολλά δεδομένα για λήψη. Παρακαλώ μειώστε.</string>
<string name="msg_download_start">Προγραμματισμένη λήψη. Ελέγξτε τη σύνδεση στο διαδίκτυο εάν δεν ξεκινά.</string>
<string name="msg_wpt_selection">τρέχουσα επιλογή σημείου:\n</string>
<string name="msg_no_wpt_selection">Αναμένεται επιλογή σημείου\n</string>
<string name="msg_alternative">Εναλλακτική</string>
<string name="msg_status_result">έκδοση = BRouter-%1$s \n
μνήμη = %2$s \n
απόσταση = %3$s km\n
φιλτραρισμένη άνοδος = %4$s m\n
απλή άνοδος = %5$s m\n
εκτιμώμενος χρόνος = %6$s</string>
<string name="msg_read_wpt_error">Σφάλμα ανάγνωσης σημείων</string>
<string name="msg_no_wpt">η πηγή συντεταγμένων δεν περιέχει σημεία!</string>
<string name="msg_too_much_wpts">η πηγή συντεταγμένων περιέχει πάρα πολλά σημεία: %1$d (παρακαλώ χρησιμοποιήστε από/προς/μέσω ονόματα)</string>
<string name="no">Όχι</string>
<string name="yes">Ναι</string>
<string name="title_delete">Επιβεβαίωση διαγραφής</string>
<string name="summary_delete">Να γίνει διαγραφή;</string>
<string name="title_version">Πρόβλημα έκδοσης</string>
<string name="summary_version">Η βασική έκδοση για τμήματα άλλαξε. Τι να κάνετε;</string>
<string name="action_version1">Συνέχεια με την τρέχουσα λήψη, διαγραφή άλλων παλαιών δεδομένων</string>
<string name="action_version2">Επιλέξτε όλα για λήψη και έναρξη</string>
<string name="action_version3">Ακύρωση τώρα, ολοκλήρωση άλλη μέρα</string>
<string name="title_version_diff">Διαφορές έκδοσης</string>
<string name="summary_version_diff">Η βασική έκδοση για ορισμένα τμήματα είναι διαφορετική. Τι να κάνετε;</string>
<string name="action_version_diff1">Λήψη όλων των διαφορετικών τμημάτων</string>
<string name="action_version_diff2">Απόρριψη όλων των διαφορετικών τμημάτων</string>
<string name="action_version_diff3">Ακύρωση τώρα, ολοκλήρωση άλλη μέρα</string>
<string name="summary_new_version">Η νέα έκδοση δεδομένων χρειάζεται νέα εφαρμογή. Ενημερώστε πρώτα το BRouter</string>
<string name="msg_download_failed">Η λήψη απέτυχε</string>
<string name="msg_download_cancel">Η λήψη ακυρώθηκε</string>
<string name="msg_download_succeed">Η λήψη ολοκληρώθηκε</string>
<string name="msg_download_started">Λήψη…</string>
</resources>

View file

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
<item quantity="one">%d segmento</item>
<item quantity="other">%d segmentos</item>
</plurals>
<string name="cancel_download">Cancelar descarga</string>
<string name="import_profile">Importar perfil</string>
<string name="action_download">Descargar %s</string>
<string name="action_delete">Eliminar %s</string>
<string name="action_update">Actualizar %s</string>
<string name="action_select">Seleccionar segmentos</string>
<string name="action_cancel">Detener descarga</string>
<string name="summary_segments">Tamaño=%1$s\nGratis=%2$s</string>
<string name="notification_title">Descargar segmentos</string>
<string name="action_choose_folder">Seleccionar directorio base brouter:</string>
<string name="action_select_profile">Seleccionar un perfil de enrutamiento</string>
<string name="main_action">Seleccionar Acción Principal</string>
<string name="main_action_1">Gestor de Descargas</string>
<string name="main_action_2">App BRouter</string>
<string name="cancel">Cancelar</string>
<string name="i_know">Lo se</string>
<string name="close">Cerrar</string>
<string name="exit">Salir</string>
<string name="ok">OK</string>
<string name="success">Salir</string>
<string name="error">Ha aparecido un error</string>
<string name="title_download">Gestor de descargas BRouter</string>
<string name="summary_download">*** 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.</string>
<string name="title_timeoutfree">Se ha preparado correctamente un cálculo sin límite de tiempo</string>
<string name="summary_timeoutfree">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.</string>
<string name="title_sdcard">Introduce el directorio base de la tarjeta SD:</string>
<string name="title_action">Seleccionar Acción</string>
<string name="action_via_select">Comprobar Selección VIA:</string>
<string name="action_nogo_select">Comprobar Selección NoGo:</string>
<string name="action_servermode">Modo-Servidor</string>
<string name="action_info">Info</string>
<string name="action_calc_route">Calc Ruta</string>
<string name="action_profile_settings">Ajustes del perfil</string>
<string name="action_share">Compartir GPX</string>
<string name="action_select_from">Seleccionar desde</string>
<string name="action_select_to">Seleccionar a/vía</string>
<string name="msg_no_profile">no hay datos en el perfil</string>
<string name="msg_no_used_profile">, ningún perfil utilizado</string>
<string name="msg_too_much_data">Demasiados datos a descargar. Por favor, reducidlos.</string>
<string name="msg_download_start">Descarga programada. Comprueba la conexión a internet si no se inicia.</string>
<string name="msg_wpt_selection">selección actual de waypoint:\n</string>
<string name="msg_no_wpt_selection">Esperando selección de waypoint\n</string>
<string name="msg_alternative">Alternativa</string>
<string name="msg_status_result">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</string>
<string name="msg_read_wpt_error">Error leyendo waypoints</string>
<string name="msg_no_wpt">la fuente de coordenadas no contiene ningún waypoint!</string>
<string name="msg_too_much_wpts">la fuente de coordenadas contiene demasiados waypoints: %1$d (por favor, usa nombres desde/a/vía)</string>
<string name="no">No</string>
<string name="yes">Si</string>
<string name="title_delete">Confirmar Eliminar</string>
<string name="summary_delete">¿Quieres realmente eliminar?</string>
<string name="title_version">Problema de versión</string>
<string name="summary_version">La versión base para as teselas ha cambiado. ¿Qué quieres hacer?</string>
<string name="action_version1">Continuar con la descarga, eliminar otros datos antiguos</string>
<string name="action_version2">Seleccionar todo para descargar e iniciar</string>
<string name="action_version3">Cancelar ahora, completar otro día</string>
<string name="title_version_diff">Diferencias de versión</string>
<string name="summary_version_diff">La versión base de varias teselas es distinta. ¿Qué quieres hacer?</string>
<string name="action_version_diff1">Descargar todas las teselas distintas</string>
<string name="action_version_diff2">Descartar las teselas distintas</string>
<string name="action_version_diff3">Cancelar ahora, completar otro día</string>
<string name="summary_new_version">La nueva versión de datos necesita un aplicación nueva. Por favor, actualiza BRouter primero</string>
<string name="msg_download_failed">Descarga fallida</string>
<string name="msg_download_cancel">Descarga cancelada</string>
<string name="msg_download_succeed">Descarga exitosa</string>
<string name="msg_download_started">Descargando…</string>
</resources>

View file

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
<item quantity="one">%d segment</item>
<item quantity="other">%d segments</item>
</plurals>
<string name="cancel_download">Annuler le téléchargement</string>
<string name="import_profile">Importer le profil</string>
<string name="action_download">Télécharger %s</string>
<string name="action_delete">Supprimer %s</string>
<string name="action_update">Mettre à jour %s</string>
<string name="action_select">Sélectionner les segments</string>
<string name="action_cancel">Arrêter le téléchargement</string>
<string name="summary_segments">Taille=%1$s\nGratuit=%2$s</string>
<string name="notification_title">Télécharger les segments</string>
<string name="action_choose_folder">Choisissez le répertoire de la base de données brouter:</string>
<string name="action_select_profile">Sélectionnez un profil de routage</string>
<string name="main_action">Sélectionner l\'action principale</string>
<string name="main_action_1">Gestionnaire de téléchargement</string>
<string name="main_action_2">Application BRouter</string>
<string name="cancel">Annuler</string>
<string name="i_know">Je sais</string>
<string name="close">Fermer</string>
<string name="exit">Quitter</string>
<string name="ok">OK</string>
<string name="success">Succès</string>
<string name="error">Une erreur s\'est produite</string>
<string name="title_download">Gestionnaire de téléchargement BRouter</string>
<string name="summary_download">*** 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.</string>
<string name="title_timeoutfree">Préparation réussie d\'un calcul sans délai d\'attente</string>
<string name="summary_timeoutfree">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.</string>
<string name="title_sdcard">Entrez le répertoire de base de la SDCARD:</string>
<string name="title_action">Sélectionner une action</string>
<string name="action_via_select">Vérifier la sélection VIA:</string>
<string name="action_nogo_select">Vérifier la sélection NoGo:</string>
<string name="action_servermode">Mode serveur</string>
<string name="action_info">Informations</string>
<string name="action_calc_route">Calculer l\'itinéraire</string>
<string name="action_profile_settings">Paramètres du profil</string>
<string name="action_share">Partager GPX</string>
<string name="action_select_from">Sélectionner de</string>
<string name="action_select_to">Sélectionner vers/via</string>
<string name="msg_no_profile">aucune donnée de profil</string>
<string name="msg_no_used_profile">, aucun profil utilisé</string>
<string name="msg_too_much_data">Trop de données à télécharger. Veuillez réduire.</string>
<string name="msg_download_start">Téléchargement planifié. Vérifiez la connexion Internet si elle ne démarre pas.</string>
<string name="msg_wpt_selection">sélection actuelle du waypoint:\n</string>
<string name="msg_no_wpt_selection">Sélection de waypoint en attente\n</string>
<string name="msg_alternative">Alternative</string>
<string name="msg_status_result">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</string>
<string name="msg_read_wpt_error">Erreur lors de la lecture des waypoints</string>
<string name="msg_no_wpt">la source de coordonnées ne contient aucun waypoint!</string>
<string name="msg_too_much_wpts">la source de coordonnées contient trop de waypoints: %1$d (veuillez utiliser les noms de/vers/via)</string>
<string name="no">Non</string>
<string name="yes">Oui</string>
<string name="title_delete">Confirmer la suppression</string>
<string name="summary_delete">Vraiment supprimer?</string>
<string name="title_version">Problème de version</string>
<string name="summary_version">La version de base des vignettes a changé. Que faire?</string>
<string name="action_version1">Continuer le téléchargement en cours, supprimer les autres anciennes données</string>
<string name="action_version2">Sélectionnez tout pour télécharger et démarrer</string>
<string name="action_version3">Annuler maintenant, terminer un autre jour</string>
<string name="title_version_diff">Différences de version</string>
<string name="summary_version_diff">La version de base de certaines vignettes est différente. Que faire?</string>
<string name="action_version_diff1">Télécharger toutes les différentes vignettes</string>
<string name="action_version_diff2">Supprimez toutes les différentes vignettes</string>
<string name="action_version_diff3">Annuler maintenant, terminer un autre jour</string>
<string name="summary_new_version">La nouvelle version des données nécessite une nouvelle application. Veuillez d\'abord mettre à jour BRouter</string>
<string name="msg_download_failed">Téléchargement échoué</string>
<string name="msg_download_cancel">Téléchargement annulé</string>
<string name="msg_download_succeed">Téléchargement réussi</string>
<string name="msg_download_started">Téléchargement…</string>
</resources>

View file

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
<item quantity="one">%d segmento</item>
<item quantity="other">%d segmenti</item>
</plurals>
<string name="cancel_download">Annulla download</string>
<string name="import_profile">Importa profilo</string>
<string name="action_download">Scarica %s</string>
<string name="action_delete">Elimina %s</string>
<string name="action_update">Aggiorna %s</string>
<string name="action_select">Seleziona segmenti</string>
<string name="action_cancel">Interrompi download</string>
<string name="summary_segments">Taglia=%1$s\nGratis=%2$s</string>
<string name="notification_title">Scarica segmenti</string>
<string name="action_choose_folder">Seleziona la directory del database brouter:</string>
<string name="action_select_profile">Seleziona un profilo di instradamento</string>
<string name="main_action">Seleziona azione principale</string>
<string name="main_action_1">Gestore dei download</string>
<string name="main_action_2">Applicazione BRouter</string>
<string name="cancel">Annulla</string>
<string name="i_know">Lo so</string>
<string name="close">Chiudi</string>
<string name="exit">Esci</string>
<string name="ok">OK</string>
<string name="success">Successo</string>
<string name="error">Si è verificato un errore</string>
<string name="title_download">Gestore download BRouter</string>
<string name="summary_download">*** 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.</string>
<string name="title_timeoutfree">Preparato con successo un calcolo senza timeout</string>
<string name="summary_timeoutfree">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.</string>
<string name="title_sdcard">Inserisci la directory base della SDCARD:</string>
<string name="title_action">Seleziona azione</string>
<string name="action_via_select">Controlla la selezione VIA:</string>
<string name="action_nogo_select">Controlla la selezione NoGo:</string>
<string name="action_servermode">Modalità server</string>
<string name="action_info">Informazioni</string>
<string name="action_calc_route">Calcola percorso</string>
<string name="action_profile_settings">Impostazioni profilo</string>
<string name="action_share">Condividi GPX</string>
<string name="action_select_from">Seleziona da</string>
<string name="action_select_to">Seleziona a/via</string>
<string name="msg_no_profile">nessun dato del profilo</string>
<string name="msg_no_used_profile">, nessun profilo utilizzato</string>
<string name="msg_too_much_data">Troppi dati per il download. Per favore riduci.</string>
<string name="msg_download_start">Download programmato. Controlla la connessione Internet se non si avvia.</string>
<string name="msg_wpt_selection">selezione del waypoint corrente:\n</string>
<string name="msg_no_wpt_selection">Attesa selezione waypoint\n</string>
<string name="msg_alternative">Alternativa</string>
<string name="msg_status_result">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</string>
<string name="msg_read_wpt_error">Errore durante la lettura dei waypoint</string>
<string name="msg_no_wpt">la fonte delle coordinate non contiene alcun waypoint!</string>
<string name="msg_too_much_wpts">la fonte delle coordinate contiene troppi waypoint: %1$d (usa i nomi da/a/via)</string>
<string name="no">No</string>
<string name="yes"></string>
<string name="title_delete">Conferma eliminazione</string>
<string name="summary_delete">Eliminare davvero?</string>
<string name="title_version">Problema di versione</string>
<string name="summary_version">La versione base per i riquadri è cambiata. Cosa fare?</string>
<string name="action_version1">Continua con il download corrente, elimina altri vecchi dati</string>
<string name="action_version2">Seleziona tutto per il download e avvia</string>
<string name="action_version3">Annulla ora, completa un altro giorno</string>
<string name="title_version_diff">Differenze di versione</string>
<string name="summary_version_diff">La versione base di alcuni riquadri è diversa. Cosa fare?</string>
<string name="action_version_diff1">Scarica tutti i diversi riquadri</string>
<string name="action_version_diff2">Rilascia tutti i diversi riquadri</string>
<string name="action_version_diff3">Annulla ora, completa un altro giorno</string>
<string name="summary_new_version">La nuova versione dei dati necessita di una nuova app. Per favore aggiorna prima BRouter</string>
<string name="msg_download_failed">Download fallito</string>
<string name="msg_download_cancel">Download annullato</string>
<string name="msg_download_succeed">Download riuscito</string>
<string name="msg_download_started">Download in corso…</string>
</resources>

View file

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
<item quantity="one">%d 구간</item>
<item quantity="other">%d 구간</item>
</plurals>
<string name="cancel_download">다운로드 취소</string>
<string name="import_profile">프로필 가져오기</string>
<string name="action_download">다운로드 %s</string>
<string name="action_delete">삭제 %s</string>
<string name="action_update">업데이트 %s</string>
<string name="action_select">구간 선택</string>
<string name="action_cancel">다운로드 중지</string>
<string name="summary_segments">크기=%1$s\n여유=%2$s</string>
<string name="notification_title">구간 다운로드</string>
<string name="action_choose_folder">brouter 데이터 베이스 선택 dir:</string>
<string name="action_select_profile">루트 프로필 선택</string>
<string name="main_action">주 활동 선택</string>
<string name="main_action_1">다운로드 관리자</string>
<string name="main_action_2">BRouter 앱</string>
<string name="cancel">취소</string>
<string name="i_know">알고 있습니다</string>
<string name="close">가까운</string>
<string name="exit">나가기</string>
<string name="ok">OK</string>
<string name="success">성공</string>
<string name="error">에러가 발생됐습니다</string>
<string name="title_download">BRouter 다운로드 관리자</string>
<string name="summary_download">*** 주의:***
\n\n다운로드 관리자는 라우팅 데이터를 다운로드하는 데 사용됩니다.
파일당 최대 170MB까지 가능합니다. 다운로드 관리자를 시작하지 마십시오
데이터 요금제 없이 셀룰러 데이터 연결로!
다운로드 속도는 16 MBit/s로 제한됩니다.</string>
<string name="title_timeoutfree">시간 초과 없는 계산을 성공적으로 준비했습니다</string>
<string name="summary_timeoutfree">이전에 시간 초과가 발생한 계산을 성공적으로 반복하셨습니다.
지도 도구에서 시작할 때. 귀하가 귀하로부터 동일한 요청을 반복하는 경우
정확히 동일한 목적지와 가까운 시작점이 있는 지도 도구,
이 요청은 시간 초과되지 않음이 보장됩니다.</string>
<string name="title_sdcard">SDCARD 기본 디렉토리 입력:</string>
<string name="title_action">작업 선택</string>
<string name="action_via_select">경유 선택 체크:</string>
<string name="action_nogo_select">NoGo 선택 체크:</string>
<string name="action_servermode">서버-모드</string>
<string name="action_info">정보</string>
<string name="action_calc_route">루트 계산</string>
<string name="action_profile_settings">프로필 설정</string>
<string name="action_share">GPX 공유</string>
<string name="action_select_from">~로부터 선택</string>
<string name="action_select_to">선택 ~로/경유하여</string>
<string name="msg_no_profile">프로필 데이터 없음</string>
<string name="msg_no_used_profile">, 사용된 프로필 없음</string>
<string name="msg_too_much_data">다운로드 하기에 너무 많은 데이터. 줄이도록 하세요.</string>
<string name="msg_download_start">다운로드 계획되어 있음. 인터넷 연결 체크 그렇지 않다면 시작.</string>
<string name="msg_wpt_selection">현재 장소 선택:\n</string>
<string name="msg_no_wpt_selection">장소 선택 기대\n</string>
<string name="msg_alternative">대체 가능한</string>
<string name="msg_status_result">버전 = BRouter-%1$s \n
메모리 = %2$s \n
거리 = %3$s km\n
필터링된 오름차순 = %4$s m\n
일반 오름차순 = %5$s m\n
예상 시간 = %6$s</string>
<string name="msg_read_wpt_error">장소 읽는데 에러</string>
<string name="msg_no_wpt">좌표정보는 어떤 장소도 포함하고 있지 않습니다!</string>
<string name="msg_too_much_wpts">좌표정보는 너무 많은 장소를 포함하고 있습니다: %1$d (~로 부터/~로/경유 이름 사용하세요)</string>
<string name="no">아니요</string>
<string name="yes"></string>
<string name="title_delete">삭제 확인</string>
<string name="summary_delete">정말 삭제하시겠습니까?</string>
<string name="title_version">버전 문제</string>
<string name="summary_version">타일을 위한 기본 버전이 바뀌었습니다. 무엇을 해야죠?</string>
<string name="action_version1">현재 다운로드 계속, 다른 오래된 데이터 삭제</string>
<string name="action_version2">다운로드 위해 모두 선택하고 시작</string>
<string name="action_version3">지금 취소, 다른 날 완료</string>
<string name="title_version_diff">버전 차이</string>
<string name="summary_version_diff">어떤 타일을 위한 기본 버전은 다릅니다, 무엇을 해야죠?</string>
<string name="action_version_diff1">모든 다른 타일 다운로드</string>
<string name="action_version_diff2">모든 다른 타일 백지화</string>
<string name="action_version_diff3">지금 취소, 다른 날 완료</string>
<string name="summary_new_version">새로운 데이터 버전은 새로운 앱을 필요로 합니다. 우선 BRouter를 업데이트 하세요</string>
<string name="msg_download_failed">다운로드 실패</string>
<string name="msg_download_cancel">다운로드 취소됨</string>
<string name="msg_download_succeed">다운로드 성공</string>
<string name="msg_download_started">다운로딩…</string>
</resources>

View file

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
<item quantity="one">%d segment</item>
<item quantity="other">%d segmenten</item>
</plurals>
<string name="cancel_download">Download annuleren</string>
<string name="import_profile">Profiel importeren</string>
<string name="action_download">Downloaden %s</string>
<string name="action_delete">Verwijderen %s</string>
<string name="action_update">Bijwerken %s</string>
<string name="action_select">Segmenten selecteren</string>
<string name="action_cancel">Download stoppen</string>
<string name="summary_segments">Grootte=%1$s\nGratis=%2$s</string>
<string name="notification_title">Segmenten downloaden</string>
<string name="action_choose_folder">Kies de brouter database map:</string>
<string name="action_select_profile">Selecteer een routingprofiel</string>
<string name="main_action">Selecteer de hoofdactie</string>
<string name="main_action_1">Download beheerder</string>
<string name="main_action_2">BRouter App</string>
<string name="cancel">Annuleer</string>
<string name="i_know">Ik weet het</string>
<string name="close">Sluiten</string>
<string name="exit">Verlaat</string>
<string name="ok">OK</string>
<string name="success">Succes</string>
<string name="error">Er is een fout opgetreden</string>
<string name="title_download">BRouter Download Manager</string>
<string name="summary_download">*** 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.</string>
<string name="title_timeoutfree">Een time-outvrije berekening is succesvol voorbereid</string>
<string name="summary_timeoutfree">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.</string>
<string name="title_sdcard">De SDCARD basis directory invoeren:</string>
<string name="title_action">Actie selecteren</string>
<string name="action_via_select">Controleer de VIA selectie:</string>
<string name="action_nogo_select">Controleer de NoGo selectie:</string>
<string name="action_servermode">Server-mode</string>
<string name="action_info">Info</string>
<string name="action_calc_route">Bereken route</string>
<string name="action_profile_settings">Profiel instellingen</string>
<string name="action_share">GPX delen</string>
<string name="action_select_from">Selecteer van</string>
<string name="action_select_to">Selecteer naar/via</string>
<string name="msg_no_profile">geen profiel data</string>
<string name="msg_no_used_profile">, geen gebruikt profiel</string>
<string name="msg_too_much_data">Te veel gegevens om te downloaden. Verminder a.u.b.</string>
<string name="msg_download_start">Download is gepland. Controleer de internetverbinding als deze niet start.</string>
<string name="msg_wpt_selection">huidige waypointselectie:\n</string>
<string name="msg_no_wpt_selection">Verwacht waypointselectie\n</string>
<string name="msg_alternative">Alternatief</string>
<string name="msg_status_result">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</string>
<string name="msg_read_wpt_error">Fout bij het lezen van waypoints</string>
<string name="msg_no_wpt">coördinaten bron bevat geen waypoints!</string>
<string name="msg_too_much_wpts">De coördinatenbron bevat te veel waypoints: %1$d (gebruik van/naar/via namen)</string>
<string name="no">Nee</string>
<string name="yes">Ja</string>
<string name="title_delete">Verwijderen bevestigen</string>
<string name="summary_delete">Echt verwijderen?</string>
<string name="title_version">Versie probleem</string>
<string name="summary_version">De basisversie voor de tegels is gewijzigd. Wat moet er gebeuren?</string>
<string name="action_version1">Doorgaan met de huidige download, verwijder de oude gegevens.</string>
<string name="action_version2">Selecteer alles om te downloaden en start</string>
<string name="action_version3">Annuleer nu, voltooi op een andere dag</string>
<string name="title_version_diff">Versieverschillen</string>
<string name="summary_version_diff">De basisversie voor sommige tiles is anders. Wat moet er gebeuren?</string>
<string name="action_version_diff1">Download alle verschillende tegels</string>
<string name="action_version_diff2">Laat alle verschillende tegels vallen</string>
<string name="action_version_diff3">Annuleer nu, en voltooi op een andere dag</string>
<string name="summary_new_version">Voor de nieuwe gegevensversie is een nieuwe app nodig. Update eerst de BRouter</string>
<string name="msg_download_failed">Download is mislukt</string>
<string name="msg_download_cancel">Download is geannuleerd</string>
<string name="msg_download_succeed">Download is geslaagd</string>
<string name="msg_download_started">Downloaden…</string>
</resources>

View file

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
<item quantity="one">%d segment</item>
<item quantity="other">%d segmenty/ów</item>
</plurals>
<string name="cancel_download">Anuluj pobieranie</string>
<string name="import_profile">Importuj profil</string>
<string name="action_download">Pobierz %s</string>
<string name="action_delete">Usuń %s</string>
<string name="action_update">Zaktualizuj %s</string>
<string name="action_select">Wybierz segmenty</string>
<string name="action_cancel">Zatrzymaj pobieranie</string>
<string name="summary_segments">Rozmiar=%1$s\nDostępne=%2$s</string>
<string name="notification_title">Pobieranie segmentów</string>
<string name="action_choose_folder">Wybierz katalog bazy danych BRoutera:</string>
<string name="action_select_profile">Wybierz profil routingu</string>
<string name="main_action">Wybierz akcję główną</string>
<string name="main_action_1">Menedżer pobierania</string>
<string name="main_action_2">Aplikacja BRouter</string>
<string name="cancel">Anuluj</string>
<string name="i_know">Rozumiem</string>
<string name="close">Zamknij</string>
<string name="exit">Wyjdź</string>
<string name="ok">OK</string>
<string name="success">Sukces</string>
<string name="error">Wystąpił błąd</string>
<string name="title_download">Menedżer pobierania BRoutera</string>
<string name="summary_download">*** 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.</string>
<string name="title_timeoutfree">Pomyślnie przygotowano obliczenia nie przekraczając limitu czasu</string>
<string name="summary_timeoutfree">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.</string>
<string name="title_sdcard">Wybierz katalog SDCARD dla bazy:</string>
<string name="title_action">Wybierz akcję</string>
<string name="action_via_select">Sprawdź wybór VIA:</string>
<string name="action_nogo_select">Sprawdź wybór NoGo:</string>
<string name="action_servermode">Tryb serwera</string>
<string name="action_info">Informacje</string>
<string name="action_calc_route">Oblicz trasę</string>
<string name="action_profile_settings">Ustawienia profilu</string>
<string name="action_share">Udostępnij GPX</string>
<string name="action_select_from">Wybierz z</string>
<string name="action_select_to">Wybierz do/przez</string>
<string name="msg_no_profile">brak danych profilu</string>
<string name="msg_no_used_profile">, brak używanego profilu</string>
<string name="msg_too_much_data">Za dużo danych do pobrania. Proszę ogranicz.</string>
<string name="msg_download_start">Pobieranie zaplanowane. Jeśli się nie rozpocznie, sprawdź połączenie internetowe.</string>
<string name="msg_wpt_selection">bieżący wybór punktu trasy:\n</string>
<string name="msg_no_wpt_selection">Oczekuję na wybór punktu trasy\n</string>
<string name="msg_alternative">Alternatywa</string>
<string name="msg_status_result">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</string>
<string name="msg_read_wpt_error">Błąd podczas odczytu punktów trasy</string>
<string name="msg_no_wpt">Źródło współrzędnych nie zawiera żadnych punktów trasy!</string>
<string name="msg_too_much_wpts">Źródło współrzędnych zawiera zbyt dużo punktów trasy: %1$d (proszę używać nazw z/do/przez)</string>
<string name="no">Nie</string>
<string name="yes">Tak</string>
<string name="title_delete">Potwierdź usunięcie</string>
<string name="summary_delete">Naprawdę usunąć?</string>
<string name="title_version">Problem z wersją</string>
<string name="summary_version">Zmieniła się podstawowa wersja kafelków. Co robić?</string>
<string name="action_version1">Kontynuuj bieżące pobieranie, usuń inne stare dane</string>
<string name="action_version2">Wybierz wszystko i rozpocznij pobieranie</string>
<string name="action_version3">Anuluj teraz, dokończ innego dnia</string>
<string name="title_version_diff">Różnice wersji</string>
<string name="summary_version_diff">Wersja podstawowa dla niektórych kafelków jest inna. Co robić?</string>
<string name="action_version_diff1">Pobierz różne kafelki</string>
<string name="action_version_diff2">Opuść wszystkie różne kafelki</string>
<string name="action_version_diff3">Anuluj teraz, dokończ innego dnia</string>
<string name="summary_new_version">Nowa wersja danych wymaga nowej aplikacji. Najpierw zaktualizuj BRoutera</string>
<string name="msg_download_failed">Pobieranie nie powiodło się</string>
<string name="msg_download_cancel">Pobieranie anulowane</string>
<string name="msg_download_succeed">Pobieranie powiodło się</string>
<string name="msg_download_started">Pobieranie…</string>
</resources>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">BRouter</string>
<string name="profile_filename_example">filename.brf</string>
<string name="notification_channel_id">brouter_download</string>
<string name="channel_name">Downloads</string>
</resources>

View file

@ -1,37 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<plurals name="numberOfSegments">
<resources xmlns:tools="http://schemas.android.com/tools">
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
<item quantity="one">%d segment</item>
<item quantity="other">%d segments</item>
</plurals>
<string name="app_name">BRouter</string>
<string name="cancel_download">Cancel Download</string>
<string name="import_profile">Import Profile</string>
<string name="profile_filename_example">filename.brf</string>
<string name="download_info_start">Starting download…</string>
<string name="download_info_cancel">Cancelling…</string>
<string name="cancel_download">Cancel download</string>
<string name="import_profile">Import profile</string>
<string name="action_download">Download %s</string>
<string name="action_delete">Delete %s</string>
<string name="action_update">Update %s</string>
<string name="action_select">Select segments</string>
<string name="action_cancel">Stop Download</string>
<string name="summary_segments">Size=%s\nFree=%s</string>
<string name="notification_channel_id">brouter_download</string>
<string name="notification_title">Download Segments</string>
<string name="channel_name">Downloads</string>
<string name="action_cancel">Stop download</string>
<string name="summary_segments">Size=%1$s\nFree=%2$s</string>
<string name="notification_title">Download segments</string>
<string name="action_choose_folder">Choose brouter data base dir:</string>
<string name="action_select_profile">Select a routing profile</string>
<string name="main_action">Select Main Action</string>
<string name="main_action_1">Download Manager</string>
<string name="main_action_2">BRouter App</string>
<string name="cancel">Cancel</string>
<string name="i_know">I know</string>
<string name="close">Close</string>
<string name="exit">Exit</string>
<string name="ok">OK</string>
<string name="success">Success</string>
<string name="error">An Error occurred</string>
<string name="title_download">BRouter Download Manager</string>
<string name="summary_download">*** 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.</string>
<string name="title_timeoutfree">Successfully prepared a timeout-free calculation</string>
<string name="summary_timeoutfree">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.</string>
<string name="title_sdcard">Enter SDCARD base dir:</string>
<string name="title_action">Select Action</string>
<string name="action_via_select">Check VIA Selection:</string>
<string name="action_nogo_select">Check NoGo Selection:</string>
<string name="action_servermode">Server-Mode</string>
<string name="action_info">Info</string>
<string name="action_calc_route">Calc Route</string>
<string name="action_profile_settings">Profile Settings</string>
<string name="action_share">Share GPX</string>
<string name="action_select_from">Select from</string>
<string name="action_select_to">Select to/via</string>
<string name="msg_no_profile">no profile data</string>
<string name="msg_no_used_profile">, no used profile</string>
<string name="msg_too_much_data">Too much data for download. Please reduce.</string>
<string name="msg_download_start">Download scheduled. Check internet connection if it doesn\'t start.</string>
<string name="msg_wpt_selection">current waypoint selection:\n</string>
<string name="msg_no_wpt_selection">Expecting waypoint selection\n</string>
<string name="msg_alternative">Alternative</string>
<string name="msg_status_result">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</string>
<string name="msg_read_wpt_error">Error reading waypoints</string>
<string name="msg_no_wpt">coordinate source does not contain any waypoints!</string>
<string name="msg_too_much_wpts">coordinate source contains too much waypoints: %1$d (please use from/to/via names)</string>
<string name="no">No</string>
<string name="yes">Yes</string>
<string name="title_delete">Confirm Delete</string>
<string name="summary_delete">Really delete?</string>
<string name="title_version">Version Problem</string>
<string name="summary_version">The base version for tiles has changed. What to do?</string>
<string name="action_version1">Continue with current download, delete other old data</string>
<string name="action_version2">Select all for download and start</string>
<string name="action_version3">Cancel now, complete on another day</string>
<string name="title_version_diff">Version Differences</string>
<string name="summary_version_diff">The base version for some tiles is different. What to do?</string>
<string name="action_version_diff1">Download all different tiles</string>
<string name="action_version_diff2">Drop all different tiles</string>
<string name="action_version_diff3">Cancel now, complete on another day</string>
<string name="summary_new_version">The new data version needs a new app. Please update BRouter first</string>
<string name="msg_download_failed">Download failed</string>
<string name="msg_download_cancel">Download cancelled</string>
<string name="msg_download_succeed">Download succeeded</string>
<string name="msg_download_started">Downloading…</string>
</resources>

View file

@ -1 +0,0 @@
/build/

View file

@ -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'
}

View file

@ -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<OsmNodeNamed> wplist = routingParamCollector.getWayPointList(lonlats);
Map<String, String> 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<OsmNodeNamed> 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 <segmentdir> <lon-from> <lat-from> <lon-to> <lat-to> <profile>");
return;
System.out.println("usage: java -jar brouter.jar <segmentdir> <profiledir> <engineMode> <profile> <lonlats-list> [parameter-list] [profile-parameter-list] ");
System.out.println(" or: java -cp %CLASSPATH% btools.server.BRouter <segmentdir>> <profiledir> <engineMode> <profile> <lonlats-list> [parameter-list] [profile-parameter-list]");
System.out.println(" or: java -jar brouter.jar <segmentdir> <profiledir> <parameter-list> [output-filename]");
System.exit(0);
}
List<OsmNodeNamed> 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<OsmNodeNamed> 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<String, String> params = routingParamCollector.getUrlParams(moreParams);
routingParamCollector.setParams(rc, wplist, params);
}
if (profileParams != null) {
Map<String, String> 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;
}
}

View file

@ -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<String, Long> newMap = new HashMap<>(ipAccess.size());
Map<String, Long> newMap = new HashMap<>(ipAccess.size());
for (Map.Entry<String, Long> e : ipAccess.entrySet()) {
if (t - e.getValue().longValue() <= MAX_IDLE) {
if (t - e.getValue() <= MAX_IDLE) {
newMap.put(e.getKey(), e.getValue());
}
}

View file

@ -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<String> lines = new ArrayList<>();
List<String> lines = new ArrayList<>();
for (; ; ) {
String line = br.readLine();

View file

@ -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<RouteServer> {
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<RouteServer> {
}
String url = getline.split(" ")[1];
Map<String, String> params = getUrlParams(url);
RoutingParamCollector routingParamCollector = new RoutingParamCollector();
Map<String, String> params = routingParamCollector.getUrlParams(url);
long maxRunningTime = getMaxRunningTime();
@ -186,33 +190,21 @@ public class RouteServer extends Thread implements Comparable<RouteServer> {
return;
}
RoutingContext rc = handler.readRoutingContext();
List<OsmNodeNamed> wplist = handler.readWayPointList();
List<OsmNodeNamed> wplist = routingParamCollector.getWayPointList(params.get("lonlats"));
if (wplist.size() < 10) {
SuspectManager.nearRecentWps.add(wplist);
}
int engineMode = 0;
for (Map.Entry<String, String> 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<RouteServer> {
} 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<RouteServer> {
ProfileCache.setSize(2 * maxthreads);
PriorityQueue<RouteServer> threadQueue = new PriorityQueue<>();
Queue<RouteServer> 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<RouteServer> {
private static Map<String, String> getUrlParams(String url) throws UnsupportedEncodingException {
HashMap<String, String> params = new HashMap<>();
Map<String, String> 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<RouteServer> {
bw.write("\n");
}
private static void cleanupThreadQueue(PriorityQueue<RouteServer> threadQueue) {
private static void cleanupThreadQueue(Queue<RouteServer> threadQueue) {
for (; ; ) {
boolean removedItem = false;
for (RouteServer t : threadQueue) {

Some files were not shown because too many files have changed in this diff Show more