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

View file

@ -13,31 +13,14 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
environment: BRouter
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Set up JDK 17 - name: Set up JDK 8
uses: actions/setup-java@v4 uses: actions/setup-java@v2
with: with:
java-version: '17' java-version: '8'
distribution: 'temurin' distribution: 'zulu'
cache: gradle 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 - 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 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 *.iml
.gradle .gradle
.idea/ .idea/
build
/local.properties /local.properties
/.idea/caches /.idea/caches
/.idea/gradle.xml /.idea/gradle.xml
@ -11,6 +10,7 @@ build
/.idea/navEditor.xml /.idea/navEditor.xml
/.idea/assetWizardSettings.xml /.idea/assetWizardSettings.xml
.DS_Store .DS_Store
/build
/captures /captures
.externalNativeBuild .externalNativeBuild
.cxx .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 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 + BRouter is a configurable OSM offline router with elevation awareness, Java +
Android. Designed to be multi-modal with a particular emphasis on bicycle Android. Designed to be multi-modal with a particular emphasis on bicycle
and energy-based car routing. 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 Store](https://play.google.com/store/apps/details?id=btools.routingapp). You
can also [build BRouter](#build-and-install) yourself. You can find detailed can also [build BRouter](#build-and-install) yourself. You can find detailed
documentation of the BRouter Android app in 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"> <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> <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. [OSMAnd](https://osmand.net/) on your Android device.
A full documentation on how to set this up is available at 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 ## 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/). [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 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 #### 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/)). of OpenStreetMap data (or a [GeoFabrik extract](https://download.geofabrik.de/)).
More documentation of this is available in the 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 ### (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. a default set of profiles covering most of the basic use cases.
Have a look at the 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. 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) [`brouter-server/src/main/java/btools/server/request/ServerHandler.java`](brouter-server/src/main/java/btools/server/request/ServerHandler.java)
file. 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 ## Documentation
More documentation is available in the [`docs`](docs) folder. More documentation is available in the [`misc/readmes`](misc/readmes) folder.
## Related Projects ## Related Projects

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

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

View file

@ -1,7 +1,8 @@
plugins { plugins {
id 'brouter.library-conventions' id 'java-library'
} }
dependencies { dependencies {
implementation project(':brouter-util') 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,7 +5,8 @@ import btools.util.BitCoderContext;
/** /**
* Container for some re-usable databuffers for the decoder * Container for some re-usable databuffers for the decoder
*/ */
public final class DataBuffers { public final class DataBuffers
{
public byte[] iobuffer; public byte[] iobuffer;
public byte[] tagbuf1 = new byte[256]; public byte[] tagbuf1 = new byte[256];
public BitCoderContext bctx1 = new BitCoderContext( tagbuf1 ); public BitCoderContext bctx1 = new BitCoderContext( tagbuf1 );
@ -16,7 +17,8 @@ public final class DataBuffers {
public int[] alon = new int[2048]; public int[] alon = new int[2048];
public int[] alat = new int[2048]; public int[] alat = new int[2048];
public DataBuffers() { public DataBuffers()
{
this( new byte[65636] ); this( new byte[65636] );
} }
@ -24,7 +26,8 @@ public final class DataBuffers {
* construct a set of databuffers except * construct a set of databuffers except
* for 'iobuffer', where the given array is used * for 'iobuffer', where the given array is used
*/ */
public DataBuffers(byte[] iobuffer) { public DataBuffers( byte[] iobuffer )
{
this.iobuffer = iobuffer; this.iobuffer = iobuffer;
} }

View file

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

View file

@ -3,7 +3,8 @@ package btools.codec;
/** /**
* Simple container for a list of lists of integers * Simple container for a list of lists of integers
*/ */
public class LinkedListContainer { public class LinkedListContainer
{
private int[] ia; // prev, data, prev, data, ... private int[] ia; // prev, data, prev, data, ...
private int size; private int size;
private int[] startpointer; // 0=void, odd=head-data-cell private int[] startpointer; // 0=void, odd=head-data-cell
@ -11,14 +12,15 @@ public class LinkedListContainer {
/** /**
* Construct a container for the given number of lists * Construct a container for the given number of lists
* <p> *
* If no default-buffer is given, an int[nlists*4] is constructed, * If no default-buffer is given, an int[nlists*4] is constructed,
* able to hold 2 entries per list on average * 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) * @param defaultbuffer an optional data array for re-use (gets replaced if too small)
*/ */
public LinkedListContainer(int nlists, int[] defaultbuffer) { public LinkedListContainer( int nlists, int[] defaultbuffer )
{
ia = defaultbuffer == null ? new int[nlists*4] : defaultbuffer; ia = defaultbuffer == null ? new int[nlists*4] : defaultbuffer;
startpointer = new int[nlists]; startpointer = new int[nlists];
} }
@ -29,8 +31,10 @@ public class LinkedListContainer {
* @param listNr the list to add the data to * @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) { public void addDataElement( int listNr, int data )
if (size + 2 > ia.length) { {
if ( size + 2 > ia.length )
{
resize(); resize();
} }
ia[size++] = startpointer[ listNr ]; ia[size++] = startpointer[ listNr ];
@ -44,10 +48,12 @@ public class LinkedListContainer {
* @param listNr the list to initialize * @param listNr the list to initialize
* @return the number of entries in that list * @return the number of entries in that list
*/ */
public int initList(int listNr) { public int initList( int listNr )
{
int cnt = 0; int cnt = 0;
int lp = listpointer = startpointer[ listNr ]; int lp = listpointer = startpointer[ listNr ];
while (lp != 0) { while( lp != 0 )
{
lp = ia[ lp-1 ]; lp = ia[ lp-1 ];
cnt++; cnt++;
} }
@ -61,8 +67,10 @@ public class LinkedListContainer {
* @return the data element * @return the data element
* @throws IllegalArgumentException if no more element * @throws IllegalArgumentException if no more element
*/ */
public int getDataElement() { public int getDataElement()
if (listpointer == 0) { {
if ( listpointer == 0 )
{
throw new IllegalArgumentException( "no more element!" ); throw new IllegalArgumentException( "no more element!" );
} }
int data = ia[ listpointer ]; int data = ia[ listpointer ];
@ -70,7 +78,8 @@ public class LinkedListContainer {
return data; return data;
} }
private void resize() { private void resize()
{
int[] ia2 = new int[2*ia.length]; int[] ia2 = new int[2*ia.length];
System.arraycopy( ia, 0, ia2, 0, ia.length ); System.arraycopy( ia, 0, ia2, 0, ia.length );
ia = ia2; 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 * a micro-cache is a data cache for an area of some square kilometers or some
* hundreds or thousands nodes * hundreds or thousands nodes
* <p> *
* This is the basic io-unit: always a full microcache is loaded from the * 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 * data-file if a node is requested at a position not yet covered by the caches
* already loaded * already loaded
* <p> *
* The nodes are represented in a compact way (typical 20-50 bytes per node), * 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 * 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. * supported to remove the nodes already consumed from the cache.
* <p> *
* The cache-internal data representation is different from that in the * The cache-internal data representation is different from that in the
* data-files, where a cache is encoded as a whole, allowing more * data-files, where a cache is encoded as a whole, allowing more
* redundancy-removal for a more compact encoding * redundancy-removal for a more compact encoding
*/ */
public class MicroCache extends ByteDataWriter { public class MicroCache extends ByteDataWriter
{
protected int[] faid; protected int[] faid;
protected int[] fapos; protected int[] fapos;
protected int size = 0; protected int size = 0;
@ -34,21 +35,25 @@ public class MicroCache extends ByteDataWriter {
public static boolean debug = false; public static boolean debug = false;
protected MicroCache(byte[] ab) { protected MicroCache( byte[] ab )
{
super( ab ); super( ab );
} }
public final static MicroCache emptyNonVirgin = new MicroCache( null ); public final static MicroCache emptyNonVirgin = new MicroCache( null );
static { static
{
emptyNonVirgin.virgin = false; emptyNonVirgin.virgin = false;
} }
public static MicroCache emptyCache() { public static MicroCache emptyCache()
{
return new MicroCache( null ); // TODO: singleton? return new MicroCache( null ); // TODO: singleton?
} }
protected void init(int size) { protected void init( int size )
{
this.size = size; this.size = size;
delcount = 0; delcount = 0;
delbytes = 0; delbytes = 0;
@ -57,31 +62,35 @@ public class MicroCache extends ByteDataWriter {
p2size >>= 1; p2size >>= 1;
} }
public final void finishNode(long id) { public final void finishNode( long id )
{
fapos[size] = aboffset; fapos[size] = aboffset;
faid[size] = shrinkId( id ); faid[size] = shrinkId( id );
size++; size++;
} }
public final void discardNode() { public final void discardNode()
{
aboffset = startPos( size ); aboffset = startPos( size );
} }
public final int getSize() { public final int getSize()
{
return size; return size;
} }
public final int getDataSize() { public final int getDataSize()
{
return ab == null ? 0 : ab.length; return ab == null ? 0 : ab.length;
} }
/** /**
* Set the internal reader (aboffset, aboffsetEnd) to the body data for the given id * 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 * 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), * (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. * 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 * Current implementation always returns false for not-found, however, for
* regression testing, at least for the case that is most likely a bug * regression testing, at least for the case that is most likely a bug
* (node found but marked as deleted = ready for garbage collection * (node found but marked as deleted = ready for garbage collection
@ -89,8 +98,10 @@ public class MicroCache extends ByteDataWriter {
* *
* @return true if id was found * @return true if id was found
*/ */
public final boolean getAndClear(long id64) { public final boolean getAndClear( long id64 )
if (size == 0) { {
if ( size == 0 )
{
return false; return false;
} }
int id = shrinkId( id64 ); int id = shrinkId( id64 );
@ -98,22 +109,27 @@ public class MicroCache extends ByteDataWriter {
int offset = p2size; int offset = p2size;
int n = 0; int n = 0;
while (offset > 0) { while (offset > 0)
{
int nn = n + offset; int nn = n + offset;
if (nn < size && a[nn] <= id) { if ( nn < size && a[nn] <= id )
{
n = nn; n = nn;
} }
offset >>= 1; offset >>= 1;
} }
if (a[n] == id) { if ( a[n] == id )
if ((fapos[n] & 0x80000000) == 0) { {
if ( ( fapos[n] & 0x80000000 ) == 0 )
{
aboffset = startPos( n ); aboffset = startPos( n );
aboffsetEnd = fapos[n]; aboffsetEnd = fapos[n];
fapos[n] |= 0x80000000; // mark deleted fapos[n] |= 0x80000000; // mark deleted
delbytes += aboffsetEnd - aboffset; delbytes += aboffsetEnd - aboffset;
delcount++; delcount++;
return true; return true;
} else // .. marked as deleted }
else // .. marked as deleted
{ {
// throw new RuntimeException( "MicroCache: node already consumed: id=" + id ); // throw new RuntimeException( "MicroCache: node already consumed: id=" + id );
} }
@ -121,31 +137,39 @@ public class MicroCache extends ByteDataWriter {
return false; return false;
} }
protected final int startPos(int n) { protected final int startPos( int n )
{
return n > 0 ? fapos[n - 1] & 0x7fffffff : 0; return n > 0 ? fapos[n - 1] & 0x7fffffff : 0;
} }
public final int collect(int threshold) { public final int collect( int threshold )
if (delcount <= threshold) { {
if ( delcount <= threshold )
{
return 0; return 0;
} }
virgin = false; virgin = false;
int nsize = size - delcount; int nsize = size - delcount;
if (nsize == 0) { if ( nsize == 0 )
{
faid = null; faid = null;
fapos = null; fapos = null;
} else { }
else
{
int[] nfaid = new int[nsize]; int[] nfaid = new int[nsize];
int[] nfapos = new int[nsize]; int[] nfapos = new int[nsize];
int idx = 0; int idx = 0;
byte[] nab = new byte[ab.length - delbytes]; byte[] nab = new byte[ab.length - delbytes];
int nab_off = 0; int nab_off = 0;
for (int i = 0; i < size; i++) { for ( int i = 0; i < size; i++ )
{
int pos = fapos[i]; int pos = fapos[i];
if ((pos & 0x80000000) == 0) { if ( ( pos & 0x80000000 ) == 0 )
{
int start = startPos( i ); int start = startPos( i );
int end = fapos[i]; int end = fapos[i];
int len = end - start; int len = end - start;
@ -165,11 +189,13 @@ public class MicroCache extends ByteDataWriter {
return deleted; return deleted;
} }
public final void unGhost() { public final void unGhost()
{
ghost = false; ghost = false;
delcount = 0; delcount = 0;
delbytes = 0; delbytes = 0;
for (int i = 0; i < size; i++) { for ( int i = 0; i < size; i++ )
{
fapos[i] &= 0x7fffffff; // clear deleted flags fapos[i] &= 0x7fffffff; // clear deleted flags
} }
} }
@ -177,7 +203,8 @@ public class MicroCache extends ByteDataWriter {
/** /**
* @return the 64-bit global id for the given cache-position * @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]; int id32 = faid[i];
return expandId( id32 ); return expandId( id32 );
} }
@ -187,7 +214,8 @@ public class MicroCache extends ByteDataWriter {
* *
* @see #shrinkId * @see #shrinkId
*/ */
public long expandId(int id32) { public long expandId( int id32 )
{
throw new IllegalArgumentException( "expandId for empty cache" ); throw new IllegalArgumentException( "expandId for empty cache" );
} }
@ -196,24 +224,28 @@ public class MicroCache extends ByteDataWriter {
* *
* @see #expandId * @see #expandId
*/ */
public int shrinkId(long id64) { public int shrinkId( long id64 )
{
throw new IllegalArgumentException( "shrinkId for empty cache" ); throw new IllegalArgumentException( "shrinkId for empty cache" );
} }
/** /**
* @return true if the given lon/lat position is internal for that micro-cache * @return true if the given lon/lat position is internal for that micro-cache
*/ */
public boolean isInternal(int ilon, int ilat) { public boolean isInternal( int ilon, int ilat )
{
throw new IllegalArgumentException( "isInternal for empty cache" ); throw new IllegalArgumentException( "isInternal for empty cache" );
} }
/** /**
* (stasticially) encode the micro-cache into the format used in the datafiles * (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 * @return the size of the encoded data
*/ */
public int encodeMicroCache(byte[] buffer) { public int encodeMicroCache( byte[] buffer )
{
throw new IllegalArgumentException( "encodeMicroCache for empty cache" ); throw new IllegalArgumentException( "encodeMicroCache for empty cache" );
} }
@ -222,9 +254,11 @@ public class MicroCache extends ByteDataWriter {
* *
* @return null if equals, else a diff-report * @return null if equals, else a diff-report
*/ */
public String compareWith(MicroCache mc) { public String compareWith( MicroCache mc )
{
String msg = _compareWith( mc ); String msg = _compareWith( mc );
if (msg != null) { if ( msg != null )
{
StringBuilder sb = new StringBuilder( msg ); StringBuilder sb = new StringBuilder( msg );
sb.append( "\nencode cache:\n" ).append( summary() ); sb.append( "\nencode cache:\n" ).append( summary() );
sb.append( "\ndecode cache:\n" ).append( mc.summary() ); sb.append( "\ndecode cache:\n" ).append( mc.summary() );
@ -233,75 +267,96 @@ public class MicroCache extends ByteDataWriter {
return null; return null;
} }
private String summary() { private String summary()
{
StringBuilder sb = new StringBuilder( "size=" + size + " aboffset=" + aboffset ); StringBuilder sb = new StringBuilder( "size=" + size + " aboffset=" + aboffset );
for (int i = 0; i < size; i++) { for ( int i = 0; i < size; i++ )
{
sb.append( "\nidx=" + i + " faid=" + faid[i] + " fapos=" + fapos[i] ); sb.append( "\nidx=" + i + " faid=" + faid[i] + " fapos=" + fapos[i] );
} }
return sb.toString(); return sb.toString();
} }
private String _compareWith(MicroCache mc) { private String _compareWith( MicroCache mc )
if (size != mc.size) { {
return "size mismatch: " + size + "->" + mc.size; if ( size != mc.size )
{
return "size missmatch: " + size + "->" + mc.size;
} }
for (int i = 0; i < size; i++) { for ( int i = 0; i < size; i++ )
if (faid[i] != mc.faid[i]) { {
return "faid mismatch at index " + i + ":" + faid[i] + "->" + mc.faid[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 start = i > 0 ? fapos[i - 1] : 0;
int end = fapos[i] < mc.fapos[i] ? fapos[i] : mc.fapos[i]; int end = fapos[i] < mc.fapos[i] ? fapos[i] : mc.fapos[i];
int len = end - start; int len = end - start;
for (int offset = 0; offset < len; offset++) { for ( int offset = 0; offset < len; offset++ )
if (mc.ab.length <= start + offset) { {
if ( mc.ab.length <= start + offset )
{
return "data buffer too small"; return "data buffer too small";
} }
if (ab[start + offset] != mc.ab[start + offset]) { if ( ab[start + offset] != mc.ab[start + offset] )
return "data mismatch at index " + i + " offset=" + offset; {
return "data missmatch at index " + i + " offset=" + offset;
} }
} }
if (fapos[i] != mc.fapos[i]) { if ( fapos[i] != mc.fapos[i] )
return "fapos mismatch at index " + i + ":" + fapos[i] + "->" + mc.fapos[i]; {
return "fapos missmatch at index " + i + ":" + fapos[i] + "->" + mc.fapos[i];
} }
} }
if (aboffset != mc.aboffset) { if ( aboffset != mc.aboffset )
return "datasize mismatch: " + aboffset + "->" + mc.aboffset; {
return "datasize missmatch: " + aboffset + "->" + mc.aboffset;
} }
return null; return null;
} }
public void calcDelta(MicroCache mc1, MicroCache mc2) { public void calcDelta( MicroCache mc1, MicroCache mc2 )
{
int idx1 = 0; int idx1 = 0;
int idx2 = 0; int idx2 = 0;
while (idx1 < mc1.size || idx2 < mc2.size) { while( idx1 < mc1.size || idx2 < mc2.size )
{
int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE; int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE;
int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE; int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE;
int id; int id;
if (id1 >= id2) { if ( id1 >= id2 )
{
id = id2; id = id2;
int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0; int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
int len2 = mc2.fapos[idx2++] - start2; int len2 = mc2.fapos[idx2++] - start2;
if (id1 == id2) { if ( id1 == id2 )
{
// id exists in both caches, compare data // id exists in both caches, compare data
int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0; int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
int len1 = mc1.fapos[idx1++] - start1; int len1 = mc1.fapos[idx1++] - start1;
if (len1 == len2) { if ( len1 == len2 )
{
int i = 0; int i = 0;
while (i < len1) { while( i<len1 )
if (mc1.ab[start1 + i] != mc2.ab[start2 + i]) { {
if ( mc1.ab[start1+i] != mc2.ab[start2+i] )
{
break; break;
} }
i++; i++;
} }
if (i == len1) { if ( i == len1 )
{
continue; // same data -> do nothing continue; // same data -> do nothing
} }
} }
} }
write( mc2.ab, start2, len2 ); write( mc2.ab, start2, len2 );
} else { }
else
{
idx1++; idx1++;
id = id1; // deleted node id = id1; // deleted node
} }
@ -311,25 +366,31 @@ public class MicroCache extends ByteDataWriter {
} }
} }
public void addDelta(MicroCache mc1, MicroCache mc2, boolean keepEmptyNodes) { public void addDelta( MicroCache mc1, MicroCache mc2, boolean keepEmptyNodes )
{
int idx1 = 0; int idx1 = 0;
int idx2 = 0; int idx2 = 0;
while (idx1 < mc1.size || idx2 < mc2.size) { while( idx1 < mc1.size || idx2 < mc2.size )
{
int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE; int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE;
int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE; int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE;
if (id1 >= id2) { // data from diff file wins if ( id1 >= id2 ) // data from diff file wins
{
int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0; int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
int len2 = mc2.fapos[idx2++] - start2; int len2 = mc2.fapos[idx2++] - start2;
if (keepEmptyNodes || len2 > 0) { if ( keepEmptyNodes || len2 > 0 )
{
write( mc2.ab, start2, len2 ); write( mc2.ab, start2, len2 );
fapos[size] = aboffset; fapos[size] = aboffset;
faid[size++] = id2; faid[size++] = id2;
} }
if (id1 == id2) { // // id exists in both caches if ( id1 == id2 ) // // id exists in both caches
{
idx1++; idx1++;
} }
} else // use data from base file }
else // use data from base file
{ {
int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0; int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
int len1 = mc1.fapos[idx1++] - start1; int len1 = mc1.fapos[idx1++] - start1;

View file

@ -1,7 +1,6 @@
package btools.codec; package btools.codec;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
import btools.util.ByteDataReader; import btools.util.ByteDataReader;
import btools.util.IByteArrayUnifier; import btools.util.IByteArrayUnifier;
@ -10,12 +9,14 @@ import btools.util.IByteArrayUnifier;
* MicroCache2 is the new format that uses statistical encoding and * MicroCache2 is the new format that uses statistical encoding and
* is able to do access filtering and waypoint matching during encoding * 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 lonBase;
private int latBase; private int latBase;
private int cellsize; private int cellsize;
public MicroCache2(int size, byte[] databuffer, int lonIdx, int latIdx, int divisor) { public MicroCache2( int size, byte[] databuffer, int lonIdx, int latIdx, int divisor ) throws Exception
{
super( databuffer ); // sets ab=databuffer, aboffset=0 super( databuffer ); // sets ab=databuffer, aboffset=0
faid = new int[size]; faid = new int[size];
@ -26,13 +27,15 @@ public final class MicroCache2 extends MicroCache {
latBase = latIdx*cellsize; latBase = latIdx*cellsize;
} }
public byte[] readUnified(int len, IByteArrayUnifier u) { public byte[] readUnified( int len, IByteArrayUnifier u )
{
byte[] b = u.unify( ab, aboffset, len ); byte[] b = u.unify( ab, aboffset, len );
aboffset += len; aboffset += len;
return b; return b;
} }
public MicroCache2(StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher) { public MicroCache2( StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher ) throws Exception
{
super( null ); super( null );
cellsize = 1000000 / divisor; cellsize = 1000000 / divisor;
lonBase = lonIdx*cellsize; lonBase = lonIdx*cellsize;
@ -51,15 +54,16 @@ public final class MicroCache2 extends MicroCache {
fapos = size > dataBuffers.ibuf3.length ? new int[size] : dataBuffers.ibuf3; fapos = size > dataBuffers.ibuf3.length ? new int[size] : dataBuffers.ibuf3;
int[] alon = size > dataBuffers.alon.length ? new int[size] : dataBuffers.alon; int[] alon = size > dataBuffers.alon.length ? new int[size] : dataBuffers.alon;
int[] alat = size > dataBuffers.alat.length ? new int[size] : dataBuffers.alat; int[] alat = size > dataBuffers.alat.length ? new int[size] : dataBuffers.alat;
if (debug) if ( debug ) System.out.println( "*** decoding cache of size=" + size + " for lonIdx=" + lonIdx + " latIdx=" + latIdx );
System.out.println("*** decoding cache of size=" + size + " for lonIdx=" + lonIdx + " latIdx=" + latIdx);
bc.decodeSortedArray( faid, 0, size, 29, 0 ); bc.decodeSortedArray( faid, 0, size, 29, 0 );
for (int n = 0; n < size; n++) { for( int n = 0; n<size; n++ )
{
long id64 = expandId( faid[n] ); long id64 = expandId( faid[n] );
alon[n] = (int)(id64 >> 32); alon[n] = (int)(id64 >> 32);
alat[n] = (int)(id64 & 0xffffffff); alat[n] = (int)(id64 & 0xffffffff);
@ -76,24 +80,30 @@ public final class MicroCache2 extends MicroCache {
LinkedListContainer reverseLinks = new LinkedListContainer( size, dataBuffers.ibuf1 ); LinkedListContainer reverseLinks = new LinkedListContainer( size, dataBuffers.ibuf1 );
int selev = 0; 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 ilon = alon[n];
int ilat = alat[n]; int ilat = alat[n];
// future escapes (turn restrictions?) // future escapes (turn restrictions?)
short trExceptions = 0; short trExceptions = 0;
int featureId = bc.decodeVarBits(); int featureId = bc.decodeVarBits();
if (featureId == 13) { if ( featureId == 13 )
{
fapos[n] = aboffset; 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) continue; // empty node escape (delta files only)
} }
while (featureId != 0) { while( featureId != 0 )
{
int bitsize = bc.decodeNoisyNumber( 5 ); int bitsize = bc.decodeNoisyNumber( 5 );
if (featureId == 2) { // exceptions to turn-restriction if ( featureId == 2 ) // exceptions to turn-restriction
{
trExceptions = (short)bc.decodeBounded( 1023 ); trExceptions = (short)bc.decodeBounded( 1023 );
} else if (featureId == 1) { // turn-restriction }
else if ( featureId == 1 ) // turn-restriction
{
writeBoolean( true ); writeBoolean( true );
writeShort( trExceptions ); // exceptions from previous feature writeShort( trExceptions ); // exceptions from previous feature
trExceptions = 0; trExceptions = 0;
@ -103,7 +113,9 @@ public final class MicroCache2 extends MicroCache {
writeInt( ilat + bc.decodeNoisyDiff( 10 ) ); // fromLat writeInt( ilat + bc.decodeNoisyDiff( 10 ) ); // fromLat
writeInt( ilon + bc.decodeNoisyDiff( 10 ) ); // toLon writeInt( ilon + bc.decodeNoisyDiff( 10 ) ); // toLon
writeInt( ilat + bc.decodeNoisyDiff( 10 ) ); // toLat writeInt( ilat + bc.decodeNoisyDiff( 10 ) ); // toLat
} else { }
else
{
for( int i=0; i< bitsize; i++ ) bc.decodeBit(); // unknown feature, just skip for( int i=0; i< bitsize; i++ ) bc.decodeBit(); // unknown feature, just skip
} }
featureId = bc.decodeVarBits(); featureId = bc.decodeVarBits();
@ -116,9 +128,9 @@ public final class MicroCache2 extends MicroCache {
writeVarBytes( nodeTags == null ? null : nodeTags.data ); writeVarBytes( nodeTags == null ? null : nodeTags.data );
int links = bc.decodeNoisyNumber( 1 ); int links = bc.decodeNoisyNumber( 1 );
if (debug) if ( debug ) System.out.println( "*** decoding node " + ilon + "/" + ilat + " with links=" + links );
System.out.println("*** decoding node " + ilon + "/" + ilat + " with links=" + links); for( int li=0; li<links; li++ )
for (int li = 0; li < links; li++) { {
int sizeoffset = 0; int sizeoffset = 0;
int nodeIdx = n + nodeIdxDiff.decodeSignedValue(); int nodeIdx = n + nodeIdxDiff.decodeSignedValue();
@ -126,21 +138,24 @@ public final class MicroCache2 extends MicroCache {
int dlat_remaining; int dlat_remaining;
boolean isReverse = false; boolean isReverse = false;
if (nodeIdx != n) { // internal (forward-) link if ( nodeIdx != n ) // internal (forward-) link
{
dlon_remaining = alon[nodeIdx] - ilon; dlon_remaining = alon[nodeIdx] - ilon;
dlat_remaining = alat[nodeIdx] - ilat; dlat_remaining = alat[nodeIdx] - ilat;
} else { }
else
{
isReverse = bc.decodeBit(); isReverse = bc.decodeBit();
dlon_remaining = extLonDiff.decodeSignedValue(); dlon_remaining = extLonDiff.decodeSignedValue();
dlat_remaining = extLatDiff.decodeSignedValue(); dlat_remaining = extLatDiff.decodeSignedValue();
} }
if (debug) if ( debug ) System.out.println( "*** decoding link to " + (ilon+dlon_remaining) + "/" + (ilat+dlat_remaining) + " extern=" + (nodeIdx == n) );
System.out.println("*** decoding link to " + (ilon + dlon_remaining) + "/" + (ilat + dlat_remaining) + " extern=" + (nodeIdx == n));
TagValueWrapper wayTags = wayTagCoder.decodeTagValueSet(); TagValueWrapper wayTags = wayTagCoder.decodeTagValueSet();
boolean linkValid = wayTags != null || wayValidator == null; boolean linkValid = wayTags != null || wayValidator == null;
if (linkValid) { if ( linkValid )
{
int startPointer = aboffset; int startPointer = aboffset;
sizeoffset = writeSizePlaceHolder(); sizeoffset = writeSizePlaceHolder();
@ -148,7 +163,8 @@ public final class MicroCache2 extends MicroCache {
writeVarLengthSigned( dlat_remaining ); writeVarLengthSigned( dlat_remaining );
validBits[ n >> 5 ] |= 1 << n; // mark source-node valid validBits[ n >> 5 ] |= 1 << n; // mark source-node valid
if (nodeIdx != n) { // valid internal (forward-) link if ( nodeIdx != n ) // valid internal (forward-) link
{
reverseLinks.addDataElement( nodeIdx, n ); // register reverse link reverseLinks.addDataElement( nodeIdx, n ); // register reverse link
finaldatasize += 1 + aboffset-startPointer; // reserve place for reverse finaldatasize += 1 + aboffset-startPointer; // reserve place for reverse
validBits[ nodeIdx >> 5 ] |= 1 << nodeIdx; // mark target-node valid validBits[ nodeIdx >> 5 ] |= 1 << nodeIdx; // mark target-node valid
@ -156,12 +172,15 @@ public final class MicroCache2 extends MicroCache {
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; WaypointMatcher matcher = wayTags == null || wayTags.accessType < 2 ? null : waypointMatcher;
int ilontarget = ilon + dlon_remaining; int ilontarget = ilon + dlon_remaining;
int ilattarget = ilat + dlat_remaining; int ilattarget = ilat + dlat_remaining;
if (matcher != null) { if ( matcher != null )
if (!matcher.start(ilon, ilat, ilontarget, ilattarget)) { {
if ( !matcher.start( ilon, ilat, ilontarget, ilattarget ) )
{
matcher = null; matcher = null;
} }
} }
@ -169,25 +188,27 @@ public final class MicroCache2 extends MicroCache {
int transcount = bc.decodeVarBits(); int transcount = bc.decodeVarBits();
if ( debug ) System.out.println( "*** decoding geometry with count=" + transcount ); if ( debug ) System.out.println( "*** decoding geometry with count=" + transcount );
int count = transcount+1; int count = transcount+1;
for (int i = 0; i < transcount; i++) { for( int i=0; i<transcount; i++ )
{
int dlon = bc.decodePredictedValue( dlon_remaining/count ); int dlon = bc.decodePredictedValue( dlon_remaining/count );
int dlat = bc.decodePredictedValue( dlat_remaining/count ); int dlat = bc.decodePredictedValue( dlat_remaining/count );
dlon_remaining -= dlon; dlon_remaining -= dlon;
dlat_remaining -= dlat; dlat_remaining -= dlat;
count--; count--;
int elediff = transEleDiff.decodeSignedValue(); int elediff = transEleDiff.decodeSignedValue();
if (wayTags != null) { if ( wayTags != null )
{
writeVarLengthSigned( dlon ); writeVarLengthSigned( dlon );
writeVarLengthSigned( dlat ); writeVarLengthSigned( dlat );
writeVarLengthSigned( elediff ); writeVarLengthSigned( elediff );
} }
if (matcher != null) if ( matcher != null ) matcher.transferNode( ilontarget - dlon_remaining, ilattarget - dlat_remaining );
matcher.transferNode(ilontarget - dlon_remaining, ilattarget - dlat_remaining);
} }
if ( matcher != null ) matcher.end(); if ( matcher != null ) matcher.end();
} }
if (linkValid) { if ( linkValid )
{
injectSize( sizeoffset ); injectSize( sizeoffset );
} }
} }
@ -197,9 +218,11 @@ public final class MicroCache2 extends MicroCache {
// calculate final data size // calculate final data size
int finalsize = 0; int finalsize = 0;
int startpos = 0; int startpos = 0;
for (int i = 0; i < size; i++) { for( int i=0; i<size; i++ )
{
int endpos = fapos[i]; int endpos = fapos[i];
if ((validBits[i >> 5] & (1 << i)) != 0) { if ( ( validBits[ i >> 5 ] & (1 << i ) ) != 0 )
{
finaldatasize += endpos-startpos; finaldatasize += endpos-startpos;
finalsize++; finalsize++;
} }
@ -217,9 +240,11 @@ public final class MicroCache2 extends MicroCache {
size = 0; size = 0;
startpos = 0; startpos = 0;
for (int n = 0; n < sizeOld; n++) { for ( int n = 0; n < sizeOld; n++ )
{
int endpos = faposOld[n]; int endpos = faposOld[n];
if ((validBits[n >> 5] & (1 << n)) != 0) { if ( ( validBits[ n >> 5 ] & (1 << n ) ) != 0 )
{
int len = endpos - startpos; int len = endpos - startpos;
System.arraycopy( abOld, startpos, ab, aboffset, len ); System.arraycopy( abOld, startpos, ab, aboffset, len );
if ( debug ) if ( debug )
@ -230,7 +255,8 @@ public final class MicroCache2 extends MicroCache {
if ( debug ) if ( debug )
System.out.println( "*** appending " + cnt + " reverse links for node " + n ); 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 nodeIdx = reverseLinks.getDataElement();
int sizeoffset = writeSizePlaceHolder(); int sizeoffset = writeSizePlaceHolder();
writeVarLengthSigned( alon[nodeIdx] - alon[n] ); writeVarLengthSigned( alon[nodeIdx] - alon[n] );
@ -248,11 +274,13 @@ public final class MicroCache2 extends MicroCache {
} }
@Override @Override
public long expandId(int id32) { public long expandId( int id32 )
{
int dlon = 0; int dlon = 0;
int dlat = 0; int dlat = 0;
for (int bm = 1; bm < 0x8000; bm <<= 1) { for( int bm = 1; bm < 0x8000; bm <<= 1 )
{
if ( (id32 & 1) != 0 ) dlon |= bm; if ( (id32 & 1) != 0 ) dlon |= bm;
if ( (id32 & 2) != 0 ) dlat |= bm; if ( (id32 & 2) != 0 ) dlat |= bm;
id32 >>= 2; id32 >>= 2;
@ -265,14 +293,16 @@ public final class MicroCache2 extends MicroCache {
} }
@Override @Override
public int shrinkId(long id64) { public int shrinkId( long id64 )
{
int lon32 = (int)(id64 >> 32); int lon32 = (int)(id64 >> 32);
int lat32 = (int)(id64 & 0xffffffff); int lat32 = (int)(id64 & 0xffffffff);
int dlon = lon32 - lonBase; int dlon = lon32 - lonBase;
int dlat = lat32 - latBase; int dlat = lat32 - latBase;
int id32 = 0; int id32 = 0;
for (int bm = 0x4000; bm > 0; bm >>= 1) { for( int bm = 0x4000; bm > 0; bm >>= 1 )
{
id32 <<= 2; id32 <<= 2;
if ( ( dlon & bm ) != 0 ) id32 |= 1; if ( ( dlon & bm ) != 0 ) id32 |= 1;
if ( ( dlat & bm ) != 0 ) id32 |= 2; if ( ( dlat & bm ) != 0 ) id32 |= 2;
@ -281,16 +311,19 @@ public final class MicroCache2 extends MicroCache {
} }
@Override @Override
public boolean isInternal(int ilon, int ilat) { public boolean isInternal( int ilon, int ilat )
{
return ilon >= lonBase && ilon < lonBase + cellsize return ilon >= lonBase && ilon < lonBase + cellsize
&& ilat >= latBase && ilat < latBase + cellsize; && ilat >= latBase && ilat < latBase + cellsize;
} }
@Override @Override
public int encodeMicroCache(byte[] buffer) { public int encodeMicroCache( byte[] buffer )
Map<Long, Integer> idMap = new HashMap<>(); {
for (int n = 0; n < size; n++) { // loop over nodes HashMap<Long,Integer> idMap = new HashMap<Long,Integer>();
idMap.put(expandId(faid[n]), n); 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 linkCounts = new IntegerFifo3Pass( 256 );
@ -307,7 +340,8 @@ public final class MicroCache2 extends MicroCache {
int netdatasize = 0; 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 dostats = pass == 3;
boolean dodebug = debug && pass == 3; boolean dodebug = debug && pass == 3;
@ -338,25 +372,28 @@ public final class MicroCache2 extends MicroCache {
if ( dodebug ) System.out.println( "*** encoding cache of size=" + size ); if ( dodebug ) System.out.println( "*** encoding cache of size=" + size );
int lastSelev = 0; int lastSelev = 0;
for (int n = 0; n < size; n++) { // loop over nodes for( int n=0; n<size; n++ ) // loop over nodes
{
aboffset = startPos( n ); aboffset = startPos( n );
aboffsetEnd = fapos[n]; aboffsetEnd = fapos[n];
if (dodebug) if ( dodebug ) System.out.println( "*** encoding node " + n + " from " + aboffset + " to " + aboffsetEnd );
System.out.println("*** encoding node " + n + " from " + aboffset + " to " + aboffsetEnd);
long id64 = expandId( faid[n] ); long id64 = expandId( faid[n] );
int ilon = (int)(id64 >> 32); int ilon = (int)(id64 >> 32);
int ilat = (int)(id64 & 0xffffffff); int ilat = (int)(id64 & 0xffffffff);
if (aboffset == aboffsetEnd) { if ( aboffset == aboffsetEnd )
{
bc.encodeVarBits( 13 ); // empty node escape (delta files only) bc.encodeVarBits( 13 ); // empty node escape (delta files only)
continue; continue;
} }
// write turn restrictions // write turn restrictions
while (readBoolean()) { while( readBoolean() )
{
short exceptions = readShort(); // except bikes, psv, ... short exceptions = readShort(); // except bikes, psv, ...
if (exceptions != 0) { if ( exceptions != 0 )
{
bc.encodeVarBits( 2 ); // 2 = tr exceptions bc.encodeVarBits( 2 ); // 2 = tr exceptions
bc.encodeNoisyNumber( 10 , 5 ); // bit-count bc.encodeNoisyNumber( 10 , 5 ); // bit-count
bc.encodeBounded( 1023 , exceptions & 1023 ); bc.encodeBounded( 1023 , exceptions & 1023 );
@ -387,7 +424,8 @@ public final class MicroCache2 extends MicroCache {
if ( dostats ) bc.assignBits( "link-counts" ); if ( dostats ) bc.assignBits( "link-counts" );
nlinks = 0; nlinks = 0;
while (hasMoreData()) { // loop over links while( hasMoreData() ) // loop over links
{
// read link data // read link data
int startPointer = aboffset; int startPointer = aboffset;
int endPointer = getEndPointer(); int endPointer = getEndPointer();
@ -399,32 +437,35 @@ public final class MicroCache2 extends MicroCache {
boolean isReverse = ( sizecode & 1 ) != 0; boolean isReverse = ( sizecode & 1 ) != 0;
int descSize = sizecode >> 1; int descSize = sizecode >> 1;
byte[] description = null; byte[] description = null;
if (descSize > 0) { if ( descSize > 0 )
{
description = new byte[descSize]; description = new byte[descSize];
readFully( description ); readFully( description );
} }
long link64 = ((long)ilonlink)<<32 | ilatlink; long link64 = ((long)ilonlink)<<32 | ilatlink;
Integer idx = idMap.get(link64); Integer idx = idMap.get( Long.valueOf( link64 ) );
boolean isInternal = idx != null; boolean isInternal = idx != null;
if (isReverse && isInternal) { if ( isReverse && isInternal )
if (dodebug) {
System.out.println("*** NOT encoding link reverse=" + isReverse + " internal=" + isInternal); if ( dodebug ) System.out.println( "*** NOT encoding link reverse=" + isReverse + " internal=" + isInternal );
netdatasize -= aboffset-startPointer; netdatasize -= aboffset-startPointer;
continue; // do not encode internal reverse links continue; // do not encode internal reverse links
} }
if (dodebug) if ( dodebug ) System.out.println( "*** encoding link reverse=" + isReverse + " internal=" + isInternal );
System.out.println("*** encoding link reverse=" + isReverse + " internal=" + isInternal);
nlinks++; nlinks++;
if (isInternal) { if ( isInternal )
int nodeIdx = idx; {
int nodeIdx = idx.intValue();
if ( dodebug ) System.out.println( "*** target nodeIdx=" + nodeIdx ); if ( dodebug ) System.out.println( "*** target nodeIdx=" + nodeIdx );
if ( nodeIdx == n ) throw new RuntimeException( "ups: self ref?" ); if ( nodeIdx == n ) throw new RuntimeException( "ups: self ref?" );
nodeIdxDiff.encodeSignedValue( nodeIdx - n ); nodeIdxDiff.encodeSignedValue( nodeIdx - n );
if ( dostats ) bc.assignBits( "nodeIdx" ); if ( dostats ) bc.assignBits( "nodeIdx" );
} else { }
else
{
nodeIdxDiff.encodeSignedValue( 0 ); nodeIdxDiff.encodeSignedValue( 0 );
bc.encodeBit( isReverse ); bc.encodeBit( isReverse );
extLonDiff.encodeSignedValue( ilonlink - ilon ); extLonDiff.encodeSignedValue( ilonlink - ilon );
@ -434,7 +475,8 @@ public final class MicroCache2 extends MicroCache {
wayTagCoder.encodeTagValueSet( description ); wayTagCoder.encodeTagValueSet( description );
if ( dostats ) bc.assignBits( "wayDescIdx" ); if ( dostats ) bc.assignBits( "wayDescIdx" );
if (!isReverse) { if ( !isReverse )
{
byte[] geometry = readDataUntil( endPointer ); byte[] geometry = readDataUntil( endPointer );
// write transition nodes // write transition nodes
int count = transCounts.getNext(); int count = transCounts.getNext();
@ -442,12 +484,14 @@ public final class MicroCache2 extends MicroCache {
bc.encodeVarBits( count++ ); bc.encodeVarBits( count++ );
if ( dostats ) bc.assignBits( "transcount" ); if ( dostats ) bc.assignBits( "transcount" );
int transcount = 0; int transcount = 0;
if (geometry != null) { if ( geometry != null )
{
int dlon_remaining = ilonlink - ilon; int dlon_remaining = ilonlink - ilon;
int dlat_remaining = ilatlink - ilat; int dlat_remaining = ilatlink - ilat;
ByteDataReader r = new ByteDataReader( geometry ); ByteDataReader r = new ByteDataReader( geometry );
while (r.hasMoreData()) { while ( r.hasMoreData() )
{
transcount++; transcount++;
int dlon = r.readVarLengthSigned(); int dlon = r.readVarLengthSigned();
@ -467,7 +511,8 @@ public final class MicroCache2 extends MicroCache {
} }
linkCounts.add( nlinks ); linkCounts.add( nlinks );
} }
if (pass == 3) { if ( pass == 3 )
{
return bc.closeAndGetEncodedLength(); return bc.closeAndGetEncodedLength();
} }
} }

View file

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

View file

@ -1,23 +1,26 @@
package btools.codec; package btools.codec;
import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import btools.util.BitCoderContext; import btools.util.BitCoderContext;
public final class StatCoderContext extends BitCoderContext { public final class StatCoderContext extends BitCoderContext
private static Map<String, long[]> statsPerName; {
private static TreeMap<String, long[]> statsPerName;
private long lastbitpos = 0; private long lastbitpos = 0;
private static final int[] noisy_bits = new int[1024]; private static final int[] noisy_bits = new int[1024];
static { static
{
// noisybits lookup // noisybits lookup
for (int i = 0; i < 1024; i++) { for( int i=0; i<1024; i++ )
{
int p = i; int p = i;
int noisybits = 0; int noisybits = 0;
while (p > 2) { while (p > 2)
{
noisybits++; noisybits++;
p >>= 1; p >>= 1;
} }
@ -26,7 +29,8 @@ public final class StatCoderContext extends BitCoderContext {
} }
public StatCoderContext(byte[] ab) { public StatCoderContext( byte[] ab )
{
super( ab ); super( ab );
} }
@ -36,13 +40,16 @@ public final class StatCoderContext extends BitCoderContext {
* *
* @see #getBitReport * @see #getBitReport
*/ */
public void assignBits(String name) { public void assignBits( String name )
{
long bitpos = getWritingBitPosition(); long bitpos = getWritingBitPosition();
if (statsPerName == null) { if ( statsPerName == null )
statsPerName = new TreeMap<>(); {
statsPerName = new TreeMap<String, long[]>();
} }
long[] stats = statsPerName.get( name ); long[] stats = statsPerName.get( name );
if (stats == null) { if ( stats == null )
{
stats = new long[2]; stats = new long[2];
statsPerName.put( name, stats ); statsPerName.put( name, stats );
} }
@ -56,12 +63,15 @@ public final class StatCoderContext extends BitCoderContext {
* *
* @see #assignBits * @see #assignBits
*/ */
public static String getBitReport() { public static String getBitReport()
if (statsPerName == null) { {
if ( statsPerName == null )
{
return "<empty bit report>"; return "<empty bit report>";
} }
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (String name : statsPerName.keySet()) { for ( String name : statsPerName.keySet() )
{
long[] stats = statsPerName.get( name ); long[] stats = statsPerName.get( name );
sb.append( name + " count=" + stats[1] + " bits=" + stats[0] + "\n" ); sb.append( name + " count=" + stats[1] + " bits=" + stats[0] + "\n" );
} }
@ -75,11 +85,14 @@ public final class StatCoderContext extends BitCoderContext {
* *
* @see #decodeNoisyNumber * @see #decodeNoisyNumber
*/ */
public void encodeNoisyNumber(int value, int noisybits) { public void encodeNoisyNumber( int value, int noisybits )
if (value < 0) { {
if ( value < 0 )
{
throw new IllegalArgumentException( "encodeVarBits expects positive value" ); throw new IllegalArgumentException( "encodeVarBits expects positive value" );
} }
if (noisybits > 0) { if ( noisybits > 0 )
{
int mask = 0xffffffff >>> ( 32 - noisybits ); int mask = 0xffffffff >>> ( 32 - noisybits );
encodeBounded( mask, value & mask ); encodeBounded( mask, value & mask );
value >>= noisybits; value >>= noisybits;
@ -93,7 +106,8 @@ public final class StatCoderContext extends BitCoderContext {
* *
* @see #encodeNoisyNumber * @see #encodeNoisyNumber
*/ */
public int decodeNoisyNumber(int noisybits) { public int decodeNoisyNumber( int noisybits )
{
int value = decodeBits( noisybits ); int value = decodeBits( noisybits );
return value | ( decodeVarBits() << noisybits ); return value | ( decodeVarBits() << noisybits );
} }
@ -104,15 +118,18 @@ public final class StatCoderContext extends BitCoderContext {
* *
* @see #decodeNoisyDiff * @see #decodeNoisyDiff
*/ */
public void encodeNoisyDiff(int value, int noisybits) { public void encodeNoisyDiff( int value, int noisybits )
if (noisybits > 0) { {
if ( noisybits > 0 )
{
value += 1 << ( noisybits - 1 ); value += 1 << ( noisybits - 1 );
int mask = 0xffffffff >>> ( 32 - noisybits ); int mask = 0xffffffff >>> ( 32 - noisybits );
encodeBounded( mask, value & mask ); encodeBounded( mask, value & mask );
value >>= noisybits; value >>= noisybits;
} }
encodeVarBits( value < 0 ? -value : value ); encodeVarBits( value < 0 ? -value : value );
if (value != 0) { if ( value != 0 )
{
encodeBit( value < 0 ); encodeBit( value < 0 );
} }
} }
@ -123,14 +140,18 @@ public final class StatCoderContext extends BitCoderContext {
* *
* @see #encodeNoisyDiff * @see #encodeNoisyDiff
*/ */
public int decodeNoisyDiff(int noisybits) { public int decodeNoisyDiff( int noisybits )
{
int value = 0; int value = 0;
if (noisybits > 0) { if ( noisybits > 0 )
{
value = decodeBits( noisybits ) - ( 1 << ( noisybits - 1 ) ); value = decodeBits( noisybits ) - ( 1 << ( noisybits - 1 ) );
} }
int val2 = decodeVarBits() << noisybits; int val2 = decodeVarBits() << noisybits;
if (val2 != 0) { if ( val2 != 0 )
if (decodeBit()) { {
if ( decodeBit() )
{
val2 = -val2; val2 = -val2;
} }
} }
@ -143,11 +164,13 @@ public final class StatCoderContext extends BitCoderContext {
* *
* @see #decodePredictedValue * @see #decodePredictedValue
*/ */
public void encodePredictedValue(int value, int predictor) { public void encodePredictedValue( int value, int predictor )
{
int p = predictor < 0 ? -predictor : predictor; int p = predictor < 0 ? -predictor : predictor;
int noisybits = 0; int noisybits = 0;
while (p > 2) { while (p > 2)
{
noisybits++; noisybits++;
p >>= 1; p >>= 1;
} }
@ -160,10 +183,12 @@ public final class StatCoderContext extends BitCoderContext {
* *
* @see #encodePredictedValue * @see #encodePredictedValue
*/ */
public int decodePredictedValue(int predictor) { public int decodePredictedValue( int predictor )
{
int p = predictor < 0 ? -predictor : predictor; int p = predictor < 0 ? -predictor : predictor;
int noisybits = 0; int noisybits = 0;
while (p > 1023) { while (p > 1023)
{
noisybits++; noisybits++;
p >>= 1; p >>= 1;
} }
@ -177,20 +202,29 @@ public final class StatCoderContext extends BitCoderContext {
* bits per value that only depends on the typical distance between subsequent * bits per value that only depends on the typical distance between subsequent
* values and also benefits * values and also benefits
* *
* @param values the array to encode * @param values
* @param offset position in this array where to start * the array to encode
* @param subsize number of values to encode * @param offset
* @param nextbit bitmask with the most significant bit set to 1 * position in this array where to start
* @param mask should be 0 * @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) { public void encodeSortedArray( int[] values, int offset, int subsize, int nextbit, int mask )
if (subsize == 1) { // last-choice shortcut {
while (nextbit != 0) { if ( subsize == 1 ) // last-choice shortcut
{
while (nextbit != 0)
{
encodeBit( ( values[offset] & nextbit ) != 0 ); encodeBit( ( values[offset] & nextbit ) != 0 );
nextbit >>= 1; nextbit >>= 1;
} }
} }
if (nextbit == 0) { if ( nextbit == 0 )
{
return; return;
} }
@ -200,8 +234,10 @@ public final class StatCoderContext extends BitCoderContext {
// count 0-bit-fraction // count 0-bit-fraction
int i = offset; int i = offset;
int end = subsize + offset; int end = subsize + offset;
for (; i < end; i++) { for ( ; i < end; i++ )
if ((values[i] & mask) != data) { {
if ( ( values[i] & mask ) != data )
{
break; break;
} }
} }
@ -209,32 +245,45 @@ public final class StatCoderContext extends BitCoderContext {
int size2 = subsize - size1; int size2 = subsize - size1;
encodeBounded( subsize, size1 ); encodeBounded( subsize, size1 );
if (size1 > 0) { if ( size1 > 0 )
{
encodeSortedArray( values, offset, size1, nextbit >> 1, mask ); encodeSortedArray( values, offset, size1, nextbit >> 1, mask );
} }
if (size2 > 0) { if ( size2 > 0 )
{
encodeSortedArray( values, i, size2, nextbit >> 1, mask ); 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 * @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) { public void decodeSortedArray( int[] values, int offset, int subsize, int nextbitpos, int value )
if (subsize == 1) { // last-choice shortcut {
if (nextbitpos >= 0) { if ( subsize == 1 ) // last-choice shortcut
{
if ( nextbitpos >= 0 )
{
value |= decodeBitsReverse( nextbitpos+1 ); value |= decodeBitsReverse( nextbitpos+1 );
} }
values[offset] = value; values[offset] = value;
return; return;
} }
if (nextbitpos < 0) { if ( nextbitpos < 0 )
while (subsize-- > 0) { {
while (subsize-- > 0)
{
values[offset++] = value; values[offset++] = value;
} }
return; return;
@ -243,10 +292,12 @@ public final class StatCoderContext extends BitCoderContext {
int size1 = decodeBounded( subsize ); int size1 = decodeBounded( subsize );
int size2 = subsize - size1; int size2 = subsize - size1;
if (size1 > 0) { if ( size1 > 0 )
{
decodeSortedArray( values, offset, size1, nextbitpos-1, value ); decodeSortedArray( values, offset, size1, nextbitpos-1, value );
} }
if (size2 > 0) { if ( size2 > 0 )
{
decodeSortedArray( values, offset + size1, size2, nextbitpos-1, value | (1 << nextbitpos) ); decodeSortedArray( values, offset + size1, size2, nextbitpos-1, value | (1 << nextbitpos) );
} }
} }

View file

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

View file

@ -1,16 +1,17 @@
package btools.codec; package btools.codec;
public interface TagValueValidator { public interface TagValueValidator
{
/** /**
* @param tagValueSet the way description to check * @param tagValueSet the way description to check
* @return 0 = nothing, 1=no matching, 2=normal * @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 * TagValueWrapper wrapps a description bitmap
* to add the access-type * to add the access-type
*/ */
public final class TagValueWrapper { public final class TagValueWrapper
{
public byte[] data; public byte[] data;
public int accessType; public int accessType;
} }

View file

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

View file

@ -3,35 +3,50 @@ package btools.codec;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
public class LinkedListContainerTest { public class LinkedListContainerTest
{
@Test @Test
public void linkedListTest1() { public void linkedListTest1()
{
int nlists = 553; int nlists = 553;
LinkedListContainer llc = new LinkedListContainer( nlists, null ); LinkedListContainer llc = new LinkedListContainer( nlists, null );
for (int ln = 0; ln < nlists; ln++) { for ( int ln = 0; ln < nlists; ln++ )
for (int i = 0; i < 10; i++) { {
for ( int i = 0; i < 10; i++ )
{
llc.addDataElement( ln, ln * i ); llc.addDataElement( ln, ln * i );
} }
} }
for (int i = 0; i < 10; i++) { for ( int i = 0; i < 10; i++ )
for (int ln = 0; ln < nlists; ln++) { {
for ( int ln = 0; ln < nlists; ln++ )
{
llc.addDataElement( ln, ln * i ); llc.addDataElement( ln, ln * i );
} }
} }
for (int ln = 0; ln < nlists; ln++) { for ( int ln = 0; ln < nlists; ln++ )
{
int cnt = llc.initList( ln ); int cnt = llc.initList( ln );
Assert.assertEquals("list size test", 20, cnt); 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(); 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,23 +6,30 @@ import java.util.Random;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
public class StatCoderContextTest { public class StatCoderContextTest
{
@Test @Test
public void noisyVarBitsEncodeDecodeTest() { public void noisyVarBitsEncodeDecodeTest()
{
byte[] ab = new byte[40000]; byte[] ab = new byte[40000];
StatCoderContext ctx = new StatCoderContext( ab ); StatCoderContext ctx = new StatCoderContext( ab );
for (int noisybits = 1; noisybits < 12; noisybits++) { for ( int noisybits = 1; noisybits < 12; noisybits++ )
for (int i = 0; i < 1000; i++) { {
for ( int i = 0; i < 1000; i++ )
{
ctx.encodeNoisyNumber( i, noisybits ); ctx.encodeNoisyNumber( i, noisybits );
} }
} }
ctx.closeAndGetEncodedLength(); ctx.closeAndGetEncodedLength();
ctx = new StatCoderContext( ab ); ctx = new StatCoderContext( ab );
for (int noisybits = 1; noisybits < 12; noisybits++) { for ( int noisybits = 1; noisybits < 12; noisybits++ )
for (int i = 0; i < 1000; i++) { {
for ( int i = 0; i < 1000; i++ )
{
int value = ctx.decodeNoisyNumber( noisybits ); int value = ctx.decodeNoisyNumber( noisybits );
if (value != i) { if ( value != i )
{
Assert.fail( "value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value ); Assert.fail( "value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value );
} }
} }
@ -30,21 +37,27 @@ public class StatCoderContextTest {
} }
@Test @Test
public void noisySignedVarBitsEncodeDecodeTest() { public void noisySignedVarBitsEncodeDecodeTest()
{
byte[] ab = new byte[80000]; byte[] ab = new byte[80000];
StatCoderContext ctx = new StatCoderContext( ab ); StatCoderContext ctx = new StatCoderContext( ab );
for (int noisybits = 0; noisybits < 12; noisybits++) { for ( int noisybits = 0; noisybits < 12; noisybits++ )
for (int i = -1000; i < 1000; i++) { {
for ( int i = -1000; i < 1000; i++ )
{
ctx.encodeNoisyDiff( i, noisybits ); ctx.encodeNoisyDiff( i, noisybits );
} }
} }
ctx.closeAndGetEncodedLength(); ctx.closeAndGetEncodedLength();
ctx = new StatCoderContext( ab ); ctx = new StatCoderContext( ab );
for (int noisybits = 0; noisybits < 12; noisybits++) { for ( int noisybits = 0; noisybits < 12; noisybits++ )
for (int i = -1000; i < 1000; i++) { {
for ( int i = -1000; i < 1000; i++ )
{
int value = ctx.decodeNoisyDiff( noisybits ); int value = ctx.decodeNoisyDiff( noisybits );
if (value != i) { if ( value != i )
{
Assert.fail( "value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value ); Assert.fail( "value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value );
} }
} }
@ -52,21 +65,27 @@ public class StatCoderContextTest {
} }
@Test @Test
public void predictedValueEncodeDecodeTest() { public void predictedValueEncodeDecodeTest()
{
byte[] ab = new byte[80000]; byte[] ab = new byte[80000];
StatCoderContext ctx = new StatCoderContext( ab ); StatCoderContext ctx = new StatCoderContext( ab );
for (int value = -100; value < 100; value += 5) { for ( int value = -100; value < 100; value += 5 )
for (int predictor = -200; predictor < 200; predictor += 7) { {
for ( int predictor = -200; predictor < 200; predictor += 7 )
{
ctx.encodePredictedValue( value, predictor ); ctx.encodePredictedValue( value, predictor );
} }
} }
ctx.closeAndGetEncodedLength(); ctx.closeAndGetEncodedLength();
ctx = new StatCoderContext( ab ); ctx = new StatCoderContext( ab );
for (int value = -100; value < 100; value += 5) { for ( int value = -100; value < 100; value += 5 )
for (int predictor = -200; predictor < 200; predictor += 7) { {
for ( int predictor = -200; predictor < 200; predictor += 7 )
{
int decodedValue = ctx.decodePredictedValue( predictor ); int decodedValue = ctx.decodePredictedValue( predictor );
if (value != decodedValue) { if ( value != decodedValue )
{
Assert.fail( "value mismatch: value=" + value + " predictor=" + predictor + " decodedValue=" + decodedValue ); Assert.fail( "value mismatch: value=" + value + " predictor=" + predictor + " decodedValue=" + decodedValue );
} }
} }
@ -74,11 +93,13 @@ public class StatCoderContextTest {
} }
@Test @Test
public void sortedArrayEncodeDecodeTest() { public void sortedArrayEncodeDecodeTest()
{
Random rand = new Random(); Random rand = new Random();
int size = 1000000; int size = 1000000;
int[] values = new int[size]; 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[i] = rand.nextInt() & 0x0fffffff;
} }
values[5] = 175384; // force collision values[5] = 175384; // force collision
@ -99,8 +120,10 @@ public class StatCoderContextTest {
int[] decodedValues = new int[size]; 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++) { for ( int i = 0; i < size; i++ )
if (values[i] != decodedValues[i]) { {
if ( values[i] != decodedValues[i] )
{
Assert.fail( "mismatch at i=" + i + " " + 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 { plugins {
id 'brouter.library-conventions' id 'java-library'
} }
dependencies { dependencies {
implementation project(':brouter-mapaccess') implementation project(':brouter-mapaccess')
implementation project(':brouter-util') implementation project(':brouter-util')
implementation project(':brouter-expressions') implementation project(':brouter-expressions')
implementation project(':brouter-codec') 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; import btools.expressions.BExpressionContextWay;
final class KinematicModel extends OsmPathModel { final class KinematicModel extends OsmPathModel
public OsmPrePath createPrePath() { {
public OsmPrePath createPrePath()
{
return new KinematicPrePath(); return new KinematicPrePath();
} }
public OsmPath createPath() { public OsmPath createPath()
{
return new KinematicPath(); return new KinematicPath();
} }
@ -52,8 +55,10 @@ final class KinematicModel extends OsmPathModel {
private double lastBreakingSpeed; private double lastBreakingSpeed;
@Override @Override
public void init(BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String, String> extraParams) { public void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String,String> extraParams )
if (!initDone) { {
if ( !initDone )
{
ctxWay = expctxWay; ctxWay = expctxWay;
ctxNode = expctxNode; ctxNode = expctxNode;
wayIdxMaxspeed = ctxWay.getOutputVariableIndex( "maxspeed", false ); wayIdxMaxspeed = ctxWay.getOutputVariableIndex( "maxspeed", false );
@ -81,38 +86,46 @@ final class KinematicModel extends OsmPathModel {
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) { protected float getParam( String name, float defaultValue )
{
String sval = params == null ? null : params.get( name ); String sval = params == null ? null : params.get( name );
if (sval != null) { if ( sval != null )
{
return Float.parseFloat( sval ); return Float.parseFloat( sval );
} }
float v = ctxWay.getVariableValue( name, defaultValue ); float v = ctxWay.getVariableValue( name, defaultValue );
if (params != null) { if ( params != null )
{
params.put( name, "" + v ); params.put( name, "" + v );
} }
return v; return v;
} }
public float getWayMaxspeed() { public float getWayMaxspeed()
{
return ctxWay.getBuildInVariable( wayIdxMaxspeed ) / 3.6f; return ctxWay.getBuildInVariable( wayIdxMaxspeed ) / 3.6f;
} }
public float getWayMaxspeedExplicit() { public float getWayMaxspeedExplicit()
{
return ctxWay.getBuildInVariable( wayIdxMaxspeedExplicit ) / 3.6f; return ctxWay.getBuildInVariable( wayIdxMaxspeedExplicit ) / 3.6f;
} }
public float getWayMinspeed() { public float getWayMinspeed()
{
return ctxWay.getBuildInVariable( wayIdxMinspeed ) / 3.6f; return ctxWay.getBuildInVariable( wayIdxMinspeed ) / 3.6f;
} }
public float getNodeMaxspeed() { public float getNodeMaxspeed()
{
return ctxNode.getBuildInVariable( nodeIdxMaxspeed ) / 3.6f; return ctxNode.getBuildInVariable( nodeIdxMaxspeed ) / 3.6f;
} }
/** /**
* get the effective speed limit from the way-limit and vmax/vmin * get the effective speed limit from the way-limit and vmax/vmin
*/ */
public double getEffectiveSpeedLimit() { public double getEffectiveSpeedLimit( )
{
// performance related inline coding // performance related inline coding
double minspeed = getWayMinspeed(); double minspeed = getWayMinspeed();
double espeed = minspeed > vmax ? minspeed : vmax; double espeed = minspeed > vmax ? minspeed : vmax;
@ -123,8 +136,10 @@ final class KinematicModel extends OsmPathModel {
/** /**
* get the breaking speed for current balance-power (pw) and effective speed limit (vl) * get the breaking speed for current balance-power (pw) and effective speed limit (vl)
*/ */
public double getBreakingSpeed(double vl) { public double getBreakingSpeed( double vl )
if (vl == lastEffectiveLimit) { {
if ( vl == lastEffectiveLimit )
{
return lastBreakingSpeed; return lastBreakingSpeed;
} }
@ -132,7 +147,8 @@ final class KinematicModel extends OsmPathModel {
double pw2 = pw+p_standby; double pw2 = pw+p_standby;
double e = recup_efficiency; double e = recup_efficiency;
double x0 = pw2/vl+f_air*e*vl*vl+(1.-e)*f_roll; double x0 = pw2/vl+f_air*e*vl*vl+(1.-e)*f_roll;
for (int i = 0; i < 5; i++) { for(int i=0;i<5;i++)
{
double v2 = v*v; double v2 = v*v;
double x = pw2/v+f_air*e*v2 - x0; double x = pw2/v+f_air*e*v2 - x0;
double dx = 2.*e*f_air*v - pw2/v2; double dx = 2.*e*f_air*v - pw2/v2;

View file

@ -5,7 +5,11 @@
*/ */
package btools.router; 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 ekin; // kinetic energy (Joule)
private double totalTime; // travel time (seconds) private double totalTime; // travel time (seconds)
private double totalEnergy; // total route energy (Joule) private double totalEnergy; // total route energy (Joule)
@ -13,17 +17,20 @@ final class KinematicPath extends OsmPath {
private float floatingAngleRight; // sliding average right bend (degree) private float floatingAngleRight; // sliding average right bend (degree)
@Override @Override
protected void init(OsmPath orig) { protected void init( OsmPath orig )
{
KinematicPath origin = (KinematicPath)orig; KinematicPath origin = (KinematicPath)orig;
ekin = origin.ekin; ekin = origin.ekin;
totalTime = origin.totalTime; totalTime = origin.totalTime;
totalEnergy = origin.totalEnergy; totalEnergy = origin.totalEnergy;
floatingAngleLeft = origin.floatingAngleLeft; floatingAngleLeft = origin.floatingAngleLeft;
floatingAngleRight = origin.floatingAngleRight; floatingAngleRight = origin.floatingAngleRight;
priorityclassifier = origin.priorityclassifier;
} }
@Override @Override
protected void resetState() { protected void resetState()
{
ekin = 0.; ekin = 0.;
totalTime = 0.; totalTime = 0.;
totalEnergy = 0.; totalEnergy = 0.;
@ -32,37 +39,45 @@ final class KinematicPath extends OsmPath {
} }
@Override @Override
protected double processWaySection(RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier) { 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; KinematicModel km = (KinematicModel)rc.pm;
double cost = 0.; double cost = 0.;
double extraTime = 0.; double extraTime = 0.;
if (isStartpoint) { if ( isStartpoint )
{
// for forward direction, we start with target speed // for forward direction, we start with target speed
if (!rc.inverseDirection) { if ( !rc.inverseDirection )
{
extraTime = 0.5 * (1. - cosangle ) * 40.; // 40 seconds turn penalty extraTime = 0.5 * (1. - cosangle ) * 40.; // 40 seconds turn penalty
} }
} else { }
else
{
double turnspeed = 999.; // just high double turnspeed = 999.; // just high
if (km.turnAngleDecayTime != 0.) { // process turn-angle slowdown if ( km.turnAngleDecayTime != 0. ) // process turn-angle slowdown
{
if ( angle < 0 ) floatingAngleLeft -= (float)angle; if ( angle < 0 ) floatingAngleLeft -= (float)angle;
else floatingAngleRight += (float)angle; else floatingAngleRight += (float)angle;
float aa = Math.max( floatingAngleLeft, floatingAngleRight ); 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 distanceTime = dist / curveSpeed;
double decayFactor = Math.exp(-distanceTime / km.turnAngleDecayTime); double decayFactor = FastMath.exp( - distanceTime / km.turnAngleDecayTime );
floatingAngleLeft = (float)( floatingAngleLeft * decayFactor ); floatingAngleLeft = (float)( floatingAngleLeft * decayFactor );
floatingAngleRight = (float)( floatingAngleRight * decayFactor ); floatingAngleRight = (float)( floatingAngleRight * decayFactor );
if (curveSpeed < 20.) { if ( curveSpeed < 20. )
{
turnspeed = curveSpeed; turnspeed = curveSpeed;
} }
} }
if (nsection == 0) { // process slowdown by crossing geometry if ( nsection == 0 ) // process slowdown by crossing geometry
{
double junctionspeed = 999.; // just high double junctionspeed = 999.; // just high
int classifiermask = (int)rc.expctxWay.getClassifierMask(); int classifiermask = (int)rc.expctxWay.getClassifierMask();
@ -71,18 +86,22 @@ final class KinematicPath extends OsmPath {
boolean hasLeftWay = false; boolean hasLeftWay = false;
boolean hasRightWay = false; boolean hasRightWay = false;
boolean hasResidential = false; boolean hasResidential = false;
for (OsmPrePath prePath = rc.firstPrePath; prePath != null; prePath = prePath.next) { for( OsmPrePath prePath = rc.firstPrePath; prePath != null; prePath = prePath.next )
{
KinematicPrePath pp = (KinematicPrePath)prePath; 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; continue;
} }
if ((pp.classifiermask & 32) != 0) { // touching a residential? if ( ( pp.classifiermask & 32 ) != 0 ) // touching a residential?
{
hasResidential = true; 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; double diff = pp.angle - angle;
if ( diff < -40. && diff > -140.) hasLeftWay = true; if ( diff < -40. && diff > -140.) hasLeftWay = true;
if ( diff > 40. && diff < 140. ) hasRightWay = true; if ( diff > 40. && diff < 140. ) hasRightWay = true;
@ -94,17 +113,20 @@ final class KinematicPath extends OsmPath {
if ( hasRightWay && junctionspeed > km.rightWaySpeed ) junctionspeed = km.rightWaySpeed; if ( hasRightWay && junctionspeed > km.rightWaySpeed ) junctionspeed = km.rightWaySpeed;
if ( hasResidential && junctionspeed > residentialSpeed ) junctionspeed = residentialSpeed; if ( hasResidential && junctionspeed > residentialSpeed ) junctionspeed = residentialSpeed;
if ((lastpriorityclassifier < 20) ^ (priorityclassifier < 20)) { if ( (lastpriorityclassifier < 20) ^ (priorityclassifier < 20) )
{
extraTime += 10.; extraTime += 10.;
junctionspeed = 0; // full stop for entering or leaving road network 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 extraTime += 2.; // two seconds for entering a link-type
} }
turnspeed = turnspeed > junctionspeed ? junctionspeed : turnspeed; turnspeed = turnspeed > junctionspeed ? junctionspeed : turnspeed;
if (message != null) { if ( message != null )
{
message.vnode0 = (int) ( junctionspeed * 3.6 + 0.5 ); message.vnode0 = (int) ( junctionspeed * 3.6 + 0.5 );
} }
} }
@ -121,7 +143,8 @@ final class KinematicPath extends OsmPath {
double distanceCost = evolveDistance( km, dist, delta_h, f_air ); double distanceCost = evolveDistance( km, dist, delta_h, f_air );
if (message != null) { if ( message != null )
{
message.costfactor = (float)(distanceCost/dist); message.costfactor = (float)(distanceCost/dist);
message.vmax = (int) ( km.getWayMaxspeed() * 3.6 + 0.5 ); message.vmax = (int) ( km.getWayMaxspeed() * 3.6 + 0.5 );
message.vmaxExplicit = (int) ( km.getWayMaxspeedExplicit() * 3.6 + 0.5 ); message.vmaxExplicit = (int) ( km.getWayMaxspeedExplicit() * 3.6 + 0.5 );
@ -136,13 +159,15 @@ final class KinematicPath extends OsmPath {
} }
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 // elevation force
double fh = delta_h * km.totalweight * 9.81 / dist; double fh = delta_h * km.totalweight * 9.81 / dist;
double effectiveSpeedLimit = km.getEffectiveSpeedLimit(); double effectiveSpeedLimit = km.getEffectiveSpeedLimit();
double emax = 0.5*km.totalweight*effectiveSpeedLimit*effectiveSpeedLimit; double emax = 0.5*km.totalweight*effectiveSpeedLimit*effectiveSpeedLimit;
if (emax <= 0.) { if ( emax <= 0. )
{
return -1.; return -1.;
} }
double vb = km.getBreakingSpeed( effectiveSpeedLimit ); double vb = km.getBreakingSpeed( effectiveSpeedLimit );
@ -153,7 +178,8 @@ final class KinematicPath extends OsmPath {
double v = Math.sqrt( 2. * ekin / km.totalweight ); double v = Math.sqrt( 2. * ekin / km.totalweight );
double d = dist; double d = dist;
while (d > 0.) { while( d > 0. )
{
boolean slow = ekin < elow; boolean slow = ekin < elow;
boolean fast = ekin >= emax; boolean fast = ekin >= emax;
double etarget = slow ? elow : emax; double etarget = slow ? elow : emax;
@ -164,24 +190,30 @@ final class KinematicPath extends OsmPath {
double delta_ekin; double delta_ekin;
double timeStep; double timeStep;
double x; double x;
if (fast) { if ( fast )
{
x = d; x = d;
delta_ekin = x*f; delta_ekin = x*f;
timeStep = x/v; timeStep = x/v;
ekin = etarget; ekin = etarget;
} else { }
else
{
delta_ekin = etarget-ekin; delta_ekin = etarget-ekin;
double b = 2.*f_air / km.totalweight; double b = 2.*f_air / km.totalweight;
double x0 = delta_ekin/f; double x0 = delta_ekin/f;
double x0b = x0*b; double x0b = x0*b;
x = x0*(1. - x0b*(0.5 + x0b*(0.333333333-x0b*0.25 ) ) ); // = ln( delta_ekin*b/f + 1.) / 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 ); double maxstep = Math.min( 50., d );
if (x >= maxstep) { if ( x >= maxstep )
{
x = maxstep; x = maxstep;
double xb = x*b; double xb = x*b;
delta_ekin = x*f*(1.+xb*(0.5+xb*(0.166666667+xb*0.0416666667 ) ) ); // = f/b* exp(xb-1) delta_ekin = x*f*(1.+xb*(0.5+xb*(0.166666667+xb*0.0416666667 ) ) ); // = f/b* exp(xb-1)
ekin += delta_ekin; ekin += delta_ekin;
} else { }
else
{
ekin = etarget; ekin = etarget;
} }
double v2 = Math.sqrt( 2. * ekin / km.totalweight ); double v2 = Math.sqrt( 2. * ekin / km.totalweight );
@ -210,19 +242,23 @@ final class KinematicPath extends OsmPath {
} }
@Override @Override
protected double processTargetNode(RoutingContext rc) { protected double processTargetNode( RoutingContext rc )
{
KinematicModel km = (KinematicModel)rc.pm; KinematicModel km = (KinematicModel)rc.pm;
// finally add node-costs for target node // finally add node-costs for target node
if (targetNode.nodeDescription != null) { if ( targetNode.nodeDescription != null )
{
rc.expctxNode.evaluate( false , targetNode.nodeDescription ); rc.expctxNode.evaluate( false , targetNode.nodeDescription );
float initialcost = rc.expctxNode.getInitialcost(); float initialcost = rc.expctxNode.getInitialcost();
if (initialcost >= 1000000.) { if ( initialcost >= 1000000. )
{
return -1.; return -1.;
} }
cutEkin( km.totalweight, km.getNodeMaxspeed() ); // apply node maxspeed cutEkin( km.totalweight, km.getNodeMaxspeed() ); // apply node maxspeed
if (message != null) { if ( message != null )
{
message.linknodecost += (int)initialcost; message.linknodecost += (int)initialcost;
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription( false, targetNode.nodeDescription ); message.nodeKeyValues = rc.expctxNode.getKeyValueDescription( false, targetNode.nodeDescription );
@ -233,19 +269,22 @@ final class KinematicPath extends OsmPath {
return 0.; return 0.;
} }
private void cutEkin(double weight, double speed) { private void cutEkin( double weight, double speed )
{
double e = 0.5*weight*speed*speed; double e = 0.5*weight*speed*speed;
if ( ekin > e ) ekin = e; if ( ekin > e ) ekin = e;
} }
@Override @Override
public int elevationCorrection() { public int elevationCorrection( RoutingContext rc )
{
return 0; return 0;
} }
@Override @Override
public boolean definitlyWorseThan(OsmPath path) { public boolean definitlyWorseThan( OsmPath path, RoutingContext rc )
{
KinematicPath p = (KinematicPath)path; KinematicPath p = (KinematicPath)path;
int c = p.cost; int c = p.cost;
@ -253,12 +292,14 @@ final class KinematicPath extends OsmPath {
} }
@Override @Override
public double getTotalTime() { public double getTotalTime()
{
return totalTime; return totalTime;
} }
@Override @Override
public double getTotalEnergy() { public double getTotalEnergy()
{
return totalEnergy; return totalEnergy;
} }
} }

View file

@ -8,12 +8,14 @@ package btools.router;
import btools.mapaccess.OsmNode; import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmTransferNode; import btools.mapaccess.OsmTransferNode;
final class KinematicPrePath extends OsmPrePath { final class KinematicPrePath extends OsmPrePath
{
public double angle; public double angle;
public int priorityclassifier; public int priorityclassifier;
public int classifiermask; public int classifiermask;
protected void initPrePath(OsmPath origin, RoutingContext rc) { protected void initPrePath(OsmPath origin, RoutingContext rc )
{
byte[] description = link.descriptionBitmap; byte[] description = link.descriptionBitmap;
if ( description == null ) throw new IllegalArgumentException( "null description for: " + link ); if ( description == null ) throw new IllegalArgumentException( "null description for: " + link );
@ -36,10 +38,13 @@ final class KinematicPrePath extends OsmPrePath {
int lon2; int lon2;
int lat2; int lat2;
if (transferNode == null) { if ( transferNode == null )
{
lon2 = targetNode.ilon; lon2 = targetNode.ilon;
lat2 = targetNode.ilat; lat2 = targetNode.ilat;
} else { }
else
{
lon2 = transferNode.ilon; lon2 = transferNode.ilon;
lat2 = transferNode.ilat; lat2 = transferNode.ilat;
} }

View file

@ -6,7 +6,9 @@
package btools.router; package btools.router;
final class MessageData implements Cloneable {
final class MessageData implements Cloneable
{
int linkdist = 0; int linkdist = 0;
int linkelevationcost = 0; int linkelevationcost = 0;
int linkturncost = 0; int linkturncost = 0;
@ -35,8 +37,10 @@ final class MessageData implements Cloneable {
int vnode1 = 999; int vnode1 = 999;
int extraTime = 0; int extraTime = 0;
String toMessage() { String toMessage()
if (wayKeyValues == null) { {
if ( wayKeyValues == null )
{
return null; return null;
} }
@ -56,7 +60,8 @@ final class MessageData implements Cloneable {
+ "\t" + ((int)energy); + "\t" + ((int)energy);
} }
void add(MessageData d) { void add( MessageData d )
{
linkdist += d.linkdist; linkdist += d.linkdist;
linkelevationcost += d.linkelevationcost; linkelevationcost += d.linkelevationcost;
linkturncost += d.linkturncost; linkturncost += d.linkturncost;
@ -64,40 +69,51 @@ final class MessageData implements Cloneable {
linkinitcost+= d.linkinitcost; linkinitcost+= d.linkinitcost;
} }
MessageData copy() { MessageData copy()
try { {
try
{
return (MessageData)clone(); return (MessageData)clone();
} catch (CloneNotSupportedException e) { }
catch( CloneNotSupportedException e )
{
throw new RuntimeException( e ); throw new RuntimeException( e );
} }
} }
@Override @Override
public String toString() { public String toString()
{
return "dist=" + linkdist + " prio=" + priorityclassifier + " turn=" + turnangle; return "dist=" + linkdist + " prio=" + priorityclassifier + " turn=" + turnangle;
} }
public int getPrio() { public int getPrio()
{
return priorityclassifier; return priorityclassifier;
} }
public boolean isBadOneway() { public boolean isBadOneway()
{
return ( classifiermask & 1 ) != 0; return ( classifiermask & 1 ) != 0;
} }
public boolean isGoodOneway() { public boolean isGoodOneway()
{
return ( classifiermask & 2 ) != 0; return ( classifiermask & 2 ) != 0;
} }
public boolean isRoundabout() { public boolean isRoundabout()
{
return ( classifiermask & 4 ) != 0; return ( classifiermask & 4 ) != 0;
} }
public boolean isLinktType() { public boolean isLinktType()
{
return ( classifiermask & 8 ) != 0; return ( classifiermask & 8 ) != 0;
} }
public boolean isGoodForCars() { public boolean isGoodForCars()
{
return ( classifiermask & 16 ) != 0; return ( classifiermask & 16 ) != 0;
} }

View file

@ -8,22 +8,25 @@ package btools.router;
import btools.mapaccess.OsmNode; import btools.mapaccess.OsmNode;
import btools.util.CheapRuler; import btools.util.CheapRuler;
public class OsmNodeNamed extends OsmNode { public class OsmNodeNamed extends OsmNode
{
public String name; public String name;
public double radius; // radius of nogopoint (in meters) public double radius; // radius of nogopoint (in meters)
public double nogoWeight; // weight for nogopoint public double nogoWeight; // weight for nogopoint
public boolean isNogo = false; public boolean isNogo = false;
public boolean direct = false; // mark direct routing
public OsmNodeNamed() { public OsmNodeNamed()
{
} }
public OsmNodeNamed(OsmNode n) { public OsmNodeNamed( OsmNode n)
{
super( n.ilon, n.ilat ); super( n.ilon, n.ilat );
} }
@Override @Override
public String toString() { public String toString()
{
if ( Double.isNaN(nogoWeight ) ) { if ( Double.isNaN(nogoWeight ) ) {
return ilon + "," + ilat + "," + name; return ilon + "," + ilat + "," + name;
} else { } else {
@ -79,7 +82,8 @@ public class OsmNodeNamed extends OsmNode {
return 2 * halfDistanceWithin; return 2 * halfDistanceWithin;
} }
public static OsmNodeNamed decodeNogo(String s) { public static OsmNodeNamed decodeNogo( String s )
{
OsmNodeNamed n = new OsmNodeNamed(); OsmNodeNamed n = new OsmNodeNamed();
int idx1 = s.indexOf( ',' ); int idx1 = s.indexOf( ',' );
n.ilon = Integer.parseInt( s.substring( 0, idx1 ) ); n.ilon = Integer.parseInt( s.substring( 0, idx1 ) );

View file

@ -5,6 +5,7 @@
http://geomalgorithms.com/a03-_inclusion.html http://geomalgorithms.com/a03-_inclusion.html
cn_PnPoly, wn_PnPoly, inSegment, intersect2D_2Segments cn_PnPoly, wn_PnPoly, inSegment, intersect2D_2Segments
**********************************************************************************************/ **********************************************************************************************/
package btools.router; package btools.router;
@ -13,28 +14,33 @@ import java.util.List;
import btools.util.CheapRuler; import btools.util.CheapRuler;
public class OsmNogoPolygon extends OsmNodeNamed { public class OsmNogoPolygon extends OsmNodeNamed
public final static class Point { {
public final static class Point
{
public final int y; public final int y;
public final int x; public final int x;
Point(final int lon, final int lat) { Point(final int lon, final int lat)
{
x = lon; x = lon;
y = lat; y = lat;
} }
} }
public final List<Point> points = new ArrayList<>(); public final List<Point> points = new ArrayList<Point>();
public final boolean isClosed; public final boolean isClosed;
public OsmNogoPolygon(boolean closed) { public OsmNogoPolygon(boolean closed)
{
this.isClosed = closed; this.isClosed = closed;
this.isNogo = true; this.isNogo = true;
this.name = ""; this.name = "";
} }
public final void addVertex(int lon, int lat) { public final void addVertex(int lon, int lat)
{
points.add(new Point(lon, lat)); points.add(new Point(lon, lat));
} }
@ -48,30 +54,36 @@ public class OsmNogoPolygon extends OsmNodeNamed {
* with each iteration. * with each iteration.
* This is done to ensure the calculated radius being used * This is done to ensure the calculated radius being used
* in RoutingContext.calcDistance will actually contain the whole polygon. * in RoutingContext.calcDistance will actually contain the whole polygon.
* <p> *
* For reasonable distributed vertices the implemented algorithm runs in O(n*ln(n)). * For reasonable distributed vertices the implemented algorithm runs in O(n*ln(n)).
* As this is only run once on initialization of OsmNogoPolygon this methods * As this is only run once on initialization of OsmNogoPolygon this methods
* overall usage of cpu is neglegible in comparism to the cpu-usage of the * overall usage of cpu is neglegible in comparism to the cpu-usage of the
* actual routing algoritm. * actual routing algoritm.
*/ */
public void calcBoundingCircle() { public void calcBoundingCircle()
{
int cxmin, cxmax, cymin, cymax; int cxmin, cxmax, cymin, cymax;
cxmin = cymin = Integer.MAX_VALUE; cxmin = cymin = Integer.MAX_VALUE;
cxmax = cymax = Integer.MIN_VALUE; cxmax = cymax = Integer.MIN_VALUE;
// first calculate a starting center point as center of boundingbox // first calculate a starting center point as center of boundingbox
for (int i = 0; i < points.size(); i++) { for (int i = 0; i < points.size(); i++)
{
final Point p = points.get(i); final Point p = points.get(i);
if (p.x < cxmin) { if (p.x < cxmin)
{
cxmin = p.x; cxmin = p.x;
} }
if (p.x > cxmax) { if (p.x > cxmax)
{
cxmax = p.x; cxmax = p.x;
} }
if (p.y < cymin) { if (p.y < cymin)
{
cymin = p.y; cymin = p.y;
} }
if (p.y > cymax) { if (p.y > cymax)
{
cymax = p.y; cymax = p.y;
} }
} }
@ -88,9 +100,11 @@ public class OsmNogoPolygon extends OsmNodeNamed {
double dmax = 0; // length of vector from center to point double dmax = 0; // length of vector from center to point
int i_max = -1; int i_max = -1;
do { do
{
// now identify the point outside of the circle that has the greatest distance // now identify the point outside of the circle that has the greatest distance
for (int i = 0; i < points.size(); i++) { for (int i = 0; i < points.size(); i++)
{
final Point p = points.get(i); final Point p = points.get(i);
// to get precisely the same results as in RoutingContext.calcDistance() // to get precisely the same results as in RoutingContext.calcDistance()
@ -99,16 +113,19 @@ public class OsmNogoPolygon extends OsmNodeNamed {
final double y1 = (cy - p.y) * dlat2m; final double y1 = (cy - p.y) * dlat2m;
final double dist = Math.sqrt( x1*x1 + y1*y1 ); final double dist = Math.sqrt( x1*x1 + y1*y1 );
if (dist <= rad) { if (dist <= rad)
{
continue; continue;
} }
if (dist > dmax) { if (dist > dmax)
{
// new maximum distance found // new maximum distance found
dmax = dist; dmax = dist;
i_max = i; i_max = i;
} }
} }
if (i_max < 0) { if (i_max < 0)
{
break; // leave loop when no point outside the circle is found any more. break; // leave loop when no point outside the circle is found any more.
} }
final double dd = 0.5 * (1 - rad / dmax); final double dd = 0.5 * (1 - rad / dmax);
@ -132,6 +149,7 @@ public class OsmNogoPolygon extends OsmNodeNamed {
ilon = cx; ilon = cx;
ilat = cy; ilat = cy;
radius = rad * 1.001 + 1.0; // ensure the outside-of-enclosing-circle test in RoutingContext.calcDistance() is not passed by segments ending very close to the radius due to limited numerical precision radius = rad * 1.001 + 1.0; // ensure the outside-of-enclosing-circle test in RoutingContext.calcDistance() is not passed by segments ending very close to the radius due to limited numerical precision
return;
} }
/** /**
@ -147,15 +165,18 @@ public class OsmNogoPolygon extends OsmNodeNamed {
* @param lat1 latitude of start point * @param lat1 latitude of start point
* @return true if segment or any of it's points are 'inside' of polygon * @return true if segment or any of it's points are 'inside' of polygon
*/ */
public boolean intersects(int lon0, int lat0, int lon1, int lat1) { public boolean intersects(int lon0, int lat0, int lon1, int lat1)
{
final Point p0 = new Point (lon0,lat0); final Point p0 = new Point (lon0,lat0);
final Point p1 = new Point (lon1,lat1); final Point p1 = new Point (lon1,lat1);
int i_last = points.size()-1; int i_last = points.size()-1;
Point p2 = points.get(isClosed ? i_last : 0 ); Point p2 = points.get(isClosed ? i_last : 0 );
for (int i = isClosed ? 0 : 1; i <= i_last; i++) { for (int i = isClosed ? 0 : 1 ; i <= i_last; i++)
{
Point p3 = points.get(i); Point p3 = points.get(i);
// does it intersect with at least one of the polygon's segments? // does it intersect with at least one of the polygon's segments?
if (intersect2D_2Segments(p0, p1, p2, p3) > 0) { if (intersect2D_2Segments(p0,p1,p2,p3) > 0)
{
return true; return true;
} }
p2 = p3; p2 = p3;
@ -163,12 +184,15 @@ public class OsmNogoPolygon extends OsmNodeNamed {
return false; return false;
} }
public boolean isOnPolyline(long px, long py) { public boolean isOnPolyline( long px, long py )
{
int i_last = points.size()-1; int i_last = points.size()-1;
Point p1 = points.get(0); Point p1 = points.get(0);
for (int i = 1; i <= i_last; i++) { for (int i = 1 ; i <= i_last; i++)
{
final Point p2 = points.get(i); final Point p2 = points.get(i);
if (isOnLine(px, py, p1.x, p1.y, p2.x, p2.y)) { if (OsmNogoPolygon.isOnLine(px,py,p1.x,p1.y,p2.x,p2.y))
{
return true; return true;
} }
p1 = p2; p1 = p2;
@ -176,30 +200,37 @@ public class OsmNogoPolygon extends OsmNodeNamed {
return false; return false;
} }
public static boolean isOnLine(long px, long py, long p0x, long p0y, long p1x, long p1y) { public static boolean isOnLine( long px, long py, long p0x, long p0y, long p1x, long p1y )
{
final double v10x = px-p0x; final double v10x = px-p0x;
final double v10y = py-p0y; final double v10y = py-p0y;
final double v12x = p1x-p0x; final double v12x = p1x-p0x;
final double v12y = p1y-p0y; final double v12y = p1y-p0y;
if (v10x == 0) { // P0->P1 vertical? if ( v10x == 0 ) // P0->P1 vertical?
if (v10y == 0) { // P0 == P1? {
if ( v10y == 0 ) // P0 == P1?
{
return true; return true;
} }
if (v12x != 0) { // P1->P2 not vertical? if ( v12x != 0 ) // P1->P2 not vertical?
{
return false; return false;
} }
return ( v12y / v10y ) >= 1; // P1->P2 at least as long as P1->P0? return ( v12y / v10y ) >= 1; // P1->P2 at least as long as P1->P0?
} }
if (v10y == 0) { // P0->P1 horizontal? if ( v10y == 0 ) // P0->P1 horizontal?
if (v12y != 0) { // P1->P2 not horizontal? {
if ( v12y != 0 ) // P1->P2 not horizontal?
{
return false; return false;
} }
// if ( P10x == 0 ) // P0 == P1? already tested // if ( P10x == 0 ) // P0 == P1? already tested
return ( v12x / v10x ) >= 1; // P1->P2 at least as long as P1->P0? return ( v12x / v10x ) >= 1; // P1->P2 at least as long as P1->P0?
} }
final double kx = v12x / v10x; final double kx = v12x / v10x;
if (kx < 1) { if ( kx < 1 )
{
return false; return false;
} }
return kx == v12y / v10y; return kx == v12y / v10y;
@ -211,7 +242,6 @@ public class OsmNogoPolygon extends OsmNodeNamed {
this code, and cannot be held liable for any real or imagined damage this code, and cannot be held liable for any real or imagined damage
resulting from its use. Users of this code must verify correctness for resulting from its use. Users of this code must verify correctness for
their application. */ their application. */
/** /**
* winding number test for a point in a polygon * winding number test for a point in a polygon
* *
@ -219,7 +249,8 @@ public class OsmNogoPolygon extends OsmNodeNamed {
* @param py latitude of the point to check * @param py latitude of the point to check
* @return a boolean whether the point is within the polygon or not. * @return a boolean whether the point is within the polygon or not.
*/ */
public boolean isWithin(final long px, final long py) { public boolean isWithin(final long px, final long py)
{
int wn = 0; // the winding number counter int wn = 0; // the winding number counter
// loop through all edges of the polygon // loop through all edges of the polygon
@ -228,26 +259,34 @@ public class OsmNogoPolygon extends OsmNodeNamed {
long p0x = p0.x; // need to use long to avoid overflow in products long p0x = p0.x; // need to use long to avoid overflow in products
long p0y = p0.y; long p0y = p0.y;
for (int i = isClosed ? 0 : 1; i <= i_last; i++) { // edge from v[i] to v[i+1] for (int i = isClosed ? 0 : 1; i <= i_last; i++) // edge from v[i] to v[i+1]
{
final Point p1 = points.get(i); final Point p1 = points.get(i);
final long p1x = p1.x; final long p1x = p1.x;
final long p1y = p1.y; final long p1y = p1.y;
if (isOnLine(px, py, p0x, p0y, p1x, p1y)) { if (OsmNogoPolygon.isOnLine(px, py, p0x, p0y, p1x, p1y))
{
return true; return true;
} }
if (p0y <= py) // start y <= p.y if (p0y <= py) // start y <= p.y
{ {
if (p1y > py) { // an upward crossing, p left of edge if (p1y > py) // an upward crossing
if (((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) > 0) { { // p left of edge
if (((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) > 0)
{
++wn; // have a valid up intersect ++wn; // have a valid up intersect
} }
} }
} else { // start y > p.y (no test needed) }
if (p1y <= py) { // a downward crossing, p right of edge else // start y > p.y (no test needed)
if (((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) < 0) { {
if (p1y <= py) // a downward crossing
{ // p right of edge
if (((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) < 0)
{
--wn; // have a valid down intersect --wn; // have a valid down intersect
} }
} }
@ -265,6 +304,7 @@ public class OsmNogoPolygon extends OsmNodeNamed {
* @param lat1 Integer latitude of the first point of the segment. * @param lat1 Integer latitude of the first point of the segment.
* @param lon2 Integer longitude of the last point of the segment. * @param lon2 Integer longitude of the last point of the segment.
* @param lat2 Integer latitude of the last point of the segment. * @param lat2 Integer latitude of the last point of the segment.
*
* @return The length, in meters, of the portion of the segment which is * @return The length, in meters, of the portion of the segment which is
* included in the polygon. * included in the polygon.
*/ */
@ -276,7 +316,8 @@ public class OsmNogoPolygon extends OsmNodeNamed {
final Point p2 = new Point (lon2, lat2); final Point p2 = new Point (lon2, lat2);
Point previousIntersectionOnSegment = null; Point previousIntersectionOnSegment = null;
if (isWithin(lon1, lat1)) { if (isWithin(lon1, lat1))
{
// Start point of the segment is within the polygon, this is the first // Start point of the segment is within the polygon, this is the first
// "intersection". // "intersection".
previousIntersectionOnSegment = p1; previousIntersectionOnSegment = p1;
@ -284,12 +325,14 @@ public class OsmNogoPolygon extends OsmNodeNamed {
// Loop over edges of the polygon to find intersections // Loop over edges of the polygon to find intersections
int i_last = points.size() - 1; int i_last = points.size() - 1;
for (int i = (isClosed ? 0 : 1), j = (isClosed ? i_last : 0); i <= i_last; j = i++) { for (int i = (isClosed ? 0 : 1), j = (isClosed ? i_last : 0); i <= i_last; j = i++)
{
Point edgePoint1 = points.get(j); Point edgePoint1 = points.get(j);
Point edgePoint2 = points.get(i); Point edgePoint2 = points.get(i);
int intersectsEdge = intersect2D_2Segments(p1, p2, edgePoint1, edgePoint2); int intersectsEdge = intersect2D_2Segments(p1, p2, edgePoint1, edgePoint2);
if (isClosed && intersectsEdge == 1) { if (isClosed && intersectsEdge == 1)
{
// Intersects with a (closed) polygon edge on a single point // Intersects with a (closed) polygon edge on a single point
// Distance is zero when crossing a polyline. // Distance is zero when crossing a polyline.
// Let's find this intersection point // Let's find this intersection point
@ -320,7 +363,8 @@ public class OsmNogoPolygon extends OsmNodeNamed {
); );
} }
previousIntersectionOnSegment = intersection; previousIntersectionOnSegment = intersection;
} else if (intersectsEdge == 2) { }
else if (intersectsEdge == 2) {
// Segment and edge overlaps // Segment and edge overlaps
// FIXME: Could probably be done in a smarter way // FIXME: Could probably be done in a smarter way
distance += Math.min( distance += Math.min(
@ -357,7 +401,6 @@ public class OsmNogoPolygon extends OsmNodeNamed {
this code, and cannot be held liable for any real or imagined damage this code, and cannot be held liable for any real or imagined damage
resulting from its use. Users of this code must verify correctness for resulting from its use. Users of this code must verify correctness for
their application. */ their application. */
/** /**
* inSegment(): determine if a point is inside a segment * inSegment(): determine if a point is inside a segment
* *
@ -367,28 +410,35 @@ public class OsmNogoPolygon extends OsmNodeNamed {
* @return 1 = P is inside S * @return 1 = P is inside S
* 0 = P is not inside S * 0 = P is not inside S
*/ */
private static boolean inSegment(final Point p, final Point seg_p0, final Point seg_p1) { private static boolean inSegment( final Point p, final Point seg_p0, final Point seg_p1)
{
final int sp0x = seg_p0.x; final int sp0x = seg_p0.x;
final int sp1x = seg_p1.x; final int sp1x = seg_p1.x;
if (sp0x != sp1x) { // S is not vertical if (sp0x != sp1x) // S is not vertical
{
final int px = p.x; final int px = p.x;
if (sp0x <= px && px <= sp1x) { if (sp0x <= px && px <= sp1x)
{
return true; return true;
} }
if (sp0x >= px && px >= sp1x) { if (sp0x >= px && px >= sp1x)
{
return true; return true;
} }
} else // S is vertical, so test y coordinate }
else // S is vertical, so test y coordinate
{ {
final int sp0y = seg_p0.y; final int sp0y = seg_p0.y;
final int sp1y = seg_p1.y; final int sp1y = seg_p1.y;
final int py = p.y; final int py = p.y;
if (sp0y <= py && py <= sp1y) { if (sp0y <= py && py <= sp1y)
{
return true; return true;
} }
if (sp0y >= py && py >= sp1y) { if (sp0y >= py && py >= sp1y)
{
return true; return true;
} }
} }
@ -401,10 +451,8 @@ public class OsmNogoPolygon extends OsmNodeNamed {
this code, and cannot be held liable for any real or imagined damage this code, and cannot be held liable for any real or imagined damage
resulting from its use. Users of this code must verify correctness for resulting from its use. Users of this code must verify correctness for
their application. */ their application. */
/** /**
* intersect2D_2Segments(): find the 2D intersection of 2 finite segments * intersect2D_2Segments(): find the 2D intersection of 2 finite segments
*
* @param s1p0 start point of segment 1 * @param s1p0 start point of segment 1
* @param s1p1 end point of segment 1 * @param s1p1 end point of segment 1
* @param s2p0 start point of segment 2 * @param s2p0 start point of segment 2
@ -413,7 +461,8 @@ public class OsmNogoPolygon extends OsmNodeNamed {
* 1=intersect in unique point I0 * 1=intersect in unique point I0
* 2=overlap in segment from I0 to I1 * 2=overlap in segment from I0 to I1
*/ */
private static int intersect2D_2Segments(final Point s1p0, final Point s1p1, final Point s2p0, final Point s2p1) { private static int intersect2D_2Segments( final Point s1p0, final Point s1p1, final Point s2p0, final Point s2p1 )
{
final long ux = s1p1.x - s1p0.x; // vector u = S1P1-S1P0 (segment 1) final long ux = s1p1.x - s1p0.x; // vector u = S1P1-S1P0 (segment 1)
final long uy = s1p1.y - s1p0.y; final long uy = s1p1.y - s1p0.y;
final long vx = s2p1.x - s2p0.x; // vector v = S2P1-S2P0 (segment 2) final long vx = s2p1.x - s2p0.x; // vector v = S2P1-S2P0 (segment 2)
@ -426,7 +475,8 @@ public class OsmNogoPolygon extends OsmNodeNamed {
// test if they are parallel (includes either being a point) // test if they are parallel (includes either being a point)
if (d == 0) // S1 and S2 are parallel if (d == 0) // S1 and S2 are parallel
{ {
if ((ux * wy - uy * wx) != 0 || (vx * wy - vy * wx) != 0) { if ((ux * wy - uy * wx) != 0 || (vx * wy - vy * wx) != 0)
{
return 0; // they are NOT collinear return 0; // they are NOT collinear
} }
@ -450,10 +500,13 @@ public class OsmNogoPolygon extends OsmNodeNamed {
double t0, t1; // endpoints of S1 in eqn for S2 double t0, t1; // endpoints of S1 in eqn for S2
final int w2x = s1p1.x - s2p0.x; // vector w2 = S1P1-S2P0 (from start of segment 2 to end of segment 1) final int w2x = s1p1.x - s2p0.x; // vector w2 = S1P1-S2P0 (from start of segment 2 to end of segment 1)
final int w2y = s1p1.y - s2p0.y; final int w2y = s1p1.y - s2p0.y;
if (vx != 0) { if (vx != 0)
{
t0 = wx / vx; t0 = wx / vx;
t1 = w2x / vx; t1 = w2x / vx;
} else { }
else
{
t0 = wy / vy; t0 = wy / vy;
t1 = w2y / vy; t1 = w2y / vy;
} }
@ -463,7 +516,8 @@ public class OsmNogoPolygon extends OsmNodeNamed {
t0=t1; t0=t1;
t1=t; t1=t;
} }
if (t0 > 1 || t1 < 0) { if (t0 > 1 || t1 < 0)
{
return 0; // NO overlap return 0; // NO overlap
} }
t0 = t0<0? 0 : t0; // clip to min 0 t0 = t0<0? 0 : t0; // clip to min 0

View file

@ -5,6 +5,8 @@
*/ */
package btools.router; package btools.router;
import java.io.IOException;
import btools.mapaccess.OsmLink; import btools.mapaccess.OsmLink;
import btools.mapaccess.OsmLinkHolder; import btools.mapaccess.OsmLinkHolder;
import btools.mapaccess.OsmNode; import btools.mapaccess.OsmNode;
@ -12,7 +14,8 @@ import btools.mapaccess.OsmTransferNode;
import btools.mapaccess.TurnRestriction; import btools.mapaccess.TurnRestriction;
import btools.util.CheapRuler; import btools.util.CheapRuler;
abstract class OsmPath implements OsmLinkHolder { abstract class OsmPath implements OsmLinkHolder
{
/** /**
* The cost of that path (a modified distance) * The cost of that path (a modified distance)
*/ */
@ -31,6 +34,8 @@ abstract class OsmPath implements OsmLinkHolder {
public OsmPathElement originElement; public OsmPathElement originElement;
public OsmPathElement myElement; public OsmPathElement myElement;
protected float traffic;
private OsmLinkHolder nextForLink = null; private OsmLinkHolder nextForLink = null;
public int treedepth = 0; public int treedepth = 0;
@ -52,23 +57,54 @@ abstract class OsmPath implements OsmLinkHolder {
private static final int HAD_DESTINATION_START_BIT = 8; private static final int HAD_DESTINATION_START_BIT = 8;
protected int bitfield = PATH_START_BIT; protected int bitfield = PATH_START_BIT;
private boolean getBit(int mask) { private boolean getBit( int mask )
{
return (bitfield & mask ) != 0; return (bitfield & mask ) != 0;
} }
private void setBit(int mask, boolean bit) { private void setBit( int mask, boolean bit )
if (getBit(mask) != bit) { {
if ( getBit( mask ) != bit )
{
bitfield ^= mask; bitfield ^= mask;
} }
} }
public boolean didEnterDestinationArea() { public boolean didEnterDestinationArea()
{
return !getBit( HAD_DESTINATION_START_BIT ) && getBit( IS_ON_DESTINATION_BIT ); return !getBit( HAD_DESTINATION_START_BIT ) && getBit( IS_ON_DESTINATION_BIT );
} }
public MessageData message; public MessageData message;
public void init(OsmLink link) { 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; this.link = link;
targetNode = link.getTarget( null ); targetNode = link.getTarget( null );
selev = targetNode.getSElev(); selev = targetNode.getSElev();
@ -77,9 +113,11 @@ abstract class OsmPath implements OsmLinkHolder {
originLat = -1; originLat = -1;
} }
public void init(OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc) { public void init( OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc )
if (origin.myElement == null) { {
origin.myElement = OsmPathElement.create(origin); if ( origin.myElement == null )
{
origin.myElement = OsmPathElement.create( origin, rc.countTraffic );
} }
this.originElement = origin.myElement; this.originElement = origin.myElement;
this.link = link; this.link = link;
@ -89,7 +127,6 @@ abstract class OsmPath implements OsmLinkHolder {
this.lastClassifier = origin.lastClassifier; this.lastClassifier = origin.lastClassifier;
this.lastInitialCost = origin.lastInitialCost; this.lastInitialCost = origin.lastInitialCost;
this.bitfield = origin.bitfield; this.bitfield = origin.bitfield;
this.priorityclassifier = origin.priorityclassifier;
init( origin ); init( origin );
addAddionalPenalty(refTrack, detailMode, origin, link, rc ); addAddionalPenalty(refTrack, detailMode, origin, link, rc );
} }
@ -98,29 +135,16 @@ abstract class OsmPath implements OsmLinkHolder {
protected abstract void resetState(); protected abstract void resetState();
static int seg = 1;
protected void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc) { protected void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc )
{
byte[] description = link.descriptionBitmap; byte[] description = link.descriptionBitmap;
if (description == null) { // could be a beeline path if ( description == null )
message = new MessageData(); {
if (message != null) { return; // could be a beeline path
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; boolean recordTransferNodes = detailMode || rc.countTraffic;
rc.nogoCost = 0.; rc.nogoCost = 0.;
@ -152,15 +176,18 @@ abstract class OsmPath implements OsmLinkHolder {
float newClassifier = rc.expctxWay.getInitialClassifier(); float newClassifier = rc.expctxWay.getInitialClassifier();
float newInitialCost = rc.expctxWay.getInitialcost(); float newInitialCost = rc.expctxWay.getInitialcost();
float classifierDiff = newClassifier - lastClassifier; float classifierDiff = newClassifier - lastClassifier;
if (newClassifier != 0. && lastClassifier != 0. && (classifierDiff > 0.0005 || classifierDiff < -0.0005)) { if ( newClassifier != 0. && lastClassifier != 0. && ( classifierDiff > 0.0005 || classifierDiff < -0.0005 ) )
{
float initialcost = rc.inverseDirection ? lastInitialCost : newInitialCost; float initialcost = rc.inverseDirection ? lastInitialCost : newInitialCost;
if (initialcost >= 1000000.) { if ( initialcost >= 1000000. )
{
cost = -1; cost = -1;
return; return;
} }
int iicost = (int)initialcost; int iicost = (int)initialcost;
if (message != null) { if ( message != null )
{
message.linkinitcost += iicost; message.linkinitcost += iicost;
} }
cost += iicost; cost += iicost;
@ -172,15 +199,22 @@ abstract class OsmPath implements OsmLinkHolder {
int classifiermask = (int)rc.expctxWay.getClassifierMask(); int classifiermask = (int)rc.expctxWay.getClassifierMask();
boolean newDestination = (classifiermask & 64) != 0; boolean newDestination = (classifiermask & 64) != 0;
boolean oldDestination = getBit( IS_ON_DESTINATION_BIT ); boolean oldDestination = getBit( IS_ON_DESTINATION_BIT );
if (getBit(PATH_START_BIT)) { if ( getBit( PATH_START_BIT ) )
{
setBit( PATH_START_BIT, false ); setBit( PATH_START_BIT, false );
setBit( CAN_LEAVE_DESTINATION_BIT, newDestination ); setBit( CAN_LEAVE_DESTINATION_BIT, newDestination );
setBit( HAD_DESTINATION_START_BIT, newDestination ); setBit( HAD_DESTINATION_START_BIT, newDestination );
} else { }
if (oldDestination && !newDestination) { else
if (getBit(CAN_LEAVE_DESTINATION_BIT)) { {
if ( oldDestination && !newDestination )
{
if ( getBit( CAN_LEAVE_DESTINATION_BIT ) )
{
setBit( CAN_LEAVE_DESTINATION_BIT, false ); setBit( CAN_LEAVE_DESTINATION_BIT, false );
} else { }
else
{
cost = -1; cost = -1;
return; return;
} }
@ -192,7 +226,8 @@ abstract class OsmPath implements OsmLinkHolder {
OsmTransferNode transferNode = link.geometry == null ? null OsmTransferNode transferNode = link.geometry == null ? null
: rc.geometryDecoder.decodeGeometry( link.geometry, sourceNode, targetNode, isReverse ); : rc.geometryDecoder.decodeGeometry( link.geometry, sourceNode, targetNode, isReverse );
for (int nsection = 0; ; nsection++) { for(int nsection=0; ;nsection++)
{
originLon = lon1; originLon = lon1;
originLat = lat1; originLat = lat1;
@ -200,33 +235,37 @@ abstract class OsmPath implements OsmLinkHolder {
int lon2; int lon2;
int lat2; int lat2;
short ele2; short ele2;
short originEle2;
if (transferNode == null) { if ( transferNode == null )
{
lon2 = targetNode.ilon; lon2 = targetNode.ilon;
lat2 = targetNode.ilat; lat2 = targetNode.ilat;
originEle2 = targetNode.selev; ele2 = targetNode.selev;
} else { }
else
{
lon2 = transferNode.ilon; lon2 = transferNode.ilon;
lat2 = transferNode.ilat; lat2 = transferNode.ilat;
originEle2 = transferNode.selev; ele2 = transferNode.selev;
} }
ele2 = originEle2;
boolean isStartpoint = lon0 == -1 && lat0 == -1; boolean isStartpoint = lon0 == -1 && lat0 == -1;
// check turn restrictions (n detail mode (=final pass) no TR to not mess up voice hints) // check turn restrictions (n detail mode (=final pass) no TR to not mess up voice hints)
if (nsection == 0 && rc.considerTurnRestrictions && !detailMode && !isStartpoint) { if ( nsection == 0 && rc.considerTurnRestrictions && !detailMode&& !isStartpoint )
{
if ( rc.inverseDirection if ( rc.inverseDirection
? TurnRestriction.isTurnForbidden(sourceNode.firstRestriction, lon2, lat2, lon0, lat0, rc.bikeMode || rc.footMode, rc.carMode) ? TurnRestriction.isTurnForbidden( sourceNode.firstRestriction, lon2, lat2, lon0, lat0, rc.bikeMode, rc.carMode )
: TurnRestriction.isTurnForbidden(sourceNode.firstRestriction, lon0, lat0, lon2, lat2, rc.bikeMode || rc.footMode, rc.carMode)) { : TurnRestriction.isTurnForbidden( sourceNode.firstRestriction, lon0, lat0, lon2, lat2, rc.bikeMode, rc.carMode ) )
{
cost = -1; cost = -1;
return; return;
} }
} }
// if recording, new MessageData for each section (needed for turn-instructions) // if recording, new MessageData for each section (needed for turn-instructions)
if (message != null && message.wayKeyValues != null) { if ( message != null && message.wayKeyValues != null )
{
originElement.message = message; originElement.message = message;
message = new MessageData(); message = new MessageData();
} }
@ -234,11 +273,15 @@ abstract class OsmPath implements OsmLinkHolder {
int dist = rc.calcDistance( lon1, lat1, lon2, lat2 ); int dist = rc.calcDistance( lon1, lat1, lon2, lat2 );
boolean stopAtEndpoint = false; boolean stopAtEndpoint = false;
if (rc.shortestmatch) { if ( rc.shortestmatch )
if (rc.isEndpoint) { {
if ( rc.isEndpoint )
{
stopAtEndpoint = true; stopAtEndpoint = true;
ele2 = interpolateEle( ele1, ele2, rc.wayfraction ); ele2 = interpolateEle( ele1, ele2, rc.wayfraction );
} else { }
else
{
// we just start here, reset everything // we just start here, reset everything
cost = 0; cost = 0;
resetState(); resetState();
@ -246,18 +289,24 @@ abstract class OsmPath implements OsmLinkHolder {
lat0 = -1; lat0 = -1;
isStartpoint = true; isStartpoint = true;
if (recordTransferNodes) { if ( recordTransferNodes )
if (rc.wayfraction > 0.) { {
if ( rc.wayfraction > 0. )
{
ele1 = interpolateEle( ele1, ele2, 1. - rc.wayfraction ); ele1 = interpolateEle( ele1, ele2, 1. - rc.wayfraction );
originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, ele1, null); originElement = OsmPathElement.create( rc.ilonshortest, rc.ilatshortest, ele1, null, rc.countTraffic );
} else { }
else
{
originElement = null; // prevent duplicate point originElement = null; // prevent duplicate point
} }
} }
if (rc.checkPendingEndpoint()) { if ( rc.checkPendingEndpoint() )
{
dist = rc.calcDistance( rc.ilonshortest, rc.ilatshortest, lon2, lat2 ); dist = rc.calcDistance( rc.ilonshortest, rc.ilatshortest, lon2, lat2 );
if (rc.shortestmatch) { if ( rc.shortestmatch )
{
stopAtEndpoint = true; stopAtEndpoint = true;
ele2 = interpolateEle( ele1, ele2, rc.wayfraction ); ele2 = interpolateEle( ele1, ele2, rc.wayfraction );
} }
@ -265,19 +314,24 @@ abstract class OsmPath implements OsmLinkHolder {
} }
} }
if (message != null) { if ( message != null )
{
message.linkdist += dist; message.linkdist += dist;
} }
linkdisttotal += dist; linkdisttotal += dist;
// apply a start-direction if appropriate (by faking the origin position) // apply a start-direction if appropriate (by faking the origin position)
if (isStartpoint) { if ( isStartpoint )
if (rc.startDirectionValid) { {
double dir = rc.startDirection * CheapRuler.DEG_TO_RAD; if ( rc.startDirectionValid )
{
double dir = rc.startDirection.intValue() * CheapRuler.DEG_TO_RAD;
double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (lon0 + lat1) >> 1 ); double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (lon0 + lat1) >> 1 );
lon0 = lon1 - (int) ( 1000. * Math.sin( dir ) / lonlat2m[0] ); lon0 = lon1 - (int) ( 1000. * Math.sin( dir ) / lonlat2m[0] );
lat0 = lat1 - (int) ( 1000. * Math.cos( dir ) / lonlat2m[1] ); lat0 = lat1 - (int) ( 1000. * Math.cos( dir ) / lonlat2m[1] );
} else { }
else
{
lon0 = lon1 - (lon2-lon1); lon0 = lon1 - (lon2-lon1);
lat0 = lat1 - (lat2-lat1); lat0 = lat1 - (lat2-lat1);
} }
@ -288,32 +342,47 @@ abstract class OsmPath implements OsmLinkHolder {
// *** elevation stuff // *** elevation stuff
double delta_h = 0.; double delta_h = 0.;
if ( ele2 == Short.MIN_VALUE ) ele2 = ele1; if ( ele2 == Short.MIN_VALUE ) ele2 = ele1;
if (ele1 != Short.MIN_VALUE) { if ( ele1 != Short.MIN_VALUE )
{
delta_h = (ele2 - ele1)/4.; delta_h = (ele2 - ele1)/4.;
if (rc.inverseDirection) { if ( rc.inverseDirection )
{
delta_h = -delta_h; delta_h = -delta_h;
} }
} }
double elevation = ele2 == Short.MIN_VALUE ? 100. : ele2/4.; double elevation = ele2 == Short.MIN_VALUE ? 100. : ele2/4.;
double sectionCost = processWaySection( rc, dist, delta_h, elevation, angle, cosangle, isStartpoint, nsection, lastpriorityclassifier ); double sectionCost = processWaySection( rc, dist, delta_h, elevation, angle, cosangle, isStartpoint, nsection, lastpriorityclassifier );
if ((sectionCost < 0. || costfactor > 9998. && !detailMode) || sectionCost + cost >= 2000000000.) { if ( ( sectionCost < 0. || costfactor > 9998. && !detailMode ) || sectionCost + cost >= 2000000000. )
{
cost = -1; cost = -1;
return; return;
} }
if (isTrafficBackbone) { if ( isTrafficBackbone )
{
sectionCost = 0.; sectionCost = 0.;
} }
cost += (int)sectionCost; 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 // compute kinematic
computeKinematic( rc, dist, delta_h, detailMode ); computeKinematic( rc, dist, delta_h, detailMode );
if (message != null) { if ( message != null )
{
message.turnangle = (float)angle; message.turnangle = (float)angle;
message.time = (float)getTotalTime(); message.time = (float)getTotalTime();
message.energy = (float)getTotalEnergy(); message.energy = (float)getTotalEnergy();
@ -321,29 +390,37 @@ abstract class OsmPath implements OsmLinkHolder {
message.classifiermask = classifiermask; message.classifiermask = classifiermask;
message.lon = lon2; message.lon = lon2;
message.lat = lat2; message.lat = lat2;
message.ele = originEle2; message.ele = ele2;
message.wayKeyValues = rc.expctxWay.getKeyValueDescription( isReverse, description ); message.wayKeyValues = rc.expctxWay.getKeyValueDescription( isReverse, description );
} }
if (stopAtEndpoint) { if ( stopAtEndpoint )
if (recordTransferNodes) { {
originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, originEle2, originElement); if ( recordTransferNodes )
{
originElement = OsmPathElement.create( rc.ilonshortest, rc.ilatshortest, ele2, originElement, rc.countTraffic );
originElement.cost = cost; originElement.cost = cost;
if (message != null) { if ( message != null )
{
originElement.message = message; originElement.message = message;
} }
} }
if (rc.nogoCost < 0) { if ( rc.nogoCost < 0)
{
cost = -1; cost = -1;
} else { }
else
{
cost += rc.nogoCost; cost += rc.nogoCost;
} }
return; return;
} }
if (transferNode == null) { if ( transferNode == null )
{
// *** penalty for being part of the reference track // *** penalty for being part of the reference track
if (refTrack != null && refTrack.containsNode(targetNode) && refTrack.containsNode(sourceNode)) { if ( refTrack != null && refTrack.containsNode( targetNode ) && refTrack.containsNode( sourceNode ) )
{
int reftrackcost = linkdisttotal; int reftrackcost = linkdisttotal;
cost += reftrackcost; cost += reftrackcost;
} }
@ -352,9 +429,12 @@ abstract class OsmPath implements OsmLinkHolder {
} }
transferNode = transferNode.next; transferNode = transferNode.next;
if (recordTransferNodes) { if ( recordTransferNodes )
originElement = OsmPathElement.create(lon2, lat2, originEle2, originElement); {
originElement = OsmPathElement.create( lon2, lat2, ele2, originElement, rc.countTraffic );
originElement.cost = cost; originElement.cost = cost;
originElement.addTraffic( traffic );
traffic = 0;
} }
lon0 = lon1; lon0 = lon1;
lat0 = lat1; lat0 = lat1;
@ -364,16 +444,20 @@ abstract class OsmPath implements OsmLinkHolder {
} }
// check for nogo-matches (after the *actual* start of segment) // check for nogo-matches (after the *actual* start of segment)
if (rc.nogoCost < 0) { if ( rc.nogoCost < 0)
{
cost = -1; cost = -1;
return; return;
} else { }
else
{
cost += rc.nogoCost; cost += rc.nogoCost;
} }
// add target-node costs // add target-node costs
double targetCost = processTargetNode( rc ); double targetCost = processTargetNode( rc );
if (targetCost < 0. || targetCost + cost >= 2000000000.) { if ( targetCost < 0. || targetCost + cost >= 2000000000. )
{
cost = -1; cost = -1;
return; return;
} }
@ -381,8 +465,10 @@ abstract class OsmPath implements OsmLinkHolder {
} }
public short interpolateEle(short e1, short e2, double fraction) { public short interpolateEle( short e1, short e2, double fraction )
if (e1 == Short.MIN_VALUE || e2 == Short.MIN_VALUE) { {
if ( e1 == Short.MIN_VALUE || e2 == Short.MIN_VALUE )
{
return Short.MIN_VALUE; return Short.MIN_VALUE;
} }
return (short)( e1*(1.-fraction) + e2*fraction ); return (short)( e1*(1.-fraction) + e2*fraction );
@ -392,39 +478,47 @@ abstract class OsmPath implements OsmLinkHolder {
protected abstract double processTargetNode( RoutingContext rc ); protected abstract double processTargetNode( RoutingContext rc );
protected void computeKinematic(RoutingContext rc, double dist, double delta_h, boolean detailMode) { protected void computeKinematic( RoutingContext rc, double dist, double delta_h, boolean detailMode )
{
} }
public abstract int elevationCorrection(); public abstract int elevationCorrection( RoutingContext rc );
public abstract boolean definitlyWorseThan(OsmPath p); public abstract boolean definitlyWorseThan( OsmPath p, RoutingContext rc );
public OsmNode getSourceNode() { public OsmNode getSourceNode()
{
return sourceNode; return sourceNode;
} }
public OsmNode getTargetNode() { public OsmNode getTargetNode()
{
return targetNode; return targetNode;
} }
public OsmLink getLink() { public OsmLink getLink()
{
return link; return link;
} }
public void setNextForLink(OsmLinkHolder holder) { public void setNextForLink( OsmLinkHolder holder )
{
nextForLink = holder; nextForLink = holder;
} }
public OsmLinkHolder getNextForLink() { public OsmLinkHolder getNextForLink()
{
return nextForLink; return nextForLink;
} }
public double getTotalTime() { public double getTotalTime()
{
return 0.; return 0.;
} }
public double getTotalEnergy() { public double getTotalEnergy()
{
return 0.; return 0.;
} }
} }

View file

@ -14,7 +14,8 @@ import btools.util.CheapRuler;
* @author ab * @author ab
*/ */
public class OsmPathElement implements OsmPos { public class OsmPathElement implements OsmPos
{
private int ilat; // latitude private int ilat; // latitude
private int ilon; // longitude private int ilon; // longitude
private short selev; // longitude private short selev; // longitude
@ -24,73 +25,77 @@ public class OsmPathElement implements OsmPos {
public int cost; public int cost;
// interface OsmPos // interface OsmPos
public final int getILat() { public final int getILat()
{
return ilat; return ilat;
} }
public final int getILon() { public final int getILon()
{
return ilon; return ilon;
} }
public final short getSElev() { public final short getSElev()
{
return selev; return selev;
} }
public final void setSElev(short s) { public final double getElev()
selev = s; {
}
public final double getElev() {
return selev / 4.; return selev / 4.;
} }
public final float getTime() { public final float getTime()
{
return message == null ? 0.f : message.time; return message == null ? 0.f : message.time;
} }
public final void setTime(float t) { public final void setTime( float t )
if (message != null) { {
if ( message != null )
{
message.time = t; message.time = t;
} }
} }
public final float getEnergy() { public final float getEnergy()
{
return message == null ? 0.f : message.energy; return message == null ? 0.f : message.energy;
} }
public final void setEnergy(float e) { public final void setEnergy( float e )
if (message != null) { {
if ( message != null )
{
message.energy = e; message.energy = e;
} }
} }
public final void setAngle(float e) { public final long getIdFromPos()
if (message != null) { {
message.turnangle = e;
}
}
public final long getIdFromPos() {
return ((long)ilon)<<32 | ilat; return ((long)ilon)<<32 | ilat;
} }
public final int calcDistance(OsmPos p) { public final int calcDistance( OsmPos p )
return (int) Math.max(1.0, Math.round(CheapRuler.distance(ilon, ilat, p.getILon(), p.getILat()))); {
return (int)(CheapRuler.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0 );
} }
public OsmPathElement origin; public OsmPathElement origin;
// construct a path element from a path // construct a path element from a path
public static final OsmPathElement create(OsmPath path) { public static final OsmPathElement create( OsmPath path, boolean countTraffic )
{
OsmNode n = path.getTargetNode(); OsmNode n = path.getTargetNode();
OsmPathElement pe = create(n.getILon(), n.getILat(), n.getSElev(), path.originElement); OsmPathElement pe = create( n.getILon(), n.getILat(), path.selev, path.originElement, countTraffic );
pe.cost = path.cost; pe.cost = path.cost;
pe.message = path.message; pe.message = path.message;
return pe; return pe;
} }
public static final OsmPathElement create(int ilon, int ilat, short selev, OsmPathElement origin) { public static final OsmPathElement create( int ilon, int ilat, short selev, OsmPathElement origin, boolean countTraffic )
OsmPathElement pe = new OsmPathElement(); {
OsmPathElement pe = countTraffic ? new OsmPathElementWithTraffic() : new OsmPathElement();
pe.ilon = ilon; pe.ilon = ilon;
pe.ilat = ilat; pe.ilat = ilat;
pe.selev = selev; pe.selev = selev;
@ -98,25 +103,29 @@ public class OsmPathElement implements OsmPos {
return pe; return pe;
} }
protected OsmPathElement() { protected OsmPathElement()
{
} }
public String toString() { public void addTraffic( float traffic )
{
}
public String toString()
{
return ilon + "_" + ilat; return ilon + "_" + ilat;
} }
public boolean positionEquals(OsmPathElement e) { public void writeToStream( DataOutput dos ) throws IOException
return this.ilat == e.ilat && this.ilon == e.ilon; {
}
public void writeToStream(DataOutput dos) throws IOException {
dos.writeInt( ilat ); dos.writeInt( ilat );
dos.writeInt( ilon ); dos.writeInt( ilon );
dos.writeShort( selev ); dos.writeShort( selev );
dos.writeInt( cost ); dos.writeInt( cost );
} }
public static OsmPathElement readFromStream(DataInput dis) throws IOException { public static OsmPathElement readFromStream( DataInput dis ) throws IOException
{
OsmPathElement pe = new OsmPathElement(); OsmPathElement pe = new OsmPathElement();
pe.ilat = dis.readInt(); pe.ilat = dis.readInt();
pe.ilon = dis.readInt(); pe.ilon = dis.readInt();

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,7 +11,8 @@ import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay; import btools.expressions.BExpressionContextWay;
abstract class OsmPathModel { abstract class OsmPathModel
{
public abstract OsmPrePath createPrePath(); public abstract OsmPrePath createPrePath();
public abstract OsmPath createPath(); public abstract OsmPath createPath();

View file

@ -7,15 +7,18 @@ package btools.router;
import btools.mapaccess.OsmLink; import btools.mapaccess.OsmLink;
import btools.mapaccess.OsmNode; import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmTransferNode;
public abstract class OsmPrePath { public abstract class OsmPrePath
{
protected OsmNode sourceNode; protected OsmNode sourceNode;
protected OsmNode targetNode; protected OsmNode targetNode;
protected OsmLink link; protected OsmLink link;
public OsmPrePath next; 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.link = link;
this.sourceNode = origin.getTargetNode(); this.sourceNode = origin.getTargetNode();
this.targetNode = link.getTarget( sourceNode ); this.targetNode = link.getTarget( sourceNode );

File diff suppressed because it is too large Load diff

View file

@ -11,7 +11,8 @@ import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay; import btools.expressions.BExpressionContextWay;
import btools.expressions.BExpressionMetaData; import btools.expressions.BExpressionMetaData;
public final class ProfileCache { public final class ProfileCache
{
private static File lastLookupFile; private static File lastLookupFile;
private static long lastLookupTimestamp; private static long lastLookupTimestamp;
@ -26,18 +27,23 @@ public final class ProfileCache {
private static ProfileCache[] apc = new ProfileCache[1]; 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]; apc = new ProfileCache[size];
} }
public static synchronized boolean parseProfile(RoutingContext rc) { public static synchronized boolean parseProfile( RoutingContext rc )
{
String profileBaseDir = System.getProperty( "profileBaseDir" ); String profileBaseDir = System.getProperty( "profileBaseDir" );
File profileDir; File profileDir;
File profileFile; File profileFile;
if (profileBaseDir == null) { if ( profileBaseDir == null )
{
profileDir = new File( rc.localFunction ).getParentFile(); profileDir = new File( rc.localFunction ).getParentFile();
profileFile = new File( rc.localFunction ) ; profileFile = new File( rc.localFunction ) ;
} else { }
else
{
profileDir = new File( profileBaseDir ); profileDir = new File( profileBaseDir );
profileFile = new File( profileDir, rc.localFunction + ".brf" ) ; profileFile = new File( profileDir, rc.localFunction + ".brf" ) ;
} }
@ -46,8 +52,10 @@ public final class ProfileCache {
File lookupFile = new File( profileDir, "lookups.dat" ); File lookupFile = new File( profileDir, "lookups.dat" );
// invalidate cache at lookup-table update // invalidate cache at lookup-table update
if (!(lookupFile.equals(lastLookupFile) && lookupFile.lastModified() == lastLookupTimestamp)) { if ( !(lookupFile.equals( lastLookupFile ) && lookupFile.lastModified() == lastLookupTimestamp ) )
if (lastLookupFile != null) { {
if ( lastLookupFile != null )
{
System.out.println( "******** invalidating profile-cache after lookup-file update ******** " ); System.out.println( "******** invalidating profile-cache after lookup-file update ******** " );
} }
apc = new ProfileCache[apc.length]; apc = new ProfileCache[apc.length];
@ -59,12 +67,16 @@ public final class ProfileCache {
int unusedSlot =-1; int unusedSlot =-1;
// check for re-use // check for re-use
for (int i = 0; i < apc.length; i++) { for( int i=0; i<apc.length; i++)
{
ProfileCache pc = apc[i]; ProfileCache pc = apc[i];
if (pc != null) { if ( pc != null )
if ((!pc.profilesBusy) && profileFile.equals(pc.lastProfileFile)) { {
if (rc.profileTimestamp == pc.lastProfileTimestamp) { if ( (!pc.profilesBusy) && profileFile.equals( pc.lastProfileFile ) )
{
if ( rc.profileTimestamp == pc.lastProfileTimestamp )
{
rc.expctxWay = pc.expctxWay; rc.expctxWay = pc.expctxWay;
rc.expctxNode = pc.expctxNode; rc.expctxNode = pc.expctxNode;
rc.readGlobalConfig(); rc.readGlobalConfig();
@ -75,10 +87,13 @@ public final class ProfileCache {
unusedSlot = -1; unusedSlot = -1;
break; break;
} }
if (lru == null || lru.lastUseTime > pc.lastUseTime) { if ( lru == null || lru.lastUseTime > pc.lastUseTime )
{
lru = pc; lru = pc;
} }
} else if (unusedSlot < 0) { }
else if ( unusedSlot < 0 )
{
unusedSlot = i; unusedSlot = i;
} }
} }
@ -91,27 +106,29 @@ public final class ProfileCache {
meta.readMetaData( new File( profileDir, "lookups.dat" ) ); meta.readMetaData( new File( profileDir, "lookups.dat" ) );
rc.expctxWay.parseFile(profileFile, "global", rc.keyValues); rc.expctxWay.parseFile( profileFile, "global" );
rc.expctxNode.parseFile(profileFile, "global", rc.keyValues); rc.expctxNode.parseFile( profileFile, "global" );
rc.readGlobalConfig(); rc.readGlobalConfig();
if (rc.processUnusedTags) { if ( rc.processUnusedTags )
{
rc.expctxWay.setAllTagsUsed(); rc.expctxWay.setAllTagsUsed();
} }
if (lru == null || unusedSlot >= 0) { if ( lru == null || unusedSlot >= 0 )
{
lru = new ProfileCache(); lru = new ProfileCache();
if (unusedSlot >= 0) { if ( unusedSlot >= 0 )
{
apc[unusedSlot] = lru; apc[unusedSlot] = lru;
if (debug) if ( debug ) System.out.println( "******* adding new profile at idx=" + unusedSlot + " for " + profileFile );
System.out.println("******* adding new profile at idx=" + unusedSlot + " for " + profileFile);
} }
} }
if (lru.lastProfileFile != null) { if ( lru.lastProfileFile != null )
if (debug) {
System.out.println("******* replacing profile of age " + ((System.currentTimeMillis() - lru.lastUseTime) / 1000L) + " sec " + lru.lastProfileFile + "->" + profileFile); if ( debug ) System.out.println( "******* replacing profile of age " + ((System.currentTimeMillis()-lru.lastUseTime)/1000L) + " sec " + lru.lastProfileFile + "->" + profileFile );
} }
lru.lastProfileTimestamp = rc.profileTimestamp; lru.lastProfileTimestamp = rc.profileTimestamp;
@ -123,13 +140,17 @@ public final class ProfileCache {
return false; return false;
} }
public static synchronized void releaseProfile(RoutingContext rc) { public static synchronized void releaseProfile( RoutingContext rc )
for (int i = 0; i < apc.length; i++) { {
for( int i=0; i<apc.length; i++)
{
ProfileCache pc = apc[i]; ProfileCache pc = apc[i];
if (pc != null) { if ( pc != null )
{
// only the thread that holds the cached instance can release it // 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; pc.profilesBusy = false;
break; break;
} }

View file

@ -5,6 +5,7 @@
*/ */
package btools.router; package btools.router;
import java.io.DataOutput;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -14,21 +15,21 @@ import btools.expressions.BExpressionContext;
import btools.expressions.BExpressionContextNode; import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay; import btools.expressions.BExpressionContextWay;
import btools.mapaccess.GeometryDecoder; import btools.mapaccess.GeometryDecoder;
import btools.mapaccess.MatchedWaypoint;
import btools.mapaccess.OsmLink; import btools.mapaccess.OsmLink;
import btools.mapaccess.OsmNode; import btools.mapaccess.OsmNode;
import btools.util.CheapAngleMeter; import btools.util.CheapAngleMeter;
import btools.util.CheapRuler; import btools.util.CheapRuler;
public final class RoutingContext { public final class RoutingContext
public void setAlternativeIdx(int idx) { {
public void setAlternativeIdx(int idx )
{
alternativeIdx = idx; alternativeIdx = idx;
} }
public int getAlternativeIdx(int min, int max)
public int getAlternativeIdx(int min, int max) { {
return alternativeIdx < min ? min : (alternativeIdx > max ? max : alternativeIdx); return alternativeIdx < min ? min : (alternativeIdx > max ? max : alternativeIdx);
} }
public int alternativeIdx = 0; public int alternativeIdx = 0;
public String localFunction; public String localFunction;
public long profileTimestamp; public long profileTimestamp;
@ -37,7 +38,8 @@ public final class RoutingContext {
public String rawTrackPath; public String rawTrackPath;
public String getProfileName() { public String getProfileName()
{
String name = localFunction == null ? "unknown" : localFunction; String name = localFunction == null ? "unknown" : localFunction;
if ( name.endsWith( ".brf" ) ) name = name.substring( 0, localFunction.length() - 4 ); if ( name.endsWith( ".brf" ) ) name = name.substring( 0, localFunction.length() - 4 );
int idx = name.lastIndexOf( File.separatorChar ); int idx = name.lastIndexOf( File.separatorChar );
@ -52,6 +54,10 @@ public final class RoutingContext {
public int memoryclass = 64; public int memoryclass = 64;
public int downhillcostdiv;
public int downhillcutoff;
public int uphillcostdiv;
public int uphillcutoff;
public boolean carMode; public boolean carMode;
public boolean bikeMode; public boolean bikeMode;
public boolean footMode; public boolean footMode;
@ -74,52 +80,75 @@ public final class RoutingContext {
public boolean transitonly; public boolean transitonly;
public double waypointCatchingRange; public double waypointCatchingRange;
public boolean correctMisplacedViaPoints;
public double correctMisplacedViaPointsDistance;
private void setModel(String className) { private void setModel( String className )
if (className == null) { {
if ( className == null )
{
pm = new StdModel(); pm = new StdModel();
} else { }
try { else
Class<?> clazz = Class.forName(className); {
pm = (OsmPathModel) clazz.getDeclaredConstructor().newInstance(); try
} catch (Exception e) { {
Class clazz = Class.forName( className );
pm = (OsmPathModel) clazz.newInstance();
}
catch( Exception e )
{
throw new RuntimeException( "Cannot create path-model: " + e ); throw new RuntimeException( "Cannot create path-model: " + e );
} }
} }
initModel(); initModel();
} }
public void initModel() { public void initModel()
{
pm.init( expctxWay, expctxNode, keyValues ); pm.init( expctxWay, expctxNode, keyValues );
} }
public long getKeyValueChecksum() { public long getKeyValueChecksum()
{
long s = 0L; long s = 0L;
if (keyValues != null) { if ( keyValues != null )
for (Map.Entry<String, String> e : keyValues.entrySet()) { {
for( Map.Entry<String,String> e : keyValues.entrySet() )
{
s += e.getKey().hashCode() + e.getValue().hashCode(); s += e.getKey().hashCode() + e.getValue().hashCode();
} }
} }
return s; return s;
} }
public void readGlobalConfig() { public void readGlobalConfig()
{
BExpressionContext expctxGlobal = expctxWay; // just one of them... BExpressionContext expctxGlobal = expctxWay; // just one of them...
if (keyValues != null) {
// add parameter to context
for (Map.Entry<String, String> e : keyValues.entrySet()) {
float f = Float.parseFloat(e.getValue());
expctxWay.setVariableValue(e.getKey(), f, false );
expctxNode.setVariableValue(e.getKey(), f, false );
}
}
setModel( expctxGlobal._modelClass ); setModel( expctxGlobal._modelClass );
downhillcostdiv = (int)expctxGlobal.getVariableValue( "downhillcost", 0.f );
downhillcutoff = (int)(expctxGlobal.getVariableValue( "downhillcutoff", 0.f )*10000);
uphillcostdiv = (int)expctxGlobal.getVariableValue( "uphillcost", 0.f );
uphillcutoff = (int)(expctxGlobal.getVariableValue( "uphillcutoff", 0.f )*10000);
if ( downhillcostdiv != 0 ) downhillcostdiv = 1000000/downhillcostdiv;
if ( uphillcostdiv != 0 ) uphillcostdiv = 1000000/uphillcostdiv;
carMode = 0.f != expctxGlobal.getVariableValue( "validForCars", 0.f ); carMode = 0.f != expctxGlobal.getVariableValue( "validForCars", 0.f );
bikeMode = 0.f != expctxGlobal.getVariableValue( "validForBikes", 0.f ); bikeMode = 0.f != expctxGlobal.getVariableValue( "validForBikes", 0.f );
footMode = 0.f != expctxGlobal.getVariableValue( "validForFoot", 0.f ); footMode = 0.f != expctxGlobal.getVariableValue( "validForFoot", 0.f );
waypointCatchingRange = expctxGlobal.getVariableValue( "waypointCatchingRange", 250.f ); waypointCatchingRange = expctxGlobal.getVariableValue( "waypointCatchingRange", 250.f );
// turn-restrictions not used per default for foot profiles // turn-restrictions used per default for car profiles
considerTurnRestrictions = 0.f != expctxGlobal.getVariableValue("considerTurnRestrictions", footMode ? 0.f : 1.f); considerTurnRestrictions = 0.f != expctxGlobal.getVariableValue( "considerTurnRestrictions", 1.f );
correctMisplacedViaPoints = 0.f != expctxGlobal.getVariableValue("correctMisplacedViaPoints", 1.f);
correctMisplacedViaPointsDistance = expctxGlobal.getVariableValue("correctMisplacedViaPointsDistance", 40.f);
// process tags not used in the profile (to have them in the data-tab) // process tags not used in the profile (to have them in the data-tab)
processUnusedTags = 0.f != expctxGlobal.getVariableValue( "processUnusedTags", 0.f ); processUnusedTags = 0.f != expctxGlobal.getVariableValue( "processUnusedTags", 0.f );
@ -140,13 +169,21 @@ public final class RoutingContext {
starttimeoffset = expctxGlobal.getVariableValue( "starttimeoffset", 0.f ); starttimeoffset = expctxGlobal.getVariableValue( "starttimeoffset", 0.f );
transitonly = expctxGlobal.getVariableValue( "transitonly", 0.f ) != 0.f; transitonly = expctxGlobal.getVariableValue( "transitonly", 0.f ) != 0.f;
farTrafficWeight = expctxGlobal.getVariableValue( "farTrafficWeight", 2.f );
nearTrafficWeight = expctxGlobal.getVariableValue( "nearTrafficWeight", 2.f );
farTrafficDecayLength = expctxGlobal.getVariableValue( "farTrafficDecayLength", 30000.f );
nearTrafficDecayLength = expctxGlobal.getVariableValue( "nearTrafficDecayLength", 3000.f );
trafficDirectionFactor = expctxGlobal.getVariableValue( "trafficDirectionFactor", 0.9f );
trafficSourceExponent = expctxGlobal.getVariableValue( "trafficSourceExponent", -0.7f );
trafficSourceMinDist = expctxGlobal.getVariableValue( "trafficSourceMinDist", 3000.f );
showspeed = 0.f != expctxGlobal.getVariableValue( "showspeed", 0.f ); showspeed = 0.f != expctxGlobal.getVariableValue( "showspeed", 0.f );
showSpeedProfile = 0.f != expctxGlobal.getVariableValue( "showSpeedProfile", 0.f ); showSpeedProfile = 0.f != expctxGlobal.getVariableValue( "showSpeedProfile", 0.f );
inverseRouting = 0.f != expctxGlobal.getVariableValue( "inverseRouting", 0.f ); inverseRouting = 0.f != expctxGlobal.getVariableValue( "inverseRouting", 0.f );
showTime = 0.f != expctxGlobal.getVariableValue("showtime", 0.f);
int tiMode = (int)expctxGlobal.getVariableValue( "turnInstructionMode", 0.f ); int tiMode = (int)expctxGlobal.getVariableValue( "turnInstructionMode", 0.f );
if (tiMode != 1) { // automatic selection from coordinate source if ( tiMode != 1 ) // automatic selection from coordinate source
{
turnInstructionMode = tiMode; turnInstructionMode = tiMode;
} }
turnInstructionCatchingRange = expctxGlobal.getVariableValue( "turnInstructionCatchingRange", 40.f ); turnInstructionCatchingRange = expctxGlobal.getVariableValue( "turnInstructionCatchingRange", 40.f );
@ -190,15 +227,21 @@ public final class RoutingContext {
public int ilatshortest; public int ilatshortest;
public int ilonshortest; public int ilonshortest;
public boolean countTraffic;
public boolean inverseDirection; public boolean inverseDirection;
public DataOutput trafficOutputStream;
public double farTrafficWeight;
public double nearTrafficWeight;
public double farTrafficDecayLength;
public double nearTrafficDecayLength;
public double trafficDirectionFactor;
public double trafficSourceExponent;
public double trafficSourceMinDist;
public boolean showspeed; public boolean showspeed;
public boolean showSpeedProfile; public boolean showSpeedProfile;
public boolean inverseRouting; public boolean inverseRouting;
public boolean showTime;
public String outputFormat = "gpx";
public boolean exportWaypoints = false;
public OsmPrePath firstPrePath; public OsmPrePath firstPrePath;
@ -213,19 +256,22 @@ public final class RoutingContext {
public double defaultC_r; public double defaultC_r;
public double bikerPower; public double bikerPower;
public static void prepareNogoPoints(List<OsmNodeNamed> nogos) { public static void prepareNogoPoints( List<OsmNodeNamed> nogos )
for (OsmNodeNamed nogo : nogos) { {
if (nogo instanceof OsmNogoPolygon) { for( OsmNodeNamed nogo : nogos )
{
if (nogo instanceof OsmNogoPolygon)
{
continue; continue;
} }
String s = nogo.name; String s = nogo.name;
int idx = s.indexOf( ' ' ); int idx = s.indexOf( ' ' );
if ( idx > 0 ) s = s.substring( 0 , idx ); if ( idx > 0 ) s = s.substring( 0 , idx );
int ir = 20; // default radius int ir = 20; // default radius
if (s.length() > 4) { if ( s.length() > 4 )
try { {
ir = Integer.parseInt(s.substring(4)); try { ir = Integer.parseInt( s.substring( 4 ) ); }
} catch (Exception e) { /* ignore */ } catch( Exception e ) { /* ignore */ }
} }
// Radius of the nogo point in meters // Radius of the nogo point in meters
nogo.radius = ir; nogo.radius = ir;
@ -235,7 +281,8 @@ public final class RoutingContext {
/** /**
* restore the full nogolist previously saved by cleanNogoList * restore the full nogolist previously saved by cleanNogoList
*/ */
public void restoreNogoList() { public void restoreNogoList()
{
nogopoints = nogopoints_all; nogopoints = nogopoints_all;
} }
@ -245,18 +292,22 @@ public final class RoutingContext {
* *
* @return true if all wayoints are all in the same (full-weigth) nogo area (triggering bee-line-mode) * @return true if all wayoints are all in the same (full-weigth) nogo area (triggering bee-line-mode)
*/ */
public void cleanNogoList(List<OsmNode> waypoints) { public void cleanNogoList( List<OsmNode> waypoints )
{
nogopoints_all = nogopoints; nogopoints_all = nogopoints;
if ( nogopoints == null ) return; if ( nogopoints == null ) return;
List<OsmNodeNamed> nogos = new ArrayList<>(); List<OsmNodeNamed> nogos = new ArrayList<OsmNodeNamed>();
for (OsmNodeNamed nogo : nogopoints) { for( OsmNodeNamed nogo : nogopoints )
{
boolean goodGuy = true; boolean goodGuy = true;
for (OsmNode wp : waypoints) { for( OsmNode wp : waypoints )
{
if ( wp.calcDistance( nogo ) < nogo.radius if ( wp.calcDistance( nogo ) < nogo.radius
&& (!(nogo instanceof OsmNogoPolygon) && (!(nogo instanceof OsmNogoPolygon)
|| (((OsmNogoPolygon)nogo).isClosed || (((OsmNogoPolygon)nogo).isClosed
? ((OsmNogoPolygon)nogo).isWithin(wp.ilon, wp.ilat) ? ((OsmNogoPolygon)nogo).isWithin(wp.ilon, wp.ilat)
: ((OsmNogoPolygon) nogo).isOnPolyline(wp.ilon, wp.ilat)))) { : ((OsmNogoPolygon)nogo).isOnPolyline(wp.ilon, wp.ilat))))
{
goodGuy = false; goodGuy = false;
} }
} }
@ -265,73 +316,22 @@ public final class RoutingContext {
nogopoints = nogos.isEmpty() ? null : nogos; nogopoints = nogos.isEmpty() ? null : nogos;
} }
public void checkMatchedWaypointAgainstNogos(List<MatchedWaypoint> matchedWaypoints) { public boolean allInOneNogo( List<OsmNode> waypoints )
if (nogopoints == null) return; {
int theSize = matchedWaypoints.size();
if (theSize<2) return;
int removed = 0;
List<MatchedWaypoint> newMatchedWaypoints = new ArrayList<>();
MatchedWaypoint prevMwp = null;
boolean prevMwpIsInside = false;
for (int i = 0; i < theSize; i++) {
MatchedWaypoint mwp = matchedWaypoints.get(i);
boolean isInsideNogo = false;
OsmNode wp = mwp.crosspoint;
for (OsmNodeNamed nogo : nogopoints) {
if (Double.isNaN(nogo.nogoWeight)
&& wp.calcDistance(nogo) < nogo.radius
&& (!(nogo instanceof OsmNogoPolygon)
|| (((OsmNogoPolygon) nogo).isClosed
? ((OsmNogoPolygon) nogo).isWithin(wp.ilon, wp.ilat)
: ((OsmNogoPolygon) nogo).isOnPolyline(wp.ilon, wp.ilat)))) {
isInsideNogo = true;
break;
}
}
if (isInsideNogo) {
boolean useAnyway = false;
if (prevMwp == null) useAnyway = true;
else if (mwp.direct) useAnyway = true;
else if (prevMwp.direct) useAnyway = true;
else if (prevMwpIsInside) useAnyway = true;
else if (i == theSize-1) {
throw new IllegalArgumentException("last wpt in restricted area ");
}
if (useAnyway) {
prevMwpIsInside = true;
newMatchedWaypoints.add(mwp);
} else {
removed++;
prevMwpIsInside = false;
}
} else {
prevMwpIsInside = false;
newMatchedWaypoints.add(mwp);
}
prevMwp = mwp;
}
if (newMatchedWaypoints.size() < 2) {
throw new IllegalArgumentException("a wpt in restricted area ");
}
if (removed > 0) {
matchedWaypoints.clear();
matchedWaypoints.addAll(newMatchedWaypoints);
}
}
public boolean allInOneNogo(List<OsmNode> waypoints) {
if ( nogopoints == null ) return false; if ( nogopoints == null ) return false;
boolean allInTotal = false; boolean allInTotal = false;
for (OsmNodeNamed nogo : nogopoints) { for( OsmNodeNamed nogo : nogopoints )
{
boolean allIn = Double.isNaN( nogo.nogoWeight ); boolean allIn = Double.isNaN( nogo.nogoWeight );
for (OsmNode wp : waypoints) { for( OsmNode wp : waypoints )
{
int dist = wp.calcDistance( nogo ); int dist = wp.calcDistance( nogo );
if ( dist < nogo.radius if ( dist < nogo.radius
&& (!(nogo instanceof OsmNogoPolygon) && (!(nogo instanceof OsmNogoPolygon)
|| (((OsmNogoPolygon)nogo).isClosed || (((OsmNogoPolygon)nogo).isClosed
? ((OsmNogoPolygon)nogo).isWithin(wp.ilon, wp.ilat) ? ((OsmNogoPolygon)nogo).isWithin(wp.ilon, wp.ilat)
: ((OsmNogoPolygon) nogo).isOnPolyline(wp.ilon, wp.ilat)))) { : ((OsmNogoPolygon)nogo).isOnPolyline(wp.ilon, wp.ilat))))
{
continue; continue;
} }
allIn = false; allIn = false;
@ -341,10 +341,12 @@ public final class RoutingContext {
return allInTotal; return allInTotal;
} }
public long[] getNogoChecksums() { public long[] getNogoChecksums()
{
long[] cs = new long[3]; long[] cs = new long[3];
int n = nogopoints == null ? 0 : nogopoints.size(); int n = nogopoints == null ? 0 : nogopoints.size();
for (int i = 0; i < n; i++) { for( int i=0; i<n; i++ )
{
OsmNodeNamed nogo = nogopoints.get(i); OsmNodeNamed nogo = nogopoints.get(i);
cs[0] += nogo.ilon; cs[0] += nogo.ilon;
cs[1] += nogo.ilat; cs[1] += nogo.ilat;
@ -354,21 +356,25 @@ public final class RoutingContext {
return cs; return cs;
} }
public void setWaypoint(OsmNodeNamed wp, boolean endpoint) { public void setWaypoint( OsmNodeNamed wp, boolean endpoint )
{
setWaypoint( wp, null, endpoint ); setWaypoint( wp, null, endpoint );
} }
public void setWaypoint(OsmNodeNamed wp, OsmNodeNamed pendingEndpoint, boolean endpoint) { public void setWaypoint( OsmNodeNamed wp, OsmNodeNamed pendingEndpoint, boolean endpoint )
{
keepnogopoints = nogopoints; keepnogopoints = nogopoints;
nogopoints = new ArrayList<>(); nogopoints = new ArrayList<OsmNodeNamed>();
nogopoints.add( wp ); nogopoints.add( wp );
if ( keepnogopoints != null ) nogopoints.addAll( keepnogopoints ); if ( keepnogopoints != null ) nogopoints.addAll( keepnogopoints );
isEndpoint = endpoint; isEndpoint = endpoint;
this.pendingEndpoint = pendingEndpoint; this.pendingEndpoint = pendingEndpoint;
} }
public boolean checkPendingEndpoint() { public boolean checkPendingEndpoint()
if (pendingEndpoint != null) { {
if ( pendingEndpoint != null )
{
isEndpoint = true; isEndpoint = true;
nogopoints.set( 0, pendingEndpoint ); nogopoints.set( 0, pendingEndpoint );
pendingEndpoint = null; pendingEndpoint = null;
@ -377,13 +383,15 @@ public final class RoutingContext {
return false; return false;
} }
public void unsetWaypoint() { public void unsetWaypoint()
{
nogopoints = keepnogopoints; nogopoints = keepnogopoints;
pendingEndpoint = null; pendingEndpoint = null;
isEndpoint = false; isEndpoint = false;
} }
public int calcDistance(int lon1, int lat1, int lon2, int lat2) { public int calcDistance( int lon1, int lat1, int lon2, int lat2 )
{
double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (lat1+lat2) >> 1 ); double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (lat1+lat2) >> 1 );
double dlon2m = lonlat2m[0]; double dlon2m = lonlat2m[0];
double dlat2m = lonlat2m[1]; double dlat2m = lonlat2m[1];
@ -393,8 +401,10 @@ public final class RoutingContext {
shortestmatch = false; shortestmatch = false;
if (nogopoints != null && !nogopoints.isEmpty() && d > 0.) { if ( nogopoints != null && !nogopoints.isEmpty() && d > 0. )
for (int ngidx = 0; ngidx < nogopoints.size(); ngidx++) { {
for( int ngidx = 0; ngidx < nogopoints.size(); ngidx++ )
{
OsmNodeNamed nogo = nogopoints.get(ngidx); OsmNodeNamed nogo = nogopoints.get(ngidx);
double x1 = (lon1 - nogo.ilon) * dlon2m; double x1 = (lon1 - nogo.ilon) * dlon2m;
double y1 = (lat1 - nogo.ilat) * dlat2m; double y1 = (lat1 - nogo.ilat) * dlat2m;
@ -404,20 +414,20 @@ public final class RoutingContext {
double r22 = x2*x2 + y2*y2; double r22 = x2*x2 + y2*y2;
double radius = Math.abs( r12 < r22 ? y1*dx - x1*dy : y2*dx - x2*dy ) / d; double radius = Math.abs( r12 < r22 ? y1*dx - x1*dy : y2*dx - x2*dy ) / d;
if (radius < nogo.radius) { // 20m if ( radius < nogo.radius ) // 20m
{
double s1 = x1*dx + y1*dy; double s1 = x1*dx + y1*dy;
double s2 = x2*dx + y2*dy; double s2 = x2*dx + y2*dy;
if (s1 < 0.) { if ( s1 < 0. ) { s1 = -s1; s2 = -s2; }
s1 = -s1; if ( s2 > 0. )
s2 = -s2; {
}
if (s2 > 0.) {
radius = Math.sqrt( s1 < s2 ? r12 : r22 ); radius = Math.sqrt( s1 < s2 ? r12 : r22 );
if ( radius > nogo.radius ) continue; if ( radius > nogo.radius ) continue;
} }
if (nogo.isNogo) { if ( nogo.isNogo )
{
if (!(nogo instanceof OsmNogoPolygon)) { // nogo is a circle if (!(nogo instanceof OsmNogoPolygon)) { // nogo is a circle
if (Double.isNaN(nogo.nogoWeight)) { if (Double.isNaN(nogo.nogoWeight)) {
// default nogo behaviour (ignore completely) // default nogo behaviour (ignore completely)
@ -426,7 +436,9 @@ public final class RoutingContext {
// nogo weight, compute distance within the circle // nogo weight, compute distance within the circle
nogoCost = nogo.distanceWithinRadius(lon1, lat1, lon2, lat2, d) * nogo.nogoWeight; nogoCost = nogo.distanceWithinRadius(lon1, lat1, lon2, lat2, d) * nogo.nogoWeight;
} }
} else if (((OsmNogoPolygon) nogo).intersects(lon1, lat1, lon2, lat2)) { }
else if (((OsmNogoPolygon)nogo).intersects(lon1, lat1, lon2, lat2))
{
// nogo is a polyline/polygon, we have to check there is indeed // nogo is a polyline/polygon, we have to check there is indeed
// an intersection in this case (radius check is not enough). // an intersection in this case (radius check is not enough).
if (Double.isNaN(nogo.nogoWeight)) { if (Double.isNaN(nogo.nogoWeight)) {
@ -442,21 +454,28 @@ public final class RoutingContext {
} }
} }
} }
} else { }
else
{
shortestmatch = true; shortestmatch = true;
nogo.radius = radius; // shortest distance to way nogo.radius = radius; // shortest distance to way
// calculate remaining distance // calculate remaining distance
if (s2 < 0.) { if ( s2 < 0. )
{
wayfraction = -s2 / (d*d); wayfraction = -s2 / (d*d);
double xm = x2 - wayfraction*dx; double xm = x2 - wayfraction*dx;
double ym = y2 - wayfraction*dy; double ym = y2 - wayfraction*dy;
ilonshortest = (int)(xm / dlon2m + nogo.ilon); ilonshortest = (int)(xm / dlon2m + nogo.ilon);
ilatshortest = (int)(ym / dlat2m + nogo.ilat); ilatshortest = (int)(ym / dlat2m + nogo.ilat);
} else if (s1 > s2) { }
else if ( s1 > s2 )
{
wayfraction = 0.; wayfraction = 0.;
ilonshortest = lon2; ilonshortest = lon2;
ilatshortest = lat2; ilatshortest = lat2;
} else { }
else
{
wayfraction = 1.; wayfraction = 1.;
ilonshortest = lon1; ilonshortest = lon1;
ilatshortest = lat1; ilatshortest = lat1;
@ -466,11 +485,14 @@ public final class RoutingContext {
// *after* the shortest distance point. In case of a shortest-match // *after* the shortest distance point. In case of a shortest-match
// we use the reduced way segment for nogo-matching, in order not // we use the reduced way segment for nogo-matching, in order not
// to cut our escape-way if we placed a nogo just in front of where we are // to cut our escape-way if we placed a nogo just in front of where we are
if (isEndpoint) { if ( isEndpoint )
{
wayfraction = 1. - wayfraction; wayfraction = 1. - wayfraction;
lon2 = ilonshortest; lon2 = ilonshortest;
lat2 = ilatshortest; lat2 = ilatshortest;
} else { }
else
{
nogoCost = 0.; nogoCost = 0.;
lon1 = ilonshortest; lon1 = ilonshortest;
lat1 = ilatshortest; lat1 = ilatshortest;
@ -482,26 +504,30 @@ public final class RoutingContext {
} }
} }
} }
return (int) Math.max(1.0, Math.round(d)); return (int)(d + 1.0 );
} }
public OsmPathModel pm; public OsmPathModel pm;
public OsmPrePath createPrePath(OsmPath origin, OsmLink link) { public OsmPrePath createPrePath( OsmPath origin, OsmLink link )
{
OsmPrePath p = pm.createPrePath(); OsmPrePath p = pm.createPrePath();
if (p != null) { if ( p != null )
{
p.init( origin, link, this ); p.init( origin, link, this );
} }
return p; return p;
} }
public OsmPath createPath(OsmLink link) { public OsmPath createPath( OsmLink link )
{
OsmPath p = pm.createPath(); OsmPath p = pm.createPath();
p.init( link ); p.init( link );
return p; return p;
} }
public OsmPath createPath(OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode) { public OsmPath createPath( OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode )
{
OsmPath p = pm.createPath(); OsmPath p = pm.createPath();
p.init( origin, link, refTrack, detailMode, this ); p.init( origin, link, refTrack, detailMode, this );
return p; return p;

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1,4 +1,5 @@
package btools.router; 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,7 +8,8 @@ package btools.router;
import btools.mapaccess.OsmNode; import btools.mapaccess.OsmNode;
public final class SearchBoundary { public final class SearchBoundary
{
private int minlon0; private int minlon0;
private int minlat0; private int minlat0;
@ -27,7 +28,8 @@ public final class SearchBoundary {
/** /**
* @param radius Search radius in meters. * @param radius Search radius in meters.
*/ */
public SearchBoundary(OsmNode n, int radius, int direction) { public SearchBoundary( OsmNode n, int radius, int direction )
{
this.radius = radius; this.radius = radius;
this.direction = direction; this.direction = direction;
@ -47,7 +49,8 @@ public final class SearchBoundary {
maxlat = lat + 6000000; maxlat = lat + 6000000;
} }
public static String getFileName(OsmNode n) { public static String getFileName( OsmNode n )
{
int lon = (n.ilon / 5000000 ) * 5000000; int lon = (n.ilon / 5000000 ) * 5000000;
int lat = (n.ilat / 5000000 ) * 5000000; int lat = (n.ilat / 5000000 ) * 5000000;
@ -59,28 +62,28 @@ public final class SearchBoundary {
return slon + "_" + slat + ".trf"; return slon + "_" + slat + ".trf";
} }
public boolean isInBoundary(OsmNode n, int cost) { public boolean isInBoundary( OsmNode n, int cost )
if (radius > 0) { {
if ( radius > 0 )
{
return n.calcDistance( p ) < radius; return n.calcDistance( p ) < radius;
} }
if (cost == 0) { if ( cost == 0 )
{
return n.ilon > minlon0 && n.ilon < maxlon0 && n.ilat > minlat0 && n.ilat < maxlat0; 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; return n.ilon > minlon && n.ilon < maxlon && n.ilat > minlat && n.ilat < maxlat;
} }
public int getBoundaryDistance(OsmNode n) { public int getBoundaryDistance( OsmNode n )
switch (direction) { {
case 0: switch( direction )
return n.calcDistance(new OsmNode(n.ilon, minlat)); {
case 1: case 0: return n.calcDistance( new OsmNode( n.ilon, minlat ) );
return n.calcDistance(new OsmNode(minlon, n.ilat)); case 1: return n.calcDistance( new OsmNode( minlon, n.ilat ) );
case 2: case 2: return n.calcDistance( new OsmNode( n.ilon, maxlat ) );
return n.calcDistance(new OsmNode(n.ilon, maxlat)); case 3: return n.calcDistance( new OsmNode( maxlon, n.ilat ) );
case 3: default: throw new IllegalArgumentException( "undefined direction: "+ direction );
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; import btools.expressions.BExpressionContextWay;
final class StdModel extends OsmPathModel { final class StdModel extends OsmPathModel
public OsmPrePath createPrePath() { {
public OsmPrePath createPrePath()
{
return null; return null;
} }
public OsmPath createPath() { public OsmPath createPath()
{
return new StdPath(); return new StdPath();
} }
@ -26,7 +29,8 @@ final class StdModel extends OsmPathModel {
@Override @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; ctxWay = expctxWay;
ctxNode = expctxNode; ctxNode = expctxNode;

View file

@ -5,7 +5,10 @@
*/ */
package btools.router; package btools.router;
final class StdPath extends OsmPath { import btools.util.FastMath;
final class StdPath extends OsmPath
{
/** /**
* The elevation-hysteresis-buffer (0-10 m) * The elevation-hysteresis-buffer (0-10 m)
*/ */
@ -16,14 +19,12 @@ final class StdPath extends OsmPath {
private float totalEnergy; // total route energy (Joule) private float totalEnergy; // total route energy (Joule)
private float elevation_buffer; // just another elevation buffer (for travel time) private float elevation_buffer; // just another elevation buffer (for travel time)
private int uphillcostdiv;
private int downhillcostdiv;
// Gravitational constant, g // Gravitational constant, g
private static final double GRAVITY = 9.81; // in meters per second^(-2) private static final double GRAVITY = 9.81; // in meters per second^(-2)
@Override @Override
public void init(OsmPath orig) { public void init( OsmPath orig )
{
StdPath origin = (StdPath)orig; StdPath origin = (StdPath)orig;
this.ehbd = origin.ehbd; this.ehbd = origin.ehbd;
this.ehbu = origin.ehbu; this.ehbu = origin.ehbu;
@ -33,61 +34,32 @@ final class StdPath extends OsmPath {
} }
@Override @Override
protected void resetState() { protected void resetState()
{
ehbd = 0; ehbd = 0;
ehbu = 0; ehbu = 0;
totalTime = 0.f; totalTime = 0.f;
totalEnergy = 0.f; totalEnergy = 0.f;
uphillcostdiv = 0;
downhillcostdiv = 0;
elevation_buffer = 0.f; elevation_buffer = 0.f;
} }
@Override @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 // calculate the costfactor inputs
float turncostbase = rc.expctxWay.getTurncost(); 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 cfup = rc.expctxWay.getUphillCostfactor();
float cfdown = rc.expctxWay.getDownhillCostfactor(); float cfdown = rc.expctxWay.getDownhillCostfactor();
float cf = rc.expctxWay.getCostfactor(); float cf = rc.expctxWay.getCostfactor();
cfup = cfup == 0.f ? cf : cfup; cfup = cfup == 0.f ? cf : cfup;
cfdown = cfdown == 0.f ? cf : cfdown; 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 // penalty for turning angle
int turncost = (int)((1.-cosangle) * turncostbase + 0.2 ); // e.g. turncost=90 -> 90 degree = 90m penalty int turncost = (int)((1.-cosangle) * turncostbase + 0.2 ); // e.g. turncost=90 -> 90 degree = 90m penalty
if (message != null) { if ( message != null )
{
message.linkturncost += turncost; message.linkturncost += turncost;
message.turnangle = (float)angle; message.turnangle = (float)angle;
} }
@ -99,77 +71,80 @@ final class StdPath extends OsmPath {
// leads to an immediate penalty // leads to an immediate penalty
int delta_h_micros = (int)(1000000. * delta_h); int delta_h_micros = (int)(1000000. * delta_h);
ehbd += -delta_h_micros - dist * downhillcutoff; ehbd += -delta_h_micros - dist * rc.downhillcutoff;
ehbu += delta_h_micros - dist * uphillcutoff; ehbu += delta_h_micros - dist * rc.uphillcutoff;
float downweight = 0.f; float downweight = 0.f;
if (ehbd > rc.elevationpenaltybuffer) { if ( ehbd > rc.elevationpenaltybuffer )
{
downweight = 1.f; downweight = 1.f;
int excess = ehbd - rc.elevationpenaltybuffer; int excess = ehbd - rc.elevationpenaltybuffer;
int reduce = dist * rc.elevationbufferreduce; int reduce = dist * rc.elevationbufferreduce;
if (reduce > excess) { if ( reduce > excess )
{
downweight = ((float)excess)/reduce; downweight = ((float)excess)/reduce;
reduce = excess; reduce = excess;
} }
excess = ehbd - rc.elevationmaxbuffer; excess = ehbd - rc.elevationmaxbuffer;
if (reduce < excess) { if ( reduce < excess )
{
reduce = excess; reduce = excess;
} }
ehbd -= reduce; ehbd -= reduce;
float elevationCost = 0.f; if ( rc.downhillcostdiv > 0 )
if (downhillcostdiv > 0) { {
elevationCost += Math.min(reduce, dist * downhillmaxslope) / downhillcostdiv; int elevationCost = reduce/rc.downhillcostdiv;
}
if (downhillmaxslopecostdiv > 0) {
elevationCost += Math.max(0, reduce - dist * downhillmaxslope) / downhillmaxslopecostdiv;
}
if (elevationCost > 0) {
sectionCost += elevationCost; sectionCost += elevationCost;
if (message != null) { if ( message != null )
{
message.linkelevationcost += elevationCost; message.linkelevationcost += elevationCost;
} }
} }
} else if (ehbd < 0) { }
else if ( ehbd < 0 )
{
ehbd = 0; ehbd = 0;
} }
float upweight = 0.f; float upweight = 0.f;
if (ehbu > rc.elevationpenaltybuffer) { if ( ehbu > rc.elevationpenaltybuffer )
{
upweight = 1.f; upweight = 1.f;
int excess = ehbu - rc.elevationpenaltybuffer; int excess = ehbu - rc.elevationpenaltybuffer;
int reduce = dist * rc.elevationbufferreduce; int reduce = dist * rc.elevationbufferreduce;
if (reduce > excess) { if ( reduce > excess )
{
upweight = ((float)excess)/reduce; upweight = ((float)excess)/reduce;
reduce = excess; reduce = excess;
} }
excess = ehbu - rc.elevationmaxbuffer; excess = ehbu - rc.elevationmaxbuffer;
if (reduce < excess) { if ( reduce < excess )
{
reduce = excess; reduce = excess;
} }
ehbu -= reduce; ehbu -= reduce;
float elevationCost = 0.f; if ( rc.uphillcostdiv > 0 )
if (uphillcostdiv > 0) { {
elevationCost += Math.min(reduce, dist * uphillmaxslope) / uphillcostdiv; int elevationCost = reduce/rc.uphillcostdiv;
}
if (uphillmaxslopecostdiv > 0) {
elevationCost += Math.max(0, reduce - dist * uphillmaxslope) / uphillmaxslopecostdiv;
}
if (elevationCost > 0) {
sectionCost += elevationCost; sectionCost += elevationCost;
if (message != null) { if ( message != null )
{
message.linkelevationcost += elevationCost; message.linkelevationcost += elevationCost;
} }
} }
} else if (ehbu < 0) { }
else if ( ehbu < 0 )
{
ehbu = 0; ehbu = 0;
} }
// get the effective costfactor (slope dependent) // 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; message.costfactor = costfactor;
} }
@ -179,16 +154,20 @@ final class StdPath extends OsmPath {
} }
@Override @Override
protected double processTargetNode(RoutingContext rc) { protected double processTargetNode( RoutingContext rc )
{
// finally add node-costs for target node // finally add node-costs for target node
if (targetNode.nodeDescription != null) { if ( targetNode.nodeDescription != null )
{
boolean nodeAccessGranted = rc.expctxWay.getNodeAccessGranted() != 0.; boolean nodeAccessGranted = rc.expctxWay.getNodeAccessGranted() != 0.;
rc.expctxNode.evaluate( nodeAccessGranted , targetNode.nodeDescription ); rc.expctxNode.evaluate( nodeAccessGranted , targetNode.nodeDescription );
float initialcost = rc.expctxNode.getInitialcost(); float initialcost = rc.expctxNode.getInitialcost();
if (initialcost >= 1000000.) { if ( initialcost >= 1000000. )
{
return -1.; return -1.;
} }
if (message != null) { if ( message != null )
{
message.linknodecost += (int)initialcost; message.linknodecost += (int)initialcost;
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription( nodeAccessGranted, targetNode.nodeDescription ); message.nodeKeyValues = rc.expctxNode.getKeyValueDescription( nodeAccessGranted, targetNode.nodeDescription );
} }
@ -198,37 +177,49 @@ final class StdPath extends OsmPath {
} }
@Override @Override
public int elevationCorrection() { public int elevationCorrection( RoutingContext rc )
return (downhillcostdiv > 0 ? ehbd / downhillcostdiv : 0) {
+ (uphillcostdiv > 0 ? ehbu / uphillcostdiv : 0); return ( rc.downhillcostdiv > 0 ? ehbd/rc.downhillcostdiv : 0 )
+ ( rc.uphillcostdiv > 0 ? ehbu/rc.uphillcostdiv : 0 );
} }
@Override @Override
public boolean definitlyWorseThan(OsmPath path) { public boolean definitlyWorseThan( OsmPath path, RoutingContext rc )
{
StdPath p = (StdPath)path; StdPath p = (StdPath)path;
int c = p.cost; int c = p.cost;
if (p.downhillcostdiv > 0) { if ( rc.downhillcostdiv > 0 )
int delta = p.ehbd / p.downhillcostdiv - (downhillcostdiv > 0 ? ehbd / downhillcostdiv : 0); {
if (delta > 0) c += delta; int delta = p.ehbd - ehbd;
if ( delta > 0 ) c += delta/rc.downhillcostdiv;
} }
if (p.uphillcostdiv > 0) { if ( rc.uphillcostdiv > 0 )
int delta = p.ehbu / p.uphillcostdiv - (uphillcostdiv > 0 ? ehbu / uphillcostdiv : 0); {
if (delta > 0) c += delta; 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 min_delta = 3.;
double shift = 0.; double shift;
if (elevation_buffer > min_delta) { if ( elevation_buffer > min_delta )
{
shift = -min_delta; shift = -min_delta;
} else if (elevation_buffer < -min_delta) {
shift = min_delta;
} }
double decayFactor = Math.exp(-dist / 100.); 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); float new_elevation_buffer = (float)( (elevation_buffer+shift) * decayFactor - shift);
double incline = ( elevation_buffer - new_elevation_buffer ) / dist; double incline = ( elevation_buffer - new_elevation_buffer ) / dist;
elevation_buffer = new_elevation_buffer; elevation_buffer = new_elevation_buffer;
@ -236,8 +227,10 @@ final class StdPath extends OsmPath {
} }
@Override @Override
protected void computeKinematic(RoutingContext rc, double dist, double delta_h, boolean detailMode) { protected void computeKinematic( RoutingContext rc, double dist, double delta_h, boolean detailMode )
if (!detailMode) { {
if ( !detailMode )
{
return; return;
} }
@ -245,41 +238,57 @@ final class StdPath extends OsmPath {
elevation_buffer += delta_h; elevation_buffer += delta_h;
double incline = calcIncline( dist ); double incline = calcIncline( dist );
double maxSpeed = rc.maxSpeed; double wayMaxspeed;
double speedLimit = rc.expctxWay.getMaxspeed() / 3.6f;
if (speedLimit > 0) {
maxSpeed = Math.min(maxSpeed, speedLimit);
}
double speed = maxSpeed; // Travel speed wayMaxspeed = rc.expctxWay.getMaxspeed() / 3.6f;
if (wayMaxspeed == 0)
{
wayMaxspeed = rc.maxSpeed;
}
wayMaxspeed = Math.min(wayMaxspeed,rc.maxSpeed);
double speed; // Travel speed
double f_roll = rc.totalMass * GRAVITY * ( rc.defaultC_r + incline ); double f_roll = rc.totalMass * GRAVITY * ( rc.defaultC_r + incline );
if (rc.footMode) { if (rc.footMode || rc.expctxWay.getCostfactor() > 4.9 )
{
// Use Tobler's hiking function for walking sections // Use Tobler's hiking function for walking sections
speed = rc.maxSpeed * Math.exp(-3.5 * Math.abs(incline + 0.05)); speed = rc.maxSpeed * 3.6;
} else if (rc.bikeMode) { speed = (speed * FastMath.exp(-3.5 * Math.abs( incline + 0.05))) / 3.6;
}
else if (rc.bikeMode)
{
speed = solveCubic( rc.S_C_x, f_roll, rc.bikerPower ); speed = solveCubic( rc.S_C_x, f_roll, rc.bikerPower );
speed = Math.min(speed, maxSpeed); speed = Math.min(speed, wayMaxspeed);
}
else // all other
{
speed = wayMaxspeed;
} }
float dt = (float) ( dist / speed ); float dt = (float) ( dist / speed );
totalTime += dt; totalTime += dt;
// Calc energy assuming biking (no good model yet for hiking) // Calc energy assuming biking (no good model yet for hiking)
// (Count only positive, negative would mean breaking to enforce maxspeed) // (Count only positive, negative would mean breaking to enforce maxspeed)
double energy = dist*(rc.S_C_x*speed*speed + f_roll); double energy = dist*(rc.S_C_x*speed*speed + f_roll);
if (energy > 0.) { if ( energy > 0. )
{
totalEnergy += energy; 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 // Solves a * v^3 + c * v = d with a Newton method
// to get the speed v for the section. // to get the speed v for the section.
double v = 8.; double v = 8.;
boolean findingStartvalue = true; boolean findingStartvalue = true;
for (int i = 0; i < 10; i++) { for ( int i = 0; i < 10; i++ )
{
double y = ( a * v * v + c ) * v - d; double y = ( a * v * v + c ) * v - d;
if (y < .1) { if ( y < .1 )
if (findingStartvalue) { {
if ( findingStartvalue )
{
v *= 2.; v *= 2.;
continue; continue;
} }
@ -293,12 +302,14 @@ final class StdPath extends OsmPath {
} }
@Override @Override
public double getTotalTime() { public double getTotalTime()
{
return totalTime; return totalTime;
} }
@Override @Override
public double getTotalEnergy() { public double getTotalEnergy()
{
return totalEnergy; 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.ArrayList;
import java.util.List; import java.util.List;
public class VoiceHint { public class VoiceHint
{
static final int C = 1; // continue (go straight) static final int C = 1; // continue (go straight)
static final int TL = 2; // turn left static final int TL = 2; // turn left
static final int TSLL = 3; // turn slightly 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 TSHR = 7; // turn sharply right
static final int KL = 8; // keep left static final int KL = 8; // keep left
static final int KR = 9; // keep right 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 TRU = 11; // Right U-turn
static final int OFFR = 12; // Off route static final int OFFR = 12; // Off route
static final int RNDB = 13; // Roundabout static final int RNDB = 13; // Roundabout
static final int RNLB = 14; // Roundabout left 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 ilon;
int ilat; int ilat;
@ -37,450 +36,168 @@ public class VoiceHint {
double distanceToNext; double distanceToNext;
int indexInTrack; int indexInTrack;
public float getTime() { public float getTime()
{
return oldWay == null ? 0.f : oldWay.time; return oldWay == null ? 0.f : oldWay.time;
} }
float angle = Float.MAX_VALUE; float angle;
boolean turnAngleConsumed; boolean turnAngleConsumed;
boolean needsRealTurn; boolean needsRealTurn;
int maxBadPrio = -1;
int roundaboutExit; int roundaboutExit;
boolean isRoundabout() { boolean isRoundabout()
{
return roundaboutExit != 0; return roundaboutExit != 0;
} }
public void addBadWay(MessageData badWay) { public void addBadWay( MessageData badWay )
if (badWay == null) { {
if ( badWay == null )
{
return; return;
} }
if (badWays == null) { if ( badWays == null )
badWays = new ArrayList<>(); {
badWays = new ArrayList<MessageData>();
} }
badWays.add( badWay ); badWays.add( badWay );
} }
public int getJsonCommandIndex() { public int getCommand()
switch (cmd) { {
case TLU: return cmd;
return 10;
case TU:
return 15;
case TSHL:
return 4;
case TL:
return 2;
case TSLL:
return 3;
case KL:
return 8;
case C:
return 1;
case KR:
return 9;
case TSLR:
return 6;
case TR:
return 5;
case TSHR:
return 7;
case TRU:
return 11;
case RNDB:
return 13;
case RNLB:
return 14;
case BL:
return 16;
case OFFR:
return 12;
default:
throw new IllegalArgumentException("unknown command: " + cmd);
}
} }
public int getExitNumber() { public int getExitNumber()
{
return roundaboutExit; return roundaboutExit;
} }
/* public String getCommandString()
* used by comment style, osmand style {
*/ switch ( cmd )
public String getCommandString() { {
switch (cmd) { case TU : return "TU";
case TLU: case TSHL : return "TSHL";
return "TU"; // should be changed to TLU when osmand uses new voice hint constants case TL : return "TL";
case TU: case TSLL : return "TSLL";
return "TU"; case KL : return "KL";
case TSHL: case C : return "C";
return "TSHL"; case KR : return "KR";
case TL: case TSLR : return "TSLR";
return "TL"; case TR : return "TR";
case TSLL: case TSHR : return "TSHR";
return "TSLL"; case TRU : return "TRU";
case KL: case RNDB : return "RNDB" + roundaboutExit;
return "KL"; case RNLB : return "RNLB" + (-roundaboutExit);
case C: default : throw new IllegalArgumentException( "unknown command: " + cmd );
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 getSymbolString()
* used by trkpt/sym style {
*/ switch ( cmd )
public String getCommandString(int c) { {
switch (c) { case TU : return "TU";
case TLU: case TSHL : return "TSHL";
return "TLU"; case TL : return "Left";
case TU: case TSLL : return "TSLL";
return "TU"; case KL : return "TSLL"; // ?
case TSHL: case C : return "Straight";
return "TSHL"; case KR : return "TSLR"; // ?
case TL: case TSLR : return "TSLR";
return "TL"; case TR : return "Right";
case TSLL: case TSHR : return "TSHR";
return "TSLL"; case TRU : return "TU";
case KL: case RNDB : return "RNDB" + roundaboutExit;
return "KL"; case RNLB : return "RNLB" + (-roundaboutExit);
case C: default : throw new IllegalArgumentException( "unknown command: " + cmd );
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()
* used by gpsies style {
*/ switch ( cmd )
public String getSymbolString() { {
switch (cmd) { case TU : return "u-turn";
case TLU: case TSHL : return "sharp left";
return "TU"; case TL : return "left";
case TU: case TSLL : return "slight left";
return "TU"; case KL : return "keep left";
case TSHL: case C : return "straight";
return "TSHL"; case KR : return "keep right";
case TL: case TSLR : return "slight right";
return "Left"; case TR : return "right";
case TSLL: case TSHR : return "sharp right";
return "TSLL"; case TRU : return "u-turn";
case KL: case RNDB : return "Take exit " + roundaboutExit;
return "TSLL"; // ? case RNLB : return "Take exit " + (-roundaboutExit);
case C: default : throw new IllegalArgumentException( "unknown command: " + cmd );
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()
* used by new locus trkpt style {
*/ switch ( cmd )
public String getLocusSymbolString() { {
switch (cmd) { case TU : return 13;
case TLU: case TSHL : return 5;
return "u-turn_left"; case TL : return 4;
case TU: case TSLL : return 3;
return "u-turn"; case KL : return 9; // ?
case TSHL: case C : return 1;
return "left_sharp"; case KR : return 10; // ?
case TL: case TSLR : return 6;
return "left"; case TR : return 7;
case TSLL: case TSHR : return 8;
return "left_slight"; case TRU : return 14;
case KL: case RNDB : return 26 + roundaboutExit;
return "stay_left"; // ? case RNLB : return 26 - roundaboutExit;
case C: default : throw new IllegalArgumentException( "unknown command: " + cmd );
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()
* used by osmand style {
*/ switch ( cmd )
public String getMessageString() { {
switch (cmd) { case TU : return 1003;
case TLU: case TSHL : return 1019;
return "u-turn"; // should be changed to u-turn-left when osmand uses new voice hint constants case TL : return 1000;
case TU: case TSLL : return 1017;
return "u-turn"; case KL : return 1015; // ?
case TSHL: case C : return 1002;
return "sharp left"; case KR : return 1014; // ?
case TL: case TSLR : return 1016;
return "left"; case TR : return 1001;
case TSLL: case TSHR : return 1018;
return "slight left"; case TRU : return 1003;
case KL: case RNDB : return 1008 + roundaboutExit;
return "keep left"; case RNLB : return 1008 + roundaboutExit;
case C: default : throw new IllegalArgumentException( "unknown command: " + cmd );
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);
} }
} }
/* public void calcCommand()
* 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() {
float lowerBadWayAngle = -181; float lowerBadWayAngle = -181;
float higherBadWayAngle = 181; float higherBadWayAngle = 181;
if (badWays != null) { if ( badWays != null )
for (MessageData badWay : badWays) { {
if (badWay.isBadOneway()) { for ( MessageData badWay : badWays )
{
if ( badWay.isBadOneway() )
{
continue; continue;
} }
if (lowerBadWayAngle < badWay.turnangle && badWay.turnangle < goodWay.turnangle) { if ( lowerBadWayAngle < badWay.turnangle && badWay.turnangle < goodWay.turnangle )
{
lowerBadWayAngle = badWay.turnangle; lowerBadWayAngle = badWay.turnangle;
} }
if (higherBadWayAngle > badWay.turnangle && badWay.turnangle > goodWay.turnangle) { if ( higherBadWayAngle > badWay.turnangle && badWay.turnangle > goodWay.turnangle )
{
higherBadWayAngle = badWay.turnangle; higherBadWayAngle = badWay.turnangle;
} }
} }
@ -489,105 +206,100 @@ public class VoiceHint {
float cmdAngle= angle; float cmdAngle= angle;
// fall back to local angle if otherwise inconsistent // fall back to local angle if otherwise inconsistent
//if ( lowerBadWayAngle > angle || higherBadWayAngle < angle ) if ( lowerBadWayAngle > angle || higherBadWayAngle < angle )
//{ {
//cmdAngle = goodWay.turnangle;
//}
if (angle == Float.MAX_VALUE) {
cmdAngle = goodWay.turnangle; cmdAngle = goodWay.turnangle;
} }
if (cmd == BL) return;
if (roundaboutExit > 0) { if (roundaboutExit > 0)
{
cmd = RNDB; cmd = RNDB;
} else if (roundaboutExit < 0) { }
else if (roundaboutExit < 0)
{
cmd = RNLB; cmd = RNLB;
} else if (is180DegAngle(cmdAngle) && cmdAngle <= -179.f && higherBadWayAngle == 181.f && lowerBadWayAngle == -181.f) { }
else if ( cmdAngle < -159. )
{
cmd = TU; cmd = TU;
} else if (cmdAngle < -159.f) { }
cmd = TLU; else if ( cmdAngle < -135. )
} else if (cmdAngle < -135.f) { {
cmd = TSHL; cmd = TSHL;
} else if (cmdAngle < -45.f) { }
else if ( cmdAngle < -45. )
{
// a TL can be pushed in either direction by a close-by alternative // 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; cmd = TSHL;
} else if (cmdAngle > -85.f && lowerBadWayAngle > -180.f && higherBadWayAngle > -10.f) { }
else if ( lowerBadWayAngle > -180. && lowerBadWayAngle < -90. && higherBadWayAngle > 0. )
{
cmd = TSLL; cmd = TSLL;
} else { }
if (cmdAngle < -110.f) { else
cmd = TSHL; {
} else if (cmdAngle > -60.f) {
cmd = TSLL;
} else {
cmd = TL; cmd = TL;
} }
} }
} else if (cmdAngle < -21.f) { else if ( cmdAngle < -21. )
if (cmd != KR) { // don't overwrite KR with TSLL {
if ( cmd != KR ) // don't overwrite KR with TSLL
{
cmd = TSLL; cmd = TSLL;
} }
} else if (cmdAngle < -5.f) { }
if (lowerBadWayAngle < -100.f && higherBadWayAngle < 45.f) { else if ( cmdAngle < 21. )
cmd = TSLL; {
} else if (lowerBadWayAngle >= -100.f && higherBadWayAngle < 45.f) { if ( cmd != KR && cmd != KL ) // don't overwrite KL/KR hints!
cmd = KL; {
} else {
cmd = C; 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 < 21.f) { else if ( cmdAngle < 45. )
{
if ( cmd != KL ) // don't overwrite KL with TSLR
{
cmd = TSLR;
}
}
else if ( cmdAngle < 135. )
{
// a TR can be pushed in either direction by a close-by alternative // 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; cmd = TSLR;
} else if (lowerBadWayAngle > -45.f && higherBadWayAngle <= 100.f) {
cmd = KR;
} else {
cmd = C;
} }
} else if (cmdAngle < 45.f) { else if ( lowerBadWayAngle > 15. && lowerBadWayAngle < 90. && higherBadWayAngle > 180. )
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) {
cmd = TSHR; cmd = TSHR;
} else { }
if (cmdAngle > 110.) { else
cmd = TSHR; {
} else if (cmdAngle < 60.) {
cmd = TSLR;
} else {
cmd = TR; cmd = TR;
} }
} }
} else if (cmdAngle < 159.f) { else if ( cmdAngle < 159. )
{
cmd = TSHR; cmd = TSHR;
} else if (is180DegAngle(cmdAngle) && cmdAngle >= 179.f && higherBadWayAngle == 181.f && lowerBadWayAngle == -181.f) { }
cmd = TU; else
} else { {
cmd = TRU; cmd = TRU;
} }
} }
static boolean is180DegAngle(float angle) { public String formatGeometry()
return (Math.abs(angle) <= 180.f && Math.abs(angle) >= 179.f); {
}
public String formatGeometry() {
float oldPrio = oldWay == null ? 0.f : oldWay.priorityclassifier; float oldPrio = oldWay == null ? 0.f : oldWay.priorityclassifier;
StringBuilder sb = new StringBuilder(30); StringBuilder sb = new StringBuilder(30);
sb.append( ' ' ).append( (int)oldPrio ); sb.append( ' ' ).append( (int)oldPrio );
appendTurnGeometry(sb,goodWay); appendTurnGeometry(sb,goodWay);
if (badWays != null) { if ( badWays != null )
for (MessageData badWay : badWays) { {
for ( MessageData badWay : badWays )
{
sb.append( " " ); sb.append( " " );
appendTurnGeometry( sb, badWay ); appendTurnGeometry( sb, badWay );
} }
@ -595,7 +307,8 @@ public class VoiceHint {
return sb.toString(); return sb.toString();
} }
private void appendTurnGeometry(StringBuilder sb, MessageData msg) { private void appendTurnGeometry( StringBuilder sb, MessageData msg )
{
sb.append( "(" ).append( (int)(msg.turnangle+0.5) ).append( ")" ).append( (int)(msg.priorityclassifier) ); 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.ArrayList;
import java.util.List; import java.util.List;
public class VoiceHintList { public class VoiceHintList
{
static final int TRANS_MODE_NONE = 0; private String transportMode;
static final int TRANS_MODE_FOOT = 1;
static final int TRANS_MODE_BIKE = 2;
static final int TRANS_MODE_CAR = 3;
private int transportMode = TRANS_MODE_BIKE;
int turnInstructionMode; int turnInstructionMode;
List<VoiceHint> list = new ArrayList<>(); ArrayList<VoiceHint> list = new ArrayList<VoiceHint>();
public void setTransportMode(boolean isCar, boolean isBike) { public void setTransportMode( boolean isCar, boolean isBike )
transportMode = isCar ? TRANS_MODE_CAR : (isBike ? TRANS_MODE_BIKE : TRANS_MODE_FOOT); {
transportMode = isCar ? "car" : ( isBike ? "bike" : "foot" );
} }
public void setTransportMode(int mode) { public String getTransportMode()
transportMode = mode; {
}
public String getTransportMode() {
String ret;
switch (transportMode) {
case TRANS_MODE_FOOT:
ret = "foot";
break;
case TRANS_MODE_CAR:
ret = "car";
break;
case TRANS_MODE_BIKE:
default:
ret = "bike";
break;
}
return ret;
}
public int transportMode() {
return transportMode; return transportMode;
} }
public int getLocusRouteType() { public int getLocusRouteType()
if (transportMode == TRANS_MODE_CAR) { {
if ( "car".equals( transportMode ) )
{
return 0; return 0;
} }
if (transportMode == TRANS_MODE_BIKE) { if ( "bike".equals( transportMode ) )
{
return 5; return 5;
} }
return 3; // foot return 3; // foot

View file

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

View file

@ -11,6 +11,7 @@ import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import btools.router.OsmNogoPolygon.Point;
import btools.util.CheapRuler; import btools.util.CheapRuler;
public class OsmNogoPolygonTest { public class OsmNogoPolygonTest {
@ -76,9 +77,9 @@ public class OsmNogoPolygonTest {
@Test @Test
public void testIsWithin() { 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[] 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}; 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}; boolean[] within = { true, false, false, false, false, true, true, true, true, true, };
for (int i=0; i<plons.length; i++) { 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))); assertEquals("("+plons[i]+","+plats[i]+")",within[i],polygon.isWithin(toOsmLon(plons[i], OFFSET_X), toOsmLat(plats[i], OFFSET_Y)));

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 { plugins {
id 'brouter.library-conventions' id 'java-library'
} }
dependencies { dependencies {
implementation project(':brouter-util') implementation project(':brouter-util')
implementation project(':brouter-codec') 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

@ -2,22 +2,22 @@ package btools.expressions;
import java.util.StringTokenizer; import java.util.StringTokenizer;
final class BExpression { final class BExpression
{
private static final int OR_EXP = 10; private static final int OR_EXP = 10;
private static final int AND_EXP = 11; private static final int AND_EXP = 11;
private static final int NOT_EXP = 12; private static final int NOT_EXP = 12;
private static final int ADD_EXP = 20; private static final int ADD_EXP = 20;
private static final int MULTIPLY_EXP = 21; private static final int MULTIPLY_EXP = 21;
private static final int DIVIDE_EXP = 22; private static final int MAX_EXP = 22;
private static final int MAX_EXP = 23; private static final int EQUAL_EXP = 23;
private static final int EQUAL_EXP = 24; private static final int GREATER_EXP = 24;
private static final int GREATER_EXP = 25; private static final int MIN_EXP = 25;
private static final int MIN_EXP = 26;
private static final int SUB_EXP = 27; private static final int SUB_EXP = 26;
private static final int LESSER_EXP = 28; private static final int LESSER_EXP = 27;
private static final int XOR_EXP = 29; private static final int XOR_EXP = 28;
private static final int SWITCH_EXP = 30; private static final int SWITCH_EXP = 30;
private static final int ASSIGN_EXP = 31; private static final int ASSIGN_EXP = 31;
@ -33,176 +33,154 @@ final class BExpression {
private BExpression op3; private BExpression op3;
private float numberValue; private float numberValue;
private int variableIdx; private int variableIdx;
private int lookupNameIdx = -1; private int lookupNameIdx;
private int[] lookupValueIdxArray; private int[] lookupValueIdxArray;
private boolean doNotChange;
// Parse the expression and all subexpression // Parse the expression and all subexpression
public static BExpression parse(BExpressionContext ctx, int level) throws Exception { public static BExpression parse( BExpressionContext ctx, int level ) throws Exception
{
return parse( ctx, level, null ); return parse( ctx, level, null );
} }
private static BExpression parse(BExpressionContext ctx, int level, String optionalToken) throws Exception { 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; boolean brackets = false;
String operator = ctx.parseToken(); String operator = ctx.parseToken();
if (optionalToken != null && optionalToken.equals(operator)) { if ( optionalToken != null && optionalToken.equals( operator ) )
{
operator = ctx.parseToken(); operator = ctx.parseToken();
} }
if ("(".equals(operator)) { if ( "(".equals( operator ) )
{
brackets = true; brackets = true;
operator = ctx.parseToken(); operator = ctx.parseToken();
} }
if (operator == null) { if ( operator == null )
{
if ( level == 0 ) return null; if ( level == 0 ) return null;
else throw new IllegalArgumentException( "unexpected end of file" ); else throw new IllegalArgumentException( "unexpected end of file" );
} }
if (level == 0) { if ( level == 0 )
if (!"assign".equals(operator)) { {
if ( !"assign".equals( operator ) )
{
throw new IllegalArgumentException( "operator " + operator + " is invalid on toplevel (only 'assign' allowed)" ); throw new IllegalArgumentException( "operator " + operator + " is invalid on toplevel (only 'assign' allowed)" );
} }
} }
BExpression exp = new BExpression(); BExpression exp = new BExpression();
int nops = 3; int nops = 3;
boolean ifThenElse = false; boolean ifThenElse = false;
if ("switch".equals(operator)) { if ( "switch".equals( operator ) )
{
exp.typ = SWITCH_EXP; exp.typ = SWITCH_EXP;
} else if ("if".equals(operator)) { }
else if ( "if".equals( operator ) )
{
exp.typ = SWITCH_EXP; exp.typ = SWITCH_EXP;
ifThenElse = true; ifThenElse = true;
} else { }
else
{
nops = 2; // check binary expressions nops = 2; // check binary expressions
if ("or".equals(operator)) { if ( "or".equals( operator ) )
{
exp.typ = OR_EXP; exp.typ = OR_EXP;
} else if ("and".equals(operator)) { }
else if ( "and".equals( operator ) )
{
exp.typ = AND_EXP; exp.typ = AND_EXP;
} else if ("multiply".equals(operator)) { }
else if ( "multiply".equals( operator ) )
{
exp.typ = MULTIPLY_EXP; exp.typ = MULTIPLY_EXP;
} else if ("divide".equals(operator)) { }
exp.typ = DIVIDE_EXP; else if ( "add".equals( operator ) )
} else if ("add".equals(operator)) { {
exp.typ = ADD_EXP; exp.typ = ADD_EXP;
} else if ("max".equals(operator)) { }
else if ( "max".equals( operator ) )
{
exp.typ = MAX_EXP; exp.typ = MAX_EXP;
} else if ("min".equals(operator)) { }
else if ( "min".equals( operator ) )
{
exp.typ = MIN_EXP; exp.typ = MIN_EXP;
} else if ("equal".equals(operator)) { }
else if ( "equal".equals( operator ) )
{
exp.typ = EQUAL_EXP; exp.typ = EQUAL_EXP;
} else if ("greater".equals(operator)) { }
else if ( "greater".equals( operator ) )
{
exp.typ = GREATER_EXP; exp.typ = GREATER_EXP;
} else if ("sub".equals(operator)) { }
else if ( "sub".equals( operator ) )
{
exp.typ = SUB_EXP; exp.typ = SUB_EXP;
} else if ("lesser".equals(operator)) { }
else if ( "lesser".equals( operator ) )
{
exp.typ = LESSER_EXP; exp.typ = LESSER_EXP;
} else if ("xor".equals(operator)) { }
else if ( "xor".equals( operator ) )
{
exp.typ = XOR_EXP; exp.typ = XOR_EXP;
} else { }
else
{
nops = 1; // check unary expressions nops = 1; // check unary expressions
if ("assign".equals(operator)) { if ( "assign".equals( operator ) )
{
if ( level > 0 ) throw new IllegalArgumentException( "assign operator within expression" ); if ( level > 0 ) throw new IllegalArgumentException( "assign operator within expression" );
exp.typ = ASSIGN_EXP; exp.typ = ASSIGN_EXP;
String variable = ctx.parseToken(); String variable = ctx.parseToken();
if ( variable == null ) throw new IllegalArgumentException( "unexpected end of file" ); if ( variable == null ) throw new IllegalArgumentException( "unexpected end of file" );
if (variable.indexOf('=') >= 0) if ( variable.indexOf( '=' ) >= 0 ) throw new IllegalArgumentException( "variable name cannot contain '=': " + variable );
throw new IllegalArgumentException("variable name cannot contain '=': " + variable); if ( variable.indexOf( ':' ) >= 0 ) throw new IllegalArgumentException( "cannot assign context-prefixed variable: " + variable );
if (variable.indexOf(':') >= 0)
throw new IllegalArgumentException("cannot assign context-prefixed variable: " + variable);
exp.variableIdx = ctx.getVariableIdx( variable, true ); exp.variableIdx = ctx.getVariableIdx( variable, true );
if (exp.variableIdx < ctx.getMinWriteIdx()) if ( exp.variableIdx < ctx.getMinWriteIdx() ) throw new IllegalArgumentException( "cannot assign to readonly variable " + variable );
throw new IllegalArgumentException("cannot assign to readonly variable " + variable); }
} else if ("not".equals(operator)) { else if ( "not".equals( operator ) )
{
exp.typ = NOT_EXP; exp.typ = NOT_EXP;
} else { }
else
{
nops = 0; // check elemantary expressions nops = 0; // check elemantary expressions
int idx = operator.indexOf( '=' ); int idx = operator.indexOf( '=' );
if (idx >= 0) { if ( idx >= 0 )
{
exp.typ = LOOKUP_EXP; exp.typ = LOOKUP_EXP;
String name = operator.substring( 0, idx ); String name = operator.substring( 0, idx );
String values = operator.substring( idx+1 ); String values = operator.substring( idx+1 );
exp.lookupNameIdx = ctx.getLookupNameIdx( name ); exp.lookupNameIdx = ctx.getLookupNameIdx( name );
if (exp.lookupNameIdx < 0) { if ( exp.lookupNameIdx < 0 )
{
throw new IllegalArgumentException( "unknown lookup name: " + name ); throw new IllegalArgumentException( "unknown lookup name: " + name );
} }
ctx.markLookupIdxUsed( exp.lookupNameIdx );
StringTokenizer tk = new StringTokenizer( values, "|" ); StringTokenizer tk = new StringTokenizer( values, "|" );
int nt = tk.countTokens(); int nt = tk.countTokens();
int nt2 = nt == 0 ? 1 : nt; int nt2 = nt == 0 ? 1 : nt;
exp.lookupValueIdxArray = new int[nt2]; exp.lookupValueIdxArray = new int[nt2];
for (int ti = 0; ti < nt2; ti++) { for( int ti=0; ti<nt2; ti++ )
{
String value = ti < nt ? tk.nextToken() : ""; String value = ti < nt ? tk.nextToken() : "";
exp.lookupValueIdxArray[ti] = ctx.getLookupValueIdx( exp.lookupNameIdx, value ); exp.lookupValueIdxArray[ti] = ctx.getLookupValueIdx( exp.lookupNameIdx, value );
if (exp.lookupValueIdxArray[ti] < 0) { if ( exp.lookupValueIdxArray[ti] < 0 )
{
throw new IllegalArgumentException( "unknown lookup value: " + value ); throw new IllegalArgumentException( "unknown lookup value: " + value );
} }
} }
} else if ((idx = operator.indexOf(':')) >= 0) { }
else if ( ( idx = operator.indexOf( ':' ) ) >= 0 )
{
/* /*
use of variable values use of variable values
assign no_height assign no_height
@ -220,20 +198,31 @@ final class BExpression {
exp.typ = FOREIGN_VARIABLE_EXP; exp.typ = FOREIGN_VARIABLE_EXP;
exp.variableIdx = ctx.getForeignVariableIdx( context, varname ); exp.variableIdx = ctx.getForeignVariableIdx( context, varname );
} }
} else if ((idx = ctx.getVariableIdx(operator, false)) >= 0) { }
else if ( (idx = ctx.getVariableIdx( operator, false )) >= 0 )
{
exp.typ = VARIABLE_EXP; exp.typ = VARIABLE_EXP;
exp.variableIdx = idx; exp.variableIdx = idx;
} else if ("true".equals(operator)) { }
else if ( "true".equals( operator ) )
{
exp.numberValue = 1.f; exp.numberValue = 1.f;
exp.typ = NUMBER_EXP; exp.typ = NUMBER_EXP;
} else if ("false".equals(operator)) { }
else if ( "false".equals( operator ) )
{
exp.numberValue = 0.f; exp.numberValue = 0.f;
exp.typ = NUMBER_EXP; exp.typ = NUMBER_EXP;
} else { }
try { else
{
try
{
exp.numberValue = Float.parseFloat( operator ); exp.numberValue = Float.parseFloat( operator );
exp.typ = NUMBER_EXP; exp.typ = NUMBER_EXP;
} catch (NumberFormatException nfe) { }
catch( NumberFormatException nfe )
{
throw new IllegalArgumentException( "unknown expression: " + operator ); throw new IllegalArgumentException( "unknown expression: " + operator );
} }
} }
@ -241,166 +230,71 @@ final class BExpression {
} }
} }
// parse operands // parse operands
if (nops > 0) { if ( nops > 0 )
exp.op1 = parse(ctx, level + 1, exp.typ == ASSIGN_EXP ? "=" : null); {
exp.op1 = BExpression.parse( ctx, level+1, exp.typ == ASSIGN_EXP ? "=" : null );
} }
if (nops > 1) { if ( nops > 1 )
{
if ( ifThenElse ) checkExpectedToken( ctx, "then" ); if ( ifThenElse ) checkExpectedToken( ctx, "then" );
exp.op2 = parse(ctx, level + 1, null); exp.op2 = BExpression.parse( ctx, level+1, null );
} }
if (nops > 2) { if ( nops > 2 )
{
if ( ifThenElse ) checkExpectedToken( ctx, "else" ); if ( ifThenElse ) checkExpectedToken( ctx, "else" );
exp.op3 = parse(ctx, level + 1, null); exp.op3 = BExpression.parse( ctx, level+1, null );
} }
if (brackets) { if ( brackets )
{
checkExpectedToken( ctx, ")" ); checkExpectedToken( ctx, ")" );
} }
return exp; return exp;
} }
private static void checkExpectedToken(BExpressionContext ctx, String expected) throws Exception { private static void checkExpectedToken( BExpressionContext ctx, String expected ) throws Exception
{
String token = ctx.parseToken(); String token = ctx.parseToken();
if (!expected.equals(token)) { if ( ! expected.equals( token ) )
{
throw new IllegalArgumentException( "unexpected token: " + token + ", expected: " + expected ); throw new IllegalArgumentException( "unexpected token: " + token + ", expected: " + expected );
} }
} }
// Evaluate the expression // Evaluate the expression
public float evaluate(BExpressionContext ctx) { public float evaluate( BExpressionContext ctx )
switch (typ) { {
case OR_EXP: switch( typ )
return op1.evaluate(ctx) != 0.f ? 1.f : (op2.evaluate(ctx) != 0.f ? 1.f : 0.f); {
case XOR_EXP: case OR_EXP: return op1.evaluate(ctx) != 0.f ? 1.f : ( op2.evaluate(ctx) != 0.f ? 1.f : 0.f );
return ((op1.evaluate(ctx) != 0.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: case AND_EXP: return op1.evaluate(ctx) != 0.f ? ( op2.evaluate(ctx) != 0.f ? 1.f : 0.f ) : 0.f;
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 ADD_EXP: case SUB_EXP: return op1.evaluate(ctx) - op2.evaluate(ctx);
return op1.evaluate(ctx) + op2.evaluate(ctx); case MULTIPLY_EXP: return op1.evaluate(ctx) * op2.evaluate(ctx);
case SUB_EXP: case MAX_EXP: return max( op1.evaluate(ctx), op2.evaluate(ctx) );
return op1.evaluate(ctx) - op2.evaluate(ctx); case MIN_EXP: return min( op1.evaluate(ctx), op2.evaluate(ctx) );
case MULTIPLY_EXP: case EQUAL_EXP: return op1.evaluate(ctx) == op2.evaluate(ctx) ? 1.f : 0.f;
return op1.evaluate(ctx) * op2.evaluate(ctx); case GREATER_EXP: return op1.evaluate(ctx) > op2.evaluate(ctx) ? 1.f : 0.f;
case DIVIDE_EXP: case LESSER_EXP: return op1.evaluate(ctx) < op2.evaluate(ctx) ? 1.f : 0.f;
return divide(op1.evaluate(ctx), op2.evaluate(ctx)); case SWITCH_EXP: return op1.evaluate(ctx) != 0.f ? op2.evaluate(ctx) : op3.evaluate(ctx);
case MAX_EXP: case ASSIGN_EXP: return ctx.assign( variableIdx, op1.evaluate(ctx) );
return max(op1.evaluate(ctx), op2.evaluate(ctx)); case LOOKUP_EXP: return ctx.getLookupMatch( lookupNameIdx, lookupValueIdxArray );
case MIN_EXP: case NUMBER_EXP: return numberValue;
return min(op1.evaluate(ctx), op2.evaluate(ctx)); case VARIABLE_EXP: return ctx.getVariableValue( variableIdx );
case EQUAL_EXP: case FOREIGN_VARIABLE_EXP: return ctx.getForeignVariableValue( variableIdx );
return op1.evaluate(ctx) == op2.evaluate(ctx) ? 1.f : 0.f; case VARIABLE_GET_EXP: return ctx.getLookupValue(lookupNameIdx);
case GREATER_EXP: case NOT_EXP: return op1.evaluate(ctx) == 0.f ? 1.f : 0.f;
return op1.evaluate(ctx) > op2.evaluate(ctx) ? 1.f : 0.f; default: throw new IllegalArgumentException( "unknown op-code: " + typ );
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 private float max( float v1, float v2 )
// 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; return v1 > v2 ? v1 : v2;
} }
private float min(float v1, float v2) { private float min( float v1, float v2 )
{
return v1 < v2 ? v1 : 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;
}
} }

View file

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

View file

@ -8,97 +8,33 @@ package btools.expressions;
import btools.codec.TagValueValidator; 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 boolean decodeForbidden = true;
private static String[] buildInVariables = private static String[] buildInVariables =
{"costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "classifiermask", "maxspeed", "uphillcost", "downhillcost", "uphillcutoff", "downhillcutoff", "uphillmaxslope", "downhillmaxslope", "uphillmaxslopecost", "downhillmaxslopecost"}; { "costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "classifiermask", "maxspeed" };
protected String[] getBuildInVariableNames() { protected String[] getBuildInVariableNames()
{
return buildInVariables; return buildInVariables;
} }
public float getCostfactor() { public float getCostfactor() { return getBuildInVariable(0); }
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() { public BExpressionContextWay( BExpressionMetaData meta )
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 ); super( "way", meta );
} }
@ -107,18 +43,22 @@ public final class BExpressionContextWay extends BExpressionContext implements T
* *
* @param hashSize size of hashmap for result caching * @param hashSize size of hashmap for result caching
*/ */
public BExpressionContextWay(int hashSize, BExpressionMetaData meta) { public BExpressionContextWay( int hashSize, BExpressionMetaData meta )
{
super( "way", hashSize, meta ); super( "way", hashSize, meta );
} }
@Override @Override
public int accessType(byte[] description) { public int accessType( byte[] description )
{
evaluate( false, description ); evaluate( false, description );
float minCostFactor = getCostfactor(); float minCostFactor = getCostfactor();
if (minCostFactor >= 9999.f) { if ( minCostFactor >= 9999.f )
{
setInverseVars(); setInverseVars();
float reverseCostFactor = getCostfactor(); float reverseCostFactor = getCostfactor();
if (reverseCostFactor < minCostFactor) { if ( reverseCostFactor < minCostFactor )
{
minCostFactor = reverseCostFactor; minCostFactor = reverseCostFactor;
} }
} }
@ -126,7 +66,8 @@ public final class BExpressionContextWay extends BExpressionContext implements T
} }
@Override @Override
public void setDecodeForbidden(boolean decodeForbidden) { public void setDecodeForbidden( boolean decodeForbidden )
{
this.decodeForbidden= decodeForbidden; this.decodeForbidden= decodeForbidden;
} }
} }

View file

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

View file

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

View file

@ -4,22 +4,27 @@ import java.util.Arrays;
import btools.util.LruMapNode; import btools.util.LruMapNode;
public final class CacheNode extends LruMapNode { public final class CacheNode extends LruMapNode
{
byte[] ab; byte[] ab;
float[] vars; float[] vars;
@Override @Override
public int hashCode() { public int hashCode()
{
return hash; return hash;
} }
@Override @Override
public boolean equals(Object o) { public boolean equals( Object o )
{
CacheNode n = (CacheNode) o; CacheNode n = (CacheNode) o;
if (hash != n.hash) { if ( hash != n.hash )
{
return false; return false;
} }
if (ab == null) { if ( ab == null )
{
return true; // hack: null = crc match only 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,9 +3,12 @@ package btools.expressions;
import java.io.File; import java.io.File;
import java.util.Random; import java.util.Random;
public final class ProfileComparator { public final class ProfileComparator
public static void main(String[] args) { {
if (args.length != 4) { public static void main( String[] args )
{
if ( args.length != 4 )
{
System.out.println( "usage: java ProfileComparator <lookup-file> <profile1> <profile2> <nsamples>" ); System.out.println( "usage: java ProfileComparator <lookup-file> <profile1> <profile2> <nsamples>" );
return; return;
} }
@ -19,29 +22,21 @@ public final class ProfileComparator {
} }
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 // read lookup.dat + profiles
BExpressionMetaData meta1 = new BExpressionMetaData(); BExpressionMetaData meta1 = new BExpressionMetaData();
BExpressionMetaData meta2 = new BExpressionMetaData(); BExpressionMetaData meta2 = new BExpressionMetaData();
BExpressionContext expctx1 = nodeContext ? new BExpressionContextNode( meta1 ) : new BExpressionContextWay( meta1 ); BExpressionContext expctx1 = nodeContext ? new BExpressionContextNode( meta1 ) : new BExpressionContextWay( meta1 );
BExpressionContext expctx2 = nodeContext ? new BExpressionContextNode( meta2 ) : new BExpressionContextWay( meta2 ); 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 ); meta1.readMetaData( lookupFile );
meta2.readMetaData( lookupFile ); meta2.readMetaData( lookupFile );
expctx1.parseFile( profile1File, "global" ); expctx1.parseFile( profile1File, "global" );
System.out.println("usedTags1=" + expctx1.usedTagList());
expctx2.parseFile( profile2File, "global" ); expctx2.parseFile( profile2File, "global" );
System.out.println("usedTags2=" + expctx2.usedTagList());
System.out.println("nodeContext=" + nodeContext + " nodeCount1=" + expctx1.expressionNodeCount + " nodeCount2=" + expctx2.expressionNodeCount);
Random rnd = new Random(); Random rnd = new Random();
for (int i = 0; i < nsamples; i++) { for( int i=0; i<nsamples; i++ )
{
int[] data = expctx1.generateRandomValues( rnd ); int[] data = expctx1.generateRandomValues( rnd );
expctx1.evaluate( data ); expctx1.evaluate( data );
expctx2.evaluate( data ); expctx2.evaluate( data );

View file

@ -4,18 +4,22 @@ import java.util.Arrays;
import btools.util.LruMapNode; import btools.util.LruMapNode;
public final class VarWrapper extends LruMapNode { public final class VarWrapper extends LruMapNode
{
float[] vars; float[] vars;
@Override @Override
public int hashCode() { public int hashCode()
{
return hash; return hash;
} }
@Override @Override
public boolean equals(Object o) { public boolean equals( Object o )
{
VarWrapper n = (VarWrapper) o; VarWrapper n = (VarWrapper) o;
if (hash != n.hash) { if ( hash != n.hash )
{
return false; 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,14 +1,17 @@
package btools.expressions; package btools.expressions;
import java.util.*;
import java.io.*;
import java.net.URL;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import java.io.File; public class EncodeDecodeTest
import java.net.URL; {
public class EncodeDecodeTest {
@Test @Test
public void encodeDecodeTest() { public void encodeDecodeTest()
{
URL testpurl = this.getClass().getResource( "/dummy.txt" ); URL testpurl = this.getClass().getResource( "/dummy.txt" );
File workingDir = new File(testpurl.getFile()).getParentFile(); File workingDir = new File(testpurl.getFile()).getParentFile();
File profileDir = new File( workingDir, "/../../../../misc/profiles2" ); File profileDir = new File( workingDir, "/../../../../misc/profiles2" );
@ -29,16 +32,16 @@ public class EncodeDecodeTest {
"depth=1'6\"", "depth=1'6\"",
// "depth=6 feet", // "depth=6 feet",
"maxheight=5.1m", "maxheight=5.1m",
"maxdraft=~3 m - 4 m", "maxdraft=~3 mt",
"reversedirection=yes" "reversedirection=yes"
}; };
// encode the tags into 64 bit description word // encode the tags into 64 bit description word
int[] lookupData = expctxWay.createNewLookupData(); int[] lookupData = expctxWay.createNewLookupData();
for (String arg : tags) { for( String arg: tags )
{
int idx = arg.indexOf( '=' ); int idx = arg.indexOf( '=' );
if (idx < 0) if ( idx < 0 ) throw new IllegalArgumentException( "bad argument (should be <tag>=<value>): " + arg );
throw new IllegalArgumentException("bad argument (should be <tag>=<value>): " + arg);
String key = arg.substring( 0, idx ); String key = arg.substring( 0, idx );
String value = arg.substring( idx+1 ); String value = arg.substring( idx+1 );

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;0000000021 mini_roundabout
construction;0000000020 turning_loop 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 ---context:node
highway;0001314954 bus_stop 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 { plugins {
id 'brouter.application-conventions' id 'java-library'
} }
dependencies { dependencies {
implementation project(':brouter-codec') implementation project(':brouter-codec')
implementation project(':brouter-util') implementation project(':brouter-util')
implementation project(':brouter-expressions') 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,32 +5,38 @@
*/ */
package btools.mapcreator; package btools.mapcreator;
import java.util.List; import java.util.ArrayList;
import btools.util.CheapRuler; import btools.util.CheapRuler;
public class DPFilter { public class DPFilter
{
private static double dp_sql_threshold = 0.4 * 0.4; private static double dp_sql_threshold = 0.4 * 0.4;
/* /*
* for each node (except first+last), eventually set the DP_SURVIVOR_BIT * 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 first = 0;
int last = nodes.size()-1; int last = nodes.size()-1;
while (first < last && (nodes.get(first + 1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0) { while( first < last && (nodes.get(first+1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0 )
{
first++; 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--; last--;
} }
if (last - first > 1) { if ( last - first > 1 )
{
doDPFilter( nodes, first, last ); 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.; double maxSqDist = -1.;
int index = -1; int index = -1;
OsmNodeP p1 = nodes.get( first ); OsmNodeP p1 = nodes.get( first );
@ -42,29 +48,36 @@ public class DPFilter {
double dx = (p2.ilon - p1.ilon) * dlon2m; double dx = (p2.ilon - p1.ilon) * dlon2m;
double dy = (p2.ilat - p1.ilat) * dlat2m; double dy = (p2.ilat - p1.ilat) * dlat2m;
double d2 = dx * dx + dy * dy; double d2 = dx * dx + dy * dy;
for (int i = first + 1; i < last; i++) { for ( int i = first + 1; i < last; i++ )
{
OsmNodeP p = nodes.get( i ); OsmNodeP p = nodes.get( i );
double t = 0.; double t = 0.;
if (d2 != 0f) { if ( d2 != 0f )
{
t = ( ( p.ilon - p1.ilon ) * dlon2m * dx + ( p.ilat - p1.ilat ) * dlat2m * dy ) / d2; t = ( ( p.ilon - p1.ilon ) * dlon2m * dx + ( p.ilat - p1.ilat ) * dlat2m * dy ) / d2;
t = t > 1. ? 1. : ( t < 0. ? 0. : t ); t = t > 1. ? 1. : ( t < 0. ? 0. : t );
} }
double dx2 = (p.ilon - ( p1.ilon + t*( p2.ilon - p1.ilon ) ) ) * dlon2m; 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 dy2 = (p.ilat - ( p1.ilat + t*( p2.ilat - p1.ilat ) ) ) * dlat2m;
double sqDist = dx2 * dx2 + dy2 * dy2; double sqDist = dx2 * dx2 + dy2 * dy2;
if (sqDist > maxSqDist) { if ( sqDist > maxSqDist )
{
index = i; index = i;
maxSqDist = sqDist; maxSqDist = sqDist;
} }
} }
if (index >= 0) { if ( index >= 0 )
if (index - first > 1) { {
if ( index - first > 1 )
{
doDPFilter( nodes, first, index ); doDPFilter( nodes, first, index );
} }
if (maxSqDist >= dp_sql_threshold) { if ( maxSqDist >= dp_sql_threshold )
{
nodes.get( index ).bits |= OsmNodeP.DP_SURVIVOR_BIT; nodes.get( index ).bits |= OsmNodeP.DP_SURVIVOR_BIT;
} }
if (last - index > 1) { if ( last - index > 1 )
{
doDPFilter( nodes, index, last ); 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

@ -14,34 +14,39 @@ import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
import btools.util.DiffCoderDataOutputStream; import btools.util.DiffCoderDataOutputStream;
public abstract class MapCreatorBase implements WayListener, NodeListener, RelationListener { public abstract class MapCreatorBase implements WayListener, NodeListener, RelationListener
{
private DiffCoderDataOutputStream[] tileOutStreams; private DiffCoderDataOutputStream[] tileOutStreams;
protected File outTileDir; protected File outTileDir;
protected Map<String, String> tags; protected HashMap<String,String> tags;
public void putTag(String key, String value) { public void putTag( String key, String value )
if (tags == null) tags = new HashMap<>(); {
if ( tags == null ) tags = new HashMap<String,String>();
tags.put( key, value ); tags.put( key, value );
} }
public String getTag(String key) { public String getTag( String key )
{
return tags == null ? null : tags.get( key ); return tags == null ? null : tags.get( key );
} }
public Map<String, String> getTagsOrNull() { public HashMap<String,String> getTagsOrNull()
{
return tags; return tags;
} }
public void setTags(Map<String, String> tags) { public void setTags( HashMap<String,String> tags )
{
this.tags = tags; this.tags = tags;
} }
protected static long readId(DataInputStream is) throws IOException { protected static long readId( DataInputStream is) throws IOException
{
int offset = is.readByte(); int offset = is.readByte();
if ( offset == 32 ) return -1; if ( offset == 32 ) return -1;
long i = is.readInt(); long i = is.readInt();
@ -49,8 +54,10 @@ public abstract class MapCreatorBase implements WayListener, NodeListener, Relat
return i | offset; return i | offset;
} }
protected static void writeId(DataOutputStream o, long id) throws IOException { protected static void writeId( DataOutputStream o, long id ) throws IOException
if (id == -1) { {
if ( id == -1 )
{
o.writeByte( 32 ); o.writeByte( 32 );
return; return;
} }
@ -61,16 +68,20 @@ public abstract class MapCreatorBase implements WayListener, NodeListener, Relat
} }
protected static File[] sortBySizeAsc(File[] files) { protected static File[] sortBySizeAsc( File[] files )
{
int n = files.length; int n = files.length;
long[] sizes = new long[n]; long[] sizes = new long[n];
File[] sorted = new File[n]; File[] sorted = new File[n];
for( int i=0; i<n; i++ ) sizes[i] = files[i].length(); for( int i=0; i<n; i++ ) sizes[i] = files[i].length();
for (int nf = 0; nf < n; nf++) { for(int nf=0; nf<n; nf++)
{
int idx = -1; int idx = -1;
long min = -1; long min = -1;
for (int i = 0; i < n; i++) { for( int i=0; i<n; i++ )
if (sizes[i] != -1 && (idx == -1 || sizes[i] < min)) { {
if ( sizes[i] != -1 && ( idx == -1 || sizes[i] < min ) )
{
min = sizes[i]; min = sizes[i];
idx = i; idx = i;
} }
@ -81,40 +92,50 @@ public abstract class MapCreatorBase implements WayListener, NodeListener, Relat
return sorted; return sorted;
} }
protected File fileFromTemplate(File template, File dir, String suffix) { protected File fileFromTemplate( File template, File dir, String suffix )
{
String filename = template.getName(); String filename = template.getName();
filename = filename.substring( 0, filename.length() - 3 ) + suffix; filename = filename.substring( 0, filename.length() - 3 ) + suffix;
return new File( dir, filename ); return new File( dir, filename );
} }
protected DataInputStream createInStream(File inFile) throws IOException { protected DataInputStream createInStream( File inFile ) throws IOException
{
return new DataInputStream( new BufferedInputStream ( new FileInputStream( inFile ) ) ); return new DataInputStream( new BufferedInputStream ( new FileInputStream( inFile ) ) );
} }
protected DiffCoderDataOutputStream createOutStream(File outFile) throws IOException { protected DiffCoderDataOutputStream createOutStream( File outFile ) throws IOException
{
return new DiffCoderDataOutputStream( new BufferedOutputStream( new FileOutputStream( outFile ) ) ); return new DiffCoderDataOutputStream( new BufferedOutputStream( new FileOutputStream( outFile ) ) );
} }
protected DiffCoderDataOutputStream getOutStreamForTile(int tileIndex) throws Exception { protected DiffCoderDataOutputStream getOutStreamForTile( int tileIndex ) throws Exception
if (tileOutStreams == null) { {
if ( tileOutStreams == null )
{
tileOutStreams = new DiffCoderDataOutputStream[64]; tileOutStreams = new DiffCoderDataOutputStream[64];
} }
if (tileOutStreams[tileIndex] == null) { if ( tileOutStreams[tileIndex] == null )
{
tileOutStreams[tileIndex] = createOutStream( new File( outTileDir, getNameForTile( tileIndex ) ) ); tileOutStreams[tileIndex] = createOutStream( new File( outTileDir, getNameForTile( tileIndex ) ) );
} }
return tileOutStreams[tileIndex]; return tileOutStreams[tileIndex];
} }
protected String getNameForTile(int tileIndex) { protected String getNameForTile( int tileIndex )
{
throw new IllegalArgumentException( "getNameForTile not implemented" ); throw new IllegalArgumentException( "getNameForTile not implemented" );
} }
protected void closeTileOutStreams() throws Exception { protected void closeTileOutStreams() throws Exception
if (tileOutStreams == null) { {
if ( tileOutStreams == null )
{
return; return;
} }
for (int tileIndex = 0; tileIndex < tileOutStreams.length; tileIndex++) { for( int tileIndex=0; tileIndex<tileOutStreams.length; tileIndex++ )
{
if ( tileOutStreams[tileIndex] != null ) tileOutStreams[tileIndex].close(); if ( tileOutStreams[tileIndex] != null ) tileOutStreams[tileIndex].close();
tileOutStreams[tileIndex] = null; tileOutStreams[tileIndex] = null;
} }
@ -124,36 +145,27 @@ public abstract class MapCreatorBase implements WayListener, NodeListener, Relat
// interface dummys // interface dummys
@Override @Override
public void nodeFileStart(File nodefile) throws Exception { public void nodeFileStart( File nodefile ) throws Exception {}
}
@Override @Override
public void nextNode(NodeData n) throws Exception { public void nextNode( NodeData n ) throws Exception {}
}
@Override @Override
public void nodeFileEnd(File nodefile) throws Exception { public void nodeFileEnd( File nodefile ) throws Exception {}
}
@Override @Override
public boolean wayFileStart(File wayfile) throws Exception { public boolean wayFileStart( File wayfile ) throws Exception { return true; }
return true;
}
@Override @Override
public void nextWay(WayData data) throws Exception { public void nextWay( WayData data ) throws Exception {}
}
@Override @Override
public void wayFileEnd(File wayfile) throws Exception { public void wayFileEnd( File wayfile ) throws Exception {}
}
@Override @Override
public void nextRelation(RelationData data) throws Exception { public void nextRelation( RelationData data ) throws Exception {}
}
@Override @Override
public void nextRestriction(RelationData data, long fromWid, long toWid, long viaNid) throws Exception { public void nextRestriction( RelationData data, long fromWid, long toWid, long viaNid ) throws Exception {}
}
} }

View file

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

View file

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

View file

@ -10,19 +10,22 @@ import btools.util.TinyDenseLongMap;
/** /**
* NodeFilter does 1 step in map-processing: * NodeFilter does 1 step in map-processing:
* <p> *
* - filters out unused nodes according to the way file * - filters out unused nodes according to the way file
* *
* @author ab * @author ab
*/ */
public class NodeFilter extends MapCreatorBase { public class NodeFilter extends MapCreatorBase
{
private DiffCoderDataOutputStream nodesOutStream; private DiffCoderDataOutputStream nodesOutStream;
private File nodeTilesOut; private File nodeTilesOut;
protected DenseLongMap nodebitmap; protected DenseLongMap nodebitmap;
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception
{
System.out.println("*** NodeFilter: Filter way related nodes"); System.out.println("*** NodeFilter: Filter way related nodes");
if (args.length != 3) { if (args.length != 3)
{
System.out.println("usage: java NodeFilter <node-tiles-in> <way-file-in> <node-tiles-out>" ); System.out.println("usage: java NodeFilter <node-tiles-in> <way-file-in> <node-tiles-out>" );
return; return;
} }
@ -30,11 +33,13 @@ public class NodeFilter extends MapCreatorBase {
new NodeFilter().process( new File( args[0] ), new File( args[1] ), new File( args[2] ) ); new NodeFilter().process( new File( args[0] ), new File( args[1] ), new File( args[2] ) );
} }
public void init() throws Exception { public void init() throws Exception
{
nodebitmap = Boolean.getBoolean( "useDenseMaps" ) ? new DenseLongMap( 512 ) : new TinyDenseLongMap(); nodebitmap = Boolean.getBoolean( "useDenseMaps" ) ? new DenseLongMap( 512 ) : new TinyDenseLongMap();
} }
public void process(File nodeTilesIn, File wayFileIn, File nodeTilesOut) throws Exception { public void process( File nodeTilesIn, File wayFileIn, File nodeTilesOut ) throws Exception
{
init(); init();
this.nodeTilesOut = nodeTilesOut; this.nodeTilesOut = nodeTilesOut;
@ -47,34 +52,41 @@ public class NodeFilter extends MapCreatorBase {
} }
@Override @Override
public void nextWay(WayData data) throws Exception { public void nextWay( WayData data ) throws Exception
{
int nnodes = data.nodes.size(); int nnodes = data.nodes.size();
for (int i = 0; i < nnodes; i++) { for (int i=0; i<nnodes; i++ )
{
nodebitmap.put( data.nodes.get(i), 0 ); nodebitmap.put( data.nodes.get(i), 0 );
} }
} }
@Override @Override
public void nodeFileStart(File nodefile) throws Exception { public void nodeFileStart( File nodefile ) throws Exception
{
String filename = nodefile.getName(); String filename = nodefile.getName();
File outfile = new File( nodeTilesOut, filename ); File outfile = new File( nodeTilesOut, filename );
nodesOutStream = new DiffCoderDataOutputStream( new BufferedOutputStream ( new FileOutputStream( outfile ) ) ); nodesOutStream = new DiffCoderDataOutputStream( new BufferedOutputStream ( new FileOutputStream( outfile ) ) );
} }
@Override @Override
public void nextNode(NodeData n) throws Exception { public void nextNode( NodeData n ) throws Exception
if (isRelevant(n)) { {
if ( isRelevant( n ) )
{
n.writeTo( nodesOutStream ); n.writeTo( nodesOutStream );
} }
} }
public boolean isRelevant(NodeData n) { public boolean isRelevant( NodeData n )
{
// check if node passes bitmap // check if node passes bitmap
return nodebitmap.getInt( n.nid ) == 0; // 0 -> bit set, -1 -> unset return nodebitmap.getInt( n.nid ) == 0; // 0 -> bit set, -1 -> unset
} }
@Override @Override
public void nodeFileEnd(File nodeFile) throws Exception { public void nodeFileEnd( File nodeFile ) throws Exception
{
nodesOutStream.close(); nodesOutStream.close();
} }
} }

View file

@ -13,46 +13,58 @@ import btools.util.DiffCoderDataInputStream;
* *
* @author ab * @author ab
*/ */
public class NodeIterator extends MapCreatorBase { public class NodeIterator extends MapCreatorBase
{
private NodeListener listener; private NodeListener listener;
private boolean delete; private boolean delete;
public NodeIterator(NodeListener nodeListener, boolean deleteAfterReading) { public NodeIterator( NodeListener nodeListener, boolean deleteAfterReading )
{
listener = nodeListener; listener = nodeListener;
delete = deleteAfterReading; delete = deleteAfterReading;
} }
public void processDir(File indir, String inSuffix) throws Exception { public void processDir( File indir, String inSuffix ) throws Exception
if (!indir.isDirectory()) { {
if ( !indir.isDirectory() )
{
throw new IllegalArgumentException( "not a directory: " + indir ); throw new IllegalArgumentException( "not a directory: " + indir );
} }
File[] af = sortBySizeAsc( indir.listFiles() ); File[] af = sortBySizeAsc( indir.listFiles() );
for (int i = 0; i < af.length; i++) { for( int i=0; i<af.length; i++ )
{
File nodefile = af[i]; File nodefile = af[i];
if (nodefile.getName().endsWith(inSuffix)) { if ( nodefile.getName().endsWith( inSuffix ) )
{
processFile( nodefile ); processFile( nodefile );
} }
} }
} }
public void processFile(File nodefile) throws Exception { public void processFile(File nodefile) throws Exception
{
System.out.println( "*** NodeIterator reading: " + nodefile ); System.out.println( "*** NodeIterator reading: " + nodefile );
listener.nodeFileStart( nodefile ); listener.nodeFileStart( nodefile );
DiffCoderDataInputStream di = new DiffCoderDataInputStream( new BufferedInputStream ( new FileInputStream( nodefile ) ) ); DiffCoderDataInputStream di = new DiffCoderDataInputStream( new BufferedInputStream ( new FileInputStream( nodefile ) ) );
try { try
for (; ; ) { {
for(;;)
{
NodeData n = new NodeData( di ); NodeData n = new NodeData( di );
listener.nextNode( n ); listener.nextNode( n );
} }
} catch (EOFException eof) { }
catch( EOFException eof )
{
di.close(); di.close();
} }
listener.nodeFileEnd( nodefile ); listener.nodeFileEnd( nodefile );
if (delete && "true".equals(System.getProperty("deletetmpfiles"))) { if ( delete && "true".equals( System.getProperty( "deletetmpfiles" ) ))
{
nodefile.delete(); nodefile.delete();
} }
} }

View file

@ -7,7 +7,8 @@ import java.io.File;
* *
* @author ab * @author ab
*/ */
public interface NodeListener { public interface NodeListener
{
void nodeFileStart( File nodefile ) throws Exception; void nodeFileStart( File nodefile ) throws Exception;
void nextNode( NodeData data ) throws Exception; void nextNode( NodeData data ) throws Exception;

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