Compare commits

..

No commits in common. "master" and "revert-342-test-and11" have entirely different histories.

453 changed files with 27878 additions and 71699 deletions

View file

@ -1,14 +0,0 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.java]
indent_style = space
indent_size = 2
[*.gradle]
indent_style = space
indent_size = 4

View file

@ -1,12 +0,0 @@
# Commits which should be ignored by git blame
# You have to instruct git to use this file using either
# `git blame --ignore-revs-file .git-blame-ignore-revs`
# or update you git config to always include those commits
# `git config blame.ignoreRevsFile .git-blame-ignore-revs`
# Reformat brouter-routing-app using Android Studio
54d5c5e9439be2c3df4c95b6fc12d33fdcc9b389
# Reformat whole codebase using Android Studio
c15913c1ab9befd8d583d4a7716d5043d2966f64

View file

@ -1,98 +0,0 @@
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]
@ -12,35 +12,25 @@ jobs:
build:
runs-on: ubuntu-latest
environment: BRouter
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: Set up JDK 17
uses: actions/setup-java@v4
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
java-version: '17'
distribution: 'temurin'
java-version: '11'
distribution: 'adopt'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
- name: Setup keystore
env:
BROUTER_KEYSTORE_BASE64: ${{ secrets.BROUTER_KEYSTORE_BASE64 }}
run: |
echo $BROUTER_KEYSTORE_BASE64 | base64 -di > ${{ github.workspace }}/brouter.jks
- name: Build with Gradle
env:
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 }}
run: gradle build
# The USERNAME and TOKEN need to correspond to the credentials environment variables used in
# the publishing section of your build.gradle
- name: Publish to GitHub Packages

View file

@ -13,31 +13,14 @@ jobs:
build:
runs-on: ubuntu-latest
environment: BRouter
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
- uses: actions/checkout@v2
- name: Set up JDK 8
uses: actions/setup-java@v2
with:
java-version: '17'
distribution: 'temurin'
java-version: '8'
distribution: 'zulu'
cache: gradle
- name: Create local.properties
run: touch local.properties
- name: Setup keystore
env:
BROUTER_KEYSTORE_BASE64: ${{ secrets.BROUTER_KEYSTORE_BASE64 }}
run: |
echo $BROUTER_KEYSTORE_BASE64 | base64 -di > ${{ github.workspace }}/brouter.jks
- name: Build with Gradle
env:
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 }}
run: ./gradlew build
- name: Upload ZIP
uses: actions/upload-artifact@v4
with:
name: ZIP
path: brouter-server/build/distributions/brouter-*.zip

2
.gitignore vendored
View file

@ -1,7 +1,6 @@
*.iml
.gradle
.idea/
build
/local.properties
/.idea/caches
/.idea/gradle.xml
@ -11,6 +10,7 @@ build
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx

View file

@ -1,14 +0,0 @@
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

121
README.md
View file

@ -1,63 +1,6 @@
BRouter
=======
# Come fare
To build the Docker image run (in the project's top level directory):
```
docker build -t brouter .
```
Download the segment files
```
wget -rkpN -np -e robots=off -l1 https://brouter.de/brouter/segments4/
```
Download the profile files from github https://github.com/gpxstudio/brouter/tree/master/misc/profiles2 using
```
https://downgit.github.io/
```
or directly copy misc/profiles2 from git clone folder
si possono creare anche i profili con le varianti usando
```
generate_profile_variants.sh
```
si possono scaricare anche i profili di brouter originale usando
```
wget -rkpN -np -e robots=off -l1 https://brouter.de/brouter/profiles2/
```
e metterli nei folder che poi verranno caricati con i volumi condivisi nel docker
```
mv ./brouter.de/brouter/segments4 /home/nvme/dockerdata/brouter/segments
mv mv ./brouter.de/brouter/profiles2 /home/nvme/dockerdata/brouter/profiles
```
oppure usando lo script
```
download_segments.sh
```
far partire il docker
```
services:
brouterserver:
container_name: brouter
image: brouter
ports:
- 17777:17777
volumes:
- /home/nvme/dockerdata/brouter/segments:/segments4
- /home/nvme/dockerdata/brouter/profiles:/profiles2
restart: unless-stopped
```
## Come si interroga
```
https://brouter.patachina.it/?lonlats=12.08349699,44.25067665|12.09165011,44.24834552&profile=Trekking-dry&format=geojson&alternativeidx=0
```
# Original
BRouter is a configurable OSM offline router with elevation awareness, Java +
Android. Designed to be multi-modal with a particular emphasis on bicycle
and energy-based car routing.
@ -72,7 +15,7 @@ You can install the BRouter app on your Android device from
Store](https://play.google.com/store/apps/details?id=btools.routingapp). You
can also [build BRouter](#build-and-install) yourself. You can find detailed
documentation of the BRouter Android app in
[`docs/users/android_quickstart.md`](docs/users/android_quickstart.md).
[`misc/readmes/readme.txt`](misc/readmes/readme.txt).
<a href="https://f-droid.org/packages/btools.routingapp" target="_blank">
<img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="90"/></a>
@ -96,7 +39,7 @@ Alternatively, you can also use BRouter as the offline routing engine for
[OSMAnd](https://osmand.net/) on your Android device.
A full documentation on how to set this up is available at
[`docs/users/osmand.md`](docs/users/osmand.md).
[`misc/readmes/osmand/README.md`](misc/readmes/osmand/README.md).
## BRouter on Windows/Linux/Mac OS
@ -155,7 +98,7 @@ Segments files from the whole planet are generated weekly at
[https://brouter.de/brouter/segments4/](http://brouter.de/brouter/segments4/).
You can download one or more segments files, covering the area of the planet
you want to route, into the `misc/segments4` directory.
your want to route, into the `misc/segments4` directory.
#### Generate your own segments files
@ -163,7 +106,7 @@ You can also generate the segments files you need directly from a planet dump
of OpenStreetMap data (or a [GeoFabrik extract](https://download.geofabrik.de/)).
More documentation of this is available in the
[`docs/developers/build_segments.md`](docs/developers/build_segments.md) file.
[`misc/readmes/mapcreation.md`](misc/readmes/mapcreation.md) file.
### (Optional) Generate profile variants
@ -177,7 +120,7 @@ to help you quickly generate variants based on the default profiles, to create
a default set of profiles covering most of the basic use cases.
Have a look at the
[`docs/developers/profile_developers_guide.md`](docs/developers/profile_developers_guide.md)
[`misc/readmes/profile_developers_guide.txt`](misc/readmes/profile_developers_guide.txt)
for an in-depth guide on profiles edition and customization.
@ -194,62 +137,10 @@ 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
More documentation is available in the [`docs`](docs) folder.
More documentation is available in the [`misc/readmes`](misc/readmes) folder.
## Related Projects

1
brouter-codec/.gitignore vendored Normal file
View file

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

View file

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

View file

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

View file

@ -5,10 +5,11 @@ import btools.util.BitCoderContext;
/**
* Container for some re-usable databuffers for the decoder
*/
public final class DataBuffers {
public final class DataBuffers
{
public byte[] iobuffer;
public byte[] tagbuf1 = new byte[256];
public BitCoderContext bctx1 = new BitCoderContext(tagbuf1);
public BitCoderContext bctx1 = new BitCoderContext( tagbuf1 );
public byte[] bbuf1 = new byte[65636];
public int[] ibuf1 = new int[4096];
public int[] ibuf2 = new int[2048];
@ -16,15 +17,17 @@ public final class DataBuffers {
public int[] alon = new int[2048];
public int[] alat = new int[2048];
public DataBuffers() {
this(new byte[65636]);
public DataBuffers()
{
this( new byte[65636] );
}
/**
* construct a set of databuffers except
* for 'iobuffer', where the given array is used
*/
public DataBuffers(byte[] iobuffer) {
public DataBuffers( byte[] iobuffer )
{
this.iobuffer = iobuffer;
}

View file

@ -3,14 +3,16 @@ package btools.codec;
/**
* Special integer fifo suitable for 3-pass encoding
*/
public class IntegerFifo3Pass {
public class IntegerFifo3Pass
{
private int[] a;
private int size;
private int pos;
private int pass;
public IntegerFifo3Pass(int capacity) {
public IntegerFifo3Pass( int capacity )
{
a = capacity < 4 ? new int[4] : new int[capacity];
}
@ -18,7 +20,8 @@ public class IntegerFifo3Pass {
* Starts a new encoding pass and resets the reading pointer
* from the stats collected in pass2 and writes that to the given context
*/
public void init() {
public void init()
{
pass++;
pos = 0;
}
@ -26,11 +29,14 @@ public class IntegerFifo3Pass {
/**
* writes to the fifo in pass2
*/
public void add(int value) {
if (pass == 2) {
if (size == a.length) {
public void add( int value )
{
if ( pass == 2 )
{
if ( size == a.length )
{
int[] aa = new int[2 * size];
System.arraycopy(a, 0, aa, 0, size);
System.arraycopy( a, 0, aa, 0, size );
a = aa;
}
a[size++] = value;
@ -40,13 +46,16 @@ public class IntegerFifo3Pass {
/**
* reads from the fifo in pass3 (in pass1/2 returns just 1)
*/
public int getNext() {
return pass == 3 ? get(pos++) : 1;
public int getNext()
{
return pass == 3 ? get( pos++ ) : 1;
}
private int get(int idx) {
if (idx >= size) {
throw new IndexOutOfBoundsException("list size=" + size + " idx=" + idx);
private int get( int idx )
{
if ( idx >= size )
{
throw new IndexOutOfBoundsException( "list size=" + size + " idx=" + idx );
}
return a[idx];
}

View file

@ -3,7 +3,8 @@ package btools.codec;
/**
* Simple container for a list of lists of integers
*/
public class LinkedListContainer {
public class LinkedListContainer
{
private int[] ia; // prev, data, prev, data, ...
private int size;
private int[] startpointer; // 0=void, odd=head-data-cell
@ -11,44 +12,49 @@ public class LinkedListContainer {
/**
* Construct a container for the given number of lists
* <p>
*
* If no default-buffer is given, an int[nlists*4] is constructed,
* able to hold 2 entries per list on average
*
* @param nlists the number of lists
* @param nlists the number of lists
* @param defaultbuffer an optional data array for re-use (gets replaced if too small)
*/
public LinkedListContainer(int nlists, int[] defaultbuffer) {
ia = defaultbuffer == null ? new int[nlists * 4] : defaultbuffer;
startpointer = new int[nlists];
public LinkedListContainer( int nlists, int[] defaultbuffer )
{
ia = defaultbuffer == null ? new int[nlists*4] : defaultbuffer;
startpointer = new int[nlists];
}
/**
* Add a data element to the given list
*
* @param listNr the list to add the data to
* @param data the data value
* @param data the data value
*/
public void addDataElement(int listNr, int data) {
if (size + 2 > ia.length) {
public void addDataElement( int listNr, int data )
{
if ( size + 2 > ia.length )
{
resize();
}
ia[size++] = startpointer[listNr];
startpointer[listNr] = size;
ia[size++] = startpointer[ listNr ];
startpointer[ listNr ] = size;
ia[size++] = data;
}
/**
* Initialize a list for reading
*
* @param listNr the list to initialize
* @return the number of entries in that list
*/
public int initList(int listNr) {
public int initList( int listNr )
{
int cnt = 0;
int lp = listpointer = startpointer[listNr];
while (lp != 0) {
lp = ia[lp - 1];
int lp = listpointer = startpointer[ listNr ];
while( lp != 0 )
{
lp = ia[ lp-1 ];
cnt++;
}
return cnt;
@ -61,18 +67,21 @@ public class LinkedListContainer {
* @return the data element
* @throws IllegalArgumentException if no more element
*/
public int getDataElement() {
if (listpointer == 0) {
throw new IllegalArgumentException("no more element!");
public int getDataElement()
{
if ( listpointer == 0 )
{
throw new IllegalArgumentException( "no more element!" );
}
int data = ia[listpointer];
listpointer = ia[listpointer - 1];
int data = ia[ listpointer ];
listpointer = ia[ listpointer-1 ];
return data;
}
private void resize() {
int[] ia2 = new int[2 * ia.length];
System.arraycopy(ia, 0, ia2, 0, ia.length);
private void resize()
{
int[] ia2 = new int[2*ia.length];
System.arraycopy( ia, 0, ia2, 0, ia.length );
ia = ia2;
}
}

View file

@ -5,20 +5,21 @@ import btools.util.ByteDataWriter;
/**
* a micro-cache is a data cache for an area of some square kilometers or some
* hundreds or thousands nodes
* <p>
*
* This is the basic io-unit: always a full microcache is loaded from the
* data-file if a node is requested at a position not yet covered by the caches
* already loaded
* <p>
*
* The nodes are represented in a compact way (typical 20-50 bytes per node),
* but in a way that they do not depend on each other, and garbage collection is
* supported to remove the nodes already consumed from the cache.
* <p>
*
* The cache-internal data representation is different from that in the
* data-files, where a cache is encoded as a whole, allowing more
* redundancy-removal for a more compact encoding
*/
public class MicroCache extends ByteDataWriter {
public class MicroCache extends ByteDataWriter
{
protected int[] faid;
protected int[] fapos;
protected int size = 0;
@ -34,21 +35,25 @@ public class MicroCache extends ByteDataWriter {
public static boolean debug = false;
protected MicroCache(byte[] ab) {
super(ab);
protected MicroCache( byte[] ab )
{
super( ab );
}
public final static MicroCache emptyNonVirgin = new MicroCache(null);
public final static MicroCache emptyNonVirgin = new MicroCache( null );
static {
static
{
emptyNonVirgin.virgin = false;
}
public static MicroCache emptyCache() {
return new MicroCache(null); // TODO: singleton?
public static MicroCache emptyCache()
{
return new MicroCache( null ); // TODO: singleton?
}
protected void init(int size) {
protected void init( int size )
{
this.size = size;
delcount = 0;
delbytes = 0;
@ -57,31 +62,35 @@ public class MicroCache extends ByteDataWriter {
p2size >>= 1;
}
public final void finishNode(long id) {
public final void finishNode( long id )
{
fapos[size] = aboffset;
faid[size] = shrinkId(id);
faid[size] = shrinkId( id );
size++;
}
public final void discardNode() {
aboffset = startPos(size);
public final void discardNode()
{
aboffset = startPos( size );
}
public final int getSize() {
public final int getSize()
{
return size;
}
public final int getDataSize() {
public final int getDataSize()
{
return ab == null ? 0 : ab.length;
}
/**
* Set the internal reader (aboffset, aboffsetEnd) to the body data for the given id
* <p>
*
* If a node is not found in an empty cache, this is usually an edge-effect
* (data-file does not exist or neighboured data-files of differnt age),
* but is can as well be a symptom of a node-identity breaking bug.
* <p>
*
* Current implementation always returns false for not-found, however, for
* regression testing, at least for the case that is most likely a bug
* (node found but marked as deleted = ready for garbage collection
@ -89,31 +98,38 @@ public class MicroCache extends ByteDataWriter {
*
* @return true if id was found
*/
public final boolean getAndClear(long id64) {
if (size == 0) {
public final boolean getAndClear( long id64 )
{
if ( size == 0 )
{
return false;
}
int id = shrinkId(id64);
int id = shrinkId( id64 );
int[] a = faid;
int offset = p2size;
int n = 0;
while (offset > 0) {
while (offset > 0)
{
int nn = n + offset;
if (nn < size && a[nn] <= id) {
if ( nn < size && a[nn] <= id )
{
n = nn;
}
offset >>= 1;
}
if (a[n] == id) {
if ((fapos[n] & 0x80000000) == 0) {
aboffset = startPos(n);
if ( a[n] == id )
{
if ( ( fapos[n] & 0x80000000 ) == 0 )
{
aboffset = startPos( n );
aboffsetEnd = fapos[n];
fapos[n] |= 0x80000000; // mark deleted
delbytes += aboffsetEnd - aboffset;
delcount++;
return true;
} else // .. marked as deleted
}
else // .. marked as deleted
{
// throw new RuntimeException( "MicroCache: node already consumed: id=" + id );
}
@ -121,35 +137,43 @@ public class MicroCache extends ByteDataWriter {
return false;
}
protected final int startPos(int n) {
protected final int startPos( int n )
{
return n > 0 ? fapos[n - 1] & 0x7fffffff : 0;
}
public final int collect(int threshold) {
if (delcount <= threshold) {
public final int collect( int threshold )
{
if ( delcount <= threshold )
{
return 0;
}
virgin = false;
int nsize = size - delcount;
if (nsize == 0) {
if ( nsize == 0 )
{
faid = null;
fapos = null;
} else {
}
else
{
int[] nfaid = new int[nsize];
int[] nfapos = new int[nsize];
int idx = 0;
byte[] nab = new byte[ab.length - delbytes];
int nab_off = 0;
for (int i = 0; i < size; i++) {
for ( int i = 0; i < size; i++ )
{
int pos = fapos[i];
if ((pos & 0x80000000) == 0) {
int start = startPos(i);
if ( ( pos & 0x80000000 ) == 0 )
{
int start = startPos( i );
int end = fapos[i];
int len = end - start;
System.arraycopy(ab, start, nab, nab_off, len);
System.arraycopy( ab, start, nab, nab_off, len );
nfaid[idx] = faid[i];
nab_off += len;
nfapos[idx] = nab_off;
@ -161,15 +185,17 @@ public class MicroCache extends ByteDataWriter {
ab = nab;
}
int deleted = delbytes;
init(nsize);
init( nsize );
return deleted;
}
public final void unGhost() {
public final void unGhost()
{
ghost = false;
delcount = 0;
delbytes = 0;
for (int i = 0; i < size; i++) {
for ( int i = 0; i < size; i++ )
{
fapos[i] &= 0x7fffffff; // clear deleted flags
}
}
@ -177,166 +203,201 @@ public class MicroCache extends ByteDataWriter {
/**
* @return the 64-bit global id for the given cache-position
*/
public final long getIdForIndex(int i) {
public final long getIdForIndex( int i )
{
int id32 = faid[i];
return expandId(id32);
return expandId( id32 );
}
/**
* expand a 32-bit micro-cache-internal id into a 64-bit (lon|lat) global-id
*
*
* @see #shrinkId
*/
public long expandId(int id32) {
throw new IllegalArgumentException("expandId for empty cache");
public long expandId( int id32 )
{
throw new IllegalArgumentException( "expandId for empty cache" );
}
/**
* shrink a 64-bit (lon|lat) global-id into a a 32-bit micro-cache-internal id
*
*
* @see #expandId
*/
public int shrinkId(long id64) {
throw new IllegalArgumentException("shrinkId for empty cache");
public int shrinkId( long id64 )
{
throw new IllegalArgumentException( "shrinkId for empty cache" );
}
/**
* @return true if the given lon/lat position is internal for that micro-cache
*/
public boolean isInternal(int ilon, int ilat) {
throw new IllegalArgumentException("isInternal for empty cache");
public boolean isInternal( int ilon, int ilat )
{
throw new IllegalArgumentException( "isInternal for empty cache" );
}
/**
* (stasticially) encode the micro-cache into the format used in the datafiles
*
* @param buffer byte array to encode into (considered big enough)
*
* @param buffer
* byte array to encode into (considered big enough)
* @return the size of the encoded data
*/
public int encodeMicroCache(byte[] buffer) {
throw new IllegalArgumentException("encodeMicroCache for empty cache");
public int encodeMicroCache( byte[] buffer )
{
throw new IllegalArgumentException( "encodeMicroCache for empty cache" );
}
/**
* Compare the content of this microcache to another
*
*
* @return null if equals, else a diff-report
*/
public String compareWith(MicroCache mc) {
String msg = _compareWith(mc);
if (msg != null) {
StringBuilder sb = new StringBuilder(msg);
sb.append("\nencode cache:\n").append(summary());
sb.append("\ndecode cache:\n").append(mc.summary());
public String compareWith( MicroCache mc )
{
String msg = _compareWith( mc );
if ( msg != null )
{
StringBuilder sb = new StringBuilder( msg );
sb.append( "\nencode cache:\n" ).append( summary() );
sb.append( "\ndecode cache:\n" ).append( mc.summary() );
return sb.toString();
}
return null;
}
private String summary() {
StringBuilder sb = new StringBuilder("size=" + size + " aboffset=" + aboffset);
for (int i = 0; i < size; i++) {
sb.append("\nidx=" + i + " faid=" + faid[i] + " fapos=" + fapos[i]);
private String summary()
{
StringBuilder sb = new StringBuilder( "size=" + size + " aboffset=" + aboffset );
for ( int i = 0; i < size; i++ )
{
sb.append( "\nidx=" + i + " faid=" + faid[i] + " fapos=" + fapos[i] );
}
return sb.toString();
}
private String _compareWith(MicroCache mc) {
if (size != mc.size) {
return "size mismatch: " + size + "->" + mc.size;
private String _compareWith( MicroCache mc )
{
if ( size != mc.size )
{
return "size missmatch: " + size + "->" + mc.size;
}
for (int i = 0; i < size; i++) {
if (faid[i] != mc.faid[i]) {
return "faid mismatch at index " + i + ":" + faid[i] + "->" + mc.faid[i];
for ( int i = 0; i < size; i++ )
{
if ( faid[i] != mc.faid[i] )
{
return "faid missmatch at index " + i + ":" + faid[i] + "->" + mc.faid[i];
}
int start = i > 0 ? fapos[i - 1] : 0;
int end = fapos[i] < mc.fapos[i] ? fapos[i] : mc.fapos[i];
int len = end - start;
for (int offset = 0; offset < len; offset++) {
if (mc.ab.length <= start + offset) {
for ( int offset = 0; offset < len; offset++ )
{
if ( mc.ab.length <= start + offset )
{
return "data buffer too small";
}
if (ab[start + offset] != mc.ab[start + offset]) {
return "data mismatch at index " + i + " offset=" + offset;
if ( ab[start + offset] != mc.ab[start + offset] )
{
return "data missmatch at index " + i + " offset=" + offset;
}
}
if (fapos[i] != mc.fapos[i]) {
return "fapos mismatch at index " + i + ":" + fapos[i] + "->" + mc.fapos[i];
if ( fapos[i] != mc.fapos[i] )
{
return "fapos missmatch at index " + i + ":" + fapos[i] + "->" + mc.fapos[i];
}
}
if (aboffset != mc.aboffset) {
return "datasize mismatch: " + aboffset + "->" + mc.aboffset;
if ( aboffset != mc.aboffset )
{
return "datasize missmatch: " + aboffset + "->" + mc.aboffset;
}
return null;
}
public void calcDelta(MicroCache mc1, MicroCache mc2) {
int idx1 = 0;
int idx2 = 0;
public void calcDelta( MicroCache mc1, MicroCache mc2 )
{
int idx1 = 0;
int idx2 = 0;
while (idx1 < mc1.size || idx2 < mc2.size) {
int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE;
int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE;
int id;
if (id1 >= id2) {
id = id2;
int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
int len2 = mc2.fapos[idx2++] - start2;
while( idx1 < mc1.size || idx2 < mc2.size )
{
int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE;
int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE;
int id;
if ( id1 >= id2 )
{
id = id2;
int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
int len2 = mc2.fapos[idx2++] - start2;
if (id1 == id2) {
// id exists in both caches, compare data
int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
int len1 = mc1.fapos[idx1++] - start1;
if (len1 == len2) {
int i = 0;
while (i < len1) {
if (mc1.ab[start1 + i] != mc2.ab[start2 + i]) {
break;
}
i++;
}
if (i == len1) {
continue; // same data -> do nothing
}
}
}
write(mc2.ab, start2, len2);
} else {
idx1++;
id = id1; // deleted node
}
fapos[size] = aboffset;
faid[size] = id;
size++;
}
if ( id1 == id2 )
{
// id exists in both caches, compare data
int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
int len1 = mc1.fapos[idx1++] - start1;
if ( len1 == len2 )
{
int i = 0;
while( i<len1 )
{
if ( mc1.ab[start1+i] != mc2.ab[start2+i] )
{
break;
}
i++;
}
if ( i == len1 )
{
continue; // same data -> do nothing
}
}
}
write( mc2.ab, start2, len2 );
}
else
{
idx1++;
id = id1; // deleted node
}
fapos[size] = aboffset;
faid[size] = id;
size++;
}
}
public void addDelta(MicroCache mc1, MicroCache mc2, boolean keepEmptyNodes) {
int idx1 = 0;
int idx2 = 0;
public void addDelta( MicroCache mc1, MicroCache mc2, boolean keepEmptyNodes )
{
int idx1 = 0;
int idx2 = 0;
while (idx1 < mc1.size || idx2 < mc2.size) {
int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE;
int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE;
if (id1 >= id2) { // data from diff file wins
int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
int len2 = mc2.fapos[idx2++] - start2;
if (keepEmptyNodes || len2 > 0) {
write(mc2.ab, start2, len2);
fapos[size] = aboffset;
faid[size++] = id2;
}
if (id1 == id2) { // // id exists in both caches
idx1++;
}
} else // use data from base file
{
int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
int len1 = mc1.fapos[idx1++] - start1;
write(mc1.ab, start1, len1);
fapos[size] = aboffset;
faid[size++] = id1;
}
}
while( idx1 < mc1.size || idx2 < mc2.size )
{
int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE;
int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE;
if ( id1 >= id2 ) // data from diff file wins
{
int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
int len2 = mc2.fapos[idx2++] - start2;
if ( keepEmptyNodes || len2 > 0 )
{
write( mc2.ab, start2, len2 );
fapos[size] = aboffset;
faid[size++] = id2;
}
if ( id1 == id2 ) // // id exists in both caches
{
idx1++;
}
}
else // use data from base file
{
int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
int len1 = mc1.fapos[idx1++] - start1;
write( mc1.ab, start1, len1 );
fapos[size] = aboffset;
faid[size++] = id1;
}
}
}
}

View file

@ -1,7 +1,6 @@
package btools.codec;
import java.util.HashMap;
import java.util.Map;
import btools.util.ByteDataReader;
import btools.util.IByteArrayUnifier;
@ -10,198 +9,222 @@ import btools.util.IByteArrayUnifier;
* MicroCache2 is the new format that uses statistical encoding and
* is able to do access filtering and waypoint matching during encoding
*/
public final class MicroCache2 extends MicroCache {
public final class MicroCache2 extends MicroCache
{
private int lonBase;
private int latBase;
private int cellsize;
public MicroCache2(int size, byte[] databuffer, int lonIdx, int latIdx, int divisor) {
super(databuffer); // sets ab=databuffer, aboffset=0
public MicroCache2( int size, byte[] databuffer, int lonIdx, int latIdx, int divisor ) throws Exception
{
super( databuffer ); // sets ab=databuffer, aboffset=0
faid = new int[size];
fapos = new int[size];
this.size = 0;
cellsize = 1000000 / divisor;
lonBase = lonIdx * cellsize;
latBase = latIdx * cellsize;
lonBase = lonIdx*cellsize;
latBase = latIdx*cellsize;
}
public byte[] readUnified( int len, IByteArrayUnifier u )
{
byte[] b = u.unify( ab, aboffset, len );
aboffset += len;
return b;
}
public byte[] readUnified(int len, IByteArrayUnifier u) {
byte[] b = u.unify(ab, aboffset, len);
aboffset += len;
return b;
}
public MicroCache2(StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher) {
super(null);
public MicroCache2( StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher ) throws Exception
{
super( null );
cellsize = 1000000 / divisor;
lonBase = lonIdx * cellsize;
latBase = latIdx * cellsize;
lonBase = lonIdx*cellsize;
latBase = latIdx*cellsize;
TagValueCoder wayTagCoder = new TagValueCoder(bc, dataBuffers, wayValidator);
TagValueCoder nodeTagCoder = new TagValueCoder(bc, dataBuffers, null);
NoisyDiffCoder nodeIdxDiff = new NoisyDiffCoder(bc);
NoisyDiffCoder nodeEleDiff = new NoisyDiffCoder(bc);
TagValueCoder wayTagCoder = new TagValueCoder( bc, dataBuffers, wayValidator );
TagValueCoder nodeTagCoder = new TagValueCoder( bc, dataBuffers, null );
NoisyDiffCoder nodeIdxDiff = new NoisyDiffCoder( bc );
NoisyDiffCoder nodeEleDiff = new NoisyDiffCoder( bc );
NoisyDiffCoder extLonDiff = new NoisyDiffCoder(bc);
NoisyDiffCoder extLatDiff = new NoisyDiffCoder(bc);
NoisyDiffCoder transEleDiff = new NoisyDiffCoder(bc);
NoisyDiffCoder transEleDiff = new NoisyDiffCoder( bc );
size = bc.decodeNoisyNumber(5);
size = bc.decodeNoisyNumber( 5 );
faid = size > dataBuffers.ibuf2.length ? new int[size] : dataBuffers.ibuf2;
fapos = size > dataBuffers.ibuf3.length ? new int[size] : dataBuffers.ibuf3;
int[] alon = size > dataBuffers.alon.length ? new int[size] : dataBuffers.alon;
int[] alat = size > dataBuffers.alat.length ? new int[size] : dataBuffers.alat;
if ( debug ) System.out.println( "*** decoding cache of size=" + size + " for lonIdx=" + lonIdx + " latIdx=" + latIdx );
int[] alon = size > dataBuffers.alon.length ? new int[size] : dataBuffers.alon;
int[] alat = size > dataBuffers.alat.length ? new int[size] : dataBuffers.alat;
if (debug)
System.out.println("*** decoding cache of size=" + size + " for lonIdx=" + lonIdx + " latIdx=" + latIdx);
bc.decodeSortedArray(faid, 0, size, 29, 0);
for (int n = 0; n < size; n++) {
long id64 = expandId(faid[n]);
alon[n] = (int) (id64 >> 32);
alat[n] = (int) (id64 & 0xffffffff);
bc.decodeSortedArray( faid, 0, size, 29, 0 );
for( int n = 0; n<size; n++ )
{
long id64 = expandId( faid[n] );
alon[n] = (int)(id64 >> 32);
alat[n] = (int)(id64 & 0xffffffff);
}
int netdatasize = bc.decodeNoisyNumber(10);
int netdatasize = bc.decodeNoisyNumber( 10 );
ab = netdatasize > dataBuffers.bbuf1.length ? new byte[netdatasize] : dataBuffers.bbuf1;
aboffset = 0;
int[] validBits = new int[(size + 31) >> 5];
int[] validBits = new int[(size+31)>>5];
int finaldatasize = 0;
LinkedListContainer reverseLinks = new LinkedListContainer(size, dataBuffers.ibuf1);
LinkedListContainer reverseLinks = new LinkedListContainer( size, dataBuffers.ibuf1 );
int selev = 0;
for (int n = 0; n < size; n++) { // loop over nodes
for( int n=0; n<size; n++ ) // loop over nodes
{
int ilon = alon[n];
int ilat = alat[n];
// future escapes (turn restrictions?)
short trExceptions = 0;
int featureId = bc.decodeVarBits();
if (featureId == 13) {
if ( featureId == 13 )
{
fapos[n] = aboffset;
validBits[n >> 5] |= 1 << n; // mark dummy-node valid
validBits[ n >> 5 ] |= 1 << n; // mark dummy-node valid
continue; // empty node escape (delta files only)
}
while (featureId != 0) {
int bitsize = bc.decodeNoisyNumber(5);
while( featureId != 0 )
{
int bitsize = bc.decodeNoisyNumber( 5 );
if (featureId == 2) { // exceptions to turn-restriction
trExceptions = (short) bc.decodeBounded(1023);
} else if (featureId == 1) { // turn-restriction
writeBoolean(true);
writeShort(trExceptions); // exceptions from previous feature
if ( featureId == 2 ) // exceptions to turn-restriction
{
trExceptions = (short)bc.decodeBounded( 1023 );
}
else if ( featureId == 1 ) // turn-restriction
{
writeBoolean( true );
writeShort( trExceptions ); // exceptions from previous feature
trExceptions = 0;
writeBoolean(bc.decodeBit()); // isPositive
writeInt(ilon + bc.decodeNoisyDiff(10)); // fromLon
writeInt(ilat + bc.decodeNoisyDiff(10)); // fromLat
writeInt(ilon + bc.decodeNoisyDiff(10)); // toLon
writeInt(ilat + bc.decodeNoisyDiff(10)); // toLat
} else {
for (int i = 0; i < bitsize; i++) bc.decodeBit(); // unknown feature, just skip
writeBoolean( bc.decodeBit() ); // isPositive
writeInt( ilon + bc.decodeNoisyDiff( 10 ) ); // fromLon
writeInt( ilat + bc.decodeNoisyDiff( 10 ) ); // fromLat
writeInt( ilon + bc.decodeNoisyDiff( 10 ) ); // toLon
writeInt( ilat + bc.decodeNoisyDiff( 10 ) ); // toLat
}
else
{
for( int i=0; i< bitsize; i++ ) bc.decodeBit(); // unknown feature, just skip
}
featureId = bc.decodeVarBits();
}
writeBoolean(false);
writeBoolean( false );
selev += nodeEleDiff.decodeSignedValue();
writeShort((short) selev);
writeShort( (short) selev );
TagValueWrapper nodeTags = nodeTagCoder.decodeTagValueSet();
writeVarBytes(nodeTags == null ? null : nodeTags.data);
writeVarBytes( nodeTags == null ? null : nodeTags.data );
int links = bc.decodeNoisyNumber(1);
if (debug)
System.out.println("*** decoding node " + ilon + "/" + ilat + " with links=" + links);
for (int li = 0; li < links; li++) {
int links = bc.decodeNoisyNumber( 1 );
if ( debug ) System.out.println( "*** decoding node " + ilon + "/" + ilat + " with links=" + links );
for( int li=0; li<links; li++ )
{
int sizeoffset = 0;
int nodeIdx = n + nodeIdxDiff.decodeSignedValue();
int dlon_remaining;
int dlat_remaining;
boolean isReverse = false;
if (nodeIdx != n) { // internal (forward-) link
if ( nodeIdx != n ) // internal (forward-) link
{
dlon_remaining = alon[nodeIdx] - ilon;
dlat_remaining = alat[nodeIdx] - ilat;
} else {
}
else
{
isReverse = bc.decodeBit();
dlon_remaining = extLonDiff.decodeSignedValue();
dlat_remaining = extLatDiff.decodeSignedValue();
}
if (debug)
System.out.println("*** decoding link to " + (ilon + dlon_remaining) + "/" + (ilat + dlat_remaining) + " extern=" + (nodeIdx == n));
if ( debug ) System.out.println( "*** decoding link to " + (ilon+dlon_remaining) + "/" + (ilat+dlat_remaining) + " extern=" + (nodeIdx == n) );
TagValueWrapper wayTags = wayTagCoder.decodeTagValueSet();
boolean linkValid = wayTags != null || wayValidator == null;
if (linkValid) {
if ( linkValid )
{
int startPointer = aboffset;
sizeoffset = writeSizePlaceHolder();
writeVarLengthSigned(dlon_remaining);
writeVarLengthSigned(dlat_remaining);
writeVarLengthSigned( dlon_remaining );
writeVarLengthSigned( dlat_remaining );
validBits[n >> 5] |= 1 << n; // mark source-node valid
if (nodeIdx != n) { // valid internal (forward-) link
reverseLinks.addDataElement(nodeIdx, n); // register reverse link
finaldatasize += 1 + aboffset - startPointer; // reserve place for reverse
validBits[nodeIdx >> 5] |= 1 << nodeIdx; // mark target-node valid
validBits[ n >> 5 ] |= 1 << n; // mark source-node valid
if ( nodeIdx != n ) // valid internal (forward-) link
{
reverseLinks.addDataElement( nodeIdx, n ); // register reverse link
finaldatasize += 1 + aboffset-startPointer; // reserve place for reverse
validBits[ nodeIdx >> 5 ] |= 1 << nodeIdx; // mark target-node valid
}
writeModeAndDesc(isReverse, wayTags == null ? null : wayTags.data);
writeModeAndDesc( isReverse, wayTags == null ? null : wayTags.data );
}
if (!isReverse) { // write geometry for forward links only
if ( !isReverse ) // write geometry for forward links only
{
WaypointMatcher matcher = wayTags == null || wayTags.accessType < 2 ? null : waypointMatcher;
int ilontarget = ilon + dlon_remaining;
int ilattarget = ilat + dlat_remaining;
if (matcher != null) {
if (!matcher.start(ilon, ilat, ilontarget, ilattarget)) {
if ( matcher != null )
{
if ( !matcher.start( ilon, ilat, ilontarget, ilattarget ) )
{
matcher = null;
}
}
int transcount = bc.decodeVarBits();
if (debug) System.out.println("*** decoding geometry with count=" + transcount);
int count = transcount + 1;
for (int i = 0; i < transcount; i++) {
int dlon = bc.decodePredictedValue(dlon_remaining / count);
int dlat = bc.decodePredictedValue(dlat_remaining / count);
if ( debug ) System.out.println( "*** decoding geometry with count=" + transcount );
int count = transcount+1;
for( int i=0; i<transcount; i++ )
{
int dlon = bc.decodePredictedValue( dlon_remaining/count );
int dlat = bc.decodePredictedValue( dlat_remaining/count );
dlon_remaining -= dlon;
dlat_remaining -= dlat;
count--;
int elediff = transEleDiff.decodeSignedValue();
if (wayTags != null) {
writeVarLengthSigned(dlon);
writeVarLengthSigned(dlat);
writeVarLengthSigned(elediff);
if ( wayTags != null )
{
writeVarLengthSigned( dlon );
writeVarLengthSigned( dlat );
writeVarLengthSigned( elediff );
}
if (matcher != null)
matcher.transferNode(ilontarget - dlon_remaining, ilattarget - dlat_remaining);
if ( matcher != null ) matcher.transferNode( ilontarget - dlon_remaining, ilattarget - dlat_remaining );
}
if (matcher != null) matcher.end();
if ( matcher != null ) matcher.end();
}
if (linkValid) {
injectSize(sizeoffset);
if ( linkValid )
{
injectSize( sizeoffset );
}
}
fapos[n] = aboffset;
}
// calculate final data size
int finalsize = 0;
int startpos = 0;
for (int i = 0; i < size; i++) {
for( int i=0; i<size; i++ )
{
int endpos = fapos[i];
if ((validBits[i >> 5] & (1 << i)) != 0) {
finaldatasize += endpos - startpos;
finalsize++;
if ( ( validBits[ i >> 5 ] & (1 << i ) ) != 0 )
{
finaldatasize += endpos-startpos;
finalsize++;
}
startpos = endpos;
}
@ -217,26 +240,29 @@ public final class MicroCache2 extends MicroCache {
size = 0;
startpos = 0;
for (int n = 0; n < sizeOld; n++) {
for ( int n = 0; n < sizeOld; n++ )
{
int endpos = faposOld[n];
if ((validBits[n >> 5] & (1 << n)) != 0) {
if ( ( validBits[ n >> 5 ] & (1 << n ) ) != 0 )
{
int len = endpos - startpos;
System.arraycopy(abOld, startpos, ab, aboffset, len);
if (debug)
System.out.println("*** copied " + len + " bytes from " + aboffset + " for node " + n);
System.arraycopy( abOld, startpos, ab, aboffset, len );
if ( debug )
System.out.println( "*** copied " + len + " bytes from " + aboffset + " for node " + n );
aboffset += len;
int cnt = reverseLinks.initList(n);
if (debug)
System.out.println("*** appending " + cnt + " reverse links for node " + n);
int cnt = reverseLinks.initList( n );
if ( debug )
System.out.println( "*** appending " + cnt + " reverse links for node " + n );
for (int ri = 0; ri < cnt; ri++) {
for ( int ri = 0; ri < cnt; ri++ )
{
int nodeIdx = reverseLinks.getDataElement();
int sizeoffset = writeSizePlaceHolder();
writeVarLengthSigned(alon[nodeIdx] - alon[n]);
writeVarLengthSigned(alat[nodeIdx] - alat[n]);
writeModeAndDesc(true, null);
injectSize(sizeoffset);
writeVarLengthSigned( alon[nodeIdx] - alon[n] );
writeVarLengthSigned( alat[nodeIdx] - alat[n] );
writeModeAndDesc( true, null );
injectSize( sizeoffset );
}
faid[size] = faidOld[n];
fapos[size] = aboffset;
@ -244,58 +270,65 @@ public final class MicroCache2 extends MicroCache {
}
startpos = endpos;
}
init(size);
init( size );
}
@Override
public long expandId(int id32) {
public long expandId( int id32 )
{
int dlon = 0;
int dlat = 0;
for (int bm = 1; bm < 0x8000; bm <<= 1) {
if ((id32 & 1) != 0) dlon |= bm;
if ((id32 & 2) != 0) dlat |= bm;
for( int bm = 1; bm < 0x8000; bm <<= 1 )
{
if ( (id32 & 1) != 0 ) dlon |= bm;
if ( (id32 & 2) != 0 ) dlat |= bm;
id32 >>= 2;
}
int lon32 = lonBase + dlon;
int lat32 = latBase + dlat;
return ((long) lon32) << 32 | lat32;
return ((long)lon32)<<32 | lat32;
}
@Override
public int shrinkId(long id64) {
int lon32 = (int) (id64 >> 32);
int lat32 = (int) (id64 & 0xffffffff);
public int shrinkId( long id64 )
{
int lon32 = (int)(id64 >> 32);
int lat32 = (int)(id64 & 0xffffffff);
int dlon = lon32 - lonBase;
int dlat = lat32 - latBase;
int id32 = 0;
for (int bm = 0x4000; bm > 0; bm >>= 1) {
for( int bm = 0x4000; bm > 0; bm >>= 1 )
{
id32 <<= 2;
if ((dlon & bm) != 0) id32 |= 1;
if ((dlat & bm) != 0) id32 |= 2;
if ( ( dlon & bm ) != 0 ) id32 |= 1;
if ( ( dlat & bm ) != 0 ) id32 |= 2;
}
return id32;
}
@Override
public boolean isInternal(int ilon, int ilat) {
public boolean isInternal( int ilon, int ilat )
{
return ilon >= lonBase && ilon < lonBase + cellsize
&& ilat >= latBase && ilat < latBase + cellsize;
&& ilat >= latBase && ilat < latBase + cellsize;
}
@Override
public int encodeMicroCache(byte[] buffer) {
Map<Long, Integer> idMap = new HashMap<>();
for (int n = 0; n < size; n++) { // loop over nodes
idMap.put(expandId(faid[n]), n);
public int encodeMicroCache( byte[] buffer )
{
HashMap<Long,Integer> idMap = new HashMap<Long,Integer>();
for( int n=0; n<size; n++ ) // loop over nodes
{
idMap.put( Long.valueOf( expandId( faid[n] ) ), Integer.valueOf( n ) );
}
IntegerFifo3Pass linkCounts = new IntegerFifo3Pass(256);
IntegerFifo3Pass transCounts = new IntegerFifo3Pass(256);
IntegerFifo3Pass restrictionBits = new IntegerFifo3Pass(16);
IntegerFifo3Pass linkCounts = new IntegerFifo3Pass( 256 );
IntegerFifo3Pass transCounts = new IntegerFifo3Pass( 256 );
IntegerFifo3Pass restrictionBits = new IntegerFifo3Pass( 16 );
TagValueCoder wayTagCoder = new TagValueCoder();
TagValueCoder nodeTagCoder = new TagValueCoder();
@ -304,170 +337,182 @@ public final class MicroCache2 extends MicroCache {
NoisyDiffCoder extLonDiff = new NoisyDiffCoder();
NoisyDiffCoder extLatDiff = new NoisyDiffCoder();
NoisyDiffCoder transEleDiff = new NoisyDiffCoder();
int netdatasize = 0;
for (int pass = 1; ; pass++) { // 3 passes: counters, stat-collection, encoding
for(int pass=1;; pass++) // 3 passes: counters, stat-collection, encoding
{
boolean dostats = pass == 3;
boolean dodebug = debug && pass == 3;
if (pass < 3) netdatasize = fapos[size - 1];
StatCoderContext bc = new StatCoderContext(buffer);
if ( pass < 3 ) netdatasize = fapos[size-1];
StatCoderContext bc = new StatCoderContext( buffer );
linkCounts.init();
transCounts.init();
restrictionBits.init();
wayTagCoder.encodeDictionary(bc);
if (dostats) bc.assignBits("wayTagDictionary");
nodeTagCoder.encodeDictionary(bc);
if (dostats) bc.assignBits("nodeTagDictionary");
nodeIdxDiff.encodeDictionary(bc);
nodeEleDiff.encodeDictionary(bc);
extLonDiff.encodeDictionary(bc);
extLatDiff.encodeDictionary(bc);
transEleDiff.encodeDictionary(bc);
if (dostats) bc.assignBits("noisebits");
bc.encodeNoisyNumber(size, 5);
if (dostats) bc.assignBits("nodecount");
bc.encodeSortedArray(faid, 0, size, 0x20000000, 0);
if (dostats) bc.assignBits("node-positions");
bc.encodeNoisyNumber(netdatasize, 10); // net-size
if (dostats) bc.assignBits("netdatasize");
if (dodebug) System.out.println("*** encoding cache of size=" + size);
wayTagCoder.encodeDictionary( bc );
if ( dostats ) bc.assignBits( "wayTagDictionary" );
nodeTagCoder.encodeDictionary( bc );
if ( dostats ) bc.assignBits( "nodeTagDictionary" );
nodeIdxDiff.encodeDictionary( bc );
nodeEleDiff.encodeDictionary( bc );
extLonDiff.encodeDictionary( bc );
extLatDiff.encodeDictionary( bc );
transEleDiff.encodeDictionary( bc );
if ( dostats ) bc.assignBits( "noisebits" );
bc.encodeNoisyNumber( size, 5 );
if ( dostats ) bc.assignBits( "nodecount" );
bc.encodeSortedArray( faid, 0, size, 0x20000000, 0 );
if ( dostats ) bc.assignBits( "node-positions" );
bc.encodeNoisyNumber( netdatasize, 10 ); // net-size
if ( dostats ) bc.assignBits( "netdatasize" );
if ( dodebug ) System.out.println( "*** encoding cache of size=" + size );
int lastSelev = 0;
for (int n = 0; n < size; n++) { // loop over nodes
aboffset = startPos(n);
for( int n=0; n<size; n++ ) // loop over nodes
{
aboffset = startPos( n );
aboffsetEnd = fapos[n];
if (dodebug)
System.out.println("*** encoding node " + n + " from " + aboffset + " to " + aboffsetEnd);
if ( dodebug ) System.out.println( "*** encoding node " + n + " from " + aboffset + " to " + aboffsetEnd );
long id64 = expandId(faid[n]);
int ilon = (int) (id64 >> 32);
int ilat = (int) (id64 & 0xffffffff);
long id64 = expandId( faid[n] );
int ilon = (int)(id64 >> 32);
int ilat = (int)(id64 & 0xffffffff);
if (aboffset == aboffsetEnd) {
bc.encodeVarBits(13); // empty node escape (delta files only)
if ( aboffset == aboffsetEnd )
{
bc.encodeVarBits( 13 ); // empty node escape (delta files only)
continue;
}
// write turn restrictions
while (readBoolean()) {
while( readBoolean() )
{
short exceptions = readShort(); // except bikes, psv, ...
if (exceptions != 0) {
bc.encodeVarBits(2); // 2 = tr exceptions
bc.encodeNoisyNumber(10, 5); // bit-count
bc.encodeBounded(1023, exceptions & 1023);
if ( exceptions != 0 )
{
bc.encodeVarBits( 2 ); // 2 = tr exceptions
bc.encodeNoisyNumber( 10 , 5 ); // bit-count
bc.encodeBounded( 1023 , exceptions & 1023 );
}
bc.encodeVarBits(1); // 1 = turn restriction
bc.encodeNoisyNumber(restrictionBits.getNext(), 5); // bit-count using look-ahead fifo
bc.encodeVarBits( 1 ); // 1 = turn restriction
bc.encodeNoisyNumber( restrictionBits.getNext(), 5 ); // bit-count using look-ahead fifo
long b0 = bc.getWritingBitPosition();
bc.encodeBit(readBoolean()); // isPositive
bc.encodeNoisyDiff(readInt() - ilon, 10); // fromLon
bc.encodeNoisyDiff(readInt() - ilat, 10); // fromLat
bc.encodeNoisyDiff(readInt() - ilon, 10); // toLon
bc.encodeNoisyDiff(readInt() - ilat, 10); // toLat
restrictionBits.add((int) (bc.getWritingBitPosition() - b0));
bc.encodeBit( readBoolean() ); // isPositive
bc.encodeNoisyDiff( readInt() - ilon, 10 ); // fromLon
bc.encodeNoisyDiff( readInt() - ilat, 10 ); // fromLat
bc.encodeNoisyDiff( readInt() - ilon, 10 ); // toLon
bc.encodeNoisyDiff( readInt() - ilat, 10 ); // toLat
restrictionBits.add( (int)( bc.getWritingBitPosition() - b0 ) );
}
bc.encodeVarBits(0); // end of extra data
bc.encodeVarBits( 0 ); // end of extra data
if (dostats) bc.assignBits("extradata");
if ( dostats ) bc.assignBits( "extradata" );
int selev = readShort();
nodeEleDiff.encodeSignedValue(selev - lastSelev);
if (dostats) bc.assignBits("nodeele");
nodeEleDiff.encodeSignedValue( selev - lastSelev );
if ( dostats ) bc.assignBits( "nodeele" );
lastSelev = selev;
nodeTagCoder.encodeTagValueSet(readVarBytes());
if (dostats) bc.assignBits("nodeTagIdx");
nodeTagCoder.encodeTagValueSet( readVarBytes() );
if ( dostats ) bc.assignBits( "nodeTagIdx" );
int nlinks = linkCounts.getNext();
if (dodebug) System.out.println("*** nlinks=" + nlinks);
bc.encodeNoisyNumber(nlinks, 1);
if (dostats) bc.assignBits("link-counts");
if ( dodebug ) System.out.println( "*** nlinks=" + nlinks );
bc.encodeNoisyNumber( nlinks, 1 );
if ( dostats ) bc.assignBits( "link-counts" );
nlinks = 0;
while (hasMoreData()) { // loop over links
while( hasMoreData() ) // loop over links
{
// read link data
int startPointer = aboffset;
int endPointer = getEndPointer();
int ilonlink = ilon + readVarLengthSigned();
int ilatlink = ilat + readVarLengthSigned();
int sizecode = readVarLengthUnsigned();
boolean isReverse = (sizecode & 1) != 0;
boolean isReverse = ( sizecode & 1 ) != 0;
int descSize = sizecode >> 1;
byte[] description = null;
if (descSize > 0) {
if ( descSize > 0 )
{
description = new byte[descSize];
readFully(description);
readFully( description );
}
long link64 = ((long) ilonlink) << 32 | ilatlink;
Integer idx = idMap.get(link64);
long link64 = ((long)ilonlink)<<32 | ilatlink;
Integer idx = idMap.get( Long.valueOf( link64 ) );
boolean isInternal = idx != null;
if (isReverse && isInternal) {
if (dodebug)
System.out.println("*** NOT encoding link reverse=" + isReverse + " internal=" + isInternal);
netdatasize -= aboffset - startPointer;
if ( isReverse && isInternal )
{
if ( dodebug ) System.out.println( "*** NOT encoding link reverse=" + isReverse + " internal=" + isInternal );
netdatasize -= aboffset-startPointer;
continue; // do not encode internal reverse links
}
if (dodebug)
System.out.println("*** encoding link reverse=" + isReverse + " internal=" + isInternal);
if ( dodebug ) System.out.println( "*** encoding link reverse=" + isReverse + " internal=" + isInternal );
nlinks++;
if (isInternal) {
int nodeIdx = idx;
if (dodebug) System.out.println("*** target nodeIdx=" + nodeIdx);
if (nodeIdx == n) throw new RuntimeException("ups: self ref?");
nodeIdxDiff.encodeSignedValue(nodeIdx - n);
if (dostats) bc.assignBits("nodeIdx");
} else {
nodeIdxDiff.encodeSignedValue(0);
bc.encodeBit(isReverse);
extLonDiff.encodeSignedValue(ilonlink - ilon);
extLatDiff.encodeSignedValue(ilatlink - ilat);
if (dostats) bc.assignBits("externalNode");
if ( isInternal )
{
int nodeIdx = idx.intValue();
if ( dodebug ) System.out.println( "*** target nodeIdx=" + nodeIdx );
if ( nodeIdx == n ) throw new RuntimeException( "ups: self ref?" );
nodeIdxDiff.encodeSignedValue( nodeIdx - n );
if ( dostats ) bc.assignBits( "nodeIdx" );
}
wayTagCoder.encodeTagValueSet(description);
if (dostats) bc.assignBits("wayDescIdx");
if (!isReverse) {
byte[] geometry = readDataUntil(endPointer);
else
{
nodeIdxDiff.encodeSignedValue( 0 );
bc.encodeBit( isReverse );
extLonDiff.encodeSignedValue( ilonlink - ilon );
extLatDiff.encodeSignedValue( ilatlink - ilat );
if ( dostats ) bc.assignBits( "externalNode" );
}
wayTagCoder.encodeTagValueSet( description );
if ( dostats ) bc.assignBits( "wayDescIdx" );
if ( !isReverse )
{
byte[] geometry = readDataUntil( endPointer );
// write transition nodes
int count = transCounts.getNext();
if (dodebug) System.out.println("*** encoding geometry with count=" + count);
bc.encodeVarBits(count++);
if (dostats) bc.assignBits("transcount");
if ( dodebug ) System.out.println( "*** encoding geometry with count=" + count );
bc.encodeVarBits( count++ );
if ( dostats ) bc.assignBits( "transcount" );
int transcount = 0;
if (geometry != null) {
if ( geometry != null )
{
int dlon_remaining = ilonlink - ilon;
int dlat_remaining = ilatlink - ilat;
ByteDataReader r = new ByteDataReader(geometry);
while (r.hasMoreData()) {
ByteDataReader r = new ByteDataReader( geometry );
while ( r.hasMoreData() )
{
transcount++;
int dlon = r.readVarLengthSigned();
int dlat = r.readVarLengthSigned();
bc.encodePredictedValue(dlon, dlon_remaining / count);
bc.encodePredictedValue(dlat, dlat_remaining / count);
bc.encodePredictedValue( dlon, dlon_remaining/count );
bc.encodePredictedValue( dlat, dlat_remaining/count );
dlon_remaining -= dlon;
dlat_remaining -= dlat;
if (count > 1) count--;
if (dostats) bc.assignBits("transpos");
transEleDiff.encodeSignedValue(r.readVarLengthSigned());
if (dostats) bc.assignBits("transele");
if ( count > 1 ) count--;
if ( dostats ) bc.assignBits( "transpos" );
transEleDiff.encodeSignedValue( r.readVarLengthSigned() );
if ( dostats ) bc.assignBits( "transele" );
}
}
transCounts.add(transcount);
transCounts.add( transcount );
}
}
linkCounts.add(nlinks);
linkCounts.add( nlinks );
}
if (pass == 3) {
if ( pass == 3 )
{
return bc.closeAndGetEncodedLength();
}
}

View file

@ -4,11 +4,12 @@ package btools.codec;
* Encoder/Decoder for signed integers that automatically detects the typical
* range of these numbers to determine a noisy-bit count as a very simple
* dictionary
* <p>
*
* Adapted for 3-pass encoding (counters -&gt; statistics -&gt; encoding )
* but doesn't do anything at pass1
*/
public final class NoisyDiffCoder {
public final class NoisyDiffCoder
{
private int tot;
private int[] freqs;
private int noisybits;
@ -18,7 +19,8 @@ public final class NoisyDiffCoder {
/**
* Create a decoder and read the noisy-bit count from the gibe context
*/
public NoisyDiffCoder(StatCoderContext bc) {
public NoisyDiffCoder( StatCoderContext bc )
{
noisybits = bc.decodeVarBits();
this.bc = bc;
}
@ -26,49 +28,60 @@ public final class NoisyDiffCoder {
/**
* Create an encoder for 3-pass-encoding
*/
public NoisyDiffCoder() {
public NoisyDiffCoder()
{
}
/**
* encodes a signed int (pass3 only, stats collection in pass2)
*/
public void encodeSignedValue(int value) {
if (pass == 3) {
bc.encodeNoisyDiff(value, noisybits);
} else if (pass == 2) {
count(value < 0 ? -value : value);
public void encodeSignedValue( int value )
{
if ( pass == 3 )
{
bc.encodeNoisyDiff( value, noisybits );
}
else if ( pass == 2 )
{
count( value < 0 ? -value : value );
}
}
/**
* decodes a signed int
*/
public int decodeSignedValue() {
return bc.decodeNoisyDiff(noisybits);
public int decodeSignedValue()
{
return bc.decodeNoisyDiff( noisybits );
}
/**
* Starts a new encoding pass and (in pass3) calculates the noisy-bit count
* from the stats collected in pass2 and writes that to the given context
*/
public void encodeDictionary(StatCoderContext bc) {
if (++pass == 3) {
public void encodeDictionary( StatCoderContext bc )
{
if ( ++pass == 3 )
{
// how many noisy bits?
for (noisybits = 0; noisybits < 14 && tot > 0; noisybits++) {
if (freqs[noisybits] < (tot >> 1))
for ( noisybits = 0; noisybits < 14 && tot > 0; noisybits++ )
{
if ( freqs[noisybits] < ( tot >> 1 ) )
break;
}
bc.encodeVarBits(noisybits);
bc.encodeVarBits( noisybits );
}
this.bc = bc;
}
private void count(int value) {
if (freqs == null)
private void count( int value )
{
if ( freqs == null )
freqs = new int[14];
int bm = 1;
for (int i = 0; i < 14; i++) {
if (value < bm)
for ( int i = 0; i < 14; i++ )
{
if ( value < bm )
break;
else
freqs[i]++;

View file

@ -1,23 +1,26 @@
package btools.codec;
import java.util.Map;
import java.util.TreeMap;
import btools.util.BitCoderContext;
public final class StatCoderContext extends BitCoderContext {
private static Map<String, long[]> statsPerName;
public final class StatCoderContext extends BitCoderContext
{
private static TreeMap<String, long[]> statsPerName;
private long lastbitpos = 0;
private static final int[] noisy_bits = new int[1024];
static {
static
{
// noisybits lookup
for (int i = 0; i < 1024; i++) {
for( int i=0; i<1024; i++ )
{
int p = i;
int noisybits = 0;
while (p > 2) {
while (p > 2)
{
noisybits++;
p >>= 1;
}
@ -26,25 +29,29 @@ public final class StatCoderContext extends BitCoderContext {
}
public StatCoderContext(byte[] ab) {
super(ab);
public StatCoderContext( byte[] ab )
{
super( ab );
}
/**
* assign the de-/encoded bits since the last call assignBits to the given
* name. Used for encoding statistics
*
*
* @see #getBitReport
*/
public void assignBits(String name) {
public void assignBits( String name )
{
long bitpos = getWritingBitPosition();
if (statsPerName == null) {
statsPerName = new TreeMap<>();
if ( statsPerName == null )
{
statsPerName = new TreeMap<String, long[]>();
}
long[] stats = statsPerName.get(name);
if (stats == null) {
long[] stats = statsPerName.get( name );
if ( stats == null )
{
stats = new long[2];
statsPerName.put(name, stats);
statsPerName.put( name, stats );
}
stats[0] += bitpos - lastbitpos;
stats[1] += 1;
@ -53,17 +60,20 @@ public final class StatCoderContext extends BitCoderContext {
/**
* Get a textual report on the bit-statistics
*
*
* @see #assignBits
*/
public static String getBitReport() {
if (statsPerName == null) {
public static String getBitReport()
{
if ( statsPerName == null )
{
return "<empty bit report>";
}
StringBuilder sb = new StringBuilder();
for (String name : statsPerName.keySet()) {
long[] stats = statsPerName.get(name);
sb.append(name + " count=" + stats[1] + " bits=" + stats[0] + "\n");
for ( String name : statsPerName.keySet() )
{
long[] stats = statsPerName.get( name );
sb.append( name + " count=" + stats[1] + " bits=" + stats[0] + "\n" );
}
statsPerName = null;
return sb.toString();
@ -72,65 +82,76 @@ public final class StatCoderContext extends BitCoderContext {
/**
* encode an unsigned integer with some of of least significant bits
* considered noisy
*
*
* @see #decodeNoisyNumber
*/
public void encodeNoisyNumber(int value, int noisybits) {
if (value < 0) {
throw new IllegalArgumentException("encodeVarBits expects positive value");
public void encodeNoisyNumber( int value, int noisybits )
{
if ( value < 0 )
{
throw new IllegalArgumentException( "encodeVarBits expects positive value" );
}
if (noisybits > 0) {
int mask = 0xffffffff >>> (32 - noisybits);
encodeBounded(mask, value & mask);
if ( noisybits > 0 )
{
int mask = 0xffffffff >>> ( 32 - noisybits );
encodeBounded( mask, value & mask );
value >>= noisybits;
}
encodeVarBits(value);
encodeVarBits( value );
}
/**
* decode an unsigned integer with some of of least significant bits
* considered noisy
*
*
* @see #encodeNoisyNumber
*/
public int decodeNoisyNumber(int noisybits) {
int value = decodeBits(noisybits);
return value | (decodeVarBits() << noisybits);
public int decodeNoisyNumber( int noisybits )
{
int value = decodeBits( noisybits );
return value | ( decodeVarBits() << noisybits );
}
/**
* encode a signed integer with some of of least significant bits considered
* noisy
*
*
* @see #decodeNoisyDiff
*/
public void encodeNoisyDiff(int value, int noisybits) {
if (noisybits > 0) {
value += 1 << (noisybits - 1);
int mask = 0xffffffff >>> (32 - noisybits);
encodeBounded(mask, value & mask);
public void encodeNoisyDiff( int value, int noisybits )
{
if ( noisybits > 0 )
{
value += 1 << ( noisybits - 1 );
int mask = 0xffffffff >>> ( 32 - noisybits );
encodeBounded( mask, value & mask );
value >>= noisybits;
}
encodeVarBits(value < 0 ? -value : value);
if (value != 0) {
encodeBit(value < 0);
encodeVarBits( value < 0 ? -value : value );
if ( value != 0 )
{
encodeBit( value < 0 );
}
}
/**
* decode a signed integer with some of of least significant bits considered
* noisy
*
*
* @see #encodeNoisyDiff
*/
public int decodeNoisyDiff(int noisybits) {
public int decodeNoisyDiff( int noisybits )
{
int value = 0;
if (noisybits > 0) {
value = decodeBits(noisybits) - (1 << (noisybits - 1));
if ( noisybits > 0 )
{
value = decodeBits( noisybits ) - ( 1 << ( noisybits - 1 ) );
}
int val2 = decodeVarBits() << noisybits;
if (val2 != 0) {
if (decodeBit()) {
if ( val2 != 0 )
{
if ( decodeBit() )
{
val2 = -val2;
}
}
@ -140,34 +161,38 @@ public final class StatCoderContext extends BitCoderContext {
/**
* encode a signed integer with the typical range and median taken from the
* predicted value
*
*
* @see #decodePredictedValue
*/
public void encodePredictedValue(int value, int predictor) {
public void encodePredictedValue( int value, int predictor )
{
int p = predictor < 0 ? -predictor : predictor;
int noisybits = 0;
while (p > 2) {
while (p > 2)
{
noisybits++;
p >>= 1;
}
encodeNoisyDiff(value - predictor, noisybits);
encodeNoisyDiff( value - predictor, noisybits );
}
/**
* decode a signed integer with the typical range and median taken from the
* predicted value
*
*
* @see #encodePredictedValue
*/
public int decodePredictedValue(int predictor) {
public int decodePredictedValue( int predictor )
{
int p = predictor < 0 ? -predictor : predictor;
int noisybits = 0;
while (p > 1023) {
while (p > 1023)
{
noisybits++;
p >>= 1;
}
return predictor + decodeNoisyDiff(noisybits + noisy_bits[p]);
return predictor + decodeNoisyDiff( noisybits + noisy_bits[p] );
}
/**
@ -176,21 +201,30 @@ public final class StatCoderContext extends BitCoderContext {
* number of values with the current bit being 0. This yields an number of
* bits per value that only depends on the typical distance between subsequent
* values and also benefits
*
* @param values the array to encode
* @param offset position in this array where to start
* @param subsize number of values to encode
* @param nextbit bitmask with the most significant bit set to 1
* @param mask should be 0
*
* @param values
* the array to encode
* @param offset
* position in this array where to start
* @param subsize
* number of values to encode
* @param nextbit
* bitmask with the most significant bit set to 1
* @param mask
* should be 0
*/
public void encodeSortedArray(int[] values, int offset, int subsize, int nextbit, int mask) {
if (subsize == 1) { // last-choice shortcut
while (nextbit != 0) {
encodeBit((values[offset] & nextbit) != 0);
public void encodeSortedArray( int[] values, int offset, int subsize, int nextbit, int mask )
{
if ( subsize == 1 ) // last-choice shortcut
{
while (nextbit != 0)
{
encodeBit( ( values[offset] & nextbit ) != 0 );
nextbit >>= 1;
}
}
if (nextbit == 0) {
if ( nextbit == 0 )
{
return;
}
@ -200,54 +234,71 @@ public final class StatCoderContext extends BitCoderContext {
// count 0-bit-fraction
int i = offset;
int end = subsize + offset;
for (; i < end; i++) {
if ((values[i] & mask) != data) {
for ( ; i < end; i++ )
{
if ( ( values[i] & mask ) != data )
{
break;
}
}
int size1 = i - offset;
int size2 = subsize - size1;
encodeBounded(subsize, size1);
if (size1 > 0) {
encodeSortedArray(values, offset, size1, nextbit >> 1, mask);
encodeBounded( subsize, size1 );
if ( size1 > 0 )
{
encodeSortedArray( values, offset, size1, nextbit >> 1, mask );
}
if (size2 > 0) {
encodeSortedArray(values, i, size2, nextbit >> 1, mask);
if ( size2 > 0 )
{
encodeSortedArray( values, i, size2, nextbit >> 1, mask );
}
}
/**
* @param values the array to encode
* @param offset position in this array where to start
* @param subsize number of values to encode
* @param nextbit bitmask with the most significant bit set to 1
* @param value should be 0
* @see #encodeSortedArray
*
* @param values
* the array to encode
* @param offset
* position in this array where to start
* @param subsize
* number of values to encode
* @param nextbit
* bitmask with the most significant bit set to 1
* @param value
* should be 0
*/
public void decodeSortedArray(int[] values, int offset, int subsize, int nextbitpos, int value) {
if (subsize == 1) { // last-choice shortcut
if (nextbitpos >= 0) {
value |= decodeBitsReverse(nextbitpos + 1);
public void decodeSortedArray( int[] values, int offset, int subsize, int nextbitpos, int value )
{
if ( subsize == 1 ) // last-choice shortcut
{
if ( nextbitpos >= 0 )
{
value |= decodeBitsReverse( nextbitpos+1 );
}
values[offset] = value;
return;
}
if (nextbitpos < 0) {
while (subsize-- > 0) {
if ( nextbitpos < 0 )
{
while (subsize-- > 0)
{
values[offset++] = value;
}
return;
}
int size1 = decodeBounded(subsize);
int size1 = decodeBounded( subsize );
int size2 = subsize - size1;
if (size1 > 0) {
decodeSortedArray(values, offset, size1, nextbitpos - 1, value);
if ( size1 > 0 )
{
decodeSortedArray( values, offset, size1, nextbitpos-1, value );
}
if (size2 > 0) {
decodeSortedArray(values, offset + size1, size2, nextbitpos - 1, value | (1 << nextbitpos));
if ( size2 > 0 )
{
decodeSortedArray( values, offset + size1, size2, nextbitpos-1, value | (1 << nextbitpos) );
}
}

View file

@ -2,50 +2,57 @@ package btools.codec;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import btools.util.BitCoderContext;
/**
* Encoder/Decoder for way-/node-descriptions
* <p>
*
* It detects identical descriptions and sorts them
* into a huffman-tree according to their frequencies
* <p>
*
* Adapted for 3-pass encoding (counters -&gt; statistics -&gt; encoding )
* but doesn't do anything at pass1
*/
public final class TagValueCoder {
private Map<TagValueSet, TagValueSet> identityMap;
public final class TagValueCoder
{
private HashMap<TagValueSet, TagValueSet> identityMap;
private Object tree;
private BitCoderContext bc;
private int pass;
private int nextTagValueSetId;
public void encodeTagValueSet(byte[] data) {
if (pass == 1) {
public void encodeTagValueSet( byte[] data )
{
if ( pass == 1 )
{
return;
}
TagValueSet tvsProbe = new TagValueSet(nextTagValueSetId);
tvsProbe.data = data;
TagValueSet tvs = identityMap.get(tvsProbe);
if (pass == 3) {
bc.encodeBounded(tvs.range - 1, tvs.code);
} else if (pass == 2) {
if (tvs == null) {
TagValueSet tvs = identityMap.get( tvsProbe );
if ( pass == 3 )
{
bc.encodeBounded( tvs.range - 1, tvs.code );
}
else if ( pass == 2 )
{
if ( tvs == null )
{
tvs = tvsProbe;
nextTagValueSetId++;
identityMap.put(tvs, tvs);
identityMap.put( tvs, tvs );
}
tvs.frequency++;
}
}
public TagValueWrapper decodeTagValueSet() {
public TagValueWrapper decodeTagValueSet()
{
Object node = tree;
while (node instanceof TreeNode) {
while (node instanceof TreeNode)
{
TreeNode tn = (TreeNode) node;
boolean nextBit = bc.decodeBit();
node = nextBit ? tn.child2 : tn.child1;
@ -53,87 +60,104 @@ public final class TagValueCoder {
return (TagValueWrapper) node;
}
public void encodeDictionary(BitCoderContext bc) {
if (++pass == 3) {
if (identityMap.size() == 0) {
public void encodeDictionary( BitCoderContext bc )
{
if ( ++pass == 3 )
{
if ( identityMap.size() == 0 )
{
TagValueSet dummy = new TagValueSet(nextTagValueSetId++);
identityMap.put(dummy, dummy);
identityMap.put( dummy, dummy );
}
Queue<TagValueSet> queue = new PriorityQueue<>(2 * identityMap.size(), new TagValueSet.FrequencyComparator());
PriorityQueue<TagValueSet> queue = new PriorityQueue<TagValueSet>(2*identityMap.size(), new TagValueSet.FrequencyComparator());
queue.addAll(identityMap.values());
while (queue.size() > 1) {
while (queue.size() > 1)
{
TagValueSet node = new TagValueSet(nextTagValueSetId++);
node.child1 = queue.poll();
node.child2 = queue.poll();
node.frequency = node.child1.frequency + node.child2.frequency;
queue.add(node);
queue.add( node );
}
TagValueSet root = queue.poll();
root.encode(bc, 1, 0);
root.encode( bc, 1, 0 );
}
this.bc = bc;
}
public TagValueCoder(BitCoderContext bc, DataBuffers buffers, TagValueValidator validator) {
tree = decodeTree(bc, buffers, validator);
public TagValueCoder( BitCoderContext bc, DataBuffers buffers, TagValueValidator validator )
{
tree = decodeTree( bc, buffers, validator );
this.bc = bc;
}
public TagValueCoder() {
identityMap = new HashMap<>();
public TagValueCoder()
{
identityMap = new HashMap<TagValueSet, TagValueSet>();
}
private Object decodeTree(BitCoderContext bc, DataBuffers buffers, TagValueValidator validator) {
private Object decodeTree( BitCoderContext bc, DataBuffers buffers, TagValueValidator validator )
{
boolean isNode = bc.decodeBit();
if (isNode) {
if ( isNode )
{
TreeNode node = new TreeNode();
node.child1 = decodeTree(bc, buffers, validator);
node.child2 = decodeTree(bc, buffers, validator);
node.child1 = decodeTree( bc, buffers, validator );
node.child2 = decodeTree( bc, buffers, validator );
return node;
}
byte[] buffer = buffers.tagbuf1;
BitCoderContext ctx = buffers.bctx1;
ctx.reset(buffer);
BitCoderContext ctx = buffers.bctx1;
ctx.reset( buffer );
int inum = 0;
int lastEncodedInum = 0;
boolean hasdata = false;
for (; ; ) {
for ( ;; )
{
int delta = bc.decodeVarBits();
if (!hasdata) {
if (delta == 0) {
if ( !hasdata )
{
if ( delta == 0 )
{
return null;
}
}
if (delta == 0) {
ctx.encodeVarBits(0);
if ( delta == 0 )
{
ctx.encodeVarBits( 0 );
break;
}
inum += delta;
int data = bc.decodeVarBits();
if (validator == null || validator.isLookupIdxUsed(inum)) {
if ( validator == null || validator.isLookupIdxUsed( inum ) )
{
hasdata = true;
ctx.encodeVarBits(inum - lastEncodedInum);
ctx.encodeVarBits(data);
ctx.encodeVarBits( inum - lastEncodedInum );
ctx.encodeVarBits( data );
lastEncodedInum = inum;
}
}
byte[] res;
int len = ctx.closeAndGetEncodedLength();
if (validator == null) {
if ( validator == null )
{
res = new byte[len];
System.arraycopy(buffer, 0, res, 0, len);
} else {
res = validator.unify(buffer, 0, len);
System.arraycopy( buffer, 0, res, 0, len );
}
else
{
res = validator.unify( buffer, 0, len );
}
int accessType = validator == null ? 2 : validator.accessType(res);
if (accessType > 0) {
int accessType = validator == null ? 2 : validator.accessType( res );
if ( accessType > 0 )
{
TagValueWrapper w = new TagValueWrapper();
w.data = res;
w.accessType = accessType;
@ -142,12 +166,14 @@ public final class TagValueCoder {
return null;
}
public static final class TreeNode {
public static final class TreeNode
{
public Object child1;
public Object child2;
}
public static final class TagValueSet {
public static final class TagValueSet
{
public byte[] data;
public int frequency;
public int code;
@ -156,51 +182,66 @@ public final class TagValueCoder {
public TagValueSet child2;
private int id; // serial number to make the comparator well defined in case of equal frequencies
public TagValueSet(int id) {
public TagValueSet( int id )
{
this.id = id;
}
public void encode(BitCoderContext bc, int range, int code) {
public void encode( BitCoderContext bc, int range, int code )
{
this.range = range;
this.code = code;
boolean isNode = child1 != null;
bc.encodeBit(isNode);
if (isNode) {
child1.encode(bc, range << 1, code);
child2.encode(bc, range << 1, code + range);
} else {
if (data == null) {
bc.encodeVarBits(0);
bc.encodeBit( isNode );
if ( isNode )
{
child1.encode( bc, range << 1, code );
child2.encode( bc, range << 1, code + range );
}
else
{
if ( data == null )
{
bc.encodeVarBits( 0 );
return;
}
BitCoderContext src = new BitCoderContext(data);
for (; ; ) {
BitCoderContext src = new BitCoderContext( data );
for ( ;; )
{
int delta = src.decodeVarBits();
bc.encodeVarBits(delta);
if (delta == 0) {
bc.encodeVarBits( delta );
if ( delta == 0 )
{
break;
}
int data = src.decodeVarBits();
bc.encodeVarBits(data);
bc.encodeVarBits( data );
}
}
}
@Override
public boolean equals(Object o) {
if (o instanceof TagValueSet) {
public boolean equals( Object o )
{
if ( o instanceof TagValueSet )
{
TagValueSet tvs = (TagValueSet) o;
if (data == null) {
if ( data == null )
{
return tvs.data == null;
}
if (tvs.data == null) {
if ( tvs.data == null )
{
return data == null;
}
if (data.length != tvs.data.length) {
if ( data.length != tvs.data.length )
{
return false;
}
for (int i = 0; i < data.length; i++) {
if (data[i] != tvs.data[i]) {
for ( int i = 0; i < data.length; i++ )
{
if ( data[i] != tvs.data[i] )
{
return false;
}
}
@ -210,34 +251,39 @@ public final class TagValueCoder {
}
@Override
public int hashCode() {
if (data == null) {
public int hashCode()
{
if ( data == null )
{
return 0;
}
int h = 17;
for (int i = 0; i < data.length; i++) {
h = (h << 8) + data[i];
for ( int i = 0; i < data.length; i++ )
{
h = ( h << 8 ) + data[i];
}
return h;
}
public static class FrequencyComparator implements Comparator<TagValueSet> {
public static class FrequencyComparator implements Comparator<TagValueSet>
{
@Override
public int compare(TagValueSet tvs1, TagValueSet tvs2) {
if (tvs1.frequency < tvs2.frequency)
if ( tvs1.frequency < tvs2.frequency )
return -1;
if (tvs1.frequency > tvs2.frequency)
if ( tvs1.frequency > tvs2.frequency )
return 1;
// to avoid ordering instability, decide on the id if frequency is equal
if (tvs1.id < tvs2.id)
if ( tvs1.id < tvs2.id )
return -1;
if (tvs1.id > tvs2.id)
if ( tvs1.id > tvs2.id )
return 1;
if (tvs1 != tvs2) {
throw new RuntimeException("identity corruption!");
if ( tvs1 != tvs2 )
{
throw new RuntimeException( "identity corruption!" );
}
return 0;
}

View file

@ -1,16 +1,17 @@
package btools.codec;
public interface TagValueValidator {
public interface TagValueValidator
{
/**
* @param tagValueSet the way description to check
* @return 0 = nothing, 1=no matching, 2=normal
*/
int accessType(byte[] tagValueSet);
public int accessType( byte[] tagValueSet );
byte[] unify(byte[] tagValueSet, int offset, int len);
public byte[] unify( byte[] tagValueSet, int offset, int len );
boolean isLookupIdxUsed(int idx);
public boolean isLookupIdxUsed( int idx );
void setDecodeForbidden(boolean decodeForbidden);
public void setDecodeForbidden( boolean decodeForbidden );
}

View file

@ -5,7 +5,8 @@ package btools.codec;
* TagValueWrapper wrapps a description bitmap
* to add the access-type
*/
public final class TagValueWrapper {
public final class TagValueWrapper
{
public byte[] data;
public int accessType;
}

View file

@ -5,10 +5,9 @@ package btools.codec;
* from the decoder to find the closest
* matches to the waypoints
*/
public interface WaypointMatcher {
boolean start(int ilonStart, int ilatStart, int ilonTarget, int ilatTarget);
void transferNode(int ilon, int ilat);
public interface WaypointMatcher
{
boolean start( int ilonStart, int ilatStart, int ilonTarget, int ilatTarget );
void transferNode( int ilon, int ilat );
void end();
}

View file

@ -3,35 +3,50 @@ package btools.codec;
import org.junit.Assert;
import org.junit.Test;
public class LinkedListContainerTest {
public class LinkedListContainerTest
{
@Test
public void linkedListTest1() {
public void linkedListTest1()
{
int nlists = 553;
LinkedListContainer llc = new LinkedListContainer(nlists, null);
LinkedListContainer llc = new LinkedListContainer( nlists, null );
for (int ln = 0; ln < nlists; ln++) {
for (int i = 0; i < 10; i++) {
llc.addDataElement(ln, ln * i);
for ( int ln = 0; ln < nlists; ln++ )
{
for ( int i = 0; i < 10; i++ )
{
llc.addDataElement( ln, ln * i );
}
}
for (int i = 0; i < 10; i++) {
for (int ln = 0; ln < nlists; ln++) {
llc.addDataElement(ln, ln * i);
for ( int i = 0; i < 10; i++ )
{
for ( int ln = 0; ln < nlists; ln++ )
{
llc.addDataElement( ln, ln * i );
}
}
for (int ln = 0; ln < nlists; ln++) {
int cnt = llc.initList(ln);
Assert.assertEquals("list size test", 20, cnt);
for ( int ln = 0; ln < nlists; ln++ )
{
int cnt = llc.initList( ln );
Assert.assertTrue( "list size test", cnt == 20 );
for (int i = 19; i >= 0; i--) {
for ( int i = 19; i >= 0; i-- )
{
int data = llc.getDataElement();
Assert.assertEquals("data value test", ln * (i % 10), data);
Assert.assertTrue( "data value test", data == ln * ( i % 10 ) );
}
}
Assert.assertThrows("no more elements expected", IllegalArgumentException.class, () -> llc.getDataElement());
try
{
llc.getDataElement();
Assert.fail( "no more elements expected" );
}
catch (IllegalArgumentException e)
{
}
}
}

View file

@ -6,79 +6,100 @@ import java.util.Random;
import org.junit.Assert;
import org.junit.Test;
public class StatCoderContextTest {
public class StatCoderContextTest
{
@Test
public void noisyVarBitsEncodeDecodeTest() {
public void noisyVarBitsEncodeDecodeTest()
{
byte[] ab = new byte[40000];
StatCoderContext ctx = new StatCoderContext(ab);
for (int noisybits = 1; noisybits < 12; noisybits++) {
for (int i = 0; i < 1000; i++) {
ctx.encodeNoisyNumber(i, noisybits);
StatCoderContext ctx = new StatCoderContext( ab );
for ( int noisybits = 1; noisybits < 12; noisybits++ )
{
for ( int i = 0; i < 1000; i++ )
{
ctx.encodeNoisyNumber( i, noisybits );
}
}
ctx.closeAndGetEncodedLength();
ctx = new StatCoderContext(ab);
ctx = new StatCoderContext( ab );
for (int noisybits = 1; noisybits < 12; noisybits++) {
for (int i = 0; i < 1000; i++) {
int value = ctx.decodeNoisyNumber(noisybits);
if (value != i) {
Assert.fail("value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value);
for ( int noisybits = 1; noisybits < 12; noisybits++ )
{
for ( int i = 0; i < 1000; i++ )
{
int value = ctx.decodeNoisyNumber( noisybits );
if ( value != i )
{
Assert.fail( "value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value );
}
}
}
}
@Test
public void noisySignedVarBitsEncodeDecodeTest() {
public void noisySignedVarBitsEncodeDecodeTest()
{
byte[] ab = new byte[80000];
StatCoderContext ctx = new StatCoderContext(ab);
for (int noisybits = 0; noisybits < 12; noisybits++) {
for (int i = -1000; i < 1000; i++) {
ctx.encodeNoisyDiff(i, noisybits);
StatCoderContext ctx = new StatCoderContext( ab );
for ( int noisybits = 0; noisybits < 12; noisybits++ )
{
for ( int i = -1000; i < 1000; i++ )
{
ctx.encodeNoisyDiff( i, noisybits );
}
}
ctx.closeAndGetEncodedLength();
ctx = new StatCoderContext(ab);
ctx = new StatCoderContext( ab );
for (int noisybits = 0; noisybits < 12; noisybits++) {
for (int i = -1000; i < 1000; i++) {
int value = ctx.decodeNoisyDiff(noisybits);
if (value != i) {
Assert.fail("value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value);
for ( int noisybits = 0; noisybits < 12; noisybits++ )
{
for ( int i = -1000; i < 1000; i++ )
{
int value = ctx.decodeNoisyDiff( noisybits );
if ( value != i )
{
Assert.fail( "value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value );
}
}
}
}
@Test
public void predictedValueEncodeDecodeTest() {
public void predictedValueEncodeDecodeTest()
{
byte[] ab = new byte[80000];
StatCoderContext ctx = new StatCoderContext(ab);
for (int value = -100; value < 100; value += 5) {
for (int predictor = -200; predictor < 200; predictor += 7) {
ctx.encodePredictedValue(value, predictor);
StatCoderContext ctx = new StatCoderContext( ab );
for ( int value = -100; value < 100; value += 5 )
{
for ( int predictor = -200; predictor < 200; predictor += 7 )
{
ctx.encodePredictedValue( value, predictor );
}
}
ctx.closeAndGetEncodedLength();
ctx = new StatCoderContext(ab);
ctx = new StatCoderContext( ab );
for (int value = -100; value < 100; value += 5) {
for (int predictor = -200; predictor < 200; predictor += 7) {
int decodedValue = ctx.decodePredictedValue(predictor);
if (value != decodedValue) {
Assert.fail("value mismatch: value=" + value + " predictor=" + predictor + " decodedValue=" + decodedValue);
for ( int value = -100; value < 100; value += 5 )
{
for ( int predictor = -200; predictor < 200; predictor += 7 )
{
int decodedValue = ctx.decodePredictedValue( predictor );
if ( value != decodedValue )
{
Assert.fail( "value mismatch: value=" + value + " predictor=" + predictor + " decodedValue=" + decodedValue );
}
}
}
}
@Test
public void sortedArrayEncodeDecodeTest() {
public void sortedArrayEncodeDecodeTest()
{
Random rand = new Random();
int size = 1000000;
int[] values = new int[size];
for (int i = 0; i < size; i++) {
for ( int i = 0; i < size; i++ )
{
values[i] = rand.nextInt() & 0x0fffffff;
}
values[5] = 175384; // force collision
@ -87,21 +108,23 @@ public class StatCoderContextTest {
values[15] = 275384; // force neighbours
values[18] = 275385;
Arrays.sort(values);
Arrays.sort( values );
byte[] ab = new byte[3000000];
StatCoderContext ctx = new StatCoderContext(ab);
ctx.encodeSortedArray(values, 0, size, 0x08000000, 0);
StatCoderContext ctx = new StatCoderContext( ab );
ctx.encodeSortedArray( values, 0, size, 0x08000000, 0 );
ctx.closeAndGetEncodedLength();
ctx = new StatCoderContext(ab);
ctx = new StatCoderContext( ab );
int[] decodedValues = new int[size];
ctx.decodeSortedArray(decodedValues, 0, size, 27, 0);
ctx.decodeSortedArray( decodedValues, 0, size, 27, 0 );
for (int i = 0; i < size; i++) {
if (values[i] != decodedValues[i]) {
Assert.fail("mismatch at i=" + i + " " + values[i] + "<>" + decodedValues[i]);
for ( int i = 0; i < size; i++ )
{
if ( values[i] != decodedValues[i] )
{
Assert.fail( "mismatch at i=" + i + " " + values[i] + "<>" + decodedValues[i] );
}
}
}

1
brouter-core/.gitignore vendored Normal file
View file

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

View file

@ -1,13 +1,13 @@
plugins {
id 'brouter.library-conventions'
id 'java-library'
}
dependencies {
implementation project(':brouter-mapaccess')
implementation project(':brouter-util')
implementation project(':brouter-expressions')
implementation project(':brouter-codec')
}
testImplementation 'junit:junit:4.13.1'
// MapcreatorTest generates segments which are used in tests
test.dependsOn ':brouter-map-creator:test'
}

View file

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

View file

@ -1,43 +0,0 @@
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

@ -1,534 +0,0 @@
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

@ -1,246 +0,0 @@
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

@ -1,91 +0,0 @@
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

@ -1,110 +0,0 @@
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

@ -11,12 +11,15 @@ import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay;
final class KinematicModel extends OsmPathModel {
public OsmPrePath createPrePath() {
final class KinematicModel extends OsmPathModel
{
public OsmPrePath createPrePath()
{
return new KinematicPrePath();
}
public OsmPath createPath() {
public OsmPath createPath()
{
return new KinematicPath();
}
@ -35,7 +38,7 @@ final class KinematicModel extends OsmPathModel {
// derived values
public double pw; // balance power
public double cost0; // minimum possible cost per meter
private int wayIdxMaxspeed;
private int wayIdxMaxspeedExplicit;
private int wayIdxMinspeed;
@ -44,7 +47,7 @@ final class KinematicModel extends OsmPathModel {
protected BExpressionContextWay ctxWay;
protected BExpressionContextNode ctxNode;
protected Map<String, String> params;
protected Map<String,String> params;
private boolean initDone = false;
@ -52,67 +55,77 @@ final class KinematicModel extends OsmPathModel {
private double lastBreakingSpeed;
@Override
public void init(BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String, String> extraParams) {
if (!initDone) {
public void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String,String> extraParams )
{
if ( !initDone )
{
ctxWay = expctxWay;
ctxNode = expctxNode;
wayIdxMaxspeed = ctxWay.getOutputVariableIndex("maxspeed", false);
wayIdxMaxspeedExplicit = ctxWay.getOutputVariableIndex("maxspeed_explicit", false);
wayIdxMinspeed = ctxWay.getOutputVariableIndex("minspeed", false);
nodeIdxMaxspeed = ctxNode.getOutputVariableIndex("maxspeed", false);
wayIdxMaxspeed = ctxWay.getOutputVariableIndex( "maxspeed", false );
wayIdxMaxspeedExplicit = ctxWay.getOutputVariableIndex( "maxspeed_explicit", false );
wayIdxMinspeed = ctxWay.getOutputVariableIndex( "minspeed", false );
nodeIdxMaxspeed = ctxNode.getOutputVariableIndex( "maxspeed", false );
initDone = true;
}
params = extraParams;
turnAngleDecayTime = getParam("turnAngleDecayTime", 5.f);
f_roll = getParam("f_roll", 232.f);
f_air = getParam("f_air", 0.4f);
f_recup = getParam("f_recup", 400.f);
p_standby = getParam("p_standby", 250.f);
outside_temp = getParam("outside_temp", 20.f);
recup_efficiency = getParam("recup_efficiency", 0.7f);
totalweight = getParam("totalweight", 1640.f);
vmax = getParam("vmax", 80.f) / 3.6;
leftWaySpeed = getParam("leftWaySpeed", 12.f) / 3.6;
rightWaySpeed = getParam("rightWaySpeed", 12.f) / 3.6;
turnAngleDecayTime = getParam( "turnAngleDecayTime", 5.f );
f_roll = getParam( "f_roll", 232.f );
f_air = getParam( "f_air", 0.4f );
f_recup = getParam( "f_recup", 400.f );
p_standby = getParam( "p_standby", 250.f );
outside_temp = getParam( "outside_temp", 20.f );
recup_efficiency = getParam( "recup_efficiency", 0.7f );
totalweight = getParam( "totalweight", 1640.f );
vmax = getParam( "vmax", 80.f ) / 3.6;
leftWaySpeed = getParam( "leftWaySpeed", 12.f ) / 3.6;
rightWaySpeed = getParam( "rightWaySpeed", 12.f ) / 3.6;
pw = 2. * f_air * vmax * vmax * vmax - p_standby;
cost0 = (pw + p_standby) / vmax + f_roll + f_air * vmax * vmax;
cost0 = (pw+p_standby)/vmax + f_roll + f_air*vmax*vmax;
}
protected float getParam(String name, float defaultValue) {
String sval = params == null ? null : params.get(name);
if (sval != null) {
return Float.parseFloat(sval);
protected float getParam( String name, float defaultValue )
{
String sval = params == null ? null : params.get( name );
if ( sval != null )
{
return Float.parseFloat( sval );
}
float v = ctxWay.getVariableValue(name, defaultValue);
if (params != null) {
params.put(name, "" + v);
float v = ctxWay.getVariableValue( name, defaultValue );
if ( params != null )
{
params.put( name, "" + v );
}
return v;
}
public float getWayMaxspeed() {
return ctxWay.getBuildInVariable(wayIdxMaxspeed) / 3.6f;
public float getWayMaxspeed()
{
return ctxWay.getBuildInVariable( wayIdxMaxspeed ) / 3.6f;
}
public float getWayMaxspeedExplicit() {
return ctxWay.getBuildInVariable(wayIdxMaxspeedExplicit) / 3.6f;
public float getWayMaxspeedExplicit()
{
return ctxWay.getBuildInVariable( wayIdxMaxspeedExplicit ) / 3.6f;
}
public float getWayMinspeed() {
return ctxWay.getBuildInVariable(wayIdxMinspeed) / 3.6f;
public float getWayMinspeed()
{
return ctxWay.getBuildInVariable( wayIdxMinspeed ) / 3.6f;
}
public float getNodeMaxspeed() {
return ctxNode.getBuildInVariable(nodeIdxMaxspeed) / 3.6f;
public float getNodeMaxspeed()
{
return ctxNode.getBuildInVariable( nodeIdxMaxspeed ) / 3.6f;
}
/**
* get the effective speed limit from the way-limit and vmax/vmin
*/
public double getEffectiveSpeedLimit() {
/**
* get the effective speed limit from the way-limit and vmax/vmin
*/
public double getEffectiveSpeedLimit( )
{
// performance related inline coding
double minspeed = getWayMinspeed();
double espeed = minspeed > vmax ? minspeed : vmax;
@ -120,27 +133,30 @@ final class KinematicModel extends OsmPathModel {
return maxspeed < espeed ? maxspeed : espeed;
}
/**
* get the breaking speed for current balance-power (pw) and effective speed limit (vl)
*/
public double getBreakingSpeed(double vl) {
if (vl == lastEffectiveLimit) {
/**
* get the breaking speed for current balance-power (pw) and effective speed limit (vl)
*/
public double getBreakingSpeed( double vl )
{
if ( vl == lastEffectiveLimit )
{
return lastBreakingSpeed;
}
double v = vl * 0.8;
double pw2 = pw + p_standby;
double v = vl*0.8;
double pw2 = pw+p_standby;
double e = recup_efficiency;
double x0 = pw2 / vl + f_air * e * vl * vl + (1. - e) * f_roll;
for (int i = 0; i < 5; i++) {
double v2 = v * v;
double x = pw2 / v + f_air * e * v2 - x0;
double dx = 2. * e * f_air * v - pw2 / v2;
v -= x / dx;
double x0 = pw2/vl+f_air*e*vl*vl+(1.-e)*f_roll;
for(int i=0;i<5;i++)
{
double v2 = v*v;
double x = pw2/v+f_air*e*v2 - x0;
double dx = 2.*e*f_air*v - pw2/v2;
v -= x/dx;
}
lastEffectiveLimit = vl;
lastBreakingSpeed = v;
return v;
}

View file

@ -5,7 +5,11 @@
*/
package btools.router;
final class KinematicPath extends OsmPath {
import btools.util.FastMath;
final class KinematicPath extends OsmPath
{
private double ekin; // kinetic energy (Joule)
private double totalTime; // travel time (seconds)
private double totalEnergy; // total route energy (Joule)
@ -13,17 +17,20 @@ final class KinematicPath extends OsmPath {
private float floatingAngleRight; // sliding average right bend (degree)
@Override
protected void init(OsmPath orig) {
KinematicPath origin = (KinematicPath) orig;
protected void init( OsmPath orig )
{
KinematicPath origin = (KinematicPath)orig;
ekin = origin.ekin;
totalTime = origin.totalTime;
totalEnergy = origin.totalEnergy;
floatingAngleLeft = origin.floatingAngleLeft;
floatingAngleRight = origin.floatingAngleRight;
priorityclassifier = origin.priorityclassifier;
}
@Override
protected void resetState() {
protected void resetState()
{
ekin = 0.;
totalTime = 0.;
totalEnergy = 0.;
@ -32,233 +39,267 @@ final class KinematicPath extends OsmPath {
}
@Override
protected double processWaySection(RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier) {
KinematicModel km = (KinematicModel) rc.pm;
protected double processWaySection( RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier )
{
KinematicModel km = (KinematicModel)rc.pm;
double cost = 0.;
double extraTime = 0.;
if (isStartpoint) {
if ( isStartpoint )
{
// for forward direction, we start with target speed
if (!rc.inverseDirection) {
extraTime = 0.5 * (1. - cosangle) * 40.; // 40 seconds turn penalty
if ( !rc.inverseDirection )
{
extraTime = 0.5 * (1. - cosangle ) * 40.; // 40 seconds turn penalty
}
} else {
}
else
{
double turnspeed = 999.; // just high
if (km.turnAngleDecayTime != 0.) { // process turn-angle slowdown
if (angle < 0) floatingAngleLeft -= (float) angle;
else floatingAngleRight += (float) angle;
float aa = Math.max(floatingAngleLeft, floatingAngleRight);
if ( km.turnAngleDecayTime != 0. ) // process turn-angle slowdown
{
if ( angle < 0 ) floatingAngleLeft -= (float)angle;
else floatingAngleRight += (float)angle;
float aa = Math.max( floatingAngleLeft, floatingAngleRight );
double curveSpeed = aa > 10. ? 200. / aa : 20.;
double curveSpeed = aa > 10. ? 200. / aa : 20.;
double distanceTime = dist / curveSpeed;
double decayFactor = Math.exp(-distanceTime / km.turnAngleDecayTime);
floatingAngleLeft = (float) (floatingAngleLeft * decayFactor);
floatingAngleRight = (float) (floatingAngleRight * decayFactor);
double decayFactor = FastMath.exp( - distanceTime / km.turnAngleDecayTime );
floatingAngleLeft = (float)( floatingAngleLeft * decayFactor );
floatingAngleRight = (float)( floatingAngleRight * decayFactor );
if (curveSpeed < 20.) {
if ( curveSpeed < 20. )
{
turnspeed = curveSpeed;
}
}
if (nsection == 0) { // process slowdown by crossing geometry
if ( nsection == 0 ) // process slowdown by crossing geometry
{
double junctionspeed = 999.; // just high
int classifiermask = (int) rc.expctxWay.getClassifierMask();
int classifiermask = (int)rc.expctxWay.getClassifierMask();
// penalty for equal priority crossing
boolean hasLeftWay = false;
boolean hasRightWay = false;
boolean hasResidential = false;
for (OsmPrePath prePath = rc.firstPrePath; prePath != null; prePath = prePath.next) {
KinematicPrePath pp = (KinematicPrePath) prePath;
for( OsmPrePath prePath = rc.firstPrePath; prePath != null; prePath = prePath.next )
{
KinematicPrePath pp = (KinematicPrePath)prePath;
if (((pp.classifiermask ^ classifiermask) & 8) != 0) { // exactly one is linktype
if ( ( (pp.classifiermask ^ classifiermask) & 8 ) != 0 ) // exactly one is linktype
{
continue;
}
if ((pp.classifiermask & 32) != 0) { // touching a residential?
if ( ( pp.classifiermask & 32 ) != 0 ) // touching a residential?
{
hasResidential = true;
}
if (pp.priorityclassifier > priorityclassifier || pp.priorityclassifier == priorityclassifier && priorityclassifier < 20) {
if ( pp.priorityclassifier > priorityclassifier || pp.priorityclassifier == priorityclassifier && priorityclassifier < 20 )
{
double diff = pp.angle - angle;
if (diff < -40. && diff > -140.) hasLeftWay = true;
if (diff > 40. && diff < 140.) hasRightWay = true;
if ( diff < -40. && diff > -140.) hasLeftWay = true;
if ( diff > 40. && diff < 140. ) hasRightWay = true;
}
}
double residentialSpeed = 13.;
if (hasLeftWay && junctionspeed > km.leftWaySpeed) junctionspeed = km.leftWaySpeed;
if (hasRightWay && junctionspeed > km.rightWaySpeed) junctionspeed = km.rightWaySpeed;
if (hasResidential && junctionspeed > residentialSpeed) junctionspeed = residentialSpeed;
if ( hasLeftWay && junctionspeed > km.leftWaySpeed ) junctionspeed = km.leftWaySpeed;
if ( hasRightWay && junctionspeed > km.rightWaySpeed ) junctionspeed = km.rightWaySpeed;
if ( hasResidential && junctionspeed > residentialSpeed ) junctionspeed = residentialSpeed;
if ((lastpriorityclassifier < 20) ^ (priorityclassifier < 20)) {
if ( (lastpriorityclassifier < 20) ^ (priorityclassifier < 20) )
{
extraTime += 10.;
junctionspeed = 0; // full stop for entering or leaving road network
}
if (lastpriorityclassifier != priorityclassifier && (classifiermask & 8) != 0) {
if ( lastpriorityclassifier != priorityclassifier && (classifiermask & 8) != 0 )
{
extraTime += 2.; // two seconds for entering a link-type
}
turnspeed = turnspeed > junctionspeed ? junctionspeed : turnspeed;
if (message != null) {
message.vnode0 = (int) (junctionspeed * 3.6 + 0.5);
if ( message != null )
{
message.vnode0 = (int) ( junctionspeed * 3.6 + 0.5 );
}
}
cutEkin(km.totalweight, turnspeed); // apply turnspeed
cutEkin( km.totalweight, turnspeed ); // apply turnspeed
}
// linear temperature correction
double tcorr = (20. - km.outside_temp) * 0.0035;
double tcorr = (20.-km.outside_temp)*0.0035;
// air_pressure down 1mb/8m
double ecorr = 0.0001375 * (elevation - 100.);
double f_air = km.f_air * (1. + tcorr - ecorr);
double f_air = km.f_air * ( 1. + tcorr - ecorr );
double distanceCost = evolveDistance(km, dist, delta_h, f_air);
double distanceCost = evolveDistance( km, dist, delta_h, f_air );
if (message != null) {
message.costfactor = (float) (distanceCost / dist);
message.vmax = (int) (km.getWayMaxspeed() * 3.6 + 0.5);
message.vmaxExplicit = (int) (km.getWayMaxspeedExplicit() * 3.6 + 0.5);
message.vmin = (int) (km.getWayMinspeed() * 3.6 + 0.5);
message.extraTime = (int) (extraTime * 1000);
if ( message != null )
{
message.costfactor = (float)(distanceCost/dist);
message.vmax = (int) ( km.getWayMaxspeed() * 3.6 + 0.5 );
message.vmaxExplicit = (int) ( km.getWayMaxspeedExplicit() * 3.6 + 0.5 );
message.vmin = (int) ( km.getWayMinspeed() * 3.6 + 0.5 );
message.extraTime = (int)(extraTime*1000);
}
cost += extraTime * km.pw / km.cost0;
cost += extraTime * km.pw / km.cost0;
totalTime += extraTime;
return cost + distanceCost;
}
protected double evolveDistance(KinematicModel km, double dist, double delta_h, double f_air) {
protected double evolveDistance( KinematicModel km, double dist, double delta_h, double f_air )
{
// elevation force
double fh = delta_h * km.totalweight * 9.81 / dist;
double effectiveSpeedLimit = km.getEffectiveSpeedLimit();
double emax = 0.5 * km.totalweight * effectiveSpeedLimit * effectiveSpeedLimit;
if (emax <= 0.) {
double emax = 0.5*km.totalweight*effectiveSpeedLimit*effectiveSpeedLimit;
if ( emax <= 0. )
{
return -1.;
}
double vb = km.getBreakingSpeed(effectiveSpeedLimit);
double elow = 0.5 * km.totalweight * vb * vb;
double vb = km.getBreakingSpeed( effectiveSpeedLimit );
double elow = 0.5*km.totalweight*vb*vb;
double elapsedTime = 0.;
double dissipatedEnergy = 0.;
double v = Math.sqrt(2. * ekin / km.totalweight);
double v = Math.sqrt( 2. * ekin / km.totalweight );
double d = dist;
while (d > 0.) {
while( d > 0. )
{
boolean slow = ekin < elow;
boolean fast = ekin >= emax;
double etarget = slow ? elow : emax;
double f = km.f_roll + f_air * v * v + fh;
double f_recup = Math.max(0., fast ? -f : (slow ? km.f_recup : 0) - fh); // additional recup for slow part
double f = km.f_roll + f_air*v*v + fh;
double f_recup = Math.max( 0., fast ? -f : (slow ? km.f_recup :0 ) -fh ); // additional recup for slow part
f += f_recup;
double delta_ekin;
double timeStep;
double x;
if (fast) {
if ( fast )
{
x = d;
delta_ekin = x * f;
timeStep = x / v;
delta_ekin = x*f;
timeStep = x/v;
ekin = etarget;
} else {
delta_ekin = etarget - ekin;
double b = 2. * f_air / km.totalweight;
double x0 = delta_ekin / f;
double x0b = x0 * b;
x = x0 * (1. - x0b * (0.5 + x0b * (0.333333333 - x0b * 0.25))); // = ln( delta_ekin*b/f + 1.) / b;
double maxstep = Math.min(50., d);
if (x >= maxstep) {
}
else
{
delta_ekin = etarget-ekin;
double b = 2.*f_air / km.totalweight;
double x0 = delta_ekin/f;
double x0b = x0*b;
x = x0*(1. - x0b*(0.5 + x0b*(0.333333333-x0b*0.25 ) ) ); // = ln( delta_ekin*b/f + 1.) / b;
double maxstep = Math.min( 50., d );
if ( x >= maxstep )
{
x = maxstep;
double xb = x * b;
delta_ekin = x * f * (1. + xb * (0.5 + xb * (0.166666667 + xb * 0.0416666667))); // = f/b* exp(xb-1)
double xb = x*b;
delta_ekin = x*f*(1.+xb*(0.5+xb*(0.166666667+xb*0.0416666667 ) ) ); // = f/b* exp(xb-1)
ekin += delta_ekin;
} else {
}
else
{
ekin = etarget;
}
double v2 = Math.sqrt(2. * ekin / km.totalweight);
double v2 = Math.sqrt( 2. * ekin / km.totalweight );
double a = f / km.totalweight; // TODO: average force?
timeStep = (v2 - v) / a;
timeStep = (v2-v)/a;
v = v2;
}
d -= x;
elapsedTime += timeStep;
// dissipated energy does not contain elevation and efficient recup
dissipatedEnergy += delta_ekin - x * (fh + f_recup * km.recup_efficiency);
dissipatedEnergy += delta_ekin - x*(fh + f_recup*km.recup_efficiency);
// correction: inefficient recup going into heating is half efficient
double ieRecup = x * f_recup * (1. - km.recup_efficiency);
double eaux = timeStep * km.p_standby;
dissipatedEnergy -= Math.max(ieRecup, eaux) * 0.5;
double ieRecup = x*f_recup*(1.-km.recup_efficiency);
double eaux = timeStep*km.p_standby;
dissipatedEnergy -= Math.max( ieRecup, eaux ) * 0.5;
}
dissipatedEnergy += elapsedTime * km.p_standby;
totalTime += elapsedTime;
totalEnergy += dissipatedEnergy + dist * fh;
totalEnergy += dissipatedEnergy + dist*fh;
return (km.pw * elapsedTime + dissipatedEnergy) / km.cost0; // =cost
return (km.pw * elapsedTime + dissipatedEnergy)/km.cost0; // =cost
}
@Override
protected double processTargetNode(RoutingContext rc) {
KinematicModel km = (KinematicModel) rc.pm;
protected double processTargetNode( RoutingContext rc )
{
KinematicModel km = (KinematicModel)rc.pm;
// finally add node-costs for target node
if (targetNode.nodeDescription != null) {
rc.expctxNode.evaluate(false, targetNode.nodeDescription);
if ( targetNode.nodeDescription != null )
{
rc.expctxNode.evaluate( false , targetNode.nodeDescription );
float initialcost = rc.expctxNode.getInitialcost();
if (initialcost >= 1000000.) {
if ( initialcost >= 1000000. )
{
return -1.;
}
cutEkin(km.totalweight, km.getNodeMaxspeed()); // apply node maxspeed
cutEkin( km.totalweight, km.getNodeMaxspeed() ); // apply node maxspeed
if (message != null) {
message.linknodecost += (int) initialcost;
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription(false, targetNode.nodeDescription);
if ( message != null )
{
message.linknodecost += (int)initialcost;
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription( false, targetNode.nodeDescription );
message.vnode1 = (int) (km.getNodeMaxspeed() * 3.6 + 0.5);
message.vnode1 = (int) ( km.getNodeMaxspeed() * 3.6 + 0.5 );
}
return initialcost;
}
return 0.;
}
private void cutEkin(double weight, double speed) {
double e = 0.5 * weight * speed * speed;
if (ekin > e) ekin = e;
private void cutEkin( double weight, double speed )
{
double e = 0.5*weight*speed*speed;
if ( ekin > e ) ekin = e;
}
@Override
public int elevationCorrection() {
public int elevationCorrection( RoutingContext rc )
{
return 0;
}
@Override
public boolean definitlyWorseThan(OsmPath path) {
KinematicPath p = (KinematicPath) path;
public boolean definitlyWorseThan( OsmPath path, RoutingContext rc )
{
KinematicPath p = (KinematicPath)path;
int c = p.cost;
return cost > c + 100;
int c = p.cost;
return cost > c + 100;
}
@Override
public double getTotalTime() {
public double getTotalTime()
{
return totalTime;
}
@Override
public double getTotalEnergy() {
public double getTotalEnergy()
{
return totalEnergy;
}
}

View file

@ -8,14 +8,16 @@ package btools.router;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmTransferNode;
final class KinematicPrePath extends OsmPrePath {
final class KinematicPrePath extends OsmPrePath
{
public double angle;
public int priorityclassifier;
public int classifiermask;
protected void initPrePath(OsmPath origin, RoutingContext rc) {
protected void initPrePath(OsmPath origin, RoutingContext rc )
{
byte[] description = link.descriptionBitmap;
if (description == null) throw new IllegalArgumentException("null description for: " + link);
if ( description == null ) throw new IllegalArgumentException( "null description for: " + link );
// extract the 3 positions of the first section
int lon0 = origin.originLon;
@ -25,29 +27,32 @@ final class KinematicPrePath extends OsmPrePath {
int lon1 = p1.getILon();
int lat1 = p1.getILat();
boolean isReverse = link.isReverse(sourceNode);
boolean isReverse = link.isReverse( sourceNode );
// evaluate the way tags
rc.expctxWay.evaluate(rc.inverseDirection ^ isReverse, description);
rc.expctxWay.evaluate( rc.inverseDirection ^ isReverse, description );
OsmTransferNode transferNode = link.geometry == null ? null
: rc.geometryDecoder.decodeGeometry(link.geometry, p1, targetNode, isReverse);
: rc.geometryDecoder.decodeGeometry( link.geometry, p1, targetNode, isReverse );
int lon2;
int lat2;
if (transferNode == null) {
if ( transferNode == null )
{
lon2 = targetNode.ilon;
lat2 = targetNode.ilat;
} else {
}
else
{
lon2 = transferNode.ilon;
lat2 = transferNode.ilat;
}
int dist = rc.calcDistance(lon1, lat1, lon2, lat2);
int dist = rc.calcDistance( lon1, lat1, lon2, lat2 );
angle = rc.anglemeter.calcAngle(lon0, lat0, lon1, lat1, lon2, lat2);
priorityclassifier = (int) rc.expctxWay.getPriorityClassifier();
classifiermask = (int) rc.expctxWay.getClassifierMask();
angle = rc.anglemeter.calcAngle( lon0, lat0, lon1, lat1, lon2, lat2 );
priorityclassifier = (int)rc.expctxWay.getPriorityClassifier();
classifiermask = (int)rc.expctxWay.getClassifierMask();
}
}

View file

@ -6,13 +6,15 @@
package btools.router;
final class MessageData implements Cloneable {
final class MessageData implements Cloneable
{
int linkdist = 0;
int linkelevationcost = 0;
int linkturncost = 0;
int linknodecost = 0;
int linkinitcost = 0;
float costfactor;
int priorityclassifier;
int classifiermask;
@ -23,7 +25,7 @@ final class MessageData implements Cloneable {
int lon;
int lat;
short ele;
float time;
float energy;
@ -35,70 +37,84 @@ final class MessageData implements Cloneable {
int vnode1 = 999;
int extraTime = 0;
String toMessage() {
if (wayKeyValues == null) {
String toMessage()
{
if ( wayKeyValues == null )
{
return null;
}
int iCost = (int) (costfactor * 1000 + 0.5f);
return (lon - 180000000) + "\t"
+ (lat - 90000000) + "\t"
+ ele / 4 + "\t"
+ linkdist + "\t"
+ iCost + "\t"
+ linkelevationcost
+ "\t" + linkturncost
+ "\t" + linknodecost
+ "\t" + linkinitcost
+ "\t" + wayKeyValues
+ "\t" + (nodeKeyValues == null ? "" : nodeKeyValues)
+ "\t" + ((int) time)
+ "\t" + ((int) energy);
int iCost = (int)(costfactor*1000 + 0.5f);
return (lon-180000000) + "\t"
+ (lat-90000000) + "\t"
+ ele/4 + "\t"
+ linkdist + "\t"
+ iCost + "\t"
+ linkelevationcost
+ "\t" + linkturncost
+ "\t" + linknodecost
+ "\t" + linkinitcost
+ "\t" + wayKeyValues
+ "\t" + ( nodeKeyValues == null ? "" : nodeKeyValues )
+ "\t" + ((int)time)
+ "\t" + ((int)energy);
}
void add(MessageData d) {
void add( MessageData d )
{
linkdist += d.linkdist;
linkelevationcost += d.linkelevationcost;
linkturncost += d.linkturncost;
linknodecost += d.linknodecost;
linkinitcost += d.linkinitcost;
linkinitcost+= d.linkinitcost;
}
MessageData copy() {
try {
return (MessageData) clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
MessageData copy()
{
try
{
return (MessageData)clone();
}
catch( CloneNotSupportedException e )
{
throw new RuntimeException( e );
}
}
@Override
public String toString() {
public String toString()
{
return "dist=" + linkdist + " prio=" + priorityclassifier + " turn=" + turnangle;
}
public int getPrio() {
public int getPrio()
{
return priorityclassifier;
}
public boolean isBadOneway() {
return (classifiermask & 1) != 0;
public boolean isBadOneway()
{
return ( classifiermask & 1 ) != 0;
}
public boolean isGoodOneway() {
return (classifiermask & 2) != 0;
public boolean isGoodOneway()
{
return ( classifiermask & 2 ) != 0;
}
public boolean isRoundabout() {
return (classifiermask & 4) != 0;
public boolean isRoundabout()
{
return ( classifiermask & 4 ) != 0;
}
public boolean isLinktType() {
return (classifiermask & 8) != 0;
public boolean isLinktType()
{
return ( classifiermask & 8 ) != 0;
}
public boolean isGoodForCars() {
return (classifiermask & 16) != 0;
public boolean isGoodForCars()
{
return ( classifiermask & 16 ) != 0;
}
}

View file

@ -1,99 +1,103 @@
/**
* Container for an osm node
*
* @author ab
*/
package btools.router;
import btools.mapaccess.OsmNode;
import btools.util.CheapRuler;
public class OsmNodeNamed extends OsmNode {
public String name;
public double radius; // radius of nogopoint (in meters)
public double nogoWeight; // weight for nogopoint
public boolean isNogo = false;
public boolean direct = false; // mark direct routing
public OsmNodeNamed() {
}
public OsmNodeNamed(OsmNode n) {
super(n.ilon, n.ilat);
}
@Override
public String toString() {
if (Double.isNaN(nogoWeight)) {
return ilon + "," + ilat + "," + name;
} else {
return ilon + "," + ilat + "," + name + "," + nogoWeight;
}
}
public double distanceWithinRadius(int lon1, int lat1, int lon2, int lat2, double totalSegmentLength) {
double[] lonlat2m = CheapRuler.getLonLatToMeterScales((lat1 + lat2) >> 1);
boolean isFirstPointWithinCircle = CheapRuler.distance(lon1, lat1, ilon, ilat) < radius;
boolean isLastPointWithinCircle = CheapRuler.distance(lon2, lat2, ilon, ilat) < radius;
// First point is within the circle
if (isFirstPointWithinCircle) {
// Last point is within the circle
if (isLastPointWithinCircle) {
return totalSegmentLength;
}
// Last point is not within the circle
// Just swap points and go on with first first point not within the
// circle now.
// Swap longitudes
int tmp = lon2;
lon2 = lon1;
lon1 = tmp;
// Swap latitudes
tmp = lat2;
lat2 = lat1;
lat1 = tmp;
// Fix boolean values
isLastPointWithinCircle = isFirstPointWithinCircle;
isFirstPointWithinCircle = false;
}
// Distance between the initial point and projection of center of
// the circle on the current segment.
double initialToProject = (
(lon2 - lon1) * (ilon - lon1) * lonlat2m[0] * lonlat2m[0]
+ (lat2 - lat1) * (ilat - lat1) * lonlat2m[1] * lonlat2m[1]
) / totalSegmentLength;
// Distance between the initial point and the center of the circle.
double initialToCenter = CheapRuler.distance(ilon, ilat, lon1, lat1);
// Half length of the segment within the circle
double halfDistanceWithin = Math.sqrt(
radius * radius - (
initialToCenter * initialToCenter -
initialToProject * initialToProject
)
);
// Last point is within the circle
if (isLastPointWithinCircle) {
return halfDistanceWithin + (totalSegmentLength - initialToProject);
}
return 2 * halfDistanceWithin;
}
public static OsmNodeNamed decodeNogo(String s) {
OsmNodeNamed n = new OsmNodeNamed();
int idx1 = s.indexOf(',');
n.ilon = Integer.parseInt(s.substring(0, idx1));
int idx2 = s.indexOf(',', idx1 + 1);
n.ilat = Integer.parseInt(s.substring(idx1 + 1, idx2));
int idx3 = s.indexOf(',', idx2 + 1);
if (idx3 == -1) {
n.name = s.substring(idx2 + 1);
n.nogoWeight = Double.NaN;
} else {
n.name = s.substring(idx2 + 1, idx3);
n.nogoWeight = Double.parseDouble(s.substring(idx3 + 1));
}
n.isNogo = true;
return n;
}
}
/**
* Container for an osm node
*
* @author ab
*/
package btools.router;
import btools.mapaccess.OsmNode;
import btools.util.CheapRuler;
public class OsmNodeNamed extends OsmNode
{
public String name;
public double radius; // radius of nogopoint (in meters)
public double nogoWeight; // weight for nogopoint
public boolean isNogo = false;
public OsmNodeNamed()
{
}
public OsmNodeNamed( OsmNode n)
{
super( n.ilon, n.ilat );
}
@Override
public String toString()
{
if ( Double.isNaN(nogoWeight ) ) {
return ilon + "," + ilat + "," + name;
} else {
return ilon + "," + ilat + "," + name + "," + nogoWeight;
}
}
public double distanceWithinRadius(int lon1, int lat1, int lon2, int lat2, double totalSegmentLength) {
double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (lat1 + lat2) >> 1 );
boolean isFirstPointWithinCircle = CheapRuler.distance(lon1, lat1, ilon, ilat) < radius;
boolean isLastPointWithinCircle = CheapRuler.distance(lon2, lat2, ilon, ilat) < radius;
// First point is within the circle
if (isFirstPointWithinCircle) {
// Last point is within the circle
if (isLastPointWithinCircle) {
return totalSegmentLength;
}
// Last point is not within the circle
// Just swap points and go on with first first point not within the
// circle now.
// Swap longitudes
int tmp = lon2;
lon2 = lon1;
lon1 = tmp;
// Swap latitudes
tmp = lat2;
lat2 = lat1;
lat1 = tmp;
// Fix boolean values
isLastPointWithinCircle = isFirstPointWithinCircle;
isFirstPointWithinCircle = false;
}
// Distance between the initial point and projection of center of
// the circle on the current segment.
double initialToProject = (
(lon2 - lon1) * (ilon - lon1) * lonlat2m[0] * lonlat2m[0]
+ (lat2 - lat1) * (ilat - lat1) * lonlat2m[1] * lonlat2m[1]
) / totalSegmentLength;
// Distance between the initial point and the center of the circle.
double initialToCenter = CheapRuler.distance(ilon, ilat, lon1, lat1);
// Half length of the segment within the circle
double halfDistanceWithin = Math.sqrt(
radius*radius - (
initialToCenter*initialToCenter -
initialToProject*initialToProject
)
);
// Last point is within the circle
if (isLastPointWithinCircle) {
return halfDistanceWithin + (totalSegmentLength - initialToProject);
}
return 2 * halfDistanceWithin;
}
public static OsmNodeNamed decodeNogo( String s )
{
OsmNodeNamed n = new OsmNodeNamed();
int idx1 = s.indexOf( ',' );
n.ilon = Integer.parseInt( s.substring( 0, idx1 ) );
int idx2 = s.indexOf( ',', idx1+1 );
n.ilat = Integer.parseInt( s.substring( idx1+1, idx2 ) );
int idx3 = s.indexOf( ',', idx2+1 );
if ( idx3 == -1) {
n.name = s.substring( idx2 + 1 );
n.nogoWeight = Double.NaN;
} else {
n.name = s.substring( idx2+1, idx3 );
n.nogoWeight = Double.parseDouble( s.substring( idx3 + 1 ) );
}
n.isNogo = true;
return n;
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,430 +1,524 @@
/**
* Container for link between two Osm nodes
*
* @author ab
*/
package btools.router;
import btools.mapaccess.OsmLink;
import btools.mapaccess.OsmLinkHolder;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmTransferNode;
import btools.mapaccess.TurnRestriction;
import btools.util.CheapRuler;
abstract class OsmPath implements OsmLinkHolder {
/**
* The cost of that path (a modified distance)
*/
public int cost = 0;
// the elevation assumed for that path can have a value
// if the corresponding node has not
public short selev;
public int airdistance = 0; // distance to endpos
protected OsmNode sourceNode;
protected OsmNode targetNode;
protected OsmLink link;
public OsmPathElement originElement;
public OsmPathElement myElement;
private OsmLinkHolder nextForLink = null;
public int treedepth = 0;
// the position of the waypoint just before
// this path position (for angle calculation)
public int originLon;
public int originLat;
// the classifier of the segment just before this paths position
protected float lastClassifier;
protected float lastInitialCost;
protected int priorityclassifier;
private static final int PATH_START_BIT = 1;
private static final int CAN_LEAVE_DESTINATION_BIT = 2;
private static final int IS_ON_DESTINATION_BIT = 4;
private static final int HAD_DESTINATION_START_BIT = 8;
protected int bitfield = PATH_START_BIT;
private boolean getBit(int mask) {
return (bitfield & mask) != 0;
}
private void setBit(int mask, boolean bit) {
if (getBit(mask) != bit) {
bitfield ^= mask;
}
}
public boolean didEnterDestinationArea() {
return !getBit(HAD_DESTINATION_START_BIT) && getBit(IS_ON_DESTINATION_BIT);
}
public MessageData message;
public void init(OsmLink link) {
this.link = link;
targetNode = link.getTarget(null);
selev = targetNode.getSElev();
originLon = -1;
originLat = -1;
}
public void init(OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc) {
if (origin.myElement == null) {
origin.myElement = OsmPathElement.create(origin);
}
this.originElement = origin.myElement;
this.link = link;
this.sourceNode = origin.targetNode;
this.targetNode = link.getTarget(sourceNode);
this.cost = origin.cost;
this.lastClassifier = origin.lastClassifier;
this.lastInitialCost = origin.lastInitialCost;
this.bitfield = origin.bitfield;
this.priorityclassifier = origin.priorityclassifier;
init(origin);
addAddionalPenalty(refTrack, detailMode, origin, link, rc);
}
protected abstract void init(OsmPath orig);
protected abstract void resetState();
static int seg = 1;
protected void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc) {
byte[] description = link.descriptionBitmap;
if (description == null) { // could be a beeline path
message = new MessageData();
if (message != null) {
message.turnangle = 0;
message.time = (float) 1;
message.energy = (float) 0;
message.priorityclassifier = 0;
message.classifiermask = 0;
message.lon = targetNode.getILon();
message.lat = targetNode.getILat();
message.ele = Short.MIN_VALUE;
message.linkdist = sourceNode.calcDistance(targetNode);
message.wayKeyValues = "direct_segment=" + seg;
seg++;
}
return;
}
boolean recordTransferNodes = detailMode;
rc.nogoCost = 0.;
// extract the 3 positions of the first section
int lon0 = origin.originLon;
int lat0 = origin.originLat;
int lon1 = sourceNode.getILon();
int lat1 = sourceNode.getILat();
short ele1 = origin.selev;
int linkdisttotal = 0;
message = detailMode ? new MessageData() : null;
boolean isReverse = link.isReverse(sourceNode);
// evaluate the way tags
rc.expctxWay.evaluate(rc.inverseDirection ^ isReverse, description);
// calculate the costfactor inputs
float costfactor = rc.expctxWay.getCostfactor();
boolean isTrafficBackbone = cost == 0 && rc.expctxWay.getIsTrafficBackbone() > 0.f;
int lastpriorityclassifier = priorityclassifier;
priorityclassifier = (int) rc.expctxWay.getPriorityClassifier();
// *** add initial cost if the classifier changed
float newClassifier = rc.expctxWay.getInitialClassifier();
float newInitialCost = rc.expctxWay.getInitialcost();
float classifierDiff = newClassifier - lastClassifier;
if (newClassifier != 0. && lastClassifier != 0. && (classifierDiff > 0.0005 || classifierDiff < -0.0005)) {
float initialcost = rc.inverseDirection ? lastInitialCost : newInitialCost;
if (initialcost >= 1000000.) {
cost = -1;
return;
}
int iicost = (int) initialcost;
if (message != null) {
message.linkinitcost += iicost;
}
cost += iicost;
}
lastClassifier = newClassifier;
lastInitialCost = newInitialCost;
// *** destination logic: no destination access in between
int classifiermask = (int) rc.expctxWay.getClassifierMask();
boolean newDestination = (classifiermask & 64) != 0;
boolean oldDestination = getBit(IS_ON_DESTINATION_BIT);
if (getBit(PATH_START_BIT)) {
setBit(PATH_START_BIT, false);
setBit(CAN_LEAVE_DESTINATION_BIT, newDestination);
setBit(HAD_DESTINATION_START_BIT, newDestination);
} else {
if (oldDestination && !newDestination) {
if (getBit(CAN_LEAVE_DESTINATION_BIT)) {
setBit(CAN_LEAVE_DESTINATION_BIT, false);
} else {
cost = -1;
return;
}
}
}
setBit(IS_ON_DESTINATION_BIT, newDestination);
OsmTransferNode transferNode = link.geometry == null ? null
: rc.geometryDecoder.decodeGeometry(link.geometry, sourceNode, targetNode, isReverse);
for (int nsection = 0; ; nsection++) {
originLon = lon1;
originLat = lat1;
int lon2;
int lat2;
short ele2;
short originEle2;
if (transferNode == null) {
lon2 = targetNode.ilon;
lat2 = targetNode.ilat;
originEle2 = targetNode.selev;
} else {
lon2 = transferNode.ilon;
lat2 = transferNode.ilat;
originEle2 = transferNode.selev;
}
ele2 = originEle2;
boolean isStartpoint = lon0 == -1 && lat0 == -1;
// check turn restrictions (n detail mode (=final pass) no TR to not mess up voice hints)
if (nsection == 0 && rc.considerTurnRestrictions && !detailMode && !isStartpoint) {
if (rc.inverseDirection
? TurnRestriction.isTurnForbidden(sourceNode.firstRestriction, lon2, lat2, lon0, lat0, rc.bikeMode || rc.footMode, rc.carMode)
: TurnRestriction.isTurnForbidden(sourceNode.firstRestriction, lon0, lat0, lon2, lat2, rc.bikeMode || rc.footMode, rc.carMode)) {
cost = -1;
return;
}
}
// if recording, new MessageData for each section (needed for turn-instructions)
if (message != null && message.wayKeyValues != null) {
originElement.message = message;
message = new MessageData();
}
int dist = rc.calcDistance(lon1, lat1, lon2, lat2);
boolean stopAtEndpoint = false;
if (rc.shortestmatch) {
if (rc.isEndpoint) {
stopAtEndpoint = true;
ele2 = interpolateEle(ele1, ele2, rc.wayfraction);
} else {
// we just start here, reset everything
cost = 0;
resetState();
lon0 = -1; // reset turncost-pipe
lat0 = -1;
isStartpoint = true;
if (recordTransferNodes) {
if (rc.wayfraction > 0.) {
ele1 = interpolateEle(ele1, ele2, 1. - rc.wayfraction);
originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, ele1, null);
} else {
originElement = null; // prevent duplicate point
}
}
if (rc.checkPendingEndpoint()) {
dist = rc.calcDistance(rc.ilonshortest, rc.ilatshortest, lon2, lat2);
if (rc.shortestmatch) {
stopAtEndpoint = true;
ele2 = interpolateEle(ele1, ele2, rc.wayfraction);
}
}
}
}
if (message != null) {
message.linkdist += dist;
}
linkdisttotal += dist;
// apply a start-direction if appropriate (by faking the origin position)
if (isStartpoint) {
if (rc.startDirectionValid) {
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]);
} else {
lon0 = lon1 - (lon2 - lon1);
lat0 = lat1 - (lat2 - lat1);
}
}
double angle = rc.anglemeter.calcAngle(lon0, lat0, lon1, lat1, lon2, lat2);
double cosangle = rc.anglemeter.getCosAngle();
// *** elevation stuff
double delta_h = 0.;
if (ele2 == Short.MIN_VALUE) ele2 = ele1;
if (ele1 != Short.MIN_VALUE) {
delta_h = (ele2 - ele1) / 4.;
if (rc.inverseDirection) {
delta_h = -delta_h;
}
}
double elevation = ele2 == Short.MIN_VALUE ? 100. : ele2 / 4.;
double sectionCost = processWaySection(rc, dist, delta_h, elevation, angle, cosangle, isStartpoint, nsection, lastpriorityclassifier);
if ((sectionCost < 0. || costfactor > 9998. && !detailMode) || sectionCost + cost >= 2000000000.) {
cost = -1;
return;
}
if (isTrafficBackbone) {
sectionCost = 0.;
}
cost += (int) sectionCost;
// compute kinematic
computeKinematic(rc, dist, delta_h, detailMode);
if (message != null) {
message.turnangle = (float) angle;
message.time = (float) getTotalTime();
message.energy = (float) getTotalEnergy();
message.priorityclassifier = priorityclassifier;
message.classifiermask = classifiermask;
message.lon = lon2;
message.lat = lat2;
message.ele = originEle2;
message.wayKeyValues = rc.expctxWay.getKeyValueDescription(isReverse, description);
}
if (stopAtEndpoint) {
if (recordTransferNodes) {
originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, originEle2, originElement);
originElement.cost = cost;
if (message != null) {
originElement.message = message;
}
}
if (rc.nogoCost < 0) {
cost = -1;
} else {
cost += rc.nogoCost;
}
return;
}
if (transferNode == null) {
// *** penalty for being part of the reference track
if (refTrack != null && refTrack.containsNode(targetNode) && refTrack.containsNode(sourceNode)) {
int reftrackcost = linkdisttotal;
cost += reftrackcost;
}
selev = ele2;
break;
}
transferNode = transferNode.next;
if (recordTransferNodes) {
originElement = OsmPathElement.create(lon2, lat2, originEle2, originElement);
originElement.cost = cost;
}
lon0 = lon1;
lat0 = lat1;
lon1 = lon2;
lat1 = lat2;
ele1 = ele2;
}
// check for nogo-matches (after the *actual* start of segment)
if (rc.nogoCost < 0) {
cost = -1;
return;
} else {
cost += rc.nogoCost;
}
// add target-node costs
double targetCost = processTargetNode(rc);
if (targetCost < 0. || targetCost + cost >= 2000000000.) {
cost = -1;
return;
}
cost += (int) targetCost;
}
public short interpolateEle(short e1, short e2, double fraction) {
if (e1 == Short.MIN_VALUE || e2 == Short.MIN_VALUE) {
return Short.MIN_VALUE;
}
return (short) (e1 * (1. - fraction) + e2 * fraction);
}
protected abstract double processWaySection(RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier);
protected abstract double processTargetNode(RoutingContext rc);
protected void computeKinematic(RoutingContext rc, double dist, double delta_h, boolean detailMode) {
}
public abstract int elevationCorrection();
public abstract boolean definitlyWorseThan(OsmPath p);
public OsmNode getSourceNode() {
return sourceNode;
}
public OsmNode getTargetNode() {
return targetNode;
}
public OsmLink getLink() {
return link;
}
public void setNextForLink(OsmLinkHolder holder) {
nextForLink = holder;
}
public OsmLinkHolder getNextForLink() {
return nextForLink;
}
public double getTotalTime() {
return 0.;
}
public double getTotalEnergy() {
return 0.;
}
}
/**
* Container for link between two Osm nodes
*
* @author ab
*/
package btools.router;
import java.io.IOException;
import btools.mapaccess.OsmLink;
import btools.mapaccess.OsmLinkHolder;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmTransferNode;
import btools.mapaccess.TurnRestriction;
import btools.util.CheapRuler;
abstract class OsmPath implements OsmLinkHolder
{
/**
* The cost of that path (a modified distance)
*/
public int cost = 0;
// the elevation assumed for that path can have a value
// if the corresponding node has not
public short selev;
public int airdistance = 0; // distance to endpos
protected OsmNode sourceNode;
protected OsmNode targetNode;
protected OsmLink link;
public OsmPathElement originElement;
public OsmPathElement myElement;
protected float traffic;
private OsmLinkHolder nextForLink = null;
public int treedepth = 0;
// the position of the waypoint just before
// this path position (for angle calculation)
public int originLon;
public int originLat;
// the classifier of the segment just before this paths position
protected float lastClassifier;
protected float lastInitialCost;
protected int priorityclassifier;
private static final int PATH_START_BIT = 1;
private static final int CAN_LEAVE_DESTINATION_BIT = 2;
private static final int IS_ON_DESTINATION_BIT = 4;
private static final int HAD_DESTINATION_START_BIT = 8;
protected int bitfield = PATH_START_BIT;
private boolean getBit( int mask )
{
return (bitfield & mask ) != 0;
}
private void setBit( int mask, boolean bit )
{
if ( getBit( mask ) != bit )
{
bitfield ^= mask;
}
}
public boolean didEnterDestinationArea()
{
return !getBit( HAD_DESTINATION_START_BIT ) && getBit( IS_ON_DESTINATION_BIT );
}
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 );
selev = targetNode.getSElev();
originLon = -1;
originLat = -1;
}
public void init( OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc )
{
if ( origin.myElement == null )
{
origin.myElement = OsmPathElement.create( origin, rc.countTraffic );
}
this.originElement = origin.myElement;
this.link = link;
this.sourceNode = origin.targetNode;
this.targetNode = link.getTarget( sourceNode );
this.cost = origin.cost;
this.lastClassifier = origin.lastClassifier;
this.lastInitialCost = origin.lastInitialCost;
this.bitfield = origin.bitfield;
init( origin );
addAddionalPenalty(refTrack, detailMode, origin, link, rc );
}
protected abstract void init( OsmPath orig );
protected abstract void resetState();
protected void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc )
{
byte[] description = link.descriptionBitmap;
if ( description == null )
{
return; // could be a beeline path
}
boolean recordTransferNodes = detailMode || rc.countTraffic;
rc.nogoCost = 0.;
// extract the 3 positions of the first section
int lon0 = origin.originLon;
int lat0 = origin.originLat;
int lon1 = sourceNode.getILon();
int lat1 = sourceNode.getILat();
short ele1 = origin.selev;
int linkdisttotal = 0;
message = detailMode ? new MessageData() : null;
boolean isReverse = link.isReverse( sourceNode );
// evaluate the way tags
rc.expctxWay.evaluate( rc.inverseDirection ^ isReverse, description );
// calculate the costfactor inputs
float costfactor = rc.expctxWay.getCostfactor();
boolean isTrafficBackbone = cost == 0 && rc.expctxWay.getIsTrafficBackbone() > 0.f;
int lastpriorityclassifier = priorityclassifier;
priorityclassifier = (int)rc.expctxWay.getPriorityClassifier();
// *** add initial cost if the classifier changed
float newClassifier = rc.expctxWay.getInitialClassifier();
float newInitialCost = rc.expctxWay.getInitialcost();
float classifierDiff = newClassifier - lastClassifier;
if ( newClassifier != 0. && lastClassifier != 0. && ( classifierDiff > 0.0005 || classifierDiff < -0.0005 ) )
{
float initialcost = rc.inverseDirection ? lastInitialCost : newInitialCost;
if ( initialcost >= 1000000. )
{
cost = -1;
return;
}
int iicost = (int)initialcost;
if ( message != null )
{
message.linkinitcost += iicost;
}
cost += iicost;
}
lastClassifier = newClassifier;
lastInitialCost = newInitialCost;
// *** destination logic: no destination access in between
int classifiermask = (int)rc.expctxWay.getClassifierMask();
boolean newDestination = (classifiermask & 64) != 0;
boolean oldDestination = getBit( IS_ON_DESTINATION_BIT );
if ( getBit( PATH_START_BIT ) )
{
setBit( PATH_START_BIT, false );
setBit( CAN_LEAVE_DESTINATION_BIT, newDestination );
setBit( HAD_DESTINATION_START_BIT, newDestination );
}
else
{
if ( oldDestination && !newDestination )
{
if ( getBit( CAN_LEAVE_DESTINATION_BIT ) )
{
setBit( CAN_LEAVE_DESTINATION_BIT, false );
}
else
{
cost = -1;
return;
}
}
}
setBit( IS_ON_DESTINATION_BIT, newDestination );
OsmTransferNode transferNode = link.geometry == null ? null
: rc.geometryDecoder.decodeGeometry( link.geometry, sourceNode, targetNode, isReverse );
for(int nsection=0; ;nsection++)
{
originLon = lon1;
originLat = lat1;
int lon2;
int lat2;
short ele2;
if ( transferNode == null )
{
lon2 = targetNode.ilon;
lat2 = targetNode.ilat;
ele2 = targetNode.selev;
}
else
{
lon2 = transferNode.ilon;
lat2 = transferNode.ilat;
ele2 = transferNode.selev;
}
boolean isStartpoint = lon0 == -1 && lat0 == -1;
// check turn restrictions (n detail mode (=final pass) no TR to not mess up voice hints)
if ( nsection == 0 && rc.considerTurnRestrictions && !detailMode&& !isStartpoint )
{
if ( rc.inverseDirection
? TurnRestriction.isTurnForbidden( sourceNode.firstRestriction, lon2, lat2, lon0, lat0, rc.bikeMode, rc.carMode )
: TurnRestriction.isTurnForbidden( sourceNode.firstRestriction, lon0, lat0, lon2, lat2, rc.bikeMode, rc.carMode ) )
{
cost = -1;
return;
}
}
// if recording, new MessageData for each section (needed for turn-instructions)
if ( message != null && message.wayKeyValues != null )
{
originElement.message = message;
message = new MessageData();
}
int dist = rc.calcDistance( lon1, lat1, lon2, lat2 );
boolean stopAtEndpoint = false;
if ( rc.shortestmatch )
{
if ( rc.isEndpoint )
{
stopAtEndpoint = true;
ele2 = interpolateEle( ele1, ele2, rc.wayfraction );
}
else
{
// we just start here, reset everything
cost = 0;
resetState();
lon0 = -1; // reset turncost-pipe
lat0 = -1;
isStartpoint = true;
if ( recordTransferNodes )
{
if ( rc.wayfraction > 0. )
{
ele1 = interpolateEle( ele1, ele2, 1. - rc.wayfraction );
originElement = OsmPathElement.create( rc.ilonshortest, rc.ilatshortest, ele1, null, rc.countTraffic );
}
else
{
originElement = null; // prevent duplicate point
}
}
if ( rc.checkPendingEndpoint() )
{
dist = rc.calcDistance( rc.ilonshortest, rc.ilatshortest, lon2, lat2 );
if ( rc.shortestmatch )
{
stopAtEndpoint = true;
ele2 = interpolateEle( ele1, ele2, rc.wayfraction );
}
}
}
}
if ( message != null )
{
message.linkdist += dist;
}
linkdisttotal += dist;
// 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[] lonlat2m = CheapRuler.getLonLatToMeterScales( (lon0 + lat1) >> 1 );
lon0 = lon1 - (int) ( 1000. * Math.sin( dir ) / lonlat2m[0] );
lat0 = lat1 - (int) ( 1000. * Math.cos( dir ) / lonlat2m[1] );
}
else
{
lon0 = lon1 - (lon2-lon1);
lat0 = lat1 - (lat2-lat1);
}
}
double angle = rc.anglemeter.calcAngle( lon0, lat0, lon1, lat1, lon2, lat2 );
double cosangle = rc.anglemeter.getCosAngle();
// *** elevation stuff
double delta_h = 0.;
if ( ele2 == Short.MIN_VALUE ) ele2 = ele1;
if ( ele1 != Short.MIN_VALUE )
{
delta_h = (ele2 - ele1)/4.;
if ( rc.inverseDirection )
{
delta_h = -delta_h;
}
}
double elevation = ele2 == Short.MIN_VALUE ? 100. : ele2/4.;
double sectionCost = processWaySection( rc, dist, delta_h, elevation, angle, cosangle, isStartpoint, nsection, lastpriorityclassifier );
if ( ( sectionCost < 0. || costfactor > 9998. && !detailMode ) || sectionCost + cost >= 2000000000. )
{
cost = -1;
return;
}
if ( isTrafficBackbone )
{
sectionCost = 0.;
}
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 );
if ( message != null )
{
message.turnangle = (float)angle;
message.time = (float)getTotalTime();
message.energy = (float)getTotalEnergy();
message.priorityclassifier = priorityclassifier;
message.classifiermask = classifiermask;
message.lon = lon2;
message.lat = lat2;
message.ele = ele2;
message.wayKeyValues = rc.expctxWay.getKeyValueDescription( isReverse, description );
}
if ( stopAtEndpoint )
{
if ( recordTransferNodes )
{
originElement = OsmPathElement.create( rc.ilonshortest, rc.ilatshortest, ele2, originElement, rc.countTraffic );
originElement.cost = cost;
if ( message != null )
{
originElement.message = message;
}
}
if ( rc.nogoCost < 0)
{
cost = -1;
}
else
{
cost += rc.nogoCost;
}
return;
}
if ( transferNode == null )
{
// *** penalty for being part of the reference track
if ( refTrack != null && refTrack.containsNode( targetNode ) && refTrack.containsNode( sourceNode ) )
{
int reftrackcost = linkdisttotal;
cost += reftrackcost;
}
selev = ele2;
break;
}
transferNode = transferNode.next;
if ( recordTransferNodes )
{
originElement = OsmPathElement.create( lon2, lat2, ele2, originElement, rc.countTraffic );
originElement.cost = cost;
originElement.addTraffic( traffic );
traffic = 0;
}
lon0 = lon1;
lat0 = lat1;
lon1 = lon2;
lat1 = lat2;
ele1 = ele2;
}
// check for nogo-matches (after the *actual* start of segment)
if ( rc.nogoCost < 0)
{
cost = -1;
return;
}
else
{
cost += rc.nogoCost;
}
// add target-node costs
double targetCost = processTargetNode( rc );
if ( targetCost < 0. || targetCost + cost >= 2000000000. )
{
cost = -1;
return;
}
cost += (int)targetCost;
}
public short interpolateEle( short e1, short e2, double fraction )
{
if ( e1 == Short.MIN_VALUE || e2 == Short.MIN_VALUE )
{
return Short.MIN_VALUE;
}
return (short)( e1*(1.-fraction) + e2*fraction );
}
protected abstract double processWaySection( RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier );
protected abstract double processTargetNode( RoutingContext rc );
protected void computeKinematic( RoutingContext rc, double dist, double delta_h, boolean detailMode )
{
}
public abstract int elevationCorrection( RoutingContext rc );
public abstract boolean definitlyWorseThan( OsmPath p, RoutingContext rc );
public OsmNode getSourceNode()
{
return sourceNode;
}
public OsmNode getTargetNode()
{
return targetNode;
}
public OsmLink getLink()
{
return link;
}
public void setNextForLink( OsmLinkHolder holder )
{
nextForLink = holder;
}
public OsmLinkHolder getNextForLink()
{
return nextForLink;
}
public double getTotalTime()
{
return 0.;
}
public double getTotalEnergy()
{
return 0.;
}
}

View file

@ -1,127 +1,136 @@
package btools.router;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmPos;
import btools.util.CheapRuler;
/**
* Container for link between two Osm nodes
*
* @author ab
*/
public class OsmPathElement implements OsmPos {
private int ilat; // latitude
private int ilon; // longitude
private short selev; // longitude
public MessageData message = null; // description
public int cost;
// interface OsmPos
public final int getILat() {
return ilat;
}
public final int getILon() {
return ilon;
}
public final short getSElev() {
return selev;
}
public final void setSElev(short s) {
selev = s;
}
public final double getElev() {
return selev / 4.;
}
public final float getTime() {
return message == null ? 0.f : message.time;
}
public final void setTime(float t) {
if (message != null) {
message.time = t;
}
}
public final float getEnergy() {
return message == null ? 0.f : message.energy;
}
public final void setEnergy(float e) {
if (message != null) {
message.energy = e;
}
}
public final void setAngle(float e) {
if (message != null) {
message.turnangle = e;
}
}
public final long getIdFromPos() {
return ((long) ilon) << 32 | ilat;
}
public final int calcDistance(OsmPos p) {
return (int) Math.max(1.0, Math.round(CheapRuler.distance(ilon, ilat, p.getILon(), p.getILat())));
}
public OsmPathElement origin;
// construct a path element from a path
public static final OsmPathElement create(OsmPath path) {
OsmNode n = path.getTargetNode();
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) {
OsmPathElement pe = new OsmPathElement();
pe.ilon = ilon;
pe.ilat = ilat;
pe.selev = selev;
pe.origin = origin;
return pe;
}
protected OsmPathElement() {
}
public String toString() {
return ilon + "_" + ilat;
}
public boolean positionEquals(OsmPathElement e) {
return this.ilat == e.ilat && this.ilon == e.ilon;
}
public void writeToStream(DataOutput dos) throws IOException {
dos.writeInt(ilat);
dos.writeInt(ilon);
dos.writeShort(selev);
dos.writeInt(cost);
}
public static OsmPathElement readFromStream(DataInput dis) throws IOException {
OsmPathElement pe = new OsmPathElement();
pe.ilat = dis.readInt();
pe.ilon = dis.readInt();
pe.selev = dis.readShort();
pe.cost = dis.readInt();
return pe;
}
}
package btools.router;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmPos;
import btools.util.CheapRuler;
/**
* Container for link between two Osm nodes
*
* @author ab
*/
public class OsmPathElement implements OsmPos
{
private int ilat; // latitude
private int ilon; // longitude
private short selev; // longitude
public MessageData message = null; // description
public int cost;
// interface OsmPos
public final int getILat()
{
return ilat;
}
public final int getILon()
{
return ilon;
}
public final short getSElev()
{
return selev;
}
public final double getElev()
{
return selev / 4.;
}
public final float getTime()
{
return message == null ? 0.f : message.time;
}
public final void setTime( float t )
{
if ( message != null )
{
message.time = t;
}
}
public final float getEnergy()
{
return message == null ? 0.f : message.energy;
}
public final void setEnergy( float e )
{
if ( message != null )
{
message.energy = e;
}
}
public final long getIdFromPos()
{
return ((long)ilon)<<32 | ilat;
}
public final int calcDistance( OsmPos p )
{
return (int)(CheapRuler.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0 );
}
public OsmPathElement origin;
// construct a path element from a path
public static final OsmPathElement create( OsmPath path, boolean countTraffic )
{
OsmNode n = path.getTargetNode();
OsmPathElement pe = create( n.getILon(), n.getILat(), path.selev, path.originElement, countTraffic );
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();
pe.ilon = ilon;
pe.ilat = ilat;
pe.selev = selev;
pe.origin = origin;
return pe;
}
protected OsmPathElement()
{
}
public void addTraffic( float traffic )
{
}
public String toString()
{
return ilon + "_" + ilat;
}
public void writeToStream( DataOutput dos ) throws IOException
{
dos.writeInt( ilat );
dos.writeInt( ilon );
dos.writeShort( selev );
dos.writeInt( cost );
}
public static OsmPathElement readFromStream( DataInput dis ) throws IOException
{
OsmPathElement pe = new OsmPathElement();
pe.ilat = dis.readInt();
pe.ilon = dis.readInt();
pe.selev = dis.readShort();
pe.cost = dis.readInt();
return pe;
}
}

View file

@ -0,0 +1,78 @@
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;
}
}

View file

@ -11,10 +11,11 @@ import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay;
abstract class OsmPathModel {
abstract class OsmPathModel
{
public abstract OsmPrePath createPrePath();
public abstract OsmPath createPath();
public abstract void init(BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String, String> keyValues);
public abstract void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String,String> keyValues );
}

View file

@ -7,20 +7,23 @@ package btools.router;
import btools.mapaccess.OsmLink;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmTransferNode;
public abstract class OsmPrePath {
public abstract class OsmPrePath
{
protected OsmNode sourceNode;
protected OsmNode targetNode;
protected OsmLink link;
public OsmPrePath next;
public void init(OsmPath origin, OsmLink link, RoutingContext rc) {
public void init( OsmPath origin, OsmLink link, RoutingContext rc )
{
this.link = link;
this.sourceNode = origin.getTargetNode();
this.targetNode = link.getTarget(sourceNode);
initPrePath(origin, rc);
this.targetNode = link.getTarget( sourceNode );
initPrePath(origin, rc );
}
protected abstract void initPrePath(OsmPath origin, RoutingContext rc);
protected abstract void initPrePath(OsmPath origin, RoutingContext rc );
}

File diff suppressed because it is too large Load diff

View file

@ -11,125 +11,146 @@ import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay;
import btools.expressions.BExpressionMetaData;
public final class ProfileCache {
public final class ProfileCache
{
private static File lastLookupFile;
private static long lastLookupTimestamp;
private BExpressionContextWay expctxWay;
private BExpressionContextNode expctxNode;
private File lastProfileFile;
private long lastProfileTimestamp;
private long lastProfileTimestamp;
private boolean profilesBusy;
private long lastUseTime;
private static ProfileCache[] apc = new ProfileCache[1];
private static boolean debug = Boolean.getBoolean("debugProfileCache");
private static boolean debug = Boolean.getBoolean( "debugProfileCache" );
public static synchronized void setSize(int size) {
public static synchronized void setSize( int size )
{
apc = new ProfileCache[size];
}
public static synchronized boolean parseProfile(RoutingContext rc) {
String profileBaseDir = System.getProperty("profileBaseDir");
File profileDir;
File profileFile;
if (profileBaseDir == null) {
profileDir = new File(rc.localFunction).getParentFile();
profileFile = new File(rc.localFunction);
} else {
profileDir = new File(profileBaseDir);
profileFile = new File(profileDir, rc.localFunction + ".brf");
}
rc.profileTimestamp = profileFile.lastModified() + rc.getKeyValueChecksum() << 24;
File lookupFile = new File(profileDir, "lookups.dat");
// invalidate cache at lookup-table update
if (!(lookupFile.equals(lastLookupFile) && lookupFile.lastModified() == lastLookupTimestamp)) {
if (lastLookupFile != null) {
System.out.println("******** invalidating profile-cache after lookup-file update ******** ");
public static synchronized boolean parseProfile( RoutingContext rc )
{
String profileBaseDir = System.getProperty( "profileBaseDir" );
File profileDir;
File profileFile;
if ( profileBaseDir == null )
{
profileDir = new File( rc.localFunction ).getParentFile();
profileFile = new File( rc.localFunction ) ;
}
else
{
profileDir = new File( profileBaseDir );
profileFile = new File( profileDir, rc.localFunction + ".brf" ) ;
}
apc = new ProfileCache[apc.length];
lastLookupFile = lookupFile;
lastLookupTimestamp = lookupFile.lastModified();
}
ProfileCache lru = null;
int unusedSlot = -1;
rc.profileTimestamp = profileFile.lastModified() + rc.getKeyValueChecksum()<<24;
File lookupFile = new File( profileDir, "lookups.dat" );
// invalidate cache at lookup-table update
if ( !(lookupFile.equals( lastLookupFile ) && lookupFile.lastModified() == lastLookupTimestamp ) )
{
if ( lastLookupFile != null )
{
System.out.println( "******** invalidating profile-cache after lookup-file update ******** " );
}
apc = new ProfileCache[apc.length];
lastLookupFile = lookupFile;
lastLookupTimestamp = lookupFile.lastModified();
}
ProfileCache lru = null;
int unusedSlot =-1;
// check for re-use
for (int i = 0; i < apc.length; i++) {
ProfileCache pc = apc[i];
if (pc != null) {
if ((!pc.profilesBusy) && profileFile.equals(pc.lastProfileFile)) {
if (rc.profileTimestamp == pc.lastProfileTimestamp) {
rc.expctxWay = pc.expctxWay;
rc.expctxNode = pc.expctxNode;
rc.readGlobalConfig();
pc.profilesBusy = true;
return true;
// check for re-use
for( int i=0; i<apc.length; i++)
{
ProfileCache pc = apc[i];
if ( pc != null )
{
if ( (!pc.profilesBusy) && profileFile.equals( pc.lastProfileFile ) )
{
if ( rc.profileTimestamp == pc.lastProfileTimestamp )
{
rc.expctxWay = pc.expctxWay;
rc.expctxNode = pc.expctxNode;
rc.readGlobalConfig();
pc.profilesBusy = true;
return true;
}
lru = pc; // name-match but timestamp-mismatch -> we overide this one
unusedSlot = -1;
break;
}
if ( lru == null || lru.lastUseTime > pc.lastUseTime )
{
lru = pc;
}
lru = pc; // name-match but timestamp-mismatch -> we overide this one
unusedSlot = -1;
break;
}
if (lru == null || lru.lastUseTime > pc.lastUseTime) {
lru = pc;
else if ( unusedSlot < 0 )
{
unusedSlot = i;
}
} else if (unusedSlot < 0) {
unusedSlot = i;
}
}
BExpressionMetaData meta = new BExpressionMetaData();
rc.expctxWay = new BExpressionContextWay( rc.memoryclass * 512, meta );
rc.expctxNode = new BExpressionContextNode( 0, meta );
rc.expctxNode.setForeignContext( rc.expctxWay );
meta.readMetaData( new File( profileDir, "lookups.dat" ) );
BExpressionMetaData meta = new BExpressionMetaData();
rc.expctxWay.parseFile( profileFile, "global" );
rc.expctxNode.parseFile( profileFile, "global" );
rc.expctxWay = new BExpressionContextWay(rc.memoryclass * 512, meta);
rc.expctxNode = new BExpressionContextNode(0, meta);
rc.expctxNode.setForeignContext(rc.expctxWay);
meta.readMetaData(new File(profileDir, "lookups.dat"));
rc.expctxWay.parseFile(profileFile, "global", rc.keyValues);
rc.expctxNode.parseFile(profileFile, "global", rc.keyValues);
rc.readGlobalConfig();
if (rc.processUnusedTags) {
rc.expctxWay.setAllTagsUsed();
}
if (lru == null || unusedSlot >= 0) {
lru = new ProfileCache();
if (unusedSlot >= 0) {
apc[unusedSlot] = lru;
if (debug)
System.out.println("******* adding new profile at idx=" + unusedSlot + " for " + profileFile);
rc.readGlobalConfig();
if ( rc.processUnusedTags )
{
rc.expctxWay.setAllTagsUsed();
}
}
if (lru.lastProfileFile != null) {
if (debug)
System.out.println("******* replacing profile of age " + ((System.currentTimeMillis() - lru.lastUseTime) / 1000L) + " sec " + lru.lastProfileFile + "->" + profileFile);
}
if ( lru == null || unusedSlot >= 0 )
{
lru = new ProfileCache();
if ( unusedSlot >= 0 )
{
apc[unusedSlot] = lru;
if ( debug ) System.out.println( "******* adding new profile at idx=" + unusedSlot + " for " + profileFile );
}
}
lru.lastProfileTimestamp = rc.profileTimestamp;
lru.lastProfileFile = profileFile;
lru.expctxWay = rc.expctxWay;
lru.expctxNode = rc.expctxNode;
lru.profilesBusy = true;
lru.lastUseTime = System.currentTimeMillis();
return false;
if ( lru.lastProfileFile != null )
{
if ( debug ) System.out.println( "******* replacing profile of age " + ((System.currentTimeMillis()-lru.lastUseTime)/1000L) + " sec " + lru.lastProfileFile + "->" + profileFile );
}
lru.lastProfileTimestamp = rc.profileTimestamp;
lru.lastProfileFile = profileFile;
lru.expctxWay = rc.expctxWay;
lru.expctxNode = rc.expctxNode;
lru.profilesBusy = true;
lru.lastUseTime = System.currentTimeMillis();
return false;
}
public static synchronized void releaseProfile(RoutingContext rc) {
for (int i = 0; i < apc.length; i++) {
public static synchronized void releaseProfile( RoutingContext rc )
{
for( int i=0; i<apc.length; i++)
{
ProfileCache pc = apc[i];
if (pc != null) {
if ( pc != null )
{
// only the thread that holds the cached instance can release it
if (rc.expctxWay == pc.expctxWay && rc.expctxNode == pc.expctxNode) {
if ( rc.expctxWay == pc.expctxWay && rc.expctxNode == pc.expctxNode )
{
pc.profilesBusy = false;
break;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -9,33 +9,41 @@ import java.io.File;
import btools.mapaccess.StorageConfigHelper;
public final class RoutingHelper {
public static File getAdditionalMaptoolDir(File segmentDir) {
return StorageConfigHelper.getAdditionalMaptoolDir(segmentDir);
}
public static File getSecondarySegmentDir(File segmentDir) {
return StorageConfigHelper.getSecondarySegmentDir(segmentDir);
}
public static boolean hasDirectoryAnyDatafiles(File segmentDir) {
if (hasAnyDatafiles(segmentDir)) {
return true;
public final class RoutingHelper
{
public static File getAdditionalMaptoolDir( File segmentDir )
{
return StorageConfigHelper.getAdditionalMaptoolDir(segmentDir);
}
// check secondary, too
File secondary = StorageConfigHelper.getSecondarySegmentDir(segmentDir);
if (secondary != null) {
return hasAnyDatafiles(secondary);
}
return false;
}
private static boolean hasAnyDatafiles(File dir) {
String[] fileNames = dir.list();
for (String fileName : fileNames) {
if (fileName.endsWith(".rd5")) return true;
public static File getSecondarySegmentDir( File segmentDir )
{
return StorageConfigHelper.getSecondarySegmentDir(segmentDir);
}
public static boolean hasDirectoryAnyDatafiles( File segmentDir )
{
if ( hasAnyDatafiles( segmentDir ) )
{
return true;
}
// check secondary, too
File secondary = StorageConfigHelper.getSecondarySegmentDir( segmentDir );
if ( secondary != null )
{
return hasAnyDatafiles( secondary );
}
return false;
}
private static boolean hasAnyDatafiles( File dir )
{
String[] fileNames = dir.list();
for( String fileName : fileNames )
{
if ( fileName.endsWith( ".rd5" ) ) return true;
}
return false;
}
return false;
}
}

View file

@ -1,4 +1,5 @@
package btools.router;
public class RoutingIslandException extends RuntimeException {
public class RoutingIslandException extends RuntimeException
{
}

View file

@ -1,356 +0,0 @@
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

@ -8,80 +8,83 @@ package btools.router;
import btools.mapaccess.OsmNode;
public final class SearchBoundary {
public final class SearchBoundary
{
private int minlon0;
private int minlat0;
private int maxlon0;
private int maxlat0;
private int minlon0;
private int minlat0;
private int maxlon0;
private int maxlat0;
private int minlon;
private int minlat;
private int maxlon;
private int maxlat;
private int radius;
private OsmNode p;
private int minlon;
private int minlat;
private int maxlon;
private int maxlat;
private int radius;
private OsmNode p;
int direction;
int direction;
/**
* @param radius Search radius in meters.
*/
public SearchBoundary(OsmNode n, int radius, int direction) {
this.radius = radius;
this.direction = direction;
/**
* @param radius Search radius in meters.
*/
public SearchBoundary( OsmNode n, int radius, int direction )
{
this.radius = radius;
this.direction = direction;
p = new OsmNode(n.ilon, n.ilat);
p = new OsmNode( n.ilon, n.ilat );
int lon = (n.ilon / 5000000) * 5000000;
int lat = (n.ilat / 5000000) * 5000000;
int lon = (n.ilon / 5000000 ) * 5000000;
int lat = (n.ilat / 5000000 ) * 5000000;
minlon0 = lon - 5000000;
minlat0 = lat - 5000000;
maxlon0 = lon + 10000000;
maxlat0 = lat + 10000000;
minlon0 = lon - 5000000;
minlat0 = lat - 5000000;
maxlon0 = lon + 10000000;
maxlat0 = lat + 10000000;
minlon = lon - 1000000;
minlat = lat - 1000000;
maxlon = lon + 6000000;
maxlat = lat + 6000000;
}
public static String getFileName(OsmNode n) {
int lon = (n.ilon / 5000000) * 5000000;
int lat = (n.ilat / 5000000) * 5000000;
int dlon = lon / 1000000 - 180;
int dlat = lat / 1000000 - 90;
String slon = dlon < 0 ? "W" + (-dlon) : "E" + dlon;
String slat = dlat < 0 ? "S" + (-dlat) : "N" + dlat;
return slon + "_" + slat + ".trf";
}
public boolean isInBoundary(OsmNode n, int cost) {
if (radius > 0) {
return n.calcDistance(p) < radius;
minlon = lon - 1000000;
minlat = lat - 1000000;
maxlon = lon + 6000000;
maxlat = lat + 6000000;
}
if (cost == 0) {
return n.ilon > minlon0 && n.ilon < maxlon0 && n.ilat > minlat0 && n.ilat < maxlat0;
}
return n.ilon > minlon && n.ilon < maxlon && n.ilat > minlat && n.ilat < maxlat;
}
public int getBoundaryDistance(OsmNode n) {
switch (direction) {
case 0:
return n.calcDistance(new OsmNode(n.ilon, minlat));
case 1:
return n.calcDistance(new OsmNode(minlon, n.ilat));
case 2:
return n.calcDistance(new OsmNode(n.ilon, maxlat));
case 3:
return n.calcDistance(new OsmNode(maxlon, n.ilat));
default:
throw new IllegalArgumentException("undefined direction: " + direction);
public static String getFileName( OsmNode n )
{
int lon = (n.ilon / 5000000 ) * 5000000;
int lat = (n.ilat / 5000000 ) * 5000000;
int dlon = lon / 1000000 -180;
int dlat = lat / 1000000 - 90;
String slon = dlon < 0 ? "W" + (-dlon) : "E" + dlon;
String slat = dlat < 0 ? "S" + (-dlat) : "N" + dlat;
return slon + "_" + slat + ".trf";
}
public boolean isInBoundary( OsmNode n, int cost )
{
if ( radius > 0 )
{
return n.calcDistance( p ) < radius;
}
if ( cost == 0 )
{
return n.ilon > minlon0 && n.ilon < maxlon0 && n.ilat > minlat0 && n.ilat < maxlat0;
}
return n.ilon > minlon && n.ilon < maxlon && n.ilat > minlat && n.ilat < maxlat;
}
public int getBoundaryDistance( OsmNode n )
{
switch( direction )
{
case 0: return n.calcDistance( new OsmNode( n.ilon, minlat ) );
case 1: return n.calcDistance( new OsmNode( minlon, n.ilat ) );
case 2: return n.calcDistance( new OsmNode( n.ilon, maxlat ) );
case 3: return n.calcDistance( new OsmNode( maxlon, n.ilat ) );
default: throw new IllegalArgumentException( "undefined direction: "+ direction );
}
}
}
}

View file

@ -12,12 +12,15 @@ import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay;
final class StdModel extends OsmPathModel {
public OsmPrePath createPrePath() {
final class StdModel extends OsmPathModel
{
public OsmPrePath createPrePath()
{
return null;
}
public OsmPath createPath() {
public OsmPath createPath()
{
return new StdPath();
}
@ -26,10 +29,11 @@ final class StdModel extends OsmPathModel {
@Override
public void init(BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String, String> keyValues) {
public void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String,String> keyValues )
{
ctxWay = expctxWay;
ctxNode = expctxNode;
BExpressionContext expctxGlobal = expctxWay; // just one of them...
}

View file

@ -5,7 +5,10 @@
*/
package btools.router;
final class StdPath extends OsmPath {
import btools.util.FastMath;
final class StdPath extends OsmPath
{
/**
* The elevation-hysteresis-buffer (0-10 m)
*/
@ -16,15 +19,13 @@ final class StdPath extends OsmPath {
private float totalEnergy; // total route energy (Joule)
private float elevation_buffer; // just another elevation buffer (for travel time)
private int uphillcostdiv;
private int downhillcostdiv;
// Gravitational constant, g
private static final double GRAVITY = 9.81; // in meters per second^(-2)
@Override
public void init(OsmPath orig) {
StdPath origin = (StdPath) orig;
public void init( OsmPath orig )
{
StdPath origin = (StdPath)orig;
this.ehbd = origin.ehbd;
this.ehbu = origin.ehbu;
this.totalTime = origin.totalTime;
@ -33,63 +34,34 @@ final class StdPath extends OsmPath {
}
@Override
protected void resetState() {
protected void resetState()
{
ehbd = 0;
ehbu = 0;
totalTime = 0.f;
totalEnergy = 0.f;
uphillcostdiv = 0;
downhillcostdiv = 0;
elevation_buffer = 0.f;
}
@Override
protected double processWaySection(RoutingContext rc, double distance, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier) {
protected double processWaySection( RoutingContext rc, double distance, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier )
{
// calculate the costfactor inputs
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();
cfup = cfup == 0.f ? cf : cfup;
cfdown = cfdown == 0.f ? cf : cfdown;
downhillcostdiv = (int) rc.expctxWay.getDownhillcost();
if (downhillcostdiv > 0) {
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
int dist = (int)distance; // legacy arithmetics needs int
// penalty for turning angle
int turncost = (int) ((1. - cosangle) * turncostbase + 0.2); // e.g. turncost=90 -> 90 degree = 90m penalty
if (message != null) {
int turncost = (int)((1.-cosangle) * turncostbase + 0.2 ); // e.g. turncost=90 -> 90 degree = 90m penalty
if ( message != null )
{
message.linkturncost += turncost;
message.turnangle = (float) angle;
message.turnangle = (float)angle;
}
double sectionCost = turncost;
@ -98,78 +70,81 @@ final class StdPath extends OsmPath {
// only the part of the descend that does not fit into the elevation-hysteresis-buffers
// leads to an immediate penalty
int delta_h_micros = (int) (1000000. * delta_h);
ehbd += -delta_h_micros - dist * downhillcutoff;
ehbu += delta_h_micros - dist * uphillcutoff;
int delta_h_micros = (int)(1000000. * delta_h);
ehbd += -delta_h_micros - dist * rc.downhillcutoff;
ehbu += delta_h_micros - dist * rc.uphillcutoff;
float downweight = 0.f;
if (ehbd > rc.elevationpenaltybuffer) {
if ( ehbd > rc.elevationpenaltybuffer )
{
downweight = 1.f;
int excess = ehbd - rc.elevationpenaltybuffer;
int reduce = dist * rc.elevationbufferreduce;
if (reduce > excess) {
downweight = ((float) excess) / reduce;
if ( reduce > excess )
{
downweight = ((float)excess)/reduce;
reduce = excess;
}
excess = ehbd - rc.elevationmaxbuffer;
if (reduce < excess) {
if ( reduce < excess )
{
reduce = excess;
}
ehbd -= reduce;
float elevationCost = 0.f;
if (downhillcostdiv > 0) {
elevationCost += Math.min(reduce, dist * downhillmaxslope) / downhillcostdiv;
}
if (downhillmaxslopecostdiv > 0) {
elevationCost += Math.max(0, reduce - dist * downhillmaxslope) / downhillmaxslopecostdiv;
}
if (elevationCost > 0) {
if ( rc.downhillcostdiv > 0 )
{
int elevationCost = reduce/rc.downhillcostdiv;
sectionCost += elevationCost;
if (message != null) {
if ( message != null )
{
message.linkelevationcost += elevationCost;
}
}
} else if (ehbd < 0) {
}
else if ( ehbd < 0 )
{
ehbd = 0;
}
float upweight = 0.f;
if (ehbu > rc.elevationpenaltybuffer) {
if ( ehbu > rc.elevationpenaltybuffer )
{
upweight = 1.f;
int excess = ehbu - rc.elevationpenaltybuffer;
int reduce = dist * rc.elevationbufferreduce;
if (reduce > excess) {
upweight = ((float) excess) / reduce;
if ( reduce > excess )
{
upweight = ((float)excess)/reduce;
reduce = excess;
}
excess = ehbu - rc.elevationmaxbuffer;
if (reduce < excess) {
if ( reduce < excess )
{
reduce = excess;
}
ehbu -= reduce;
float elevationCost = 0.f;
if (uphillcostdiv > 0) {
elevationCost += Math.min(reduce, dist * uphillmaxslope) / uphillcostdiv;
}
if (uphillmaxslopecostdiv > 0) {
elevationCost += Math.max(0, reduce - dist * uphillmaxslope) / uphillmaxslopecostdiv;
}
if (elevationCost > 0) {
if ( rc.uphillcostdiv > 0 )
{
int elevationCost = reduce/rc.uphillcostdiv;
sectionCost += elevationCost;
if (message != null) {
if ( message != null )
{
message.linkelevationcost += elevationCost;
}
}
} else if (ehbu < 0) {
}
else if ( ehbu < 0 )
{
ehbu = 0;
}
// get the effective costfactor (slope dependent)
float costfactor = cfup * upweight + cf * (1.f - upweight - downweight) + cfdown * downweight;
float costfactor = cfup*upweight + cf*(1.f - upweight - downweight) + cfdown*downweight;
if (message != null) {
if ( message != null )
{
message.costfactor = costfactor;
}
@ -179,18 +154,22 @@ final class StdPath extends OsmPath {
}
@Override
protected double processTargetNode(RoutingContext rc) {
protected double processTargetNode( RoutingContext rc )
{
// finally add node-costs for target node
if (targetNode.nodeDescription != null) {
if ( targetNode.nodeDescription != null )
{
boolean nodeAccessGranted = rc.expctxWay.getNodeAccessGranted() != 0.;
rc.expctxNode.evaluate(nodeAccessGranted, targetNode.nodeDescription);
rc.expctxNode.evaluate( nodeAccessGranted , targetNode.nodeDescription );
float initialcost = rc.expctxNode.getInitialcost();
if (initialcost >= 1000000.) {
if ( initialcost >= 1000000. )
{
return -1.;
}
if (message != null) {
message.linknodecost += (int) initialcost;
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription(nodeAccessGranted, targetNode.nodeDescription);
if ( message != null )
{
message.linknodecost += (int)initialcost;
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription( nodeAccessGranted, targetNode.nodeDescription );
}
return initialcost;
}
@ -198,88 +177,118 @@ final class StdPath extends OsmPath {
}
@Override
public int elevationCorrection() {
return (downhillcostdiv > 0 ? ehbd / downhillcostdiv : 0)
+ (uphillcostdiv > 0 ? ehbu / uphillcostdiv : 0);
public int elevationCorrection( RoutingContext rc )
{
return ( rc.downhillcostdiv > 0 ? ehbd/rc.downhillcostdiv : 0 )
+ ( rc.uphillcostdiv > 0 ? ehbu/rc.uphillcostdiv : 0 );
}
@Override
public boolean definitlyWorseThan(OsmPath path) {
StdPath p = (StdPath) path;
public boolean definitlyWorseThan( OsmPath path, RoutingContext rc )
{
StdPath p = (StdPath)path;
int c = p.cost;
if (p.downhillcostdiv > 0) {
int delta = p.ehbd / p.downhillcostdiv - (downhillcostdiv > 0 ? ehbd / downhillcostdiv : 0);
if (delta > 0) c += delta;
}
if (p.uphillcostdiv > 0) {
int delta = p.ehbu / p.uphillcostdiv - (uphillcostdiv > 0 ? ehbu / uphillcostdiv : 0);
if (delta > 0) c += delta;
}
int c = p.cost;
if ( rc.downhillcostdiv > 0 )
{
int delta = p.ehbd - ehbd;
if ( delta > 0 ) c += delta/rc.downhillcostdiv;
}
if ( rc.uphillcostdiv > 0 )
{
int delta = p.ehbu - ehbu;
if ( delta > 0 ) c += delta/rc.uphillcostdiv;
}
return cost > c;
return cost > c;
}
private double calcIncline(double dist) {
private double calcIncline( double dist )
{
double min_delta = 3.;
double shift = 0.;
if (elevation_buffer > min_delta) {
double shift;
if ( elevation_buffer > min_delta )
{
shift = -min_delta;
} else if (elevation_buffer < -min_delta) {
shift = min_delta;
}
double decayFactor = Math.exp(-dist / 100.);
float new_elevation_buffer = (float) ((elevation_buffer + shift) * decayFactor - shift);
double incline = (elevation_buffer - new_elevation_buffer) / dist;
else if ( elevation_buffer < min_delta )
{
shift = -min_delta;
}
else
{
return 0.;
}
double decayFactor = FastMath.exp( - dist / 100. );
float new_elevation_buffer = (float)( (elevation_buffer+shift) * decayFactor - shift);
double incline = ( elevation_buffer - new_elevation_buffer ) / dist;
elevation_buffer = new_elevation_buffer;
return incline;
}
@Override
protected void computeKinematic(RoutingContext rc, double dist, double delta_h, boolean detailMode) {
if (!detailMode) {
protected void computeKinematic( RoutingContext rc, double dist, double delta_h, boolean detailMode )
{
if ( !detailMode )
{
return;
}
// compute incline
elevation_buffer += delta_h;
double incline = calcIncline(dist);
double incline = calcIncline( dist );
double maxSpeed = rc.maxSpeed;
double speedLimit = rc.expctxWay.getMaxspeed() / 3.6f;
if (speedLimit > 0) {
maxSpeed = Math.min(maxSpeed, speedLimit);
double wayMaxspeed;
wayMaxspeed = rc.expctxWay.getMaxspeed() / 3.6f;
if (wayMaxspeed == 0)
{
wayMaxspeed = rc.maxSpeed;
}
double speed = maxSpeed; // Travel speed
double f_roll = rc.totalMass * GRAVITY * (rc.defaultC_r + incline);
if (rc.footMode) {
wayMaxspeed = Math.min(wayMaxspeed,rc.maxSpeed);
double speed; // Travel speed
double f_roll = rc.totalMass * GRAVITY * ( rc.defaultC_r + incline );
if (rc.footMode || rc.expctxWay.getCostfactor() > 4.9 )
{
// Use Tobler's hiking function for walking sections
speed = rc.maxSpeed * Math.exp(-3.5 * Math.abs(incline + 0.05));
} else if (rc.bikeMode) {
speed = solveCubic(rc.S_C_x, f_roll, rc.bikerPower);
speed = Math.min(speed, maxSpeed);
speed = rc.maxSpeed * 3.6;
speed = (speed * FastMath.exp(-3.5 * Math.abs( incline + 0.05))) / 3.6;
}
float dt = (float) (dist / speed);
else if (rc.bikeMode)
{
speed = solveCubic( rc.S_C_x, f_roll, rc.bikerPower );
speed = Math.min(speed, wayMaxspeed);
}
else // all other
{
speed = wayMaxspeed;
}
float dt = (float) ( dist / speed );
totalTime += dt;
// Calc energy assuming biking (no good model yet for hiking)
// (Count only positive, negative would mean breaking to enforce maxspeed)
double energy = dist * (rc.S_C_x * speed * speed + f_roll);
if (energy > 0.) {
double energy = dist*(rc.S_C_x*speed*speed + f_roll);
if ( energy > 0. )
{
totalEnergy += energy;
}
}
private static double solveCubic(double a, double c, double d) {
private static double solveCubic( double a, double c, double d )
{
// Solves a * v^3 + c * v = d with a Newton method
// to get the speed v for the section.
double v = 8.;
boolean findingStartvalue = true;
for (int i = 0; i < 10; i++) {
double y = (a * v * v + c) * v - d;
if (y < .1) {
if (findingStartvalue) {
for ( int i = 0; i < 10; i++ )
{
double y = ( a * v * v + c ) * v - d;
if ( y < .1 )
{
if ( findingStartvalue )
{
v *= 2.;
continue;
}
@ -293,12 +302,14 @@ final class StdPath extends OsmPath {
}
@Override
public double getTotalTime() {
public double getTotalTime()
{
return totalTime;
}
@Override
public double getTotalEnergy() {
public double getTotalEnergy()
{
return totalEnergy;
}
}

View file

@ -1,59 +0,0 @@
package btools.router;
import java.util.Map;
public class SuspectInfo {
public static final int TRIGGER_DEAD_END = 1;
public static final int TRIGGER_DEAD_START = 2;
public static final int TRIGGER_NODE_BLOCK = 4;
public static final int TRIGGER_BAD_ACCESS = 8;
public static final int TRIGGER_UNK_ACCESS = 16;
public static final int TRIGGER_SHARP_EXIT = 32;
public static final int TRIGGER_SHARP_ENTRY = 64;
public static final int TRIGGER_SHARP_LINK = 128;
public static final int TRIGGER_BAD_TR = 256;
public int prio;
public int triggers;
public static void addSuspect(Map<Long, SuspectInfo> map, long id, int prio, int trigger) {
Long iD = id;
SuspectInfo info = map.get(iD);
if (info == null) {
info = new SuspectInfo();
map.put(iD, info);
}
info.prio = Math.max(info.prio, prio);
info.triggers |= trigger;
}
public static SuspectInfo addTrigger(SuspectInfo old, int prio, int trigger) {
if (old == null) {
old = new SuspectInfo();
}
old.prio = Math.max(old.prio, prio);
old.triggers |= trigger;
return old;
}
public static String getTriggerText(int triggers) {
StringBuilder sb = new StringBuilder();
addText(sb, "dead-end", triggers, TRIGGER_DEAD_END);
addText(sb, "dead-start", triggers, TRIGGER_DEAD_START);
addText(sb, "node-block", triggers, TRIGGER_NODE_BLOCK);
addText(sb, "bad-access", triggers, TRIGGER_BAD_ACCESS);
addText(sb, "unkown-access", triggers, TRIGGER_UNK_ACCESS);
addText(sb, "sharp-exit", triggers, TRIGGER_SHARP_EXIT);
addText(sb, "sharp-entry", triggers, TRIGGER_SHARP_ENTRY);
addText(sb, "sharp-link", triggers, TRIGGER_SHARP_LINK);
addText(sb, "bad-tr", triggers, TRIGGER_BAD_TR);
return sb.toString();
}
private static void addText(StringBuilder sb, String text, int mask, int bit) {
if ((bit & mask) == 0) return;
if (sb.length() > 0) sb.append(",");
sb.append(text);
}
}

View file

@ -9,7 +9,8 @@ package btools.router;
import java.util.ArrayList;
import java.util.List;
public class VoiceHint {
public class VoiceHint
{
static final int C = 1; // continue (go straight)
static final int TL = 2; // turn left
static final int TSLL = 3; // turn slightly left
@ -19,13 +20,11 @@ public class VoiceHint {
static final int TSHR = 7; // turn sharply right
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 = 10; // U-turn
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;
int ilat;
@ -37,566 +36,280 @@ public class VoiceHint {
double distanceToNext;
int indexInTrack;
public float getTime() {
public float getTime()
{
return oldWay == null ? 0.f : oldWay.time;
}
float angle = Float.MAX_VALUE;
float angle;
boolean turnAngleConsumed;
boolean needsRealTurn;
int maxBadPrio = -1;
int roundaboutExit;
boolean isRoundabout() {
boolean isRoundabout()
{
return roundaboutExit != 0;
}
public void addBadWay(MessageData badWay) {
if (badWay == null) {
public void addBadWay( MessageData badWay )
{
if ( badWay == null )
{
return;
}
if (badWays == null) {
badWays = new ArrayList<>();
if ( badWays == null )
{
badWays = new ArrayList<MessageData>();
}
badWays.add(badWay);
badWays.add( badWay );
}
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 getCommand()
{
return cmd;
}
public int getExitNumber() {
public int getExitNumber()
{
return roundaboutExit;
}
/*
* used by comment style, osmand style
*/
public String getCommandString() {
switch (cmd) {
case TLU:
return "TU"; // should be changed to TLU when osmand uses new voice hint constants
case TU:
return "TU";
case TSHL:
return "TSHL";
case TL:
return "TL";
case TSLL:
return "TSLL";
case KL:
return "KL";
case C:
return "C";
case KR:
return "KR";
case TSLR:
return "TSLR";
case TR:
return "TR";
case TSHR:
return "TSHR";
case TRU:
return "TRU";
case RNDB:
return "RNDB" + roundaboutExit;
case RNLB:
return "RNLB" + (-roundaboutExit);
case BL:
return "BL";
case OFFR:
return "OFFR";
default:
throw new IllegalArgumentException("unknown command: " + cmd);
public String getCommandString()
{
switch ( cmd )
{
case TU : return "TU";
case TSHL : return "TSHL";
case TL : return "TL";
case TSLL : return "TSLL";
case KL : return "KL";
case C : return "C";
case KR : return "KR";
case TSLR : return "TSLR";
case TR : return "TR";
case TSHR : return "TSHR";
case TRU : return "TRU";
case RNDB : return "RNDB" + roundaboutExit;
case RNLB : return "RNLB" + (-roundaboutExit);
default : throw new IllegalArgumentException( "unknown command: " + cmd );
}
}
public String getSymbolString()
{
switch ( cmd )
{
case TU : return "TU";
case TSHL : return "TSHL";
case TL : return "Left";
case TSLL : return "TSLL";
case KL : return "TSLL"; // ?
case C : return "Straight";
case KR : return "TSLR"; // ?
case TSLR : return "TSLR";
case TR : return "Right";
case TSHR : return "TSHR";
case TRU : return "TU";
case RNDB : return "RNDB" + roundaboutExit;
case RNLB : return "RNLB" + (-roundaboutExit);
default : throw new IllegalArgumentException( "unknown command: " + cmd );
}
}
/*
* used by trkpt/sym style
*/
public String getCommandString(int c) {
switch (c) {
case TLU:
return "TLU";
case TU:
return "TU";
case TSHL:
return "TSHL";
case TL:
return "TL";
case TSLL:
return "TSLL";
case KL:
return "KL";
case C:
return "C";
case KR:
return "KR";
case TSLR:
return "TSLR";
case TR:
return "TR";
case TSHR:
return "TSHR";
case TRU:
return "TRU";
case RNDB:
return "RNDB" + roundaboutExit;
case RNLB:
return "RNLB" + (-roundaboutExit);
case BL:
return "BL";
case OFFR:
return "OFFR";
default:
return "unknown command: " + c;
public String getMessageString()
{
switch ( cmd )
{
case TU : return "u-turn";
case TSHL : return "sharp left";
case TL : return "left";
case TSLL : return "slight left";
case KL : return "keep left";
case C : return "straight";
case KR : return "keep right";
case TSLR : return "slight right";
case TR : return "right";
case TSHR : return "sharp right";
case TRU : return "u-turn";
case RNDB : return "Take exit " + roundaboutExit;
case RNLB : return "Take exit " + (-roundaboutExit);
default : throw new IllegalArgumentException( "unknown command: " + cmd );
}
}
/*
* used by gpsies style
*/
public String getSymbolString() {
switch (cmd) {
case TLU:
return "TU";
case TU:
return "TU";
case TSHL:
return "TSHL";
case TL:
return "Left";
case TSLL:
return "TSLL";
case KL:
return "TSLL"; // ?
case C:
return "Straight";
case KR:
return "TSLR"; // ?
case TSLR:
return "TSLR";
case TR:
return "Right";
case TSHR:
return "TSHR";
case TRU:
return "TU";
case RNDB:
return "RNDB" + roundaboutExit;
case RNLB:
return "RNLB" + (-roundaboutExit);
case BL:
return "BL";
case OFFR:
return "OFFR";
default:
throw new IllegalArgumentException("unknown command: " + cmd);
public int getLocusAction()
{
switch ( cmd )
{
case TU : return 13;
case TSHL : return 5;
case TL : return 4;
case TSLL : return 3;
case KL : return 9; // ?
case C : return 1;
case KR : return 10; // ?
case TSLR : return 6;
case TR : return 7;
case TSHR : return 8;
case TRU : return 14;
case RNDB : return 26 + roundaboutExit;
case RNLB : return 26 - roundaboutExit;
default : throw new IllegalArgumentException( "unknown command: " + cmd );
}
}
/*
* used by new locus trkpt style
*/
public String getLocusSymbolString() {
switch (cmd) {
case TLU:
return "u-turn_left";
case TU:
return "u-turn";
case TSHL:
return "left_sharp";
case TL:
return "left";
case TSLL:
return "left_slight";
case KL:
return "stay_left"; // ?
case C:
return "straight";
case KR:
return "stay_right"; // ?
case TSLR:
return "right_slight";
case TR:
return "right";
case TSHR:
return "right_sharp";
case TRU:
return "u-turn_right";
case RNDB:
return "roundabout_e" + roundaboutExit;
case RNLB:
return "roundabout_e" + (-roundaboutExit);
case BL:
return "beeline";
default:
throw new IllegalArgumentException("unknown command: " + cmd);
}
public int getOruxAction()
{
switch ( cmd )
{
case TU : return 1003;
case TSHL : return 1019;
case TL : return 1000;
case TSLL : return 1017;
case KL : return 1015; // ?
case C : return 1002;
case KR : return 1014; // ?
case TSLR : return 1016;
case TR : return 1001;
case TSHR : return 1018;
case TRU : return 1003;
case RNDB : return 1008 + roundaboutExit;
case RNLB : return 1008 + roundaboutExit;
default : throw new IllegalArgumentException( "unknown command: " + cmd );
}
}
/*
* used by osmand style
*/
public String getMessageString() {
switch (cmd) {
case TLU:
return "u-turn"; // should be changed to u-turn-left when osmand uses new voice hint constants
case TU:
return "u-turn";
case TSHL:
return "sharp left";
case TL:
return "left";
case TSLL:
return "slight left";
case KL:
return "keep left";
case C:
return "straight";
case KR:
return "keep right";
case TSLR:
return "slight right";
case TR:
return "right";
case TSHR:
return "sharp right";
case TRU:
return "u-turn"; // should be changed to u-turn-right when osmand uses new voice hint constants
case RNDB:
return "Take exit " + roundaboutExit;
case RNLB:
return "Take exit " + (-roundaboutExit);
default:
throw new IllegalArgumentException("unknown command: " + cmd);
}
}
/*
* used by old locus style
*/
public int getLocusAction() {
switch (cmd) {
case TLU:
return 13;
case TU:
return 12;
case TSHL:
return 5;
case TL:
return 4;
case TSLL:
return 3;
case KL:
return 9; // ?
case C:
return 1;
case KR:
return 10; // ?
case TSLR:
return 6;
case TR:
return 7;
case TSHR:
return 8;
case TRU:
return 14;
case RNDB:
return 26 + roundaboutExit;
case RNLB:
return 26 - roundaboutExit;
default:
throw new IllegalArgumentException("unknown command: " + cmd);
}
}
/*
* used by orux style
*/
public int getOruxAction() {
switch (cmd) {
case TLU:
return 1003;
case TU:
return 1003;
case TSHL:
return 1019;
case TL:
return 1000;
case TSLL:
return 1017;
case KL:
return 1015; // ?
case C:
return 1002;
case KR:
return 1014; // ?
case TSLR:
return 1016;
case TR:
return 1001;
case TSHR:
return 1018;
case TRU:
return 1003;
case RNDB:
return 1008 + roundaboutExit;
case RNLB:
return 1008 + roundaboutExit;
default:
throw new IllegalArgumentException("unknown command: " + cmd);
}
}
/*
* used by cruiser, equivalent to getCommandString() - osmand style - when osmand changes the voice hint constants
*/
public String getCruiserCommandString() {
switch (cmd) {
case TLU:
return "TLU";
case TU:
return "TU";
case TSHL:
return "TSHL";
case TL:
return "TL";
case TSLL:
return "TSLL";
case KL:
return "KL";
case C:
return "C";
case KR:
return "KR";
case TSLR:
return "TSLR";
case TR:
return "TR";
case TSHR:
return "TSHR";
case TRU:
return "TRU";
case RNDB:
return "RNDB" + roundaboutExit;
case RNLB:
return "RNLB" + (-roundaboutExit);
case BL:
return "BL";
case OFFR:
return "OFFR";
default:
throw new IllegalArgumentException("unknown command: " + cmd);
}
}
/*
* used by cruiser, equivalent to getMessageString() - osmand style - when osmand changes the voice hint constants
*/
public String getCruiserMessageString() {
switch (cmd) {
case TLU:
return "u-turn left";
case TU:
return "u-turn";
case TSHL:
return "sharp left";
case TL:
return "left";
case TSLL:
return "slight left";
case KL:
return "keep left";
case C:
return "straight";
case KR:
return "keep right";
case TSLR:
return "slight right";
case TR:
return "right";
case TSHR:
return "sharp right";
case TRU:
return "u-turn right";
case RNDB:
return "take exit " + roundaboutExit;
case RNLB:
return "take exit " + (-roundaboutExit);
case BL:
return "beeline";
case OFFR:
return "offroad";
default:
throw new IllegalArgumentException("unknown command: " + cmd);
}
}
public void calcCommand() {
public void calcCommand()
{
float lowerBadWayAngle = -181;
float higherBadWayAngle = 181;
if (badWays != null) {
for (MessageData badWay : badWays) {
if (badWay.isBadOneway()) {
if ( badWays != null )
{
for ( MessageData badWay : badWays )
{
if ( badWay.isBadOneway() )
{
continue;
}
if (lowerBadWayAngle < badWay.turnangle && badWay.turnangle < goodWay.turnangle) {
if ( lowerBadWayAngle < badWay.turnangle && badWay.turnangle < goodWay.turnangle )
{
lowerBadWayAngle = badWay.turnangle;
}
if (higherBadWayAngle > badWay.turnangle && badWay.turnangle > goodWay.turnangle) {
if ( higherBadWayAngle > badWay.turnangle && badWay.turnangle > goodWay.turnangle )
{
higherBadWayAngle = badWay.turnangle;
}
}
}
float cmdAngle = angle;
float cmdAngle= angle;
// fall back to local angle if otherwise inconsistent
//if ( lowerBadWayAngle > angle || higherBadWayAngle < angle )
//{
//cmdAngle = goodWay.turnangle;
//}
if (angle == Float.MAX_VALUE) {
if ( lowerBadWayAngle > angle || higherBadWayAngle < angle )
{
cmdAngle = goodWay.turnangle;
}
if (cmd == BL) return;
if (roundaboutExit > 0) {
if (roundaboutExit > 0)
{
cmd = RNDB;
} else if (roundaboutExit < 0) {
}
else if (roundaboutExit < 0)
{
cmd = RNLB;
} else if (is180DegAngle(cmdAngle) && cmdAngle <= -179.f && higherBadWayAngle == 181.f && lowerBadWayAngle == -181.f) {
}
else if ( cmdAngle < -159. )
{
cmd = TU;
} else if (cmdAngle < -159.f) {
cmd = TLU;
} else if (cmdAngle < -135.f) {
}
else if ( cmdAngle < -135. )
{
cmd = TSHL;
} else if (cmdAngle < -45.f) {
}
else if ( cmdAngle < -45. )
{
// a TL can be pushed in either direction by a close-by alternative
if (cmdAngle < -95.f && higherBadWayAngle < -30.f && lowerBadWayAngle < -180.f) {
if ( higherBadWayAngle > -90. && higherBadWayAngle < -15. && lowerBadWayAngle < -180. )
{
cmd = TSHL;
} else if (cmdAngle > -85.f && lowerBadWayAngle > -180.f && higherBadWayAngle > -10.f) {
cmd = TSLL;
} else {
if (cmdAngle < -110.f) {
cmd = TSHL;
} else if (cmdAngle > -60.f) {
cmd = TSLL;
} else {
cmd = TL;
}
}
} else if (cmdAngle < -21.f) {
if (cmd != KR) { // don't overwrite KR with TSLL
else if ( lowerBadWayAngle > -180. && lowerBadWayAngle < -90. && higherBadWayAngle > 0. )
{
cmd = TSLL;
}
} else if (cmdAngle < -5.f) {
if (lowerBadWayAngle < -100.f && higherBadWayAngle < 45.f) {
else
{
cmd = TL;
}
}
else if ( cmdAngle < -21. )
{
if ( cmd != KR ) // don't overwrite KR with TSLL
{
cmd = TSLL;
} else if (lowerBadWayAngle >= -100.f && higherBadWayAngle < 45.f) {
cmd = KL;
} else {
}
}
else if ( cmdAngle < 21. )
{
if ( cmd != KR && cmd != KL ) // don't overwrite KL/KR hints!
{
cmd = C;
}
} else if (cmdAngle < 5.f) {
if (lowerBadWayAngle > -30.f) {
cmd = KR;
} else if (higherBadWayAngle < 30.f) {
cmd = KL;
} else {
cmd = C;
}
else if ( cmdAngle < 45. )
{
if ( cmd != KL ) // don't overwrite KL with TSLR
{
cmd = TSLR;
}
} else if (cmdAngle < 21.f) {
}
else if ( cmdAngle < 135. )
{
// a TR can be pushed in either direction by a close-by alternative
if (lowerBadWayAngle > -45.f && higherBadWayAngle > 100.f) {
if ( higherBadWayAngle > 90. && higherBadWayAngle < 180. && lowerBadWayAngle < 0. )
{
cmd = TSLR;
} else if (lowerBadWayAngle > -45.f && higherBadWayAngle <= 100.f) {
cmd = KR;
} else {
cmd = C;
}
} else if (cmdAngle < 45.f) {
cmd = TSLR;
} else if (cmdAngle < 135.f) {
if (cmdAngle < 85.f && higherBadWayAngle < 180.f && lowerBadWayAngle < 10.f) {
cmd = TSLR;
} else if (cmdAngle > 95.f && lowerBadWayAngle > 30.f && higherBadWayAngle > 180.f) {
else if ( lowerBadWayAngle > 15. && lowerBadWayAngle < 90. && higherBadWayAngle > 180. )
{
cmd = TSHR;
} else {
if (cmdAngle > 110.) {
cmd = TSHR;
} else if (cmdAngle < 60.) {
cmd = TSLR;
} else {
cmd = TR;
}
}
} else if (cmdAngle < 159.f) {
else
{
cmd = TR;
}
}
else if ( cmdAngle < 159. )
{
cmd = TSHR;
} else if (is180DegAngle(cmdAngle) && cmdAngle >= 179.f && higherBadWayAngle == 181.f && lowerBadWayAngle == -181.f) {
cmd = TU;
} else {
}
else
{
cmd = TRU;
}
}
static boolean is180DegAngle(float angle) {
return (Math.abs(angle) <= 180.f && Math.abs(angle) >= 179.f);
}
public String formatGeometry() {
public String formatGeometry()
{
float oldPrio = oldWay == null ? 0.f : oldWay.priorityclassifier;
StringBuilder sb = new StringBuilder(30);
sb.append(' ').append((int) oldPrio);
appendTurnGeometry(sb, goodWay);
if (badWays != null) {
for (MessageData badWay : badWays) {
sb.append(" ");
appendTurnGeometry(sb, badWay);
sb.append( ' ' ).append( (int)oldPrio );
appendTurnGeometry(sb,goodWay);
if ( badWays != null )
{
for ( MessageData badWay : badWays )
{
sb.append( " " );
appendTurnGeometry( sb, badWay );
}
}
return sb.toString();
}
private void appendTurnGeometry(StringBuilder sb, MessageData msg) {
sb.append("(").append((int) (msg.turnangle + 0.5)).append(")").append((int) (msg.priorityclassifier));
private void appendTurnGeometry( StringBuilder sb, MessageData msg )
{
sb.append( "(" ).append( (int)(msg.turnangle+0.5) ).append( ")" ).append( (int)(msg.priorityclassifier) );
}
}

View file

@ -9,51 +9,30 @@ package btools.router;
import java.util.ArrayList;
import java.util.List;
public class VoiceHintList {
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;
public class VoiceHintList
{
private String transportMode;
int turnInstructionMode;
List<VoiceHint> list = new ArrayList<>();
ArrayList<VoiceHint> list = new ArrayList<VoiceHint>();
public void setTransportMode(boolean isCar, boolean isBike) {
transportMode = isCar ? TRANS_MODE_CAR : (isBike ? TRANS_MODE_BIKE : TRANS_MODE_FOOT);
public void setTransportMode( boolean isCar, boolean isBike )
{
transportMode = isCar ? "car" : ( isBike ? "bike" : "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() {
public String getTransportMode()
{
return transportMode;
}
public int getLocusRouteType() {
if (transportMode == TRANS_MODE_CAR) {
public int getLocusRouteType()
{
if ( "car".equals( transportMode ) )
{
return 0;
}
if (transportMode == TRANS_MODE_BIKE) {
if ( "bike".equals( transportMode ) )
{
return 5;
}
return 3; // foot

View file

@ -8,27 +8,26 @@ package btools.router;
import java.util.ArrayList;
import java.util.List;
public final class VoiceHintProcessor {
double SIGNIFICANT_ANGLE = 22.5;
double INTERNAL_CATCHING_RANGE = 2.;
// private double catchingRange; // range to catch angles and merge turns
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, int transportMode) {
// this.catchingRange = catchingRange;
public VoiceHintProcessor( double catchingRange, boolean explicitRoundabouts )
{
this.catchingRange = catchingRange;
this.explicitRoundabouts = explicitRoundabouts;
this.transportMode = transportMode;
}
private float sumNonConsumedWithinCatchingRange(List<VoiceHint> inputs, int offset) {
private float sumNonConsumedWithinCatchingRange( List<VoiceHint> inputs, int offset )
{
double distance = 0.;
float angle = 0.f;
while (offset >= 0 && distance < INTERNAL_CATCHING_RANGE) {
VoiceHint input = inputs.get(offset--);
if (input.turnAngleConsumed) {
while( offset >= 0 && distance < catchingRange )
{
VoiceHint input = inputs.get( offset-- );
if ( input.turnAngleConsumed )
{
break;
}
angle += input.goodWay.turnangle;
@ -45,10 +44,10 @@ public final class VoiceHintProcessor {
* order (from target to start), but output is
* returned in travel-direction and only for
* those nodes that trigger a voice hint.
* <p>
*
* Input objects are expected for every segment
* of the track, also for those without a junction
* <p>
*
* VoiceHint objects in the output list are enriched
* by the voice-command, the total angle and the distance
* to the next hint
@ -56,91 +55,56 @@ public final class VoiceHintProcessor {
* @param inputs tracknodes, un reverse order
* @return voice hints, in forward order
*/
public List<VoiceHint> process(List<VoiceHint> inputs) {
List<VoiceHint> results = new ArrayList<>();
public List<VoiceHint> process( List<VoiceHint> inputs )
{
List<VoiceHint> results = new ArrayList<VoiceHint>();
double distance = 0.;
float roundAboutTurnAngle = 0.f; // sums up angles in roundabout
int roundaboutExit = 0;
int roundaboudStartIdx = -1;
for (int hintIdx = 0; hintIdx < inputs.size(); hintIdx++) {
VoiceHint input = inputs.get(hintIdx);
for ( int hintIdx = 0; hintIdx < inputs.size(); hintIdx++ )
{
VoiceHint input = inputs.get( hintIdx );
if (input.cmd == VoiceHint.BL) {
results.add(input);
continue;
}
float turnAngle = input.goodWay.turnangle;
distance += input.goodWay.linkdist;
int currentPrio = input.goodWay.getPrio();
int oldPrio = input.oldWay.getPrio();
int minPrio = Math.min(oldPrio, currentPrio);
int minPrio = Math.min( oldPrio, currentPrio );
boolean isLink2Highway = input.oldWay.isLinktType() && !input.goodWay.isLinktType();
boolean isHighway2Link = !input.oldWay.isLinktType() && input.goodWay.isLinktType();
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;
}
}
}
if ( input.oldWay.isRoundabout() )
{
roundAboutTurnAngle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
boolean isExit = roundaboutExit == 0; // exit point is always exit
if (input.badWays != null) {
for (MessageData badWay : input.badWays) {
if (!badWay.isBadOneway() &&
badWay.isGoodForCars()) {
if ( input.badWays != null )
{
for ( MessageData badWay : input.badWays )
{
if ( !badWay.isBadOneway() && badWay.isGoodForCars() && Math.abs( badWay.turnangle ) < 120. )
{
isExit = true;
}
}
}
if (isExit) {
if ( isExit )
{
roundaboutExit++;
}
continue;
}
if (roundaboutExit > 0) {
//roundAboutTurnAngle += sumNonConsumedWithinCatchingRange(inputs, hintIdx);
//double startTurn = (roundaboudStartIdx != -1 ? inputs.get(roundaboudStartIdx + 1).goodWay.turnangle : turnAngle);
if ( roundaboutExit > 0 )
{
roundAboutTurnAngle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
input.angle = roundAboutTurnAngle;
input.goodWay.turnangle = roundAboutTurnAngle;
input.distanceToNext = distance;
//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);
}
}
}
}
input.roundaboutExit = turnAngle < 0 ? -roundaboutExit : roundaboutExit;
distance = 0.;
input.badWays = tmpRndAbt.badWays;
results.add(input);
results.add( input );
roundAboutTurnAngle = 0.f;
roundaboutExit = 0;
roundaboudStartIdx = -1;
continue;
}
int maxPrioAll = -1; // max prio of all detours
@ -150,265 +114,134 @@ public final class VoiceHintProcessor {
float minAngle = 180.f;
float minAbsAngeRaw = 180.f;
boolean isBadwayLink = false;
if (input.badWays != null) {
for (MessageData badWay : input.badWays) {
if ( input.badWays != null )
{
for ( MessageData badWay : input.badWays )
{
int badPrio = badWay.getPrio();
float badTurn = badWay.turnangle;
if (badWay.isLinktType()) {
isBadwayLink = true;
}
boolean isBadHighway2Link = !input.oldWay.isLinktType() && badWay.isLinktType();
if (badPrio > maxPrioAll && !isBadHighway2Link) {
boolean isHighway2Link = !input.oldWay.isLinktType() && badWay.isLinktType();
if ( badPrio > maxPrioAll && !isHighway2Link )
{
maxPrioAll = badPrio;
input.maxBadPrio = Math.max(input.maxBadPrio, badPrio);
}
if (badPrio < minPrio) {
if ( badWay.costfactor < 20.f && Math.abs( badTurn ) < minAbsAngeRaw )
{
minAbsAngeRaw = Math.abs( badTurn );
}
if ( badPrio < minPrio )
{
continue; // ignore low prio ways
}
if (badWay.isBadOneway()) {
if ( badWay.isBadOneway() )
{
continue; // ignore wrong oneways
}
if (Math.abs(badTurn) - Math.abs(turnAngle) > 80.f) {
if ( Math.abs( badTurn ) - Math.abs( turnAngle ) > 80.f )
{
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) {
if ( badPrio > maxPrioCandidates )
{
maxPrioCandidates = badPrio;
input.maxBadPrio = Math.max(input.maxBadPrio, badPrio);
}
if (badTurn > maxAngle) {
if ( badTurn > maxAngle )
{
maxAngle = badTurn;
}
if (badTurn < minAngle) {
if ( badTurn < minAngle )
{
minAngle = badTurn;
}
}
}
// boolean hasSomethingMoreStraight = (Math.abs(turnAngle) - minAbsAngeRaw) > 20.;
boolean hasSomethingMoreStraight = (Math.abs(turnAngle - minAbsAngeRaw)) > 20. && input.badWays != null; // && !ignoreBadway;
boolean hasSomethingMoreStraight = Math.abs( turnAngle ) - minAbsAngeRaw > 20.;
// unconditional triggers are all junctions with
// - higher detour prios than the minimum route prio (except link->highway junctions)
// - or candidate detours with higher prio then the route exit leg
boolean unconditionalTrigger = hasSomethingMoreStraight ||
(maxPrioAll > minPrio && !isLink2Highway) ||
(maxPrioCandidates > currentPrio) ||
VoiceHint.is180DegAngle(turnAngle) ||
(!isHighway2Link && isBadwayLink && Math.abs(turnAngle) > 5.f) ||
(isHighway2Link && !isBadwayLink && Math.abs(turnAngle) < 5.f);
boolean unconditionalTrigger = hasSomethingMoreStraight || ( maxPrioAll > minPrio && !isLink2Highway ) || ( maxPrioCandidates > currentPrio );
// conditional triggers (=real turning angle required) are junctions
// with candidate detours equal in priority than the route exit leg
boolean conditionalTrigger = maxPrioCandidates >= minPrio;
if (unconditionalTrigger || conditionalTrigger) {
if ( unconditionalTrigger || conditionalTrigger )
{
input.angle = turnAngle;
input.calcCommand();
boolean isStraight = input.cmd == VoiceHint.C;
input.needsRealTurn = (!unconditionalTrigger) && isStraight;
// check for KR/KL
if (Math.abs(turnAngle) > 5.) { // don't use too small angles
if (maxAngle < turnAngle && maxAngle > turnAngle - 45.f - (Math.max(turnAngle, 0.f))) {
input.cmd = VoiceHint.KR;
}
if (minAngle > turnAngle && minAngle < turnAngle + 45.f - (Math.min(turnAngle, 0.f))) {
input.cmd = VoiceHint.KL;
}
if ( maxAngle < turnAngle && maxAngle > turnAngle - 45.f - (turnAngle > 0.f ? turnAngle : 0.f ) )
{
input.cmd = VoiceHint.KR;
}
if ( minAngle > turnAngle && minAngle < turnAngle + 45.f - (turnAngle < 0.f ? turnAngle : 0.f ) )
{
input.cmd = VoiceHint.KL;
}
input.angle = sumNonConsumedWithinCatchingRange(inputs, hintIdx);
input.angle = sumNonConsumedWithinCatchingRange( inputs, hintIdx );
input.distanceToNext = distance;
distance = 0.;
results.add(input);
results.add( input );
}
if (results.size() > 0 && distance < INTERNAL_CATCHING_RANGE) { //catchingRange
results.get(results.size() - 1).angle += sumNonConsumedWithinCatchingRange(inputs, hintIdx);
if ( results.size() > 0 && distance < catchingRange )
{
results.get( results.size()-1 ).angle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
}
}
// go through the hint list again in reverse order (=travel direction)
// and filter out non-significant hints and hints too close to its predecessor
// and filter out non-signficant hints and hints too close to it's predecessor
List<VoiceHint> results2 = new ArrayList<>();
List<VoiceHint> results2 = new ArrayList<VoiceHint>();
int i = results.size();
while (i > 0) {
while( i > 0 )
{
VoiceHint hint = results.get(--i);
if (hint.cmd == 0) {
if ( hint.cmd == 0 )
{
hint.calcCommand();
}
if (!(hint.needsRealTurn && (hint.cmd == VoiceHint.C || hint.cmd == VoiceHint.BL))) {
if ( ! ( hint.needsRealTurn && hint.cmd == VoiceHint.C ) )
{
double dist = hint.distanceToNext;
// sum up other hints within the catching range (e.g. 40m)
while (dist < INTERNAL_CATCHING_RANGE && i > 0) {
VoiceHint h2 = results.get(i - 1);
while( dist < catchingRange && i > 0 )
{
VoiceHint h2 = results.get(i-1);
dist = h2.distanceToNext;
hint.distanceToNext += dist;
hint.distanceToNext+= dist;
hint.angle += h2.angle;
i--;
if (h2.isRoundabout()) { // if we hit a roundabout, use that as the trigger
if ( h2.isRoundabout() ) // if we hit a roundabout, use that as the trigger
{
h2.angle = hint.angle;
hint = h2;
break;
}
}
if (!explicitRoundabouts) {
if ( !explicitRoundabouts )
{
hint.roundaboutExit = 0; // use an angular hint instead
}
hint.calcCommand();
results2.add(hint);
} else if (hint.cmd == VoiceHint.BL) {
results2.add(hint);
} else {
if (results2.size() > 0)
results2.get(results2.size() - 1).distanceToNext += hint.distanceToNext;
results2.add( hint );
}
}
return results2;
}
public List<VoiceHint> postProcess(List<VoiceHint> inputs, double catchingRange, double minRange) {
List<VoiceHint> results = new ArrayList<>();
double distance = 0;
VoiceHint inputLast = null;
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 (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;
}
} else {
results.add(input);
}
} else {
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 = 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;
}
}
} 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
inputLastSaved = input;
}
} else {
results.add(input);
inputLastSaved = input;
}
}
inputLast = input;
}
return results;
}
}

View file

@ -1,18 +1,22 @@
package btools.router;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import btools.util.CheapRuler;
public class OsmNodeNamedTest {
static int toOsmLon(double lon) {
return (int) ((lon + 180.) / CheapRuler.ILATLNG_TO_LATLNG + 0.5);
return (int)( ( lon + 180. ) / CheapRuler.ILATLNG_TO_LATLNG + 0.5);
}
static int toOsmLat(double lat) {
return (int) ((lat + 90.) / CheapRuler.ILATLNG_TO_LATLNG + 0.5);
return (int)( ( lat + 90. ) / CheapRuler.ILATLNG_TO_LATLNG + 0.5);
}
@Test

View file

@ -1,6 +1,6 @@
/**********************************************************************************************
Copyright (C) 2018 Norbert Truchsess norbert.truchsess@t-online.de
**********************************************************************************************/
Copyright (C) 2018 Norbert Truchsess norbert.truchsess@t-online.de
**********************************************************************************************/
package btools.router;
import static org.junit.Assert.assertEquals;
@ -11,6 +11,7 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import btools.router.OsmNogoPolygon.Point;
import btools.util.CheapRuler;
public class OsmNogoPolygonTest {
@ -21,26 +22,26 @@ public class OsmNogoPolygonTest {
static OsmNogoPolygon polygon;
static OsmNogoPolygon polyline;
static final double[] lons = {1.0, 1.0, 0.5, 0.5, 1.0, 1.0, -1.1, -1.0};
static final double[] lats = {-1.0, -0.1, -0.1, 0.1, 0.1, 1.0, 1.1, -1.0};
static final double[] lons = { 1.0, 1.0, 0.5, 0.5, 1.0, 1.0, -1.1, -1.0 };
static final double[] lats = { -1.0, -0.1, -0.1, 0.1, 0.1, 1.0, 1.1, -1.0 };
static int toOsmLon(double lon, int offset_x) {
return (int) ((lon + 180.) * 1000000. + 0.5) + offset_x; // see ServerHandler.readPosition()
return (int)( ( lon + 180. ) *1000000. + 0.5)+offset_x; // see ServerHandler.readPosition()
}
static int toOsmLat(double lat, int offset_y) {
return (int) ((lat + 90.) * 1000000. + 0.5) + offset_y;
return (int)( ( lat + 90. ) *1000000. + 0.5)+offset_y;
}
@BeforeClass
public static void setUp() throws Exception {
polygon = new OsmNogoPolygon(true);
for (int i = 0; i < lons.length; i++) {
polygon.addVertex(toOsmLon(lons[i], OFFSET_X), toOsmLat(lats[i], OFFSET_Y));
for (int i = 0; i<lons.length; i++) {
polygon.addVertex(toOsmLon(lons[i], OFFSET_X),toOsmLat(lats[i], OFFSET_Y));
}
polyline = new OsmNogoPolygon(false);
for (int i = 0; i < lons.length; i++) {
polyline.addVertex(toOsmLon(lons[i], OFFSET_X), toOsmLat(lats[i], OFFSET_Y));
for (int i = 0; i<lons.length; i++) {
polyline.addVertex(toOsmLon(lons[i], OFFSET_X),toOsmLat(lats[i], OFFSET_Y));
}
}
@ -50,162 +51,162 @@ public class OsmNogoPolygonTest {
@Test
public void testCalcBoundingCircle() {
double[] lonlat2m = CheapRuler.getLonLatToMeterScales(polygon.ilat);
double[] lonlat2m = CheapRuler.getLonLatToMeterScales( polygon.ilat );
double dlon2m = lonlat2m[0];
double dlat2m = lonlat2m[1];
polygon.calcBoundingCircle();
double r = polygon.radius;
for (int i = 0; i < lons.length; i++) {
for (int i=0; i<lons.length; i++) {
double dpx = (toOsmLon(lons[i], OFFSET_X) - polygon.ilon) * dlon2m;
double dpy = (toOsmLat(lats[i], OFFSET_Y) - polygon.ilat) * dlat2m;
double r1 = Math.sqrt(dpx * dpx + dpy * dpy);
double diff = r - r1;
assertTrue("i: " + i + " r(" + r + ") >= r1(" + r1 + ")", diff >= 0);
double diff = r-r1;
assertTrue("i: "+i+" r("+r+") >= r1("+r1+")", diff >= 0);
}
polyline.calcBoundingCircle();
r = polyline.radius;
for (int i = 0; i < lons.length; i++) {
for (int i=0; i<lons.length; i++) {
double dpx = (toOsmLon(lons[i], OFFSET_X) - polyline.ilon) * dlon2m;
double dpy = (toOsmLat(lats[i], OFFSET_Y) - polyline.ilat) * dlat2m;
double r1 = Math.sqrt(dpx * dpx + dpy * dpy);
double diff = r - r1;
assertTrue("i: " + i + " r(" + r + ") >= r1(" + r1 + ")", diff >= 0);
double diff = r-r1;
assertTrue("i: "+i+" r("+r+") >= r1("+r1+")", diff >= 0);
}
}
@Test
public void testIsWithin() {
double[] plons = {0.0, 0.5, 1.0, -1.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5};
double[] plats = {0.0, 1.5, 0.0, 0.5, -1.5, -1.0, -0.1, -0.1, 0.0, 0.1};
boolean[] within = {true, false, false, false, false, true, true, true, true, true};
double[] plons = { 0.0, 0.5, 1.0, -1.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5, };
double[] plats = { 0.0, 1.5, 0.0, 0.5, -1.5, -1.0, -0.1, -0.1, 0.0, 0.1, };
boolean[] within = { true, false, false, false, false, true, true, true, true, true, };
for (int i = 0; i < plons.length; i++) {
assertEquals("(" + plons[i] + "," + plats[i] + ")", within[i], polygon.isWithin(toOsmLon(plons[i], OFFSET_X), toOsmLat(plats[i], OFFSET_Y)));
for (int i=0; i<plons.length; i++) {
assertEquals("("+plons[i]+","+plats[i]+")",within[i],polygon.isWithin(toOsmLon(plons[i], OFFSET_X), toOsmLat(plats[i], OFFSET_Y)));
}
}
@Test
public void testIntersectsPolygon() {
double[] p0lons = {0.0, 1.0, -0.5, 0.5, 0.7, 0.7, 0.7, -1.5, -1.5, 0.0};
double[] p0lats = {0.0, 0.0, 0.5, 0.5, 0.5, 0.05, 0.05, -1.5, 0.2, 0.0};
double[] p1lons = {0.0, 1.0, 0.5, 1.0, 0.7, 0.7, 0.7, -0.5, -0.2, 0.5};
double[] p1lats = {0.0, 0.0, 0.5, 0.5, -0.5, -0.5, -0.05, -0.5, 1.5, -1.5};
boolean[] within = {false, false, false, true, true, true, false, true, true, true};
double[] p0lons = { 0.0, 1.0, -0.5, 0.5, 0.7, 0.7, 0.7, -1.5, -1.5, 0.0 };
double[] p0lats = { 0.0, 0.0, 0.5, 0.5, 0.5, 0.05, 0.05, -1.5, 0.2, 0.0 };
double[] p1lons = { 0.0, 1.0, 0.5, 1.0, 0.7, 0.7, 0.7, -0.5, -0.2, 0.5 };
double[] p1lats = { 0.0, 0.0, 0.5, 0.5, -0.5, -0.5, -0.05, -0.5, 1.5, -1.5 };
boolean[] within = { false, false, false, true, true, true, false, true, true, true };
for (int i = 0; i < p0lons.length; i++) {
assertEquals("(" + p0lons[i] + "," + p0lats[i] + ")-(" + p1lons[i] + "," + p1lats[i] + ")", within[i], polygon.intersects(toOsmLon(p0lons[i], OFFSET_X), toOsmLat(p0lats[i], OFFSET_Y), toOsmLon(p1lons[i], OFFSET_X), toOsmLat(p1lats[i], OFFSET_Y)));
for (int i=0; i<p0lons.length; i++) {
assertEquals("("+p0lons[i]+","+p0lats[i]+")-("+p1lons[i]+","+p1lats[i]+")",within[i],polygon.intersects(toOsmLon(p0lons[i], OFFSET_X), toOsmLat(p0lats[i], OFFSET_Y), toOsmLon(p1lons[i], OFFSET_X), toOsmLat(p1lats[i], OFFSET_Y)));
}
}
@Test
public void testIntersectsPolyline() {
double[] p0lons = {0.0, 1.0, -0.5, 0.5, 0.7, 0.7, 0.7, -1.5, -1.5, 0.0};
double[] p0lats = {0.0, 0.0, 0.5, 0.5, 0.5, 0.05, 0.05, -1.5, 0.2, 0.0};
double[] p1lons = {0.0, 1.0, 0.5, 1.0, 0.7, 0.7, 0.7, -0.5, -0.2, 0.5};
double[] p1lats = {0.0, 0.0, 0.5, 0.5, -0.5, -0.5, -0.05, -0.5, 1.5, -1.5};
boolean[] within = {false, false, false, true, true, true, false, true, true, false};
double[] p0lons = { 0.0, 1.0, -0.5, 0.5, 0.7, 0.7, 0.7, -1.5, -1.5, 0.0 };
double[] p0lats = { 0.0, 0.0, 0.5, 0.5, 0.5, 0.05, 0.05, -1.5, 0.2, 0.0 };
double[] p1lons = { 0.0, 1.0, 0.5, 1.0, 0.7, 0.7, 0.7, -0.5, -0.2, 0.5 };
double[] p1lats = { 0.0, 0.0, 0.5, 0.5, -0.5, -0.5, -0.05, -0.5, 1.5, -1.5 };
boolean[] within = { false, false, false, true, true, true, false, true, true, false };
for (int i = 0; i < p0lons.length; i++) {
assertEquals("(" + p0lons[i] + "," + p0lats[i] + ")-(" + p1lons[i] + "," + p1lats[i] + ")", within[i], polyline.intersects(toOsmLon(p0lons[i], OFFSET_X), toOsmLat(p0lats[i], OFFSET_Y), toOsmLon(p1lons[i], OFFSET_X), toOsmLat(p1lats[i], OFFSET_Y)));
for (int i=0; i<p0lons.length; i++) {
assertEquals("("+p0lons[i]+","+p0lats[i]+")-("+p1lons[i]+","+p1lats[i]+")",within[i],polyline.intersects(toOsmLon(p0lons[i], OFFSET_X), toOsmLat(p0lats[i], OFFSET_Y), toOsmLon(p1lons[i], OFFSET_X), toOsmLat(p1lats[i], OFFSET_Y)));
}
}
@Test
public void testBelongsToLine() {
assertTrue(OsmNogoPolygon.isOnLine(10, 10, 10, 10, 10, 20));
assertTrue(OsmNogoPolygon.isOnLine(10, 10, 10, 10, 20, 10));
assertTrue(OsmNogoPolygon.isOnLine(10, 10, 20, 10, 10, 10));
assertTrue(OsmNogoPolygon.isOnLine(10, 10, 10, 20, 10, 10));
assertTrue(OsmNogoPolygon.isOnLine(10, 15, 10, 10, 10, 20));
assertTrue(OsmNogoPolygon.isOnLine(15, 10, 10, 10, 20, 10));
assertTrue(OsmNogoPolygon.isOnLine(10, 10, 10, 10, 20, 30));
assertTrue(OsmNogoPolygon.isOnLine(20, 30, 10, 10, 20, 30));
assertTrue(OsmNogoPolygon.isOnLine(15, 20, 10, 10, 20, 30));
assertFalse(OsmNogoPolygon.isOnLine(11, 11, 10, 10, 10, 20));
assertFalse(OsmNogoPolygon.isOnLine(11, 11, 10, 10, 20, 10));
assertFalse(OsmNogoPolygon.isOnLine(15, 21, 10, 10, 20, 30));
assertFalse(OsmNogoPolygon.isOnLine(15, 19, 10, 10, 20, 30));
assertFalse(OsmNogoPolygon.isOnLine(0, -10, 10, 10, 20, 30));
assertFalse(OsmNogoPolygon.isOnLine(30, 50, 10, 10, 20, 30));
assertTrue(OsmNogoPolygon.isOnLine(10,10, 10,10, 10,20));
assertTrue(OsmNogoPolygon.isOnLine(10,10, 10,10, 20,10));
assertTrue(OsmNogoPolygon.isOnLine(10,10, 20,10, 10,10));
assertTrue(OsmNogoPolygon.isOnLine(10,10, 10,20, 10,10));
assertTrue(OsmNogoPolygon.isOnLine(10,15, 10,10, 10,20));
assertTrue(OsmNogoPolygon.isOnLine(15,10, 10,10, 20,10));
assertTrue(OsmNogoPolygon.isOnLine(10,10, 10,10, 20,30));
assertTrue(OsmNogoPolygon.isOnLine(20,30, 10,10, 20,30));
assertTrue(OsmNogoPolygon.isOnLine(15,20, 10,10, 20,30));
assertFalse(OsmNogoPolygon.isOnLine(11,11, 10,10, 10,20));
assertFalse(OsmNogoPolygon.isOnLine(11,11, 10,10, 20,10));
assertFalse(OsmNogoPolygon.isOnLine(15,21, 10,10, 20,30));
assertFalse(OsmNogoPolygon.isOnLine(15,19, 10,10, 20,30));
assertFalse(OsmNogoPolygon.isOnLine(0,-10, 10,10, 20,30));
assertFalse(OsmNogoPolygon.isOnLine(30,50, 10,10, 20,30));
}
@Test
public void testDistanceWithinPolygon() {
// Testing polygon
final double[] lons = {2.333523, 2.333432, 2.333833, 2.333983, 2.334815, 2.334766};
final double[] lats = {48.823778, 48.824091, 48.82389, 48.824165, 48.824232, 48.82384};
OsmNogoPolygon polygon = new OsmNogoPolygon(true);
for (int i = 0; i < lons.length; i++) {
polygon.addVertex(toOsmLon(lons[i], 0), toOsmLat(lats[i], 0));
}
OsmNogoPolygon polyline = new OsmNogoPolygon(false);
for (int i = 0; i < lons.length; i++) {
polyline.addVertex(toOsmLon(lons[i], 0), toOsmLat(lats[i], 0));
}
// Testing polygon
final double[] lons = { 2.333523, 2.333432, 2.333833, 2.333983, 2.334815, 2.334766 };
final double[] lats = { 48.823778, 48.824091, 48.82389, 48.824165, 48.824232, 48.82384 };
OsmNogoPolygon polygon = new OsmNogoPolygon(true);
for (int i = 0; i < lons.length; i++) {
polygon.addVertex(toOsmLon(lons[i], 0), toOsmLat(lats[i], 0));
}
OsmNogoPolygon polyline = new OsmNogoPolygon(false);
for (int i = 0; i < lons.length; i++) {
polyline.addVertex(toOsmLon(lons[i], 0), toOsmLat(lats[i], 0));
}
// Check with a segment with a single intersection with the polygon
int lon1 = toOsmLon(2.33308732509613, 0);
int lat1 = toOsmLat(48.8238790443901, 0);
int lon2 = toOsmLon(2.33378201723099, 0);
int lat2 = toOsmLat(48.8239585098974, 0);
assertEquals(
"Should give the correct length for a segment with a single intersection",
17.5,
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * 17.5
);
// Check with a segment with a single intersection with the polygon
int lon1 = toOsmLon(2.33308732509613, 0);
int lat1 = toOsmLat(48.8238790443901, 0);
int lon2 = toOsmLon(2.33378201723099, 0);
int lat2 = toOsmLat(48.8239585098974, 0);
assertEquals(
"Should give the correct length for a segment with a single intersection",
17.5,
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * 17.5
);
// Check with a segment crossing multiple times the polygon
lon2 = toOsmLon(2.33488172292709, 0);
lat2 = toOsmLat(48.8240891862353, 0);
assertEquals(
"Should give the correct length for a segment with multiple intersections",
85,
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * 85
);
// Check with a segment crossing multiple times the polygon
lon2 = toOsmLon(2.33488172292709, 0);
lat2 = toOsmLat(48.8240891862353, 0);
assertEquals(
"Should give the correct length for a segment with multiple intersections",
85,
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * 85
);
// Check that it works when a point is within the polygon
lon2 = toOsmLon(2.33433187007904, 0);
lat2 = toOsmLat(48.8240238480664, 0);
assertEquals(
"Should give the correct length when last point is within the polygon",
50,
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * 50
);
lon1 = toOsmLon(2.33433187007904, 0);
lat1 = toOsmLat(48.8240238480664, 0);
lon2 = toOsmLon(2.33488172292709, 0);
lat2 = toOsmLat(48.8240891862353, 0);
assertEquals(
"Should give the correct length when first point is within the polygon",
35,
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * 35
);
// Check that it works when a point is within the polygon
lon2 = toOsmLon(2.33433187007904, 0);
lat2 = toOsmLat(48.8240238480664, 0);
assertEquals(
"Should give the correct length when last point is within the polygon",
50,
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * 50
);
lon1 = toOsmLon(2.33433187007904, 0);
lat1 = toOsmLat(48.8240238480664, 0);
lon2 = toOsmLon(2.33488172292709, 0);
lat2 = toOsmLat(48.8240891862353, 0);
assertEquals(
"Should give the correct length when first point is within the polygon",
35,
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * 35
);
lon1 = toOsmLon(2.333523, 0);
lat1 = toOsmLat(48.823778, 0);
lon2 = toOsmLon(2.333432, 0);
lat2 = toOsmLat(48.824091, 0);
assertEquals(
"Should give the correct length if the segment overlaps with an edge of the polygon",
CheapRuler.distance(lon1, lat1, lon2, lat2),
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * CheapRuler.distance(lon1, lat1, lon2, lat2)
);
lon1 = toOsmLon(2.333523, 0);
lat1 = toOsmLat(48.823778, 0);
lon2 = toOsmLon(2.333432, 0);
lat2 = toOsmLat(48.824091, 0);
assertEquals(
"Should give the correct length if the segment overlaps with an edge of the polygon",
CheapRuler.distance(lon1, lat1, lon2, lat2),
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * CheapRuler.distance(lon1, lat1, lon2, lat2)
);
lon1 = toOsmLon(2.333523, 0);
lat1 = toOsmLat(48.823778, 0);
lon2 = toOsmLon(2.3334775, 0);
lat2 = toOsmLat(48.8239345, 0);
assertEquals(
"Should give the correct length if the segment overlaps with a polyline",
CheapRuler.distance(lon1, lat1, lon2, lat2),
polyline.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * CheapRuler.distance(lon1, lat1, lon2, lat2)
);
lon1 = toOsmLon(2.333523, 0);
lat1 = toOsmLat(48.823778, 0);
lon2 = toOsmLon(2.3334775, 0);
lat2 = toOsmLat(48.8239345, 0);
assertEquals(
"Should give the correct length if the segment overlaps with a polyline",
CheapRuler.distance(lon1, lat1, lon2, lat2),
polyline.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * CheapRuler.distance(lon1, lat1, lon2, lat2)
);
}
}

View file

@ -1,68 +0,0 @@
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,86 +0,0 @@
package btools.router;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class RoutingEngineTest {
private File workingDir;
@Before
public void before() {
URL resulturl = this.getClass().getResource("/testtrack0.gpx");
Assert.assertNotNull("reference result not found: ", resulturl);
File resultfile = new File(resulturl.getFile());
workingDir = resultfile.getParentFile();
}
@Test
public void routeCrossingSegmentBorder() {
String msg = calcRoute(8.720897, 50.002515, 8.723658, 49.997510, "testtrack", new RoutingContext());
// error message from router?
Assert.assertNull("routing failed: " + msg, msg);
// if the track didn't change, we expect the first alternative also
File a1 = new File(workingDir, "testtrack1.gpx");
a1.deleteOnExit();
Assert.assertTrue("result content mismatch", a1.exists());
}
@Test
public void routeDestinationPointFarOff() {
String msg = calcRoute(8.720897, 50.002515, 16.723658, 49.997510, "notrack", new RoutingContext());
Assert.assertTrue(msg, msg != null && msg.contains("not found"));
}
@Test
public void overrideParam() {
RoutingContext rctx = new RoutingContext();
rctx.keyValues = new HashMap<>();
rctx.keyValues.put("avoid_unsafe", "1.0");
String msg = calcRoute(8.723037, 50.000491, 8.712737, 50.002899, "paramTrack", rctx);
Assert.assertNull("routing failed: " + msg, msg);
File trackFile = new File(workingDir, "paramTrack1.gpx");
trackFile.deleteOnExit();
Assert.assertTrue("result content mismatch", trackFile.exists());
}
private String calcRoute(double flon, double flat, double tlon, double tlat, String trackname, RoutingContext rctx) {
String wd = workingDir.getAbsolutePath();
List<OsmNodeNamed> wplist = new ArrayList<>();
OsmNodeNamed n;
n = new OsmNodeNamed();
n.name = "from";
n.ilon = 180000000 + (int) (flon * 1000000 + 0.5);
n.ilat = 90000000 + (int) (flat * 1000000 + 0.5);
wplist.add(n);
n = new OsmNodeNamed();
n.name = "to";
n.ilon = 180000000 + (int) (tlon * 1000000 + 0.5);
n.ilat = 90000000 + (int) (tlat * 1000000 + 0.5);
wplist.add(n);
rctx.localFunction = wd + "/../../../../misc/profiles2/trekking.brf";
RoutingEngine re = new RoutingEngine(
wd + "/" + trackname,
wd + "/" + trackname,
new File(wd, "/../../../../brouter-map-creator/build/resources/test/tmp/segments"),
wplist,
rctx);
re.doRun(0);
return re.getErrorMessage();
}
}

View file

@ -1,67 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- track-length = 1570 filtered ascend = 4 plain-ascend = -15 cost=2840 energy=.0kwh time=3m 45s -->
<gpx
xmlns="http://www.topografix.com/GPX/1/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"
creator="BRouter-1.7.0" version="1.1">
<trk>
<name>brouter_trekking_0</name>
<trkseg>
<trkpt lon="8.723027" lat="50.000499"><ele>175.25</ele></trkpt>
<trkpt lon="8.723285" lat="50.000610"><ele>176.75</ele></trkpt>
<trkpt lon="8.724003" lat="50.000939"><ele>179.25</ele></trkpt>
<trkpt lon="8.723553" lat="50.001028"><ele>177.5</ele></trkpt>
<trkpt lon="8.723041" lat="50.001194"><ele>174.5</ele></trkpt>
<trkpt lon="8.722781" lat="50.001312"><ele>173.25</ele></trkpt>
<trkpt lon="8.722027" lat="50.001834"><ele>169.5</ele></trkpt>
<trkpt lon="8.721982" lat="50.001865"><ele>169.5</ele></trkpt>
<trkpt lon="8.722320" lat="50.002050"><ele>171.0</ele></trkpt>
<trkpt lon="8.722449" lat="50.002197"><ele>171.25</ele></trkpt>
<trkpt lon="8.722497" lat="50.002337"><ele>171.25</ele></trkpt>
<trkpt lon="8.722506" lat="50.002463"><ele>171.0</ele></trkpt>
<trkpt lon="8.722486" lat="50.002697"><ele>170.25</ele></trkpt>
<trkpt lon="8.721892" lat="50.002631"><ele>167.75</ele></trkpt>
<trkpt lon="8.721836" lat="50.002624"><ele>167.5</ele></trkpt>
<trkpt lon="8.721209" lat="50.002553"><ele>165.25</ele></trkpt>
<trkpt lon="8.721118" lat="50.002538"><ele>164.75</ele></trkpt>
<trkpt lon="8.721021" lat="50.002493"><ele>164.5</ele></trkpt>
<trkpt lon="8.720994" lat="50.002509"><ele>164.25</ele></trkpt>
<trkpt lon="8.720960" lat="50.002518"><ele>164.25</ele></trkpt>
<trkpt lon="8.720888" lat="50.002517"><ele>163.75</ele></trkpt>
<trkpt lon="8.720853" lat="50.002586"><ele>163.75</ele></trkpt>
<trkpt lon="8.720782" lat="50.002704"><ele>163.25</ele></trkpt>
<trkpt lon="8.720554" lat="50.002937"><ele>162.25</ele></trkpt>
<trkpt lon="8.720469" lat="50.003004"><ele>162.0</ele></trkpt>
<trkpt lon="8.718899" lat="50.003724"><ele>160.0</ele></trkpt>
<trkpt lon="8.718254" lat="50.004051"><ele>159.25</ele></trkpt>
<trkpt lon="8.718123" lat="50.004087"><ele>159.0</ele></trkpt>
<trkpt lon="8.717543" lat="50.004244"><ele>159.0</ele></trkpt>
<trkpt lon="8.717181" lat="50.004357"><ele>159.0</ele></trkpt>
<trkpt lon="8.716729" lat="50.004515"><ele>158.0</ele></trkpt>
<trkpt lon="8.716463" lat="50.004600"><ele>157.5</ele></trkpt>
<trkpt lon="8.715713" lat="50.004799"><ele>156.25</ele></trkpt>
<trkpt lon="8.715490" lat="50.004843"><ele>156.0</ele></trkpt>
<trkpt lon="8.714977" lat="50.004918"><ele>155.25</ele></trkpt>
<trkpt lon="8.714539" lat="50.005012"><ele>154.25</ele></trkpt>
<trkpt lon="8.713784" lat="50.005136"><ele>152.5</ele></trkpt>
<trkpt lon="8.713582" lat="50.005177"><ele>152.5</ele></trkpt>
<trkpt lon="8.713316" lat="50.005086"><ele>153.0</ele></trkpt>
<trkpt lon="8.713067" lat="50.005001"><ele>153.25</ele></trkpt>
<trkpt lon="8.712848" lat="50.004896"><ele>153.75</ele></trkpt>
<trkpt lon="8.712781" lat="50.004859"><ele>154.0</ele></trkpt>
<trkpt lon="8.712667" lat="50.004765"><ele>154.25</ele></trkpt>
<trkpt lon="8.712563" lat="50.004683"><ele>154.5</ele></trkpt>
<trkpt lon="8.712154" lat="50.004321"><ele>156.25</ele></trkpt>
<trkpt lon="8.712066" lat="50.004245"><ele>156.5</ele></trkpt>
<trkpt lon="8.713422" lat="50.003599"><ele>158.5</ele></trkpt>
<trkpt lon="8.713452" lat="50.003572"><ele>158.5</ele></trkpt>
<trkpt lon="8.713592" lat="50.003347"><ele>158.5</ele></trkpt>
<trkpt lon="8.713620" lat="50.003326"><ele>158.5</ele></trkpt>
<trkpt lon="8.713956" lat="50.003142"><ele>158.5</ele></trkpt>
<trkpt lon="8.713468" lat="50.002781"><ele>159.5</ele></trkpt>
<trkpt lon="8.713293" lat="50.002684"><ele>159.75</ele></trkpt>
<trkpt lon="8.712770" lat="50.002929"><ele>159.5</ele></trkpt>
</trkseg>
</trk>
</gpx>

View file

@ -1,64 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- track-length = 736 filtered ascend = 0 plain-ascend = 0 cost=736 energy=.0kwh time=1m 53s -->
<gpx
xmlns="http://www.topografix.com/GPX/1/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"
creator="BRouter-1.6.3" version="1.1">
<trk>
<name>brouter_trekking_0</name>
<trkseg>
<trkpt lon="8.720895" lat="50.002517"></trkpt>
<trkpt lon="8.720888" lat="50.002517"></trkpt>
<trkpt lon="8.720832" lat="50.002494"></trkpt>
<trkpt lon="8.720814" lat="50.002476"></trkpt>
<trkpt lon="8.720802" lat="50.002433"></trkpt>
<trkpt lon="8.720810" lat="50.002412"></trkpt>
<trkpt lon="8.720682" lat="50.002377"></trkpt>
<trkpt lon="8.720553" lat="50.002342"></trkpt>
<trkpt lon="8.720339" lat="50.002251"></trkpt>
<trkpt lon="8.720068" lat="50.002110"></trkpt>
<trkpt lon="8.719973" lat="50.002051"></trkpt>
<trkpt lon="8.719838" lat="50.001948"></trkpt>
<trkpt lon="8.719759" lat="50.001864"></trkpt>
<trkpt lon="8.719712" lat="50.001780"></trkpt>
<trkpt lon="8.719678" lat="50.001789"></trkpt>
<trkpt lon="8.719641" lat="50.001790"></trkpt>
<trkpt lon="8.719600" lat="50.001783"></trkpt>
<trkpt lon="8.719564" lat="50.001768"></trkpt>
<trkpt lon="8.719539" lat="50.001745"></trkpt>
<trkpt lon="8.719527" lat="50.001719"></trkpt>
<trkpt lon="8.719530" lat="50.001692"></trkpt>
<trkpt lon="8.719546" lat="50.001667"></trkpt>
<trkpt lon="8.719574" lat="50.001647"></trkpt>
<trkpt lon="8.719610" lat="50.001634"></trkpt>
<trkpt lon="8.719652" lat="50.001630"></trkpt>
<trkpt lon="8.719693" lat="50.001634"></trkpt>
<trkpt lon="8.719730" lat="50.001647"></trkpt>
<trkpt lon="8.719788" lat="50.001576"></trkpt>
<trkpt lon="8.719887" lat="50.001483"></trkpt>
<trkpt lon="8.719994" lat="50.001425"></trkpt>
<trkpt lon="8.720206" lat="50.001297"></trkpt>
<trkpt lon="8.720324" lat="50.001211"></trkpt>
<trkpt lon="8.720403" lat="50.001137"></trkpt>
<trkpt lon="8.720482" lat="50.001041"></trkpt>
<trkpt lon="8.720539" lat="50.000948"></trkpt>
<trkpt lon="8.720600" lat="50.000799"></trkpt>
<trkpt lon="8.720672" lat="50.000551"></trkpt>
<trkpt lon="8.720760" lat="50.000387"></trkpt>
<trkpt lon="8.720921" lat="50.000228"></trkpt>
<trkpt lon="8.721129" lat="50.000074"></trkpt>
<trkpt lon="8.721391" lat="49.999871"></trkpt>
<trkpt lon="8.721602" lat="49.999714"></trkpt>
<trkpt lon="8.722176" lat="49.999309"></trkpt>
<trkpt lon="8.722416" lat="49.999100"></trkpt>
<trkpt lon="8.722474" lat="49.999038"></trkpt>
<trkpt lon="8.722547" lat="49.998975"></trkpt>
<trkpt lon="8.722669" lat="49.998853"></trkpt>
<trkpt lon="8.723033" lat="49.998411"></trkpt>
<trkpt lon="8.723136" lat="49.998266"></trkpt>
<trkpt lon="8.723659" lat="49.997526"></trkpt>
<trkpt lon="8.723669" lat="49.997514"></trkpt>
</trkseg>
</trk>
</gpx>

1
brouter-expressions/.gitignore vendored Normal file
View file

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

View file

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

View file

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

View file

@ -1,406 +1,300 @@
package btools.expressions;
import java.util.StringTokenizer;
final class BExpression {
private static final int OR_EXP = 10;
private static final int AND_EXP = 11;
private static final int NOT_EXP = 12;
private static final int ADD_EXP = 20;
private static final int MULTIPLY_EXP = 21;
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 = 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;
private static final int LOOKUP_EXP = 32;
private static final int NUMBER_EXP = 33;
private static final int VARIABLE_EXP = 34;
private static final int FOREIGN_VARIABLE_EXP = 35;
private static final int VARIABLE_GET_EXP = 36;
private int typ;
private BExpression op1;
private BExpression op2;
private BExpression op3;
private float numberValue;
private int variableIdx;
private int lookupNameIdx = -1;
private int[] lookupValueIdxArray;
private boolean doNotChange;
// Parse the expression and all subexpression
public static BExpression parse(BExpressionContext ctx, int level) throws Exception {
return parse(ctx, level, null);
}
private static BExpression parse(BExpressionContext ctx, int level, String optionalToken) throws Exception {
BExpression e = parseRaw(ctx, level, optionalToken);
if (e == null) {
return null;
}
if (ASSIGN_EXP == e.typ) {
// manage assined an injected values
BExpression assignedBefore = ctx.lastAssignedExpression.get(e.variableIdx);
if (assignedBefore != null && assignedBefore.doNotChange) {
e.op1 = assignedBefore; // was injected as key-value
e.op1.doNotChange = false; // protect just once, can be changed in second assignement
}
ctx.lastAssignedExpression.set(e.variableIdx, e.op1);
}
else if (!ctx.skipConstantExpressionOptimizations) {
// try to simplify the expression
if (VARIABLE_EXP == e.typ) {
BExpression ae = ctx.lastAssignedExpression.get(e.variableIdx);
if (ae != null && ae.typ == NUMBER_EXP) {
e = ae;
}
} else {
BExpression eCollapsed = e.tryCollapse();
if (e != eCollapsed) {
e = eCollapsed; // allow breakpoint..
}
BExpression eEvaluated = e.tryEvaluateConstant();
if (e != eEvaluated) {
e = eEvaluated; // allow breakpoint..
}
}
}
if (level == 0) {
// mark the used lookups after the
// expression is collapsed to not mark
// lookups as used that appear in the profile
// but are de-activated by constant expressions
int nodeCount = e.markLookupIdxUsed(ctx);
ctx.expressionNodeCount += nodeCount;
}
return e;
}
private int markLookupIdxUsed(BExpressionContext ctx) {
int nodeCount = 1;
if (lookupNameIdx >= 0) {
ctx.markLookupIdxUsed(lookupNameIdx);
}
if (op1 != null) {
nodeCount += op1.markLookupIdxUsed(ctx);
}
if (op2 != null) {
nodeCount += op2.markLookupIdxUsed(ctx);
}
if (op3 != null) {
nodeCount += op3.markLookupIdxUsed(ctx);
}
return nodeCount;
}
private static BExpression parseRaw(BExpressionContext ctx, int level, String optionalToken) throws Exception {
boolean brackets = false;
String operator = ctx.parseToken();
if (optionalToken != null && optionalToken.equals(operator)) {
operator = ctx.parseToken();
}
if ("(".equals(operator)) {
brackets = true;
operator = ctx.parseToken();
}
if (operator == null) {
if (level == 0) return null;
else throw new IllegalArgumentException("unexpected end of file");
}
if (level == 0) {
if (!"assign".equals(operator)) {
throw new IllegalArgumentException("operator " + operator + " is invalid on toplevel (only 'assign' allowed)");
}
}
BExpression exp = new BExpression();
int nops = 3;
boolean ifThenElse = false;
if ("switch".equals(operator)) {
exp.typ = SWITCH_EXP;
} else if ("if".equals(operator)) {
exp.typ = SWITCH_EXP;
ifThenElse = true;
} else {
nops = 2; // check binary expressions
if ("or".equals(operator)) {
exp.typ = OR_EXP;
} else if ("and".equals(operator)) {
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)) {
exp.typ = MAX_EXP;
} else if ("min".equals(operator)) {
exp.typ = MIN_EXP;
} else if ("equal".equals(operator)) {
exp.typ = EQUAL_EXP;
} else if ("greater".equals(operator)) {
exp.typ = GREATER_EXP;
} else if ("sub".equals(operator)) {
exp.typ = SUB_EXP;
} else if ("lesser".equals(operator)) {
exp.typ = LESSER_EXP;
} else if ("xor".equals(operator)) {
exp.typ = XOR_EXP;
} else {
nops = 1; // check unary expressions
if ("assign".equals(operator)) {
if (level > 0) throw new IllegalArgumentException("assign operator within expression");
exp.typ = ASSIGN_EXP;
String variable = ctx.parseToken();
if (variable == null) throw new IllegalArgumentException("unexpected end of file");
if (variable.indexOf('=') >= 0)
throw new IllegalArgumentException("variable name cannot contain '=': " + variable);
if (variable.indexOf(':') >= 0)
throw new IllegalArgumentException("cannot assign context-prefixed variable: " + variable);
exp.variableIdx = ctx.getVariableIdx(variable, true);
if (exp.variableIdx < ctx.getMinWriteIdx())
throw new IllegalArgumentException("cannot assign to readonly variable " + variable);
} else if ("not".equals(operator)) {
exp.typ = NOT_EXP;
} else {
nops = 0; // check elemantary expressions
int idx = operator.indexOf('=');
if (idx >= 0) {
exp.typ = LOOKUP_EXP;
String name = operator.substring(0, idx);
String values = operator.substring(idx + 1);
exp.lookupNameIdx = ctx.getLookupNameIdx(name);
if (exp.lookupNameIdx < 0) {
throw new IllegalArgumentException("unknown lookup name: " + name);
}
StringTokenizer tk = new StringTokenizer(values, "|");
int nt = tk.countTokens();
int nt2 = nt == 0 ? 1 : nt;
exp.lookupValueIdxArray = new int[nt2];
for (int ti = 0; ti < nt2; ti++) {
String value = ti < nt ? tk.nextToken() : "";
exp.lookupValueIdxArray[ti] = ctx.getLookupValueIdx(exp.lookupNameIdx, value);
if (exp.lookupValueIdxArray[ti] < 0) {
throw new IllegalArgumentException("unknown lookup value: " + value);
}
}
} else if ((idx = operator.indexOf(':')) >= 0) {
/*
use of variable values
assign no_height
switch and not maxheight=
lesser v:maxheight my_height true
false
*/
if (operator.startsWith("v:")) {
String name = operator.substring(2);
exp.typ = VARIABLE_GET_EXP;
exp.lookupNameIdx = ctx.getLookupNameIdx(name);
} else {
String context = operator.substring(0, idx);
String varname = operator.substring(idx + 1);
exp.typ = FOREIGN_VARIABLE_EXP;
exp.variableIdx = ctx.getForeignVariableIdx(context, varname);
}
} else if ((idx = ctx.getVariableIdx(operator, false)) >= 0) {
exp.typ = VARIABLE_EXP;
exp.variableIdx = idx;
} else if ("true".equals(operator)) {
exp.numberValue = 1.f;
exp.typ = NUMBER_EXP;
} else if ("false".equals(operator)) {
exp.numberValue = 0.f;
exp.typ = NUMBER_EXP;
} else {
try {
exp.numberValue = Float.parseFloat(operator);
exp.typ = NUMBER_EXP;
} catch (NumberFormatException nfe) {
throw new IllegalArgumentException("unknown expression: " + operator);
}
}
}
}
}
// parse operands
if (nops > 0) {
exp.op1 = parse(ctx, level + 1, exp.typ == ASSIGN_EXP ? "=" : null);
}
if (nops > 1) {
if (ifThenElse) checkExpectedToken(ctx, "then");
exp.op2 = parse(ctx, level + 1, null);
}
if (nops > 2) {
if (ifThenElse) checkExpectedToken(ctx, "else");
exp.op3 = parse(ctx, level + 1, null);
}
if (brackets) {
checkExpectedToken(ctx, ")");
}
return exp;
}
private static void checkExpectedToken(BExpressionContext ctx, String expected) throws Exception {
String token = ctx.parseToken();
if (!expected.equals(token)) {
throw new IllegalArgumentException("unexpected token: " + token + ", expected: " + expected);
}
}
// Evaluate the expression
public float evaluate(BExpressionContext ctx) {
switch (typ) {
case OR_EXP:
return op1.evaluate(ctx) != 0.f ? 1.f : (op2.evaluate(ctx) != 0.f ? 1.f : 0.f);
case XOR_EXP:
return ((op1.evaluate(ctx) != 0.f) ^ (op2.evaluate(ctx) != 0.f) ? 1.f : 0.f);
case AND_EXP:
return op1.evaluate(ctx) != 0.f ? (op2.evaluate(ctx) != 0.f ? 1.f : 0.f) : 0.f;
case ADD_EXP:
return op1.evaluate(ctx) + op2.evaluate(ctx);
case SUB_EXP:
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:
return min(op1.evaluate(ctx), op2.evaluate(ctx));
case EQUAL_EXP:
return op1.evaluate(ctx) == op2.evaluate(ctx) ? 1.f : 0.f;
case GREATER_EXP:
return op1.evaluate(ctx) > op2.evaluate(ctx) ? 1.f : 0.f;
case LESSER_EXP:
return op1.evaluate(ctx) < op2.evaluate(ctx) ? 1.f : 0.f;
case SWITCH_EXP:
return op1.evaluate(ctx) != 0.f ? op2.evaluate(ctx) : op3.evaluate(ctx);
case ASSIGN_EXP:
return ctx.assign(variableIdx, op1.evaluate(ctx));
case LOOKUP_EXP:
return ctx.getLookupMatch(lookupNameIdx, lookupValueIdxArray);
case NUMBER_EXP:
return numberValue;
case VARIABLE_EXP:
return ctx.getVariableValue(variableIdx);
case FOREIGN_VARIABLE_EXP:
return ctx.getForeignVariableValue(variableIdx);
case VARIABLE_GET_EXP:
return ctx.getLookupValue(lookupNameIdx);
case NOT_EXP:
return op1.evaluate(ctx) == 0.f ? 1.f : 0.f;
default:
throw new IllegalArgumentException("unknown op-code: " + typ);
}
}
// Try to collapse the expression
// if logically possible
private BExpression tryCollapse() {
switch (typ) {
case OR_EXP:
return NUMBER_EXP == op1.typ ?
(op1.numberValue != 0.f ? op1 : op2)
: (NUMBER_EXP == op2.typ ?
(op2.numberValue != 0.f ? op2 : op1)
: this);
case AND_EXP:
return NUMBER_EXP == op1.typ ?
(op1.numberValue == 0.f ? op1 : op2)
: (NUMBER_EXP == op2.typ ?
(op2.numberValue == 0.f ? op2 : op1)
: this);
case ADD_EXP:
return NUMBER_EXP == op1.typ ?
(op1.numberValue == 0.f ? op2 : this)
: (NUMBER_EXP == op2.typ ?
(op2.numberValue == 0.f ? op1 : this)
: this);
case SWITCH_EXP:
return NUMBER_EXP == op1.typ ?
(op1.numberValue == 0.f ? op3 : op2) : this;
default:
return this;
}
}
// Try to evaluate the expression
// if all operands are constant
private BExpression tryEvaluateConstant() {
if (op1 != null && NUMBER_EXP == op1.typ
&& (op2 == null || NUMBER_EXP == op2.typ)
&& (op3 == null || NUMBER_EXP == op3.typ)) {
BExpression exp = new BExpression();
exp.typ = NUMBER_EXP;
exp.numberValue = evaluate(null);
return exp;
}
return this;
}
private float max(float v1, float v2) {
return v1 > v2 ? v1 : v2;
}
private float min(float v1, float v2) {
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) {
return "" + numberValue;
}
if (typ == VARIABLE_EXP) {
return "vidx=" + variableIdx;
}
StringBuilder sb = new StringBuilder("typ=" + typ + " ops=(");
addOp(sb, op1);
addOp(sb, op2);
addOp(sb, op3);
sb.append(')');
return sb.toString();
}
private void addOp(StringBuilder sb, BExpression e) {
if (e != null) {
sb.append('[').append(e.toString()).append(']');
}
}
static BExpression createAssignExpressionFromKeyValue(BExpressionContext ctx, String key, String value) {
BExpression e = new BExpression();
e.typ = ASSIGN_EXP;
e.variableIdx = ctx.getVariableIdx(key, true);
e.op1 = new BExpression();
e.op1.typ = NUMBER_EXP;
e.op1.numberValue = Float.parseFloat(value);
e.op1.doNotChange = true;
ctx.lastAssignedExpression.set(e.variableIdx, e.op1);
return e;
}
}
package btools.expressions;
import java.util.StringTokenizer;
final class BExpression
{
private static final int OR_EXP = 10;
private static final int AND_EXP = 11;
private static final int NOT_EXP = 12;
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 SUB_EXP = 26;
private static final int LESSER_EXP = 27;
private static final int XOR_EXP = 28;
private static final int SWITCH_EXP = 30;
private static final int ASSIGN_EXP = 31;
private static final int LOOKUP_EXP = 32;
private static final int NUMBER_EXP = 33;
private static final int VARIABLE_EXP = 34;
private static final int FOREIGN_VARIABLE_EXP = 35;
private static final int VARIABLE_GET_EXP = 36;
private int typ;
private BExpression op1;
private BExpression op2;
private BExpression op3;
private float numberValue;
private int variableIdx;
private int lookupNameIdx;
private int[] lookupValueIdxArray;
// Parse the expression and all subexpression
public static BExpression parse( BExpressionContext ctx, int level ) throws Exception
{
return parse( ctx, level, null );
}
private static BExpression parse( BExpressionContext ctx, int level, String optionalToken ) throws Exception
{
boolean brackets = false;
String operator = ctx.parseToken();
if ( optionalToken != null && optionalToken.equals( operator ) )
{
operator = ctx.parseToken();
}
if ( "(".equals( operator ) )
{
brackets = true;
operator = ctx.parseToken();
}
if ( operator == null )
{
if ( level == 0 ) return null;
else throw new IllegalArgumentException( "unexpected end of file" );
}
if ( level == 0 )
{
if ( !"assign".equals( operator ) )
{
throw new IllegalArgumentException( "operator " + operator + " is invalid on toplevel (only 'assign' allowed)" );
}
}
BExpression exp = new BExpression();
int nops = 3;
boolean ifThenElse = false;
if ( "switch".equals( operator ) )
{
exp.typ = SWITCH_EXP;
}
else if ( "if".equals( operator ) )
{
exp.typ = SWITCH_EXP;
ifThenElse = true;
}
else
{
nops = 2; // check binary expressions
if ( "or".equals( operator ) )
{
exp.typ = OR_EXP;
}
else if ( "and".equals( operator ) )
{
exp.typ = AND_EXP;
}
else if ( "multiply".equals( operator ) )
{
exp.typ = MULTIPLY_EXP;
}
else if ( "add".equals( operator ) )
{
exp.typ = ADD_EXP;
}
else if ( "max".equals( operator ) )
{
exp.typ = MAX_EXP;
}
else if ( "min".equals( operator ) )
{
exp.typ = MIN_EXP;
}
else if ( "equal".equals( operator ) )
{
exp.typ = EQUAL_EXP;
}
else if ( "greater".equals( operator ) )
{
exp.typ = GREATER_EXP;
}
else if ( "sub".equals( operator ) )
{
exp.typ = SUB_EXP;
}
else if ( "lesser".equals( operator ) )
{
exp.typ = LESSER_EXP;
}
else if ( "xor".equals( operator ) )
{
exp.typ = XOR_EXP;
}
else
{
nops = 1; // check unary expressions
if ( "assign".equals( operator ) )
{
if ( level > 0 ) throw new IllegalArgumentException( "assign operator within expression" );
exp.typ = ASSIGN_EXP;
String variable = ctx.parseToken();
if ( variable == null ) throw new IllegalArgumentException( "unexpected end of file" );
if ( variable.indexOf( '=' ) >= 0 ) throw new IllegalArgumentException( "variable name cannot contain '=': " + variable );
if ( variable.indexOf( ':' ) >= 0 ) throw new IllegalArgumentException( "cannot assign context-prefixed variable: " + variable );
exp.variableIdx = ctx.getVariableIdx( variable, true );
if ( exp.variableIdx < ctx.getMinWriteIdx() ) throw new IllegalArgumentException( "cannot assign to readonly variable " + variable );
}
else if ( "not".equals( operator ) )
{
exp.typ = NOT_EXP;
}
else
{
nops = 0; // check elemantary expressions
int idx = operator.indexOf( '=' );
if ( idx >= 0 )
{
exp.typ = LOOKUP_EXP;
String name = operator.substring( 0, idx );
String values = operator.substring( idx+1 );
exp.lookupNameIdx = ctx.getLookupNameIdx( name );
if ( exp.lookupNameIdx < 0 )
{
throw new IllegalArgumentException( "unknown lookup name: " + name );
}
ctx.markLookupIdxUsed( exp.lookupNameIdx );
StringTokenizer tk = new StringTokenizer( values, "|" );
int nt = tk.countTokens();
int nt2 = nt == 0 ? 1 : nt;
exp.lookupValueIdxArray = new int[nt2];
for( int ti=0; ti<nt2; ti++ )
{
String value = ti < nt ? tk.nextToken() : "";
exp.lookupValueIdxArray[ti] = ctx.getLookupValueIdx( exp.lookupNameIdx, value );
if ( exp.lookupValueIdxArray[ti] < 0 )
{
throw new IllegalArgumentException( "unknown lookup value: " + value );
}
}
}
else if ( ( idx = operator.indexOf( ':' ) ) >= 0 )
{
/*
use of variable values
assign no_height
switch and not maxheight=
lesser v:maxheight my_height true
false
*/
if (operator.startsWith("v:")) {
String name = operator.substring(2);
exp.typ = VARIABLE_GET_EXP;
exp.lookupNameIdx = ctx.getLookupNameIdx( name );
} else {
String context = operator.substring( 0, idx );
String varname = operator.substring( idx+1 );
exp.typ = FOREIGN_VARIABLE_EXP;
exp.variableIdx = ctx.getForeignVariableIdx( context, varname );
}
}
else if ( (idx = ctx.getVariableIdx( operator, false )) >= 0 )
{
exp.typ = VARIABLE_EXP;
exp.variableIdx = idx;
}
else if ( "true".equals( operator ) )
{
exp.numberValue = 1.f;
exp.typ = NUMBER_EXP;
}
else if ( "false".equals( operator ) )
{
exp.numberValue = 0.f;
exp.typ = NUMBER_EXP;
}
else
{
try
{
exp.numberValue = Float.parseFloat( operator );
exp.typ = NUMBER_EXP;
}
catch( NumberFormatException nfe )
{
throw new IllegalArgumentException( "unknown expression: " + operator );
}
}
}
}
}
// parse operands
if ( nops > 0 )
{
exp.op1 = BExpression.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 );
}
if ( nops > 2 )
{
if ( ifThenElse ) checkExpectedToken( ctx, "else" );
exp.op3 = BExpression.parse( ctx, level+1, null );
}
if ( brackets )
{
checkExpectedToken( ctx, ")" );
}
return exp;
}
private static void checkExpectedToken( BExpressionContext ctx, String expected ) throws Exception
{
String token = ctx.parseToken();
if ( ! expected.equals( token ) )
{
throw new IllegalArgumentException( "unexpected token: " + token + ", expected: " + expected );
}
}
// Evaluate the expression
public float evaluate( BExpressionContext ctx )
{
switch( typ )
{
case OR_EXP: return op1.evaluate(ctx) != 0.f ? 1.f : ( op2.evaluate(ctx) != 0.f ? 1.f : 0.f );
case XOR_EXP: return ( (op1.evaluate(ctx) != 0.f) ^ ( op2.evaluate(ctx) != 0.f ) ? 1.f : 0.f );
case AND_EXP: return op1.evaluate(ctx) != 0.f ? ( op2.evaluate(ctx) != 0.f ? 1.f : 0.f ) : 0.f;
case ADD_EXP: return op1.evaluate(ctx) + op2.evaluate(ctx);
case SUB_EXP: return op1.evaluate(ctx) - op2.evaluate(ctx);
case MULTIPLY_EXP: return op1.evaluate(ctx) * op2.evaluate(ctx);
case MAX_EXP: return max( op1.evaluate(ctx), op2.evaluate(ctx) );
case MIN_EXP: return min( op1.evaluate(ctx), op2.evaluate(ctx) );
case EQUAL_EXP: return op1.evaluate(ctx) == op2.evaluate(ctx) ? 1.f : 0.f;
case GREATER_EXP: return op1.evaluate(ctx) > op2.evaluate(ctx) ? 1.f : 0.f;
case LESSER_EXP: return op1.evaluate(ctx) < op2.evaluate(ctx) ? 1.f : 0.f;
case SWITCH_EXP: return op1.evaluate(ctx) != 0.f ? op2.evaluate(ctx) : op3.evaluate(ctx);
case ASSIGN_EXP: return ctx.assign( variableIdx, op1.evaluate(ctx) );
case LOOKUP_EXP: return ctx.getLookupMatch( lookupNameIdx, lookupValueIdxArray );
case NUMBER_EXP: return numberValue;
case VARIABLE_EXP: return ctx.getVariableValue( variableIdx );
case FOREIGN_VARIABLE_EXP: return ctx.getForeignVariableValue( variableIdx );
case VARIABLE_GET_EXP: return ctx.getLookupValue(lookupNameIdx);
case NOT_EXP: return op1.evaluate(ctx) == 0.f ? 1.f : 0.f;
default: throw new IllegalArgumentException( "unknown op-code: " + typ );
}
}
private float max( float v1, float v2 )
{
return v1 > v2 ? v1 : v2;
}
private float min( float v1, float v2 )
{
return v1 < v2 ? v1 : v2;
}
}

View file

@ -7,29 +7,32 @@
package btools.expressions;
public final class BExpressionContextNode extends BExpressionContext {
private static String[] buildInVariables =
{"initialcost"};
protected String[] getBuildInVariableNames() {
public final class BExpressionContextNode extends BExpressionContext
{
private static String[] buildInVariables =
{ "initialcost" };
protected String[] getBuildInVariableNames()
{
return buildInVariables;
}
public float getInitialcost() {
return getBuildInVariable(0);
}
public float getInitialcost() { return getBuildInVariable(0); }
public BExpressionContextNode(BExpressionMetaData meta) {
super("node", meta);
public BExpressionContextNode( BExpressionMetaData meta )
{
super( "node", meta );
}
/**
* Create an Expression-Context for way context
*
* @param hashSize size of hashmap for result caching
* @param hashSize size of hashmap for result caching
*/
public BExpressionContextNode(int hashSize, BExpressionMetaData meta) {
super("node", hashSize, meta);
public BExpressionContextNode( int hashSize, BExpressionMetaData meta )
{
super( "node", hashSize, meta );
}
}

View file

@ -8,125 +8,66 @@ package btools.expressions;
import btools.codec.TagValueValidator;
public final class BExpressionContextWay extends BExpressionContext implements TagValueValidator {
public final class BExpressionContextWay extends BExpressionContext implements TagValueValidator
{
private boolean decodeForbidden = true;
private static String[] buildInVariables =
{"costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "classifiermask", "maxspeed", "uphillcost", "downhillcost", "uphillcutoff", "downhillcutoff", "uphillmaxslope", "downhillmaxslope", "uphillmaxslopecost", "downhillmaxslopecost"};
protected String[] getBuildInVariableNames() {
{ "costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "classifiermask", "maxspeed" };
protected String[] getBuildInVariableNames()
{
return buildInVariables;
}
public float getCostfactor() {
return getBuildInVariable(0);
}
public float getCostfactor() { return getBuildInVariable(0); }
public float getTurncost() { return getBuildInVariable(1); }
public float getUphillCostfactor() { return getBuildInVariable(2); }
public float getDownhillCostfactor() { return getBuildInVariable(3); }
public float getInitialcost() { return getBuildInVariable(4); }
public float getNodeAccessGranted() { return getBuildInVariable(5); }
public float getInitialClassifier() { return getBuildInVariable(6); }
public float getTrafficSourceDensity() { return getBuildInVariable(7); }
public float getIsTrafficBackbone() { return getBuildInVariable(8); }
public float getPriorityClassifier() { return getBuildInVariable(9); }
public float getClassifierMask() { return getBuildInVariable(10); }
public float getMaxspeed() { return getBuildInVariable(11); }
public float getTurncost() {
return getBuildInVariable(1);
}
public float getUphillCostfactor() {
return getBuildInVariable(2);
}
public float getDownhillCostfactor() {
return getBuildInVariable(3);
}
public float getInitialcost() {
return getBuildInVariable(4);
}
public float getNodeAccessGranted() {
return getBuildInVariable(5);
}
public float getInitialClassifier() {
return getBuildInVariable(6);
}
public float getTrafficSourceDensity() {
return getBuildInVariable(7);
}
public float getIsTrafficBackbone() {
return getBuildInVariable(8);
}
public float getPriorityClassifier() {
return getBuildInVariable(9);
}
public float getClassifierMask() {
return getBuildInVariable(10);
}
public float getMaxspeed() {
return getBuildInVariable(11);
}
public float getUphillcost() {
return getBuildInVariable(12);
}
public float getDownhillcost() {
return getBuildInVariable(13);
}
public float getUphillcutoff() {
return getBuildInVariable(14);
}
public float getDownhillcutoff() {
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);
public BExpressionContextWay( BExpressionMetaData meta )
{
super( "way", meta );
}
/**
* Create an Expression-Context for way context
*
* @param hashSize size of hashmap for result caching
* @param hashSize size of hashmap for result caching
*/
public BExpressionContextWay(int hashSize, BExpressionMetaData meta) {
super("way", hashSize, meta);
public BExpressionContextWay( int hashSize, BExpressionMetaData meta )
{
super( "way", hashSize, meta );
}
@Override
public int accessType(byte[] description) {
evaluate(false, description);
public int accessType( byte[] description )
{
evaluate( false, description );
float minCostFactor = getCostfactor();
if (minCostFactor >= 9999.f) {
if ( minCostFactor >= 9999.f )
{
setInverseVars();
float reverseCostFactor = getCostfactor();
if (reverseCostFactor < minCostFactor) {
if ( reverseCostFactor < minCostFactor )
{
minCostFactor = reverseCostFactor;
}
}
return minCostFactor < 9999.f ? 2 : decodeForbidden ? (minCostFactor < 10000.f ? 1 : 0) : 0;
}
@Override
public void setDecodeForbidden(boolean decodeForbidden) {
this.decodeForbidden = decodeForbidden;
public void setDecodeForbidden( boolean decodeForbidden )
{
this.decodeForbidden= decodeForbidden;
}
}

View file

@ -1,57 +1,66 @@
/**
* A lookup value with optional aliases
* <p>
* toString just gives the primary value,
* equals just compares against primary value
* matches() also compares aliases
*
* @author ab
*/
package btools.expressions;
import java.util.ArrayList;
import java.util.List;
final class BExpressionLookupValue {
String value;
List<String> aliases;
@Override
public String toString() {
return value;
}
public BExpressionLookupValue(String value) {
this.value = value;
}
public void addAlias(String alias) {
if (aliases == null) aliases = new ArrayList<>();
aliases.add(alias);
}
@Override
public boolean equals(Object o) {
if (o instanceof String) {
String v = (String) o;
return value.equals(v);
}
if (o instanceof BExpressionLookupValue) {
BExpressionLookupValue v = (BExpressionLookupValue) o;
return value.equals(v.value);
}
return false;
}
public boolean matches(String s) {
if (value.equals(s)) return true;
if (aliases != null) {
for (String alias : aliases) {
if (alias.equals(s)) return true;
}
}
return false;
}
}
/**
* A lookup value with optional aliases
*
* toString just gives the primary value,
* equals just compares against primary value
* matches() also compares aliases
*
* @author ab
*/
package btools.expressions;
import java.util.ArrayList;
final class BExpressionLookupValue
{
String value;
ArrayList<String> aliases;
@Override
public String toString()
{
return value;
}
public BExpressionLookupValue( String value )
{
this.value = value;
}
public void addAlias( String alias )
{
if ( aliases == null ) aliases = new ArrayList<String>();
aliases.add( alias );
}
@Override
public boolean equals( Object o )
{
if ( o instanceof String )
{
String v = (String)o;
return value.equals( v );
}
if ( o instanceof BExpressionLookupValue )
{
BExpressionLookupValue v = (BExpressionLookupValue)o;
return value.equals( v.value );
}
return false;
}
public boolean matches( String s )
{
if ( value.equals( s ) ) return true;
if ( aliases != null )
{
for( String alias : aliases )
{
if ( alias.equals( s ) ) return true;
}
}
return false;
}
}

View file

@ -9,67 +9,81 @@ package btools.expressions;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;
import btools.util.BitCoderContext;
import btools.util.Crc32;
public final class BExpressionMetaData {
private static final String CONTEXT_TAG = "---context:";
public final class BExpressionMetaData
{
private static final String CONTEXT_TAG = "---context:";
private static final String VERSION_TAG = "---lookupversion:";
private static final String MINOR_VERSION_TAG = "---minorversion:";
private static final String VARLENGTH_TAG = "---readvarlength";
private static final String MIN_APP_VERSION_TAG = "---minappversion:";
public short lookupVersion = -1;
public short lookupMinorVersion = -1;
public short minAppVersion = -1;
private Map<String, BExpressionContext> listeners = new HashMap<>();
public void registerListener(String context, BExpressionContext ctx) {
listeners.put(context, ctx);
private HashMap<String,BExpressionContext> listeners = new HashMap<String,BExpressionContext>();
public void registerListener( String context, BExpressionContext ctx )
{
listeners.put( context, ctx );
}
public void readMetaData(File lookupsFile) {
try {
BufferedReader br = new BufferedReader(new FileReader(lookupsFile));
BExpressionContext ctx = null;
for (; ; ) {
String line = br.readLine();
if (line == null) break;
line = line.trim();
if (line.length() == 0 || line.startsWith("#")) continue;
if (line.startsWith(CONTEXT_TAG)) {
ctx = listeners.get(line.substring(CONTEXT_TAG.length()));
continue;
}
if (line.startsWith(VERSION_TAG)) {
lookupVersion = Short.parseShort(line.substring(VERSION_TAG.length()));
continue;
}
if (line.startsWith(MINOR_VERSION_TAG)) {
lookupMinorVersion = Short.parseShort(line.substring(MINOR_VERSION_TAG.length()));
continue;
}
if (line.startsWith(MIN_APP_VERSION_TAG)) {
minAppVersion = Short.parseShort(line.substring(MIN_APP_VERSION_TAG.length()));
continue;
}
if (line.startsWith(VARLENGTH_TAG)) { // tag removed...
continue;
}
if (ctx != null) ctx.parseMetaLine(line);
public void readMetaData( File lookupsFile )
{
try
{
BufferedReader br = new BufferedReader( new FileReader( lookupsFile ) );
BExpressionContext ctx = null;
for(;;)
{
String line = br.readLine();
if ( line == null ) break;
line = line.trim();
if ( line.length() == 0 || line.startsWith( "#" ) ) continue;
if ( line.startsWith( CONTEXT_TAG ) )
{
ctx = listeners.get( line.substring( CONTEXT_TAG.length() ) );
continue;
}
br.close();
for (BExpressionContext c : listeners.values()) {
c.finishMetaParsing();
if ( line.startsWith( VERSION_TAG ) )
{
lookupVersion = Short.parseShort( line.substring( VERSION_TAG.length() ) );
continue;
}
} catch (Exception e) {
throw new RuntimeException(e);
if ( line.startsWith( MINOR_VERSION_TAG ) )
{
lookupMinorVersion = Short.parseShort( line.substring( MINOR_VERSION_TAG.length() ) );
continue;
}
if ( line.startsWith( VARLENGTH_TAG ) ) // tag removed...
{
continue;
}
if ( ctx != null ) ctx.parseMetaLine( line );
}
br.close();
for( BExpressionContext c : listeners.values() )
{
c.finishMetaParsing();
}
}
catch( Exception e )
{
throw new RuntimeException( e );
}
}
}

View file

@ -4,24 +4,29 @@ import java.util.Arrays;
import btools.util.LruMapNode;
public final class CacheNode extends LruMapNode {
public final class CacheNode extends LruMapNode
{
byte[] ab;
float[] vars;
@Override
public int hashCode() {
public int hashCode()
{
return hash;
}
@Override
public boolean equals(Object o) {
public boolean equals( Object o )
{
CacheNode n = (CacheNode) o;
if (hash != n.hash) {
if ( hash != n.hash )
{
return false;
}
if (ab == null) {
if ( ab == null )
{
return true; // hack: null = crc match only
}
return Arrays.equals(ab, n.ab);
return Arrays.equals( ab, n.ab );
}
}

View file

@ -1,49 +0,0 @@
package btools.expressions;
import java.io.File;
public class IntegrityCheckProfile {
public static void main(final String[] args) {
if (args.length != 2) {
System.out.println("usage: java IntegrityCheckProfile <lookup-file> <profile-folder>");
return;
}
IntegrityCheckProfile test = new IntegrityCheckProfile();
try {
File lookupFile = new File(args[0]);
File profileDir = new File(args[1]);
test.integrityTestProfiles(lookupFile, profileDir);
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
public void integrityTestProfiles(File lookupFile, File profileDir) {
File[] files = profileDir.listFiles();
if (files == null) {
System.err.println("no files " + profileDir);
return;
}
if (!lookupFile.exists()) {
System.err.println("no lookup file " + lookupFile);
return;
}
for (File f : files) {
if (f.getName().endsWith(".brf")) {
BExpressionMetaData meta = new BExpressionMetaData();
BExpressionContext expctxWay = new BExpressionContextWay(meta);
BExpressionContext expctxNode = new BExpressionContextNode(meta);
meta.readMetaData(lookupFile);
expctxNode.setForeignContext(expctxWay);
expctxWay.parseFile(f, "global");
expctxNode.parseFile(f, "global");
System.out.println("test " + meta.lookupVersion + "." + meta.lookupMinorVersion + " " + f);
}
}
}
}

View file

@ -3,50 +3,45 @@ package btools.expressions;
import java.io.File;
import java.util.Random;
public final class ProfileComparator {
public static void main(String[] args) {
if (args.length != 4) {
System.out.println("usage: java ProfileComparator <lookup-file> <profile1> <profile2> <nsamples>");
public final class ProfileComparator
{
public static void main( String[] args )
{
if ( args.length != 4 )
{
System.out.println( "usage: java ProfileComparator <lookup-file> <profile1> <profile2> <nsamples>" );
return;
}
File lookupFile = new File(args[0]);
File profile1File = new File(args[1]);
File profile2File = new File(args[2]);
int nsamples = Integer.parseInt(args[3]);
testContext(lookupFile, profile1File, profile2File, nsamples, false);
testContext(lookupFile, profile1File, profile2File, nsamples, true);
File lookupFile = new File( args[0] );
File profile1File = new File( args[1] );
File profile2File = new File( args[2] );
int nsamples = Integer.parseInt( args[3] );
testContext( lookupFile, profile1File, profile2File, nsamples, false );
testContext( lookupFile, profile1File, profile2File, nsamples, true );
}
private static void testContext(File lookupFile, File profile1File, File profile2File, int nsamples, boolean nodeContext) {
private static void testContext( File lookupFile, File profile1File, File profile2File, int nsamples, boolean nodeContext )
{
// read lookup.dat + profiles
BExpressionMetaData meta1 = new BExpressionMetaData();
BExpressionMetaData meta2 = new BExpressionMetaData();
BExpressionContext expctx1 = nodeContext ? new BExpressionContextNode(meta1) : new BExpressionContextWay(meta1);
BExpressionContext expctx2 = nodeContext ? new BExpressionContextNode(meta2) : new BExpressionContextWay(meta2);
// if same profiles, compare different optimization levels
if (profile1File.getName().equals(profile2File.getName())) {
expctx2.skipConstantExpressionOptimizations = true;
}
meta1.readMetaData(lookupFile);
meta2.readMetaData(lookupFile);
expctx1.parseFile(profile1File, "global");
System.out.println("usedTags1=" + expctx1.usedTagList());
expctx2.parseFile(profile2File, "global");
System.out.println("usedTags2=" + expctx2.usedTagList());
System.out.println("nodeContext=" + nodeContext + " nodeCount1=" + expctx1.expressionNodeCount + " nodeCount2=" + expctx2.expressionNodeCount);
BExpressionContext expctx1 = nodeContext ? new BExpressionContextNode( meta1 ) : new BExpressionContextWay( meta1 );
BExpressionContext expctx2 = nodeContext ? new BExpressionContextNode( meta2 ) : new BExpressionContextWay( meta2 );
meta1.readMetaData( lookupFile );
meta2.readMetaData( lookupFile );
expctx1.parseFile( profile1File, "global" );
expctx2.parseFile( profile2File, "global" );
Random rnd = new Random();
for (int i = 0; i < nsamples; i++) {
int[] data = expctx1.generateRandomValues(rnd);
expctx1.evaluate(data);
expctx2.evaluate(data);
expctx1.assertAllVariablesEqual(expctx2);
for( int i=0; i<nsamples; i++ )
{
int[] data = expctx1.generateRandomValues( rnd );
expctx1.evaluate( data );
expctx2.evaluate( data );
expctx1.assertAllVariablesEqual( expctx2 );
}
}
}
}

View file

@ -4,20 +4,24 @@ import java.util.Arrays;
import btools.util.LruMapNode;
public final class VarWrapper extends LruMapNode {
public final class VarWrapper extends LruMapNode
{
float[] vars;
@Override
public int hashCode() {
public int hashCode()
{
return hash;
}
@Override
public boolean equals(Object o) {
public boolean equals( Object o )
{
VarWrapper n = (VarWrapper) o;
if (hash != n.hash) {
if ( hash != n.hash )
{
return false;
}
return Arrays.equals(vars, n.vars);
return Arrays.equals( vars, n.vars );
}
}

View file

@ -1,50 +0,0 @@
package btools.expressions;
import org.junit.Assert;
import org.junit.Test;
import java.io.File;
import java.util.Random;
import java.util.Map;
import java.util.HashMap;
public class ConstantOptimizerTest {
@Test
public void compareOptimizerModesTest() {
File lookupFile = new File(getClass().getResource("/lookups_test.dat").getPath());
File profileFile = new File(getClass().getResource("/profile_test.brf").getPath());
BExpressionMetaData meta1 = new BExpressionMetaData();
BExpressionMetaData meta2 = new BExpressionMetaData();
BExpressionContext expctx1 = new BExpressionContextWay(meta1);
BExpressionContext expctx2 = new BExpressionContextWay(meta2);
expctx2.skipConstantExpressionOptimizations = true;
Map<String, String> keyValue = new HashMap<>();
keyValue.put("global_inject1", "5");
keyValue.put("global_inject2", "6");
keyValue.put("global_inject3", "7");
meta1.readMetaData(lookupFile);
meta2.readMetaData(lookupFile);
expctx1.parseFile(profileFile, "global", keyValue);
expctx2.parseFile(profileFile, "global", keyValue);
float d = 0.0001f;
Assert.assertEquals(5f, expctx1.getVariableValue("global_inject1", 0f), d);
Assert.assertEquals(9f, expctx1.getVariableValue("global_inject2", 0f), d); // should be modified in 2. assign!
Assert.assertEquals(7f, expctx1.getVariableValue("global_inject3", 0f), d);
Assert.assertEquals(3f, expctx1.getVariableValue("global_inject4", 3f), d); // un-assigned
Assert.assertTrue("expected far less exporessions nodes if optimized", expctx2.expressionNodeCount - expctx1.expressionNodeCount >= 311-144);
Random rnd = new Random(17464); // fixed seed for unit test...
for (int i = 0; i < 10000; i++) {
int[] data = expctx1.generateRandomValues(rnd);
expctx1.evaluate(data);
expctx2.evaluate(data);
expctx1.assertAllVariablesEqual(expctx2);
}
}
}

View file

@ -1,57 +1,60 @@
package btools.expressions;
import java.util.*;
import java.io.*;
import java.net.URL;
import org.junit.Assert;
import org.junit.Test;
import java.io.File;
import java.net.URL;
public class EncodeDecodeTest {
public class EncodeDecodeTest
{
@Test
public void encodeDecodeTest() {
URL testpurl = this.getClass().getResource("/dummy.txt");
public void encodeDecodeTest()
{
URL testpurl = this.getClass().getResource( "/dummy.txt" );
File workingDir = new File(testpurl.getFile()).getParentFile();
File profileDir = new File(workingDir, "/../../../../misc/profiles2");
File profileDir = new File( workingDir, "/../../../../misc/profiles2" );
//File lookupFile = new File( profileDir, "lookups.dat" );
// add a test lookup
URL testlookup = this.getClass().getResource("/lookups_test.dat");
File lookupFile = new File(testlookup.getPath());
// add a test lookup
URL testlookup = this.getClass().getResource( "/lookups_test.dat" );
File lookupFile = new File( testlookup.getPath() );
// read lookup.dat + trekking.brf
BExpressionMetaData meta = new BExpressionMetaData();
BExpressionContextWay expctxWay = new BExpressionContextWay(meta);
meta.readMetaData(lookupFile);
expctxWay.parseFile(new File(profileDir, "trekking.brf"), "global");
BExpressionContextWay expctxWay = new BExpressionContextWay( meta );
meta.readMetaData( lookupFile );
expctxWay.parseFile( new File( profileDir, "trekking.brf" ), "global" );
String[] tags = {
"highway=residential",
"oneway=yes",
"depth=1'6\"",
// "depth=6 feet",
"maxheight=5.1m",
"maxdraft=~3 m - 4 m",
"reversedirection=yes"
"highway=residential",
"oneway=yes",
"depth=1'6\"",
// "depth=6 feet",
"maxheight=5.1m",
"maxdraft=~3 mt",
"reversedirection=yes"
};
// encode the tags into 64 bit description word
int[] lookupData = expctxWay.createNewLookupData();
for (String arg : tags) {
int idx = arg.indexOf('=');
if (idx < 0)
throw new IllegalArgumentException("bad argument (should be <tag>=<value>): " + arg);
String key = arg.substring(0, idx);
String value = arg.substring(idx + 1);
expctxWay.addLookupValue(key, value, lookupData);
for( String arg: tags )
{
int idx = arg.indexOf( '=' );
if ( idx < 0 ) throw new IllegalArgumentException( "bad argument (should be <tag>=<value>): " + arg );
String key = arg.substring( 0, idx );
String value = arg.substring( idx+1 );
expctxWay.addLookupValue( key, value, lookupData );
}
byte[] description = expctxWay.encode(lookupData);
// calculate the cost factor from that description
expctxWay.evaluate(true, description); // true = "reversedirection=yes" (not encoded in description anymore)
expctxWay.evaluate( true, description ); // true = "reversedirection=yes" (not encoded in description anymore)
System.out.println("description: " + expctxWay.getKeyValueDescription(true, description));
System.out.println( "description: " + expctxWay.getKeyValueDescription(true, description) );
float costfactor = expctxWay.getCostfactor();
Assert.assertTrue("costfactor mismatch", Math.abs(costfactor - 5.15) < 0.00001);
Assert.assertTrue( "costfactor mismatch", Math.abs( costfactor - 5.15 ) < 0.00001 );
}
}

View file

@ -1,33 +0,0 @@
package btools.expressions;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
public class IntegrityCheckProfileTest {
@Test
public void integrityTestProfiles() throws IOException {
File workingDir = new File(".").getCanonicalFile();
File profileDir = new File(workingDir, "../misc/profiles2");
File[] files = profileDir.listFiles();
assertNotNull("Missing profiles", files);
for (File f : files) {
if (f.getName().endsWith(".brf")) {
BExpressionMetaData meta = new BExpressionMetaData();
BExpressionContext expctxWay = new BExpressionContextWay(meta);
BExpressionContext expctxNode = new BExpressionContextNode(meta);
meta.readMetaData(new File(profileDir, "lookups.dat"));
expctxNode.setForeignContext(expctxWay);
expctxWay.parseFile(f, "global");
expctxNode.parseFile(f, "global");
}
}
}
}

View file

@ -687,35 +687,6 @@ construction;0000000037 driveway
construction;0000000021 mini_roundabout
construction;0000000020 turning_loop
estimated_forest_class;0000000001 1
estimated_forest_class;0000000001 2
estimated_forest_class;0000000001 3
estimated_forest_class;0000000001 4
estimated_forest_class;0000000001 5
estimated_forest_class;0000000001 6
estimated_noise_class;0000000001 1
estimated_noise_class;0000000001 2
estimated_noise_class;0000000001 3
estimated_noise_class;0000000001 4
estimated_noise_class;0000000001 5
estimated_noise_class;0000000001 6
estimated_river_class;0000000001 1
estimated_river_class;0000000001 2
estimated_river_class;0000000001 3
estimated_river_class;0000000001 4
estimated_river_class;0000000001 5
estimated_river_class;0000000001 6
estimated_town_class;0000000001 1
estimated_town_class;0000000001 2
estimated_town_class;0000000001 3
estimated_town_class;0000000001 4
estimated_town_class;0000000001 5
estimated_town_class;0000000001 6
---context:node
highway;0001314954 bus_stop

View file

@ -1,88 +0,0 @@
---context:global # following code refers to global config
assign global_false = false
assign global_true = true
assign global_and = and false global_true
assign global_inject1 = 5
assign global_inject2 = 13
assign global_inject2 = add global_inject2 3
assign global_or = or ( or global_true global_false ) ( or global_false global_true )
assign global_and = and ( and global_true true ) false
---context:way # following code refers to way-tags
assign v = highway=primary
assign w = surface=asphalt
# test constant or/and
assign costfactor =
add multiply 1 or 1 1
add multiply 2 or 1 0
add multiply 4 or 0 1
add multiply 8 or 0 0
add multiply 16 and 1 1
add multiply 32 and 1 1
add multiply 64 and 1 1
multiply 128 and 1 1
# test variable or
assign turncost =
add multiply 1 or v 1
add multiply 2 or v 0
add multiply 4 or 0 v
add multiply 8 or 1 v
multiply 16 or v w
# test variable and
assign uphillcostfactor =
add multiply 1 and v 1
add multiply 2 and v 0
add multiply 4 and 0 v
add multiply 8 and 1 v
multiply 16 and v w
# test add
assign downhillcostfactor =
add multiply 1 add 1 1
add multiply 2 add 1 0
add multiply 4 add 0 1
add multiply 8 add 0 0
add multiply 16 add v 1
add multiply 32 add v 0
add multiply 64 add 1 v
multiply 128 add 0 v
# test max
assign initialcost =
add multiply 1 max 1 2
add multiply 2 max multiply 2 v 1
add multiply 4 max 1 multiply 2 v
multiply 8 max multiply 2 v v
# test switch
assign initialclassifier =
add multiply 1 switch 1 1 0
add multiply 2 switch 0 1 0
add multiply 4 switch 1 0 1
add multiply 8 switch 0 0 1
add multiply 16 switch v 1 1
add multiply 32 switch v 0 1
add multiply 64 switch v 1 0
add multiply 128 switch v 0 1
multiply 256 switch 1 v w
# test global calcs
assign priorityclassifier =
add multiply 1 global_false
add multiply 2 global_true
add multiply 4 global_and
add multiply 8 global_inject1
add multiply 16 global_inject2
add multiply 32 global_or
multiply 64 global_and
---context:node # following code refers to node tags
assign initialcost = 1

1
brouter-map-creator/.gitignore vendored Normal file
View file

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

View file

@ -1,11 +1,12 @@
plugins {
id 'brouter.application-conventions'
id 'java-library'
}
dependencies {
implementation project(':brouter-codec')
implementation project(':brouter-util')
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,258 +0,0 @@
package btools.mapcreator;
import com.google.protobuf.InvalidProtocolBufferException;
import org.openstreetmap.osmosis.osmbinary.Fileformat;
import org.openstreetmap.osmosis.osmbinary.Osmformat;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import btools.util.LongList;
/**
* Converts PBF block data into decoded entities ready to be passed into an Osmosis pipeline. This
* class is designed to be passed into a pool of worker threads to allow multi-threaded decoding.
* <p/>
*
* @author Brett Henderson
*/
public class BPbfBlobDecoder {
private String blobType;
private byte[] rawBlob;
private OsmParser parser;
/**
* Creates a new instance.
* <p/>
*
* @param blobType The type of blob.
* @param rawBlob The raw data of the blob.
* @param listener The listener for receiving decoding results.
*/
public BPbfBlobDecoder(String blobType, byte[] rawBlob, OsmParser parser) {
this.blobType = blobType;
this.rawBlob = rawBlob;
this.parser = parser;
}
public void process() throws Exception {
if ("OSMHeader".equals(blobType)) {
processOsmHeader(readBlobContent());
} else if ("OSMData".equals(blobType)) {
processOsmPrimitives(readBlobContent());
} else {
System.out.println("Skipping unrecognised blob type " + blobType);
}
}
private byte[] readBlobContent() throws IOException {
Fileformat.Blob blob = Fileformat.Blob.parseFrom(rawBlob);
byte[] blobData;
if (blob.hasRaw()) {
blobData = blob.getRaw().toByteArray();
} else if (blob.hasZlibData()) {
Inflater inflater = new Inflater();
inflater.setInput(blob.getZlibData().toByteArray());
blobData = new byte[blob.getRawSize()];
try {
inflater.inflate(blobData);
} catch (DataFormatException e) {
throw new RuntimeException("Unable to decompress PBF blob.", e);
}
if (!inflater.finished()) {
throw new RuntimeException("PBF blob contains incomplete compressed data.");
}
} else {
throw new RuntimeException("PBF blob uses unsupported compression, only raw or zlib may be used.");
}
return blobData;
}
private void processOsmHeader(byte[] data) throws InvalidProtocolBufferException {
Osmformat.HeaderBlock header = Osmformat.HeaderBlock.parseFrom(data);
// Build the list of active and unsupported features in the file.
List<String> supportedFeatures = Arrays.asList("OsmSchema-V0.6", "DenseNodes");
List<String> activeFeatures = new ArrayList<>();
List<String> unsupportedFeatures = new ArrayList<>();
for (String feature : header.getRequiredFeaturesList()) {
if (supportedFeatures.contains(feature)) {
activeFeatures.add(feature);
} else {
unsupportedFeatures.add(feature);
}
}
// We can't continue if there are any unsupported features. We wait
// until now so that we can display all unsupported features instead of
// just the first one we encounter.
if (unsupportedFeatures.size() > 0) {
throw new RuntimeException("PBF file contains unsupported features " + unsupportedFeatures);
}
}
private Map<String, String> buildTags(List<Integer> keys, List<Integer> values, BPbfFieldDecoder fieldDecoder) {
Iterator<Integer> keyIterator = keys.iterator();
Iterator<Integer> valueIterator = values.iterator();
if (keyIterator.hasNext()) {
Map<String, String> tags = new HashMap<>();
while (keyIterator.hasNext()) {
String key = fieldDecoder.decodeString(keyIterator.next());
String value = fieldDecoder.decodeString(valueIterator.next());
tags.put(key, value);
}
return tags;
}
return null;
}
private void processNodes(List<Osmformat.Node> nodes, BPbfFieldDecoder fieldDecoder) {
for (Osmformat.Node node : nodes) {
Map<String, String> tags = buildTags(node.getKeysList(), node.getValsList(), fieldDecoder);
parser.addNode(node.getId(), tags, fieldDecoder.decodeLatitude(node
.getLat()), fieldDecoder.decodeLatitude(node.getLon()));
}
}
private void processNodes(Osmformat.DenseNodes nodes, BPbfFieldDecoder fieldDecoder) {
List<Long> idList = nodes.getIdList();
List<Long> latList = nodes.getLatList();
List<Long> lonList = nodes.getLonList();
Iterator<Integer> keysValuesIterator = nodes.getKeysValsList().iterator();
long nodeId = 0;
long latitude = 0;
long longitude = 0;
for (int i = 0; i < idList.size(); i++) {
// Delta decode node fields.
nodeId += idList.get(i);
latitude += latList.get(i);
longitude += lonList.get(i);
// Build the tags. The key and value string indexes are sequential
// in the same PBF array. Each set of tags is delimited by an index
// with a value of 0.
Map<String, String> tags = null;
while (keysValuesIterator.hasNext()) {
int keyIndex = keysValuesIterator.next();
if (keyIndex == 0) {
break;
}
int valueIndex = keysValuesIterator.next();
if (tags == null) {
tags = new HashMap<>();
}
tags.put(fieldDecoder.decodeString(keyIndex), fieldDecoder.decodeString(valueIndex));
}
parser.addNode(nodeId, tags, ((double) latitude) / 10000000, ((double) longitude) / 10000000);
}
}
private void processWays(List<Osmformat.Way> ways, BPbfFieldDecoder fieldDecoder) {
for (Osmformat.Way way : ways) {
Map<String, String> tags = buildTags(way.getKeysList(), way.getValsList(), fieldDecoder);
// Build up the list of way nodes for the way. The node ids are
// delta encoded meaning that each id is stored as a delta against
// the previous one.
long nodeId = 0;
LongList wayNodes = new LongList(16);
for (long nodeIdOffset : way.getRefsList()) {
nodeId += nodeIdOffset;
wayNodes.add(nodeId);
}
parser.addWay(way.getId(), tags, wayNodes);
}
}
private LongList fromWid;
private LongList toWid;
private LongList viaNid;
private LongList addLong(LongList ll, long l) {
if (ll == null) {
ll = new LongList(1);
}
ll.add(l);
return ll;
}
private LongList buildRelationMembers(
List<Long> memberIds, List<Integer> memberRoles, List<Osmformat.Relation.MemberType> memberTypes,
BPbfFieldDecoder fieldDecoder) {
LongList wayIds = new LongList(16);
fromWid = toWid = viaNid = null;
Iterator<Long> memberIdIterator = memberIds.iterator();
Iterator<Integer> memberRoleIterator = memberRoles.iterator();
Iterator<Osmformat.Relation.MemberType> memberTypeIterator = memberTypes.iterator();
// Build up the list of relation members for the way. The member ids are
// delta encoded meaning that each id is stored as a delta against
// the previous one.
long refId = 0;
while (memberIdIterator.hasNext()) {
Osmformat.Relation.MemberType memberType = memberTypeIterator.next();
refId += memberIdIterator.next();
String role = fieldDecoder.decodeString(memberRoleIterator.next());
if (memberType == Osmformat.Relation.MemberType.WAY) { // currently just waymembers
wayIds.add(refId);
if ("from".equals(role)) fromWid = addLong(fromWid, refId);
if ("to".equals(role)) toWid = addLong(toWid, refId);
}
if (memberType == Osmformat.Relation.MemberType.NODE) { // currently just waymembers
if ("via".equals(role)) viaNid = addLong(viaNid, refId);
}
}
return wayIds;
}
private void processRelations(List<Osmformat.Relation> relations, BPbfFieldDecoder fieldDecoder) {
for (Osmformat.Relation relation : relations) {
Map<String, String> tags = buildTags(relation.getKeysList(), relation.getValsList(), fieldDecoder);
LongList wayIds = buildRelationMembers(relation.getMemidsList(), relation.getRolesSidList(),
relation.getTypesList(), fieldDecoder);
parser.addRelation(relation.getId(), tags, wayIds, fromWid, toWid, viaNid);
}
}
private void processOsmPrimitives(byte[] data) throws InvalidProtocolBufferException {
Osmformat.PrimitiveBlock block = Osmformat.PrimitiveBlock.parseFrom(data);
BPbfFieldDecoder fieldDecoder = new BPbfFieldDecoder(block);
for (Osmformat.PrimitiveGroup primitiveGroup : block.getPrimitivegroupList()) {
processNodes(primitiveGroup.getDense(), fieldDecoder);
processNodes(primitiveGroup.getNodesList(), fieldDecoder);
processWays(primitiveGroup.getWaysList(), fieldDecoder);
processRelations(primitiveGroup.getRelationsList(), fieldDecoder);
}
}
}

View file

@ -1,84 +0,0 @@
package btools.mapcreator;
import java.util.Date;
import org.openstreetmap.osmosis.osmbinary.Osmformat;
/**
* Manages decoding of the lower level PBF data structures.
* <p/>
*
* @author Brett Henderson
* <p/>
*/
public class BPbfFieldDecoder {
private static final double COORDINATE_SCALING_FACTOR = 0.000000001;
private String[] strings;
private int coordGranularity;
private long coordLatitudeOffset;
private long coordLongitudeOffset;
private int dateGranularity;
/**
* Creates a new instance.
* <p/>
*
* @param primitiveBlock The primitive block containing the fields to be decoded.
*/
public BPbfFieldDecoder(Osmformat.PrimitiveBlock primitiveBlock) {
this.coordGranularity = primitiveBlock.getGranularity();
this.coordLatitudeOffset = primitiveBlock.getLatOffset();
this.coordLongitudeOffset = primitiveBlock.getLonOffset();
this.dateGranularity = primitiveBlock.getDateGranularity();
Osmformat.StringTable stringTable = primitiveBlock.getStringtable();
strings = new String[stringTable.getSCount()];
for (int i = 0; i < strings.length; i++) {
strings[i] = stringTable.getS(i).toStringUtf8();
}
}
/**
* Decodes a raw latitude value into degrees.
* <p/>
*
* @param rawLatitude The PBF encoded value.
* @return The latitude in degrees.
*/
public double decodeLatitude(long rawLatitude) {
return COORDINATE_SCALING_FACTOR * (coordLatitudeOffset + (coordGranularity * rawLatitude));
}
/**
* Decodes a raw longitude value into degrees.
* <p/>
*
* @param rawLongitude The PBF encoded value.
* @return The longitude in degrees.
*/
public double decodeLongitude(long rawLongitude) {
return COORDINATE_SCALING_FACTOR * (coordLongitudeOffset + (coordGranularity * rawLongitude));
}
/**
* Decodes a raw timestamp value into a Date.
* <p/>
*
* @param rawTimestamp The PBF encoded timestamp.
* @return The timestamp as a Date.
*/
public Date decodeTimestamp(long rawTimestamp) {
return new Date(dateGranularity * rawTimestamp);
}
/**
* Decodes a raw string into a String.
* <p/>
*
* @param rawString The PBF encoding string.
* @return The string as a String.
*/
public String decodeString(int rawString) {
return strings[rawString];
}
}

View file

@ -0,0 +1,220 @@
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.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class ConvertLidarTile
{
public static int NROWS;
public static int NCOLS;
public static final short NODATA2 = -32767; // hgt-formats nodata
public static final short NODATA = Short.MIN_VALUE;
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.getName().endsWith( ".hgt" ) )
{
readHgtFromStream( zis, rowOffset, colOffset );
return;
}
}
}
finally
{
zis.close();
}
}
private static void readHgtFromStream( InputStream is, int rowOffset, int colOffset )
throws Exception
{
DataInputStream dis = new DataInputStream( new BufferedInputStream( is ) );
for ( int ir = 0; ir < 1201; ir++ )
{
int row = rowOffset + ir;
for ( int ic = 0; ic < 1201; ic++ )
{
int col = colOffset + ic;
int i1 = dis.read(); // msb first!
int i0 = dis.read();
if ( i0 == -1 || i1 == -1 )
throw new RuntimeException( "unexcepted 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 = "E";
}
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 );
}
}

View file

@ -0,0 +1,311 @@
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

@ -0,0 +1,60 @@
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

@ -1,335 +0,0 @@
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

@ -5,67 +5,80 @@
*/
package btools.mapcreator;
import java.util.List;
import java.util.ArrayList;
import btools.util.CheapRuler;
public class DPFilter {
public class DPFilter
{
private static double dp_sql_threshold = 0.4 * 0.4;
/*
* for each node (except first+last), eventually set the DP_SURVIVOR_BIT
*/
public static void doDPFilter(List<OsmNodeP> nodes) {
public static void doDPFilter( ArrayList<OsmNodeP> nodes )
{
int first = 0;
int last = nodes.size() - 1;
while (first < last && (nodes.get(first + 1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0) {
int last = nodes.size()-1;
while( first < last && (nodes.get(first+1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0 )
{
first++;
}
while (first < last && (nodes.get(last - 1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0) {
while( first < last && (nodes.get(last-1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0 )
{
last--;
}
if (last - first > 1) {
doDPFilter(nodes, first, last);
if ( last - first > 1 )
{
doDPFilter( nodes, first, last );
}
}
public static void doDPFilter(List<OsmNodeP> nodes, int first, int last) {
public static void doDPFilter( ArrayList<OsmNodeP> nodes, int first, int last )
{
double maxSqDist = -1.;
int index = -1;
OsmNodeP p1 = nodes.get(first);
OsmNodeP p2 = nodes.get(last);
OsmNodeP p1 = nodes.get( first );
OsmNodeP p2 = nodes.get( last );
double[] lonlat2m = CheapRuler.getLonLatToMeterScales((p1.ilat + p2.ilat) >> 1);
double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (p1.ilat+p2.ilat) >> 1 );
double dlon2m = lonlat2m[0];
double dlat2m = lonlat2m[1];
double dx = (p2.ilon - p1.ilon) * dlon2m;
double dy = (p2.ilat - p1.ilat) * dlat2m;
double d2 = dx * dx + dy * dy;
for (int i = first + 1; i < last; i++) {
OsmNodeP p = nodes.get(i);
for ( int i = first + 1; i < last; i++ )
{
OsmNodeP p = nodes.get( i );
double t = 0.;
if (d2 != 0f) {
t = ((p.ilon - p1.ilon) * dlon2m * dx + (p.ilat - p1.ilat) * dlat2m * dy) / d2;
t = t > 1. ? 1. : (t < 0. ? 0. : t);
if ( d2 != 0f )
{
t = ( ( p.ilon - p1.ilon ) * dlon2m * dx + ( p.ilat - p1.ilat ) * dlat2m * dy ) / d2;
t = t > 1. ? 1. : ( t < 0. ? 0. : t );
}
double dx2 = (p.ilon - (p1.ilon + t * (p2.ilon - p1.ilon))) * dlon2m;
double dy2 = (p.ilat - (p1.ilat + t * (p2.ilat - p1.ilat))) * dlat2m;
double dx2 = (p.ilon - ( p1.ilon + t*( p2.ilon - p1.ilon ) ) ) * dlon2m;
double dy2 = (p.ilat - ( p1.ilat + t*( p2.ilat - p1.ilat ) ) ) * dlat2m;
double sqDist = dx2 * dx2 + dy2 * dy2;
if (sqDist > maxSqDist) {
if ( sqDist > maxSqDist )
{
index = i;
maxSqDist = sqDist;
}
}
if (index >= 0) {
if (index - first > 1) {
doDPFilter(nodes, first, index);
if ( index >= 0 )
{
if ( index - first > 1 )
{
doDPFilter( nodes, first, index );
}
if (maxSqDist >= dp_sql_threshold) {
nodes.get(index).bits |= OsmNodeP.DP_SURVIVOR_BIT;
if ( maxSqDist >= dp_sql_threshold )
{
nodes.get( index ).bits |= OsmNodeP.DP_SURVIVOR_BIT;
}
if (last - index > 1) {
doDPFilter(nodes, index, last);
if ( last - index > 1 )
{
doDPFilter( nodes, index, last );
}
}
}

View file

@ -1,199 +0,0 @@
/**
* DatabasePseudoTagProvider reads Pseudo Tags from a database and adds them
* to the osm-data
*/
package btools.mapcreator;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import btools.util.CompactLongMap;
import btools.util.FrozenLongMap;
public class DatabasePseudoTagProvider {
private long cntOsmWays = 0L;
private long cntWayModified = 0L;
private Map<String, Long> pseudoTagsFound = new HashMap<>();
FrozenLongMap<Map<String, String>> dbData;
public static void main(String[] args) {
String jdbcurl = args[0];
String filename = args[1];
try (Connection conn = DriverManager.getConnection(jdbcurl);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
filename.endsWith(".gz") ? new GZIPOutputStream(new FileOutputStream(filename)) : new FileOutputStream(filename)))) {
conn.setAutoCommit(false);
System.out.println("DatabasePseudoTagProvider dumping data from " + jdbcurl + " to file " + filename);
bw.write("losmid;noise_class;river_class;forest_class;town_class;traffic_class\n");
String sql_all_tags = "SELECT * from all_tags";
try(PreparedStatement psAllTags = conn.prepareStatement(sql_all_tags)) {
psAllTags.setFetchSize(100);
// process the results
ResultSet rs = psAllTags.executeQuery();
long dbRows = 0L;
while (rs.next()) {
StringBuilder line = new StringBuilder();
line.append(rs.getLong("losmid"));
appendDBTag(line, rs, "noise_class");
appendDBTag(line, rs, "river_class");
appendDBTag(line, rs, "forest_class");
appendDBTag(line, rs, "town_class");
appendDBTag(line, rs, "traffic_class");
line.append('\n');
bw.write(line.toString());
dbRows++;
if (dbRows % 1000000L == 0L) {
System.out.println(".. from database: rows =" + dbRows);
}
}
}
} catch (SQLException g) {
System.err.format("DatabasePseudoTagProvider execute sql .. SQL State: %s\n%s\n", g.getSQLState(), g.getMessage());
System.exit(1);
} catch (Exception f) {
f.printStackTrace();
System.exit(1);
}
}
private static void appendDBTag(StringBuilder sb, ResultSet rs, String name) throws SQLException {
sb.append(';');
String v = rs.getString(name);
if (v != null) {
sb.append(v);
}
}
public DatabasePseudoTagProvider(String filename) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(
filename.endsWith(".gz") ? new GZIPInputStream(new FileInputStream(filename)) : new FileInputStream(filename)))) {
System.out.println("DatabasePseudoTagProvider reading from file: " + filename);
br.readLine(); // skip header line
Map<Map<String, String>, Map<String, String>> mapUnifier = new HashMap<>();
CompactLongMap<Map<String, String>> data = new CompactLongMap<>();
long dbRows = 0L;
for (;;) {
String line = br.readLine();
if (line == null) {
break;
}
List<String> tokens = tokenize(line);
long osm_id = Long.parseLong(tokens.get(0));
Map<String, String> row = new HashMap<>(5);
addTag(row, tokens.get(1), "estimated_noise_class");
addTag(row, tokens.get(2), "estimated_river_class");
addTag(row, tokens.get(3), "estimated_forest_class");
addTag(row, tokens.get(4), "estimated_town_class");
addTag(row, tokens.get(5), "estimated_traffic_class");
// apply the instance-unifier for the row-map
Map<String, String> knownRow = mapUnifier.get(row);
if (knownRow != null) {
row = knownRow;
} else {
mapUnifier.put(row, row);
}
data.put(osm_id, row);
dbRows++;
if (dbRows % 1000000L == 0L) {
System.out.println(".. from database: rows =" + data.size() + " unique rows=" + mapUnifier.size());
}
}
System.out.println("freezing result map..");
dbData = new FrozenLongMap<>(data);
System.out.println("read from file: rows =" + dbData.size() + " unique rows=" + mapUnifier.size());
} catch (Exception f) {
f.printStackTrace();
System.exit(1);
}
}
// use own tokenizer as String.split, StringTokenizer
// etc. have issues with empty elements
private List<String> tokenize(String s) {
List<String> l = new ArrayList<>();
StringBuilder sb = new StringBuilder();
for (int i=0; i<s.length(); i++) {
char c = s.charAt(i);
if (c == ';') {
l.add(sb.toString());
sb.setLength(0);
} else {
sb.append(c);
}
}
l.add(sb.toString());
return l;
}
private static void addTag(Map<String, String> row, String s, String name) {
if (!s.isEmpty()) {
row.put(name, s);
}
}
public void addTags(long osm_id, Map<String, String> map) {
if (map == null || !map.containsKey("highway")) {
return;
}
cntOsmWays++;
if ((cntOsmWays % 1000000L) == 0) {
String out = "Osm Ways processed=" + cntOsmWays + " way modifs=" + cntWayModified;
for (String key : pseudoTagsFound.keySet()) {
out += " " + key + "=" + pseudoTagsFound.get(key);
}
System.out.println(out);
}
Map<String, String> dbTags = dbData.get(osm_id);
if (dbTags == null) {
return;
}
cntWayModified++;
for (String key : dbTags.keySet()) {
map.put(key, dbTags.get(key));
Long cnt = pseudoTagsFound.get(key);
if (cnt == null) {
cnt = 0L;
}
pseudoTagsFound.put(key, cnt + 1L);
}
}
}

View file

@ -1,264 +0,0 @@
package btools.mapcreator;
import btools.util.ReducedMedianFilter;
/**
* Container for a elevation raster + it's meta-data
*
* @author ab
*/
public class ElevationRaster {
public int ncols;
public int nrows;
public boolean halfcol;
public double xllcorner;
public double yllcorner;
public double cellsize;
public short[] eval_array;
public short noDataValue;
public boolean usingWeights = false;
private boolean missingData = false;
public short getElevation(int ilon, int ilat) {
double lon = ilon / 1000000. - 180.;
double lat = ilat / 1000000. - 90.;
if (usingWeights) {
return getElevationFromShiftWeights(lon, lat);
}
// no weights calculated, use 2d linear interpolation
double dcol = (lon - xllcorner) / cellsize - 0.5;
double drow = (lat - yllcorner) / cellsize - 0.5;
int row = (int) drow;
int col = (int) dcol;
if (col < 0) col = 0;
if (col >= ncols - 1) col = ncols - 2;
if (row < 0) row = 0;
if (row >= nrows - 1) row = nrows - 2;
double wrow = drow - row;
double wcol = dcol - col;
missingData = false;
// System.out.println( "wrow=" + wrow + " wcol=" + wcol + " row=" + row + " col=" + col );
double eval = (1. - wrow) * (1. - wcol) * get(row, col)
+ (wrow) * (1. - wcol) * get(row + 1, col)
+ (1. - wrow) * (wcol) * get(row, col + 1)
+ (wrow) * (wcol) * get(row + 1, col + 1);
// System.out.println( "eval=" + eval );
return missingData ? Short.MIN_VALUE : (short) (eval * 4);
}
private short get(int r, int c) {
short e = eval_array[(nrows - 1 - r) * ncols + c];
if (e == Short.MIN_VALUE) missingData = true;
return e;
}
private short getElevationFromShiftWeights(double lon, double lat) {
// calc lat-idx and -weight
double alat = lat < 0. ? -lat : lat;
alat /= 5.;
int latIdx = (int) alat;
double wlat = alat - latIdx;
double dcol = (lon - xllcorner) / cellsize;
double drow = (lat - yllcorner) / cellsize;
int row = (int) drow;
int col = (int) dcol;
double dgx = (dcol - col) * gridSteps;
double dgy = (drow - row) * gridSteps;
// System.out.println( "wrow=" + wrow + " wcol=" + wcol + " row=" + row + " col=" + col );
int gx = (int) (dgx);
int gy = (int) (dgy);
double wx = dgx - gx;
double wy = dgy - gy;
double w00 = (1. - wx) * (1. - wy);
double w01 = (1. - wx) * (wy);
double w10 = (wx) * (1. - wy);
double w11 = (wx) * (wy);
Weights[][] w0 = getWeights(latIdx);
Weights[][] w1 = getWeights(latIdx + 1);
missingData = false;
double m0 = w00 * getElevation(w0[gx][gy], row, col)
+ w01 * getElevation(w0[gx][gy + 1], row, col)
+ w10 * getElevation(w0[gx + 1][gy], row, col)
+ w11 * getElevation(w0[gx + 1][gy + 1], row, col);
double m1 = w00 * getElevation(w1[gx][gy], row, col)
+ w01 * getElevation(w1[gx][gy + 1], row, col)
+ w10 * getElevation(w1[gx + 1][gy], row, col)
+ w11 * getElevation(w1[gx + 1][gy + 1], row, col);
if (missingData) return Short.MIN_VALUE;
double m = (1. - wlat) * m0 + wlat * m1;
return (short) (m * 2);
}
private ReducedMedianFilter rmf = new ReducedMedianFilter(256);
private double getElevation(Weights w, int row, int col) {
if (missingData) {
return 0.;
}
int nx = w.nx;
int ny = w.ny;
int mx = nx / 2; // mean pixels
int my = ny / 2;
// System.out.println( "nx="+ nx + " ny=" + ny );
rmf.reset();
for (int ix = 0; ix < nx; ix++) {
for (int iy = 0; iy < ny; iy++) {
short val = get(row + iy - my, col + ix - mx);
rmf.addSample(w.getWeight(ix, iy), val);
}
}
return missingData ? 0. : rmf.calcEdgeReducedMedian(filterCenterFraction);
}
private static class Weights {
int nx;
int ny;
double[] weights;
long total = 0;
Weights(int nx, int ny) {
this.nx = nx;
this.ny = ny;
weights = new double[nx * ny];
}
void inc(int ix, int iy) {
weights[iy * nx + ix] += 1.;
total++;
}
void normalize(boolean verbose) {
for (int iy = 0; iy < ny; iy++) {
StringBuilder sb = verbose ? new StringBuilder() : null;
for (int ix = 0; ix < nx; ix++) {
weights[iy * nx + ix] /= total;
if (sb != null) {
int iweight = (int) (1000 * weights[iy * nx + ix] + 0.5);
String sval = " " + iweight;
sb.append(sval.substring(sval.length() - 4));
}
}
if (sb != null) {
System.out.println(sb);
System.out.println();
}
}
}
double getWeight(int ix, int iy) {
return weights[iy * nx + ix];
}
}
private static int gridSteps = 10;
private static Weights[][][] allShiftWeights = new Weights[17][][];
private static double filterCenterFraction = 0.2;
private static double filterDiscRadius = 4.999; // in pixels
static {
String sRadius = System.getProperty("filterDiscRadius");
if (sRadius != null && sRadius.length() > 0) {
filterDiscRadius = Integer.parseInt(sRadius);
System.out.println("using filterDiscRadius = " + filterDiscRadius);
}
String sFraction = System.getProperty("filterCenterFraction");
if (sFraction != null && sFraction.length() > 0) {
filterCenterFraction = Integer.parseInt(sFraction) / 100.;
System.out.println("using filterCenterFraction = " + filterCenterFraction);
}
}
// calculate interpolation weights from the overlap of a probe disc of given radius at given latitude
// ( latIndex = 0 -> 0 deg, latIndex = 16 -> 80 degree)
private static Weights[][] getWeights(int latIndex) {
int idx = latIndex < 16 ? latIndex : 16;
Weights[][] res = allShiftWeights[idx];
if (res == null) {
res = calcWeights(idx);
allShiftWeights[idx] = res;
}
return res;
}
private static Weights[][] calcWeights(int latIndex) {
double coslat = Math.cos(latIndex * 5. / 57.3);
// radius in pixel units
double ry = filterDiscRadius;
double rx = ry / coslat;
// gridsize is 2*radius + 1 cell
int nx = ((int) rx) * 2 + 3;
int ny = ((int) ry) * 2 + 3;
System.out.println("nx=" + nx + " ny=" + ny);
int mx = nx / 2; // mean pixels
int my = ny / 2;
// create a matrix for the relative intergrid-position
Weights[][] shiftWeights = new Weights[gridSteps + 1][];
// loop the intergrid-position
for (int gx = 0; gx <= gridSteps; gx++) {
shiftWeights[gx] = new Weights[gridSteps + 1];
double x0 = mx + ((double) gx) / gridSteps;
for (int gy = 0; gy <= gridSteps; gy++) {
double y0 = my + ((double) gy) / gridSteps;
// create the weight-matrix
Weights weights = new Weights(nx, ny);
shiftWeights[gx][gy] = weights;
double sampleStep = 0.001;
for (double x = -1. + sampleStep / 2.; x < 1.; x += sampleStep) {
double mx2 = 1. - x * x;
int x_idx = (int) (x0 + x * rx);
for (double y = -1. + sampleStep / 2.; y < 1.; y += sampleStep) {
if (y * y > mx2) {
continue;
}
// we are in the ellipse, see what pixel we are on
int y_idx = (int) (y0 + y * ry);
weights.inc(x_idx, y_idx);
}
}
weights.normalize(true);
}
}
return shiftWeights;
}
@Override
public String toString() {
return ncols + "," + nrows + "," + halfcol + "," + xllcorner + "," + yllcorner + "," + cellsize + "," + noDataValue + "," + usingWeights;
}
}

View file

@ -1,545 +0,0 @@
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

@ -1,342 +0,0 @@
// 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

@ -1,159 +1,171 @@
/**
* common base class for the map-filters
*
* @author ab
*/
package btools.mapcreator;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import btools.util.DiffCoderDataOutputStream;
public abstract class MapCreatorBase implements WayListener, NodeListener, RelationListener {
private DiffCoderDataOutputStream[] tileOutStreams;
protected File outTileDir;
protected Map<String, String> tags;
public void putTag(String key, String value) {
if (tags == null) tags = new HashMap<>();
tags.put(key, value);
}
public String getTag(String key) {
return tags == null ? null : tags.get(key);
}
public Map<String, String> getTagsOrNull() {
return tags;
}
public void setTags(Map<String, String> tags) {
this.tags = tags;
}
protected static long readId(DataInputStream is) throws IOException {
int offset = is.readByte();
if (offset == 32) return -1;
long i = is.readInt();
i = i << 5;
return i | offset;
}
protected static void writeId(DataOutputStream o, long id) throws IOException {
if (id == -1) {
o.writeByte(32);
return;
}
int offset = (int) (id & 0x1f);
int i = (int) (id >> 5);
o.writeByte(offset);
o.writeInt(i);
}
protected static File[] sortBySizeAsc(File[] files) {
int n = files.length;
long[] sizes = new long[n];
File[] sorted = new File[n];
for (int i = 0; i < n; i++) sizes[i] = files[i].length();
for (int nf = 0; nf < n; nf++) {
int idx = -1;
long min = -1;
for (int i = 0; i < n; i++) {
if (sizes[i] != -1 && (idx == -1 || sizes[i] < min)) {
min = sizes[i];
idx = i;
}
}
sizes[idx] = -1;
sorted[nf] = files[idx];
}
return sorted;
}
protected File fileFromTemplate(File template, File dir, String suffix) {
String filename = template.getName();
filename = filename.substring(0, filename.length() - 3) + suffix;
return new File(dir, filename);
}
protected DataInputStream createInStream(File inFile) throws IOException {
return new DataInputStream(new BufferedInputStream(new FileInputStream(inFile)));
}
protected DiffCoderDataOutputStream createOutStream(File outFile) throws IOException {
return new DiffCoderDataOutputStream(new BufferedOutputStream(new FileOutputStream(outFile)));
}
protected DiffCoderDataOutputStream getOutStreamForTile(int tileIndex) throws Exception {
if (tileOutStreams == null) {
tileOutStreams = new DiffCoderDataOutputStream[64];
}
if (tileOutStreams[tileIndex] == null) {
tileOutStreams[tileIndex] = createOutStream(new File(outTileDir, getNameForTile(tileIndex)));
}
return tileOutStreams[tileIndex];
}
protected String getNameForTile(int tileIndex) {
throw new IllegalArgumentException("getNameForTile not implemented");
}
protected void closeTileOutStreams() throws Exception {
if (tileOutStreams == null) {
return;
}
for (int tileIndex = 0; tileIndex < tileOutStreams.length; tileIndex++) {
if (tileOutStreams[tileIndex] != null) tileOutStreams[tileIndex].close();
tileOutStreams[tileIndex] = null;
}
}
// interface dummys
@Override
public void nodeFileStart(File nodefile) throws Exception {
}
@Override
public void nextNode(NodeData n) throws Exception {
}
@Override
public void nodeFileEnd(File nodefile) throws Exception {
}
@Override
public boolean wayFileStart(File wayfile) throws Exception {
return true;
}
@Override
public void nextWay(WayData data) throws Exception {
}
@Override
public void wayFileEnd(File wayfile) throws Exception {
}
@Override
public void nextRelation(RelationData data) throws Exception {
}
@Override
public void nextRestriction(RelationData data, long fromWid, long toWid, long viaNid) throws Exception {
}
}
/**
* common base class for the map-filters
*
* @author ab
*/
package btools.mapcreator;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import btools.util.DiffCoderDataOutputStream;
public abstract class MapCreatorBase implements WayListener, NodeListener, RelationListener
{
private DiffCoderDataOutputStream[] tileOutStreams;
protected File outTileDir;
protected HashMap<String,String> tags;
public void putTag( String key, String value )
{
if ( tags == null ) tags = new HashMap<String,String>();
tags.put( key, value );
}
public String getTag( String key )
{
return tags == null ? null : tags.get( key );
}
public HashMap<String,String> getTagsOrNull()
{
return tags;
}
public void setTags( HashMap<String,String> tags )
{
this.tags = tags;
}
protected static long readId( DataInputStream is) throws IOException
{
int offset = is.readByte();
if ( offset == 32 ) return -1;
long i = is.readInt();
i = i << 5;
return i | offset;
}
protected static void writeId( DataOutputStream o, long id ) throws IOException
{
if ( id == -1 )
{
o.writeByte( 32 );
return;
}
int offset = (int)( id & 0x1f );
int i = (int)( id >> 5 );
o.writeByte( offset );
o.writeInt( i );
}
protected static File[] sortBySizeAsc( File[] files )
{
int n = files.length;
long[] sizes = new long[n];
File[] sorted = new File[n];
for( int i=0; i<n; i++ ) sizes[i] = files[i].length();
for(int nf=0; nf<n; nf++)
{
int idx = -1;
long min = -1;
for( int i=0; i<n; i++ )
{
if ( sizes[i] != -1 && ( idx == -1 || sizes[i] < min ) )
{
min = sizes[i];
idx = i;
}
}
sizes[idx] = -1;
sorted[nf] = files[idx];
}
return sorted;
}
protected File fileFromTemplate( File template, File dir, String suffix )
{
String filename = template.getName();
filename = filename.substring( 0, filename.length() - 3 ) + suffix;
return new File( dir, filename );
}
protected DataInputStream createInStream( File inFile ) throws IOException
{
return new DataInputStream( new BufferedInputStream ( new FileInputStream( inFile ) ) );
}
protected DiffCoderDataOutputStream createOutStream( File outFile ) throws IOException
{
return new DiffCoderDataOutputStream( new BufferedOutputStream( new FileOutputStream( outFile ) ) );
}
protected DiffCoderDataOutputStream getOutStreamForTile( int tileIndex ) throws Exception
{
if ( tileOutStreams == null )
{
tileOutStreams = new DiffCoderDataOutputStream[64];
}
if ( tileOutStreams[tileIndex] == null )
{
tileOutStreams[tileIndex] = createOutStream( new File( outTileDir, getNameForTile( tileIndex ) ) );
}
return tileOutStreams[tileIndex];
}
protected String getNameForTile( int tileIndex )
{
throw new IllegalArgumentException( "getNameForTile not implemented" );
}
protected void closeTileOutStreams() throws Exception
{
if ( tileOutStreams == null )
{
return;
}
for( int tileIndex=0; tileIndex<tileOutStreams.length; tileIndex++ )
{
if ( tileOutStreams[tileIndex] != null ) tileOutStreams[tileIndex].close();
tileOutStreams[tileIndex] = null;
}
}
// interface dummys
@Override
public void nodeFileStart( File nodefile ) throws Exception {}
@Override
public void nextNode( NodeData n ) throws Exception {}
@Override
public void nodeFileEnd( File nodefile ) throws Exception {}
@Override
public boolean wayFileStart( File wayfile ) throws Exception { return true; }
@Override
public void nextWay( WayData data ) throws Exception {}
@Override
public void wayFileEnd( File wayfile ) throws Exception {}
@Override
public void nextRelation( RelationData data ) throws Exception {}
@Override
public void nextRestriction( RelationData data, long fromWid, long toWid, long viaNid ) throws Exception {}
}

View file

@ -1,75 +1,84 @@
package btools.mapcreator;
import java.io.File;
/**
* NodeCutter does 1 step in map-processing:
* <p>
* - cuts the 45*30 node tiles into 5*5 pieces
*
* @author ab
*/
public class NodeCutter extends MapCreatorBase {
private int lonoffset;
private int latoffset;
public static void main(String[] args) throws Exception {
System.out.println("*** NodeCutter: Cut big node-tiles into 5x5 tiles");
if (args.length != 2) {
System.out.println("usage: java NodeCutter <node-tiles-in> <node-tiles-out>");
return;
}
new NodeCutter().process(new File(args[0]), new File(args[1]));
}
public void init(File nodeTilesOut) {
this.outTileDir = nodeTilesOut;
}
public void process(File nodeTilesIn, File nodeTilesOut) throws Exception {
init(nodeTilesOut);
new NodeIterator(this, true).processDir(nodeTilesIn, ".tlf");
}
@Override
public void nodeFileStart(File nodefile) throws Exception {
lonoffset = -1;
latoffset = -1;
}
@Override
public void nextNode(NodeData n) throws Exception {
n.writeTo(getOutStreamForTile(getTileIndex(n.ilon, n.ilat)));
}
@Override
public void nodeFileEnd(File nodeFile) throws Exception {
closeTileOutStreams();
}
private int getTileIndex(int ilon, int ilat) {
int lonoff = (ilon / 45000000) * 45;
int latoff = (ilat / 30000000) * 30;
if (lonoffset == -1) lonoffset = lonoff;
if (latoffset == -1) latoffset = latoff;
if (lonoff != lonoffset || latoff != latoffset)
throw new IllegalArgumentException("inconsistent node: " + ilon + " " + ilat);
int lon = (ilon / 5000000) % 9;
int lat = (ilat / 5000000) % 6;
if (lon < 0 || lon > 8 || lat < 0 || lat > 5)
throw new IllegalArgumentException("illegal pos: " + ilon + "," + ilat);
return lon * 6 + lat;
}
protected String getNameForTile(int tileIndex) {
int lon = (tileIndex / 6) * 5 + lonoffset - 180;
int lat = (tileIndex % 6) * 5 + latoffset - 90;
String slon = lon < 0 ? "W" + (-lon) : "E" + lon;
String slat = lat < 0 ? "S" + (-lat) : "N" + lat;
return slon + "_" + slat + ".n5d";
}
}
package btools.mapcreator;
import java.io.File;
/**
* NodeCutter does 1 step in map-processing:
*
* - cuts the 45*30 node tiles into 5*5 pieces
*
* @author ab
*/
public class NodeCutter extends MapCreatorBase
{
private int lonoffset;
private int latoffset;
public static void main(String[] args) throws Exception
{
System.out.println("*** NodeCutter: Cut big node-tiles into 5x5 tiles");
if (args.length != 2)
{
System.out.println("usage: java NodeCutter <node-tiles-in> <node-tiles-out>" );
return;
}
new NodeCutter().process( new File( args[0] ), new File( args[1] ) );
}
public void init( File nodeTilesOut )
{
this.outTileDir = nodeTilesOut;
}
public void process( File nodeTilesIn, File nodeTilesOut ) throws Exception
{
init( nodeTilesOut );
new NodeIterator( this, true ).processDir( nodeTilesIn, ".tlf" );
}
@Override
public void nodeFileStart( File nodefile ) throws Exception
{
lonoffset = -1;
latoffset = -1;
}
@Override
public void nextNode( NodeData n ) throws Exception
{
n.writeTo( getOutStreamForTile( getTileIndex( n.ilon, n.ilat ) ) );
}
@Override
public void nodeFileEnd( File nodeFile ) throws Exception
{
closeTileOutStreams();
}
private int getTileIndex( int ilon, int ilat )
{
int lonoff = (ilon / 45000000 ) * 45;
int latoff = (ilat / 30000000 ) * 30;
if ( lonoffset == -1 ) lonoffset = lonoff;
if ( latoffset == -1 ) latoffset = latoff;
if ( lonoff != lonoffset || latoff != latoffset )
throw new IllegalArgumentException( "inconsistent node: " + ilon + " " + ilat );
int lon = (ilon / 5000000) % 9;
int lat = (ilat / 5000000) % 6;
if ( lon < 0 || lon > 8 || lat < 0 || lat > 5 ) throw new IllegalArgumentException( "illegal pos: " + ilon + "," + ilat );
return lon*6 + lat;
}
protected String getNameForTile( int tileIndex )
{
int lon = (tileIndex / 6 ) * 5 + lonoffset - 180;
int lat = (tileIndex % 6 ) * 5 + latoffset - 90;
String slon = lon < 0 ? "W" + (-lon) : "E" + lon;
String slat = lat < 0 ? "S" + (-lat) : "N" + lat;
return slon + "_" + slat + ".n5d";
}
}

View file

@ -1,49 +1,46 @@
package btools.mapcreator;
import btools.util.DiffCoderDataInputStream;
import btools.util.DiffCoderDataOutputStream;
/**
* Container for node data on the preprocessor level
*
* @author ab
*/
public class NodeData extends MapCreatorBase {
public long nid;
public int ilon;
public int ilat;
public byte[] description;
public short selev = Short.MIN_VALUE;
public NodeData(long id, double lon, double lat) {
nid = id;
ilat = (int) ((lat + 90.) * 1000000. + 0.5);
ilon = (int) ((lon + 180.) * 1000000. + 0.5);
}
public NodeData(DiffCoderDataInputStream dis) throws Exception {
nid = dis.readDiffed(0);
ilon = (int) dis.readDiffed(1);
ilat = (int) dis.readDiffed(2);
int mode = dis.readByte();
if ((mode & 1) != 0) {
int dlen = dis.readShort();
description = new byte[dlen];
dis.readFully(description);
}
if ((mode & 2) != 0) selev = dis.readShort();
}
public void writeTo(DiffCoderDataOutputStream dos) throws Exception {
dos.writeDiffed(nid, 0);
dos.writeDiffed(ilon, 1);
dos.writeDiffed(ilat, 2);
int mode = (description == null ? 0 : 1) | (selev == Short.MIN_VALUE ? 0 : 2);
dos.writeByte((byte) mode);
if ((mode & 1) != 0) {
dos.writeShort(description.length);
dos.write(description);
}
if ((mode & 2) != 0) dos.writeShort(selev);
}
}
package btools.mapcreator;
import btools.util.DiffCoderDataInputStream;
import btools.util.DiffCoderDataOutputStream;
/**
* Container for node data on the preprocessor level
*
* @author ab
*/
public class NodeData extends MapCreatorBase
{
public long nid;
public int ilon;
public int ilat;
public byte[] description;
public short selev = Short.MIN_VALUE;
public NodeData( long id, double lon, double lat )
{
nid = id;
ilat = (int)( ( lat + 90. )*1000000. + 0.5);
ilon = (int)( ( lon + 180. )*1000000. + 0.5);
}
public NodeData( DiffCoderDataInputStream dis ) throws Exception
{
nid = dis.readDiffed( 0 );
ilon = (int)dis.readDiffed( 1 );
ilat = (int)dis.readDiffed( 2 );
int mode = dis.readByte();
if ( ( mode & 1 ) != 0 ) { int dlen = dis.readShort(); description = new byte[dlen]; dis.readFully( description ); }
if ( ( mode & 2 ) != 0 ) selev = dis.readShort();
}
public void writeTo( DiffCoderDataOutputStream dos ) throws Exception
{
dos.writeDiffed( nid, 0 );
dos.writeDiffed( ilon, 1 );
dos.writeDiffed( ilat, 2 );
int mode = (description == null ? 0 : 1 ) | ( selev == Short.MIN_VALUE ? 0 : 2 );
dos.writeByte( (byte) mode);
if ( ( mode & 1 ) != 0 ) { dos.writeShort( description.length ); dos.write( description ); }
if ( ( mode & 2 ) != 0 ) dos.writeShort( selev );
}
}

View file

@ -1,80 +1,92 @@
package btools.mapcreator;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import btools.util.DenseLongMap;
import btools.util.DiffCoderDataOutputStream;
import btools.util.TinyDenseLongMap;
/**
* NodeFilter does 1 step in map-processing:
* <p>
* - filters out unused nodes according to the way file
*
* @author ab
*/
public class NodeFilter extends MapCreatorBase {
private DiffCoderDataOutputStream nodesOutStream;
private File nodeTilesOut;
protected DenseLongMap nodebitmap;
public static void main(String[] args) throws Exception {
System.out.println("*** NodeFilter: Filter way related nodes");
if (args.length != 3) {
System.out.println("usage: java NodeFilter <node-tiles-in> <way-file-in> <node-tiles-out>");
return;
}
new NodeFilter().process(new File(args[0]), new File(args[1]), new File(args[2]));
}
public void init() throws Exception {
nodebitmap = Boolean.getBoolean("useDenseMaps") ? new DenseLongMap(512) : new TinyDenseLongMap();
}
public void process(File nodeTilesIn, File wayFileIn, File nodeTilesOut) throws Exception {
init();
this.nodeTilesOut = nodeTilesOut;
// read the wayfile into a bitmap of used nodes
new WayIterator(this, false).processFile(wayFileIn);
// finally filter all node files
new NodeIterator(this, true).processDir(nodeTilesIn, ".tls");
}
@Override
public void nextWay(WayData data) throws Exception {
int nnodes = data.nodes.size();
for (int i = 0; i < nnodes; i++) {
nodebitmap.put(data.nodes.get(i), 0);
}
}
@Override
public void nodeFileStart(File nodefile) throws Exception {
String filename = nodefile.getName();
File outfile = new File(nodeTilesOut, filename);
nodesOutStream = new DiffCoderDataOutputStream(new BufferedOutputStream(new FileOutputStream(outfile)));
}
@Override
public void nextNode(NodeData n) throws Exception {
if (isRelevant(n)) {
n.writeTo(nodesOutStream);
}
}
public boolean isRelevant(NodeData n) {
// check if node passes bitmap
return nodebitmap.getInt(n.nid) == 0; // 0 -> bit set, -1 -> unset
}
@Override
public void nodeFileEnd(File nodeFile) throws Exception {
nodesOutStream.close();
}
}
package btools.mapcreator;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import btools.util.DenseLongMap;
import btools.util.DiffCoderDataOutputStream;
import btools.util.TinyDenseLongMap;
/**
* NodeFilter does 1 step in map-processing:
*
* - filters out unused nodes according to the way file
*
* @author ab
*/
public class NodeFilter extends MapCreatorBase
{
private DiffCoderDataOutputStream nodesOutStream;
private File nodeTilesOut;
protected DenseLongMap nodebitmap;
public static void main(String[] args) throws Exception
{
System.out.println("*** NodeFilter: Filter way related nodes");
if (args.length != 3)
{
System.out.println("usage: java NodeFilter <node-tiles-in> <way-file-in> <node-tiles-out>" );
return;
}
new NodeFilter().process( new File( args[0] ), new File( args[1] ), new File( args[2] ) );
}
public void init() throws Exception
{
nodebitmap = Boolean.getBoolean( "useDenseMaps" ) ? new DenseLongMap( 512 ) : new TinyDenseLongMap();
}
public void process( File nodeTilesIn, File wayFileIn, File nodeTilesOut ) throws Exception
{
init();
this.nodeTilesOut = nodeTilesOut;
// read the wayfile into a bitmap of used nodes
new WayIterator( this, false ).processFile( wayFileIn );
// finally filter all node files
new NodeIterator( this, true ).processDir( nodeTilesIn, ".tls" );
}
@Override
public void nextWay( WayData data ) throws Exception
{
int nnodes = data.nodes.size();
for (int i=0; i<nnodes; i++ )
{
nodebitmap.put( data.nodes.get(i), 0 );
}
}
@Override
public void nodeFileStart( File nodefile ) throws Exception
{
String filename = nodefile.getName();
File outfile = new File( nodeTilesOut, filename );
nodesOutStream = new DiffCoderDataOutputStream( new BufferedOutputStream ( new FileOutputStream( outfile ) ) );
}
@Override
public void nextNode( NodeData n ) throws Exception
{
if ( isRelevant( n ) )
{
n.writeTo( nodesOutStream );
}
}
public boolean isRelevant( NodeData n )
{
// check if node passes bitmap
return nodebitmap.getInt( n.nid ) == 0; // 0 -> bit set, -1 -> unset
}
@Override
public void nodeFileEnd( File nodeFile ) throws Exception
{
nodesOutStream.close();
}
}

View file

@ -1,59 +1,71 @@
package btools.mapcreator;
import java.io.BufferedInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import btools.util.DiffCoderDataInputStream;
/**
* Iterate over a singe nodefile or a directory
* of nodetiles and feed the nodes to the callback listener
*
* @author ab
*/
public class NodeIterator extends MapCreatorBase {
private NodeListener listener;
private boolean delete;
public NodeIterator(NodeListener nodeListener, boolean deleteAfterReading) {
listener = nodeListener;
delete = deleteAfterReading;
}
public void processDir(File indir, String inSuffix) throws Exception {
if (!indir.isDirectory()) {
throw new IllegalArgumentException("not a directory: " + indir);
}
File[] af = sortBySizeAsc(indir.listFiles());
for (int i = 0; i < af.length; i++) {
File nodefile = af[i];
if (nodefile.getName().endsWith(inSuffix)) {
processFile(nodefile);
}
}
}
public void processFile(File nodefile) throws Exception {
System.out.println("*** NodeIterator reading: " + nodefile);
listener.nodeFileStart(nodefile);
DiffCoderDataInputStream di = new DiffCoderDataInputStream(new BufferedInputStream(new FileInputStream(nodefile)));
try {
for (; ; ) {
NodeData n = new NodeData(di);
listener.nextNode(n);
}
} catch (EOFException eof) {
di.close();
}
listener.nodeFileEnd(nodefile);
if (delete && "true".equals(System.getProperty("deletetmpfiles"))) {
nodefile.delete();
}
}
}
package btools.mapcreator;
import java.io.BufferedInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import btools.util.DiffCoderDataInputStream;
/**
* Iterate over a singe nodefile or a directory
* of nodetiles and feed the nodes to the callback listener
*
* @author ab
*/
public class NodeIterator extends MapCreatorBase
{
private NodeListener listener;
private boolean delete;
public NodeIterator( NodeListener nodeListener, boolean deleteAfterReading )
{
listener = nodeListener;
delete = deleteAfterReading;
}
public void processDir( File indir, String inSuffix ) throws Exception
{
if ( !indir.isDirectory() )
{
throw new IllegalArgumentException( "not a directory: " + indir );
}
File[] af = sortBySizeAsc( indir.listFiles() );
for( int i=0; i<af.length; i++ )
{
File nodefile = af[i];
if ( nodefile.getName().endsWith( inSuffix ) )
{
processFile( nodefile );
}
}
}
public void processFile(File nodefile) throws Exception
{
System.out.println( "*** NodeIterator reading: " + nodefile );
listener.nodeFileStart( nodefile );
DiffCoderDataInputStream di = new DiffCoderDataInputStream( new BufferedInputStream ( new FileInputStream( nodefile ) ) );
try
{
for(;;)
{
NodeData n = new NodeData( di );
listener.nextNode( n );
}
}
catch( EOFException eof )
{
di.close();
}
listener.nodeFileEnd( nodefile );
if ( delete && "true".equals( System.getProperty( "deletetmpfiles" ) ))
{
nodefile.delete();
}
}
}

View file

@ -1,16 +1,17 @@
package btools.mapcreator;
import java.io.File;
/**
* Callbacklistener for NodeIterator
*
* @author ab
*/
public interface NodeListener {
void nodeFileStart(File nodefile) throws Exception;
void nextNode(NodeData data) throws Exception;
void nodeFileEnd(File nodefile) throws Exception;
}
package btools.mapcreator;
import java.io.File;
/**
* Callbacklistener for NodeIterator
*
* @author ab
*/
public interface NodeListener
{
void nodeFileStart( File nodefile ) throws Exception;
void nextNode( NodeData data ) throws Exception;
void nodeFileEnd( File nodefile ) throws Exception;
}

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