Compare commits
No commits in common. "master" and "revert-342-test-and11" have entirely different histories.
master
...
revert-342
453 changed files with 27878 additions and 71699 deletions
|
|
@ -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
|
||||
|
|
@ -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
|
||||
98
.github/workflows/docker-publish.yml
vendored
98
.github/workflows/docker-publish.yml
vendored
|
|
@ -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}
|
||||
24
.github/workflows/gradle-publish.yml
vendored
24
.github/workflows/gradle-publish.yml
vendored
|
|
@ -4,7 +4,7 @@
|
|||
name: Gradle Package
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
|
|
@ -12,35 +12,25 @@ jobs:
|
|||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
environment: BRouter
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
|
||||
- name: Setup keystore
|
||||
env:
|
||||
BROUTER_KEYSTORE_BASE64: ${{ secrets.BROUTER_KEYSTORE_BASE64 }}
|
||||
run: |
|
||||
echo $BROUTER_KEYSTORE_BASE64 | base64 -di > ${{ github.workspace }}/brouter.jks
|
||||
- name: Build with Gradle
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_RELEASE_STORE_FILE: ${{ secrets.BROUTER_KEYSTORE_FILE }}
|
||||
ORG_GRADLE_PROJECT_RELEASE_KEY_ALIAS: ${{ secrets.BROUTER_KEY_ALIAS }}
|
||||
ORG_GRADLE_PROJECT_RELEASE_KEY_PASSWORD: ${{ secrets.BROUTER_KEY_PASSWORD }}
|
||||
ORG_GRADLE_PROJECT_RELEASE_STORE_PASSWORD: ${{ secrets.BROUTER_STORE_PASSWORD }}
|
||||
run: gradle build
|
||||
|
||||
|
||||
# The USERNAME and TOKEN need to correspond to the credentials environment variables used in
|
||||
# the publishing section of your build.gradle
|
||||
- name: Publish to GitHub Packages
|
||||
|
|
|
|||
29
.github/workflows/gradle.yml
vendored
29
.github/workflows/gradle.yml
vendored
|
|
@ -13,31 +13,14 @@ jobs:
|
|||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
environment: BRouter
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 8
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
java-version: '8'
|
||||
distribution: 'zulu'
|
||||
cache: gradle
|
||||
- name: Create local.properties
|
||||
run: touch local.properties
|
||||
- name: Setup keystore
|
||||
env:
|
||||
BROUTER_KEYSTORE_BASE64: ${{ secrets.BROUTER_KEYSTORE_BASE64 }}
|
||||
run: |
|
||||
echo $BROUTER_KEYSTORE_BASE64 | base64 -di > ${{ github.workspace }}/brouter.jks
|
||||
- name: Build with Gradle
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_RELEASE_STORE_FILE: ${{ secrets.BROUTER_KEYSTORE_FILE }}
|
||||
ORG_GRADLE_PROJECT_RELEASE_KEY_ALIAS: ${{ secrets.BROUTER_KEY_ALIAS }}
|
||||
ORG_GRADLE_PROJECT_RELEASE_KEY_PASSWORD: ${{ secrets.BROUTER_KEY_PASSWORD }}
|
||||
ORG_GRADLE_PROJECT_RELEASE_STORE_PASSWORD: ${{ secrets.BROUTER_STORE_PASSWORD }}
|
||||
run: ./gradlew build
|
||||
- name: Upload ZIP
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ZIP
|
||||
path: brouter-server/build/distributions/brouter-*.zip
|
||||
|
|
|
|||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,7 +1,6 @@
|
|||
*.iml
|
||||
.gradle
|
||||
.idea/
|
||||
build
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/gradle.xml
|
||||
|
|
@ -11,6 +10,7 @@ build
|
|||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
|
|
|
|||
14
Dockerfile
14
Dockerfile
|
|
@ -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
121
README.md
|
|
@ -1,63 +1,6 @@
|
|||
BRouter
|
||||
=======
|
||||
|
||||
# Come fare
|
||||
|
||||
To build the Docker image run (in the project's top level directory):
|
||||
|
||||
```
|
||||
docker build -t brouter .
|
||||
```
|
||||
|
||||
Download the segment files
|
||||
```
|
||||
wget -rkpN -np -e robots=off -l1 https://brouter.de/brouter/segments4/
|
||||
```
|
||||
|
||||
Download the profile files from github https://github.com/gpxstudio/brouter/tree/master/misc/profiles2 using
|
||||
```
|
||||
https://downgit.github.io/
|
||||
```
|
||||
or directly copy misc/profiles2 from git clone folder
|
||||
|
||||
si possono creare anche i profili con le varianti usando
|
||||
```
|
||||
generate_profile_variants.sh
|
||||
```
|
||||
si possono scaricare anche i profili di brouter originale usando
|
||||
```
|
||||
wget -rkpN -np -e robots=off -l1 https://brouter.de/brouter/profiles2/
|
||||
```
|
||||
e metterli nei folder che poi verranno caricati con i volumi condivisi nel docker
|
||||
```
|
||||
mv ./brouter.de/brouter/segments4 /home/nvme/dockerdata/brouter/segments
|
||||
mv mv ./brouter.de/brouter/profiles2 /home/nvme/dockerdata/brouter/profiles
|
||||
```
|
||||
oppure usando lo script
|
||||
```
|
||||
download_segments.sh
|
||||
```
|
||||
far partire il docker
|
||||
```
|
||||
services:
|
||||
brouterserver:
|
||||
container_name: brouter
|
||||
image: brouter
|
||||
ports:
|
||||
- 17777:17777
|
||||
volumes:
|
||||
- /home/nvme/dockerdata/brouter/segments:/segments4
|
||||
- /home/nvme/dockerdata/brouter/profiles:/profiles2
|
||||
restart: unless-stopped
|
||||
```
|
||||
## Come si interroga
|
||||
|
||||
```
|
||||
https://brouter.patachina.it/?lonlats=12.08349699,44.25067665|12.09165011,44.24834552&profile=Trekking-dry&format=geojson&alternativeidx=0
|
||||
```
|
||||
|
||||
# Original
|
||||
|
||||
BRouter is a configurable OSM offline router with elevation awareness, Java +
|
||||
Android. Designed to be multi-modal with a particular emphasis on bicycle
|
||||
and energy-based car routing.
|
||||
|
|
@ -72,7 +15,7 @@ You can install the BRouter app on your Android device from
|
|||
Store](https://play.google.com/store/apps/details?id=btools.routingapp). You
|
||||
can also [build BRouter](#build-and-install) yourself. You can find detailed
|
||||
documentation of the BRouter Android app in
|
||||
[`docs/users/android_quickstart.md`](docs/users/android_quickstart.md).
|
||||
[`misc/readmes/readme.txt`](misc/readmes/readme.txt).
|
||||
|
||||
<a href="https://f-droid.org/packages/btools.routingapp" target="_blank">
|
||||
<img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="90"/></a>
|
||||
|
|
@ -96,7 +39,7 @@ Alternatively, you can also use BRouter as the offline routing engine for
|
|||
[OSMAnd](https://osmand.net/) on your Android device.
|
||||
|
||||
A full documentation on how to set this up is available at
|
||||
[`docs/users/osmand.md`](docs/users/osmand.md).
|
||||
[`misc/readmes/osmand/README.md`](misc/readmes/osmand/README.md).
|
||||
|
||||
|
||||
## BRouter on Windows/Linux/Mac OS
|
||||
|
|
@ -155,7 +98,7 @@ Segments files from the whole planet are generated weekly at
|
|||
[https://brouter.de/brouter/segments4/](http://brouter.de/brouter/segments4/).
|
||||
|
||||
You can download one or more segments files, covering the area of the planet
|
||||
you want to route, into the `misc/segments4` directory.
|
||||
your want to route, into the `misc/segments4` directory.
|
||||
|
||||
#### Generate your own segments files
|
||||
|
||||
|
|
@ -163,7 +106,7 @@ You can also generate the segments files you need directly from a planet dump
|
|||
of OpenStreetMap data (or a [GeoFabrik extract](https://download.geofabrik.de/)).
|
||||
|
||||
More documentation of this is available in the
|
||||
[`docs/developers/build_segments.md`](docs/developers/build_segments.md) file.
|
||||
[`misc/readmes/mapcreation.md`](misc/readmes/mapcreation.md) file.
|
||||
|
||||
|
||||
### (Optional) Generate profile variants
|
||||
|
|
@ -177,7 +120,7 @@ to help you quickly generate variants based on the default profiles, to create
|
|||
a default set of profiles covering most of the basic use cases.
|
||||
|
||||
Have a look at the
|
||||
[`docs/developers/profile_developers_guide.md`](docs/developers/profile_developers_guide.md)
|
||||
[`misc/readmes/profile_developers_guide.txt`](misc/readmes/profile_developers_guide.txt)
|
||||
for an in-depth guide on profiles edition and customization.
|
||||
|
||||
|
||||
|
|
@ -194,62 +137,10 @@ The API endpoints exposed by this HTTP server are documented in the
|
|||
[`brouter-server/src/main/java/btools/server/request/ServerHandler.java`](brouter-server/src/main/java/btools/server/request/ServerHandler.java)
|
||||
file.
|
||||
|
||||
The server emits log data for each routing request on stdout. For each routing
|
||||
request a line with the following eight fields is printed. The fields are
|
||||
separated by whitespace.
|
||||
|
||||
- timestamp, in ISO8601 format, e.g. `2024-05-14T21:11:26.499+02:00`
|
||||
- current server session count (integer number 1-999) or "new" when a new
|
||||
IP address is detected
|
||||
- IP address (IPv4 or IPv6), prefixed by `ip=`
|
||||
- duration of routing request in ms, prefixed by `ms=`
|
||||
- divider `->`
|
||||
- HTTP request method
|
||||
- HTTP request URL
|
||||
- HTTP request version
|
||||
|
||||
Example log output:
|
||||
|
||||
```
|
||||
2024-05-14T21:11:26.499+02:00 new ip=127.0.0.1 ms=189 -> GET /brouter?lonlats=13.377485,52.516247%7C13.351221,52.515004&profile=trekking&alternativeidx=0&format=geojson HTTP/1.1
|
||||
2024-05-14T21:11:33.229+02:00 1 ip=127.0.0.1 ms=65 -> GET /brouter?lonlats=13.377485,52.516247%7C13.351221,52.515004&profile=trekking&alternativeidx=0&format=geojson HTTP/1.1
|
||||
```
|
||||
|
||||
## BRouter with Docker
|
||||
|
||||
To build the Docker image run (in the project's top level directory):
|
||||
|
||||
```
|
||||
docker build -t brouter .
|
||||
```
|
||||
|
||||
Download the segment files as described in the previous chapter. The folder containing the
|
||||
segment files can be mounted into the container. Run BRouter as follows:
|
||||
|
||||
```
|
||||
docker run --rm \
|
||||
-v ./misc/scripts/segments4:/segments4 \
|
||||
-p 17777:17777 \
|
||||
--name brouter \
|
||||
brouter
|
||||
```
|
||||
|
||||
This will start brouter with a set of default routing profiles. It will be accessible on port 17777.
|
||||
|
||||
If you want to provide your own routing profiles, you can also mount the folder containing the custom profiles:
|
||||
|
||||
```
|
||||
docker run --rm \
|
||||
-v ./misc/scripts/segments4:/segments4 \
|
||||
-v /path/to/custom/profiles:/profiles2 \
|
||||
-p 17777:17777 \
|
||||
--name brouter \
|
||||
brouter
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
More documentation is available in the [`docs`](docs) folder.
|
||||
More documentation is available in the [`misc/readmes`](misc/readmes) folder.
|
||||
|
||||
|
||||
## Related Projects
|
||||
|
|
|
|||
1
brouter-codec/.gitignore
vendored
Normal file
1
brouter-codec/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build/
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
plugins {
|
||||
id 'brouter.library-conventions'
|
||||
id 'java-library'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':brouter-util')
|
||||
testImplementation 'junit:junit:4.13.1'
|
||||
}
|
||||
|
|
|
|||
3
brouter-codec/src/main/AndroidManifest.xml
Normal file
3
brouter-codec/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<manifest package="btools.codec" />
|
||||
|
|
@ -5,10 +5,11 @@ import btools.util.BitCoderContext;
|
|||
/**
|
||||
* Container for some re-usable databuffers for the decoder
|
||||
*/
|
||||
public final class DataBuffers {
|
||||
public final class DataBuffers
|
||||
{
|
||||
public byte[] iobuffer;
|
||||
public byte[] tagbuf1 = new byte[256];
|
||||
public BitCoderContext bctx1 = new BitCoderContext(tagbuf1);
|
||||
public BitCoderContext bctx1 = new BitCoderContext( tagbuf1 );
|
||||
public byte[] bbuf1 = new byte[65636];
|
||||
public int[] ibuf1 = new int[4096];
|
||||
public int[] ibuf2 = new int[2048];
|
||||
|
|
@ -16,15 +17,17 @@ public final class DataBuffers {
|
|||
public int[] alon = new int[2048];
|
||||
public int[] alat = new int[2048];
|
||||
|
||||
public DataBuffers() {
|
||||
this(new byte[65636]);
|
||||
public DataBuffers()
|
||||
{
|
||||
this( new byte[65636] );
|
||||
}
|
||||
|
||||
/**
|
||||
* construct a set of databuffers except
|
||||
* for 'iobuffer', where the given array is used
|
||||
*/
|
||||
public DataBuffers(byte[] iobuffer) {
|
||||
public DataBuffers( byte[] iobuffer )
|
||||
{
|
||||
this.iobuffer = iobuffer;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,14 +3,16 @@ package btools.codec;
|
|||
/**
|
||||
* Special integer fifo suitable for 3-pass encoding
|
||||
*/
|
||||
public class IntegerFifo3Pass {
|
||||
public class IntegerFifo3Pass
|
||||
{
|
||||
private int[] a;
|
||||
private int size;
|
||||
private int pos;
|
||||
|
||||
private int pass;
|
||||
|
||||
public IntegerFifo3Pass(int capacity) {
|
||||
public IntegerFifo3Pass( int capacity )
|
||||
{
|
||||
a = capacity < 4 ? new int[4] : new int[capacity];
|
||||
}
|
||||
|
||||
|
|
@ -18,7 +20,8 @@ public class IntegerFifo3Pass {
|
|||
* Starts a new encoding pass and resets the reading pointer
|
||||
* from the stats collected in pass2 and writes that to the given context
|
||||
*/
|
||||
public void init() {
|
||||
public void init()
|
||||
{
|
||||
pass++;
|
||||
pos = 0;
|
||||
}
|
||||
|
|
@ -26,11 +29,14 @@ public class IntegerFifo3Pass {
|
|||
/**
|
||||
* writes to the fifo in pass2
|
||||
*/
|
||||
public void add(int value) {
|
||||
if (pass == 2) {
|
||||
if (size == a.length) {
|
||||
public void add( int value )
|
||||
{
|
||||
if ( pass == 2 )
|
||||
{
|
||||
if ( size == a.length )
|
||||
{
|
||||
int[] aa = new int[2 * size];
|
||||
System.arraycopy(a, 0, aa, 0, size);
|
||||
System.arraycopy( a, 0, aa, 0, size );
|
||||
a = aa;
|
||||
}
|
||||
a[size++] = value;
|
||||
|
|
@ -40,13 +46,16 @@ public class IntegerFifo3Pass {
|
|||
/**
|
||||
* reads from the fifo in pass3 (in pass1/2 returns just 1)
|
||||
*/
|
||||
public int getNext() {
|
||||
return pass == 3 ? get(pos++) : 1;
|
||||
public int getNext()
|
||||
{
|
||||
return pass == 3 ? get( pos++ ) : 1;
|
||||
}
|
||||
|
||||
private int get(int idx) {
|
||||
if (idx >= size) {
|
||||
throw new IndexOutOfBoundsException("list size=" + size + " idx=" + idx);
|
||||
private int get( int idx )
|
||||
{
|
||||
if ( idx >= size )
|
||||
{
|
||||
throw new IndexOutOfBoundsException( "list size=" + size + " idx=" + idx );
|
||||
}
|
||||
return a[idx];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ package btools.codec;
|
|||
/**
|
||||
* Simple container for a list of lists of integers
|
||||
*/
|
||||
public class LinkedListContainer {
|
||||
public class LinkedListContainer
|
||||
{
|
||||
private int[] ia; // prev, data, prev, data, ...
|
||||
private int size;
|
||||
private int[] startpointer; // 0=void, odd=head-data-cell
|
||||
|
|
@ -11,44 +12,49 @@ public class LinkedListContainer {
|
|||
|
||||
/**
|
||||
* Construct a container for the given number of lists
|
||||
* <p>
|
||||
*
|
||||
* If no default-buffer is given, an int[nlists*4] is constructed,
|
||||
* able to hold 2 entries per list on average
|
||||
*
|
||||
* @param nlists the number of lists
|
||||
* @param nlists the number of lists
|
||||
* @param defaultbuffer an optional data array for re-use (gets replaced if too small)
|
||||
*/
|
||||
public LinkedListContainer(int nlists, int[] defaultbuffer) {
|
||||
ia = defaultbuffer == null ? new int[nlists * 4] : defaultbuffer;
|
||||
startpointer = new int[nlists];
|
||||
public LinkedListContainer( int nlists, int[] defaultbuffer )
|
||||
{
|
||||
ia = defaultbuffer == null ? new int[nlists*4] : defaultbuffer;
|
||||
startpointer = new int[nlists];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a data element to the given list
|
||||
*
|
||||
* @param listNr the list to add the data to
|
||||
* @param data the data value
|
||||
* @param data the data value
|
||||
*/
|
||||
public void addDataElement(int listNr, int data) {
|
||||
if (size + 2 > ia.length) {
|
||||
public void addDataElement( int listNr, int data )
|
||||
{
|
||||
if ( size + 2 > ia.length )
|
||||
{
|
||||
resize();
|
||||
}
|
||||
ia[size++] = startpointer[listNr];
|
||||
startpointer[listNr] = size;
|
||||
ia[size++] = startpointer[ listNr ];
|
||||
startpointer[ listNr ] = size;
|
||||
ia[size++] = data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize a list for reading
|
||||
*
|
||||
* @param listNr the list to initialize
|
||||
* @return the number of entries in that list
|
||||
*/
|
||||
public int initList(int listNr) {
|
||||
public int initList( int listNr )
|
||||
{
|
||||
int cnt = 0;
|
||||
int lp = listpointer = startpointer[listNr];
|
||||
while (lp != 0) {
|
||||
lp = ia[lp - 1];
|
||||
int lp = listpointer = startpointer[ listNr ];
|
||||
while( lp != 0 )
|
||||
{
|
||||
lp = ia[ lp-1 ];
|
||||
cnt++;
|
||||
}
|
||||
return cnt;
|
||||
|
|
@ -61,18 +67,21 @@ public class LinkedListContainer {
|
|||
* @return the data element
|
||||
* @throws IllegalArgumentException if no more element
|
||||
*/
|
||||
public int getDataElement() {
|
||||
if (listpointer == 0) {
|
||||
throw new IllegalArgumentException("no more element!");
|
||||
public int getDataElement()
|
||||
{
|
||||
if ( listpointer == 0 )
|
||||
{
|
||||
throw new IllegalArgumentException( "no more element!" );
|
||||
}
|
||||
int data = ia[listpointer];
|
||||
listpointer = ia[listpointer - 1];
|
||||
int data = ia[ listpointer ];
|
||||
listpointer = ia[ listpointer-1 ];
|
||||
return data;
|
||||
}
|
||||
|
||||
private void resize() {
|
||||
int[] ia2 = new int[2 * ia.length];
|
||||
System.arraycopy(ia, 0, ia2, 0, ia.length);
|
||||
|
||||
private void resize()
|
||||
{
|
||||
int[] ia2 = new int[2*ia.length];
|
||||
System.arraycopy( ia, 0, ia2, 0, ia.length );
|
||||
ia = ia2;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,20 +5,21 @@ import btools.util.ByteDataWriter;
|
|||
/**
|
||||
* a micro-cache is a data cache for an area of some square kilometers or some
|
||||
* hundreds or thousands nodes
|
||||
* <p>
|
||||
*
|
||||
* This is the basic io-unit: always a full microcache is loaded from the
|
||||
* data-file if a node is requested at a position not yet covered by the caches
|
||||
* already loaded
|
||||
* <p>
|
||||
*
|
||||
* The nodes are represented in a compact way (typical 20-50 bytes per node),
|
||||
* but in a way that they do not depend on each other, and garbage collection is
|
||||
* supported to remove the nodes already consumed from the cache.
|
||||
* <p>
|
||||
*
|
||||
* The cache-internal data representation is different from that in the
|
||||
* data-files, where a cache is encoded as a whole, allowing more
|
||||
* redundancy-removal for a more compact encoding
|
||||
*/
|
||||
public class MicroCache extends ByteDataWriter {
|
||||
public class MicroCache extends ByteDataWriter
|
||||
{
|
||||
protected int[] faid;
|
||||
protected int[] fapos;
|
||||
protected int size = 0;
|
||||
|
|
@ -34,21 +35,25 @@ public class MicroCache extends ByteDataWriter {
|
|||
|
||||
public static boolean debug = false;
|
||||
|
||||
protected MicroCache(byte[] ab) {
|
||||
super(ab);
|
||||
protected MicroCache( byte[] ab )
|
||||
{
|
||||
super( ab );
|
||||
}
|
||||
|
||||
public final static MicroCache emptyNonVirgin = new MicroCache(null);
|
||||
public final static MicroCache emptyNonVirgin = new MicroCache( null );
|
||||
|
||||
static {
|
||||
static
|
||||
{
|
||||
emptyNonVirgin.virgin = false;
|
||||
}
|
||||
|
||||
public static MicroCache emptyCache() {
|
||||
return new MicroCache(null); // TODO: singleton?
|
||||
public static MicroCache emptyCache()
|
||||
{
|
||||
return new MicroCache( null ); // TODO: singleton?
|
||||
}
|
||||
|
||||
protected void init(int size) {
|
||||
protected void init( int size )
|
||||
{
|
||||
this.size = size;
|
||||
delcount = 0;
|
||||
delbytes = 0;
|
||||
|
|
@ -57,31 +62,35 @@ public class MicroCache extends ByteDataWriter {
|
|||
p2size >>= 1;
|
||||
}
|
||||
|
||||
public final void finishNode(long id) {
|
||||
public final void finishNode( long id )
|
||||
{
|
||||
fapos[size] = aboffset;
|
||||
faid[size] = shrinkId(id);
|
||||
faid[size] = shrinkId( id );
|
||||
size++;
|
||||
}
|
||||
|
||||
public final void discardNode() {
|
||||
aboffset = startPos(size);
|
||||
public final void discardNode()
|
||||
{
|
||||
aboffset = startPos( size );
|
||||
}
|
||||
|
||||
public final int getSize() {
|
||||
public final int getSize()
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
public final int getDataSize() {
|
||||
public final int getDataSize()
|
||||
{
|
||||
return ab == null ? 0 : ab.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the internal reader (aboffset, aboffsetEnd) to the body data for the given id
|
||||
* <p>
|
||||
*
|
||||
* If a node is not found in an empty cache, this is usually an edge-effect
|
||||
* (data-file does not exist or neighboured data-files of differnt age),
|
||||
* but is can as well be a symptom of a node-identity breaking bug.
|
||||
* <p>
|
||||
*
|
||||
* Current implementation always returns false for not-found, however, for
|
||||
* regression testing, at least for the case that is most likely a bug
|
||||
* (node found but marked as deleted = ready for garbage collection
|
||||
|
|
@ -89,31 +98,38 @@ public class MicroCache extends ByteDataWriter {
|
|||
*
|
||||
* @return true if id was found
|
||||
*/
|
||||
public final boolean getAndClear(long id64) {
|
||||
if (size == 0) {
|
||||
public final boolean getAndClear( long id64 )
|
||||
{
|
||||
if ( size == 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int id = shrinkId(id64);
|
||||
int id = shrinkId( id64 );
|
||||
int[] a = faid;
|
||||
int offset = p2size;
|
||||
int n = 0;
|
||||
|
||||
while (offset > 0) {
|
||||
while (offset > 0)
|
||||
{
|
||||
int nn = n + offset;
|
||||
if (nn < size && a[nn] <= id) {
|
||||
if ( nn < size && a[nn] <= id )
|
||||
{
|
||||
n = nn;
|
||||
}
|
||||
offset >>= 1;
|
||||
}
|
||||
if (a[n] == id) {
|
||||
if ((fapos[n] & 0x80000000) == 0) {
|
||||
aboffset = startPos(n);
|
||||
if ( a[n] == id )
|
||||
{
|
||||
if ( ( fapos[n] & 0x80000000 ) == 0 )
|
||||
{
|
||||
aboffset = startPos( n );
|
||||
aboffsetEnd = fapos[n];
|
||||
fapos[n] |= 0x80000000; // mark deleted
|
||||
delbytes += aboffsetEnd - aboffset;
|
||||
delcount++;
|
||||
return true;
|
||||
} else // .. marked as deleted
|
||||
}
|
||||
else // .. marked as deleted
|
||||
{
|
||||
// throw new RuntimeException( "MicroCache: node already consumed: id=" + id );
|
||||
}
|
||||
|
|
@ -121,35 +137,43 @@ public class MicroCache extends ByteDataWriter {
|
|||
return false;
|
||||
}
|
||||
|
||||
protected final int startPos(int n) {
|
||||
protected final int startPos( int n )
|
||||
{
|
||||
return n > 0 ? fapos[n - 1] & 0x7fffffff : 0;
|
||||
}
|
||||
|
||||
public final int collect(int threshold) {
|
||||
if (delcount <= threshold) {
|
||||
public final int collect( int threshold )
|
||||
{
|
||||
if ( delcount <= threshold )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virgin = false;
|
||||
|
||||
int nsize = size - delcount;
|
||||
if (nsize == 0) {
|
||||
if ( nsize == 0 )
|
||||
{
|
||||
faid = null;
|
||||
fapos = null;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
int[] nfaid = new int[nsize];
|
||||
int[] nfapos = new int[nsize];
|
||||
int idx = 0;
|
||||
|
||||
byte[] nab = new byte[ab.length - delbytes];
|
||||
int nab_off = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
for ( int i = 0; i < size; i++ )
|
||||
{
|
||||
int pos = fapos[i];
|
||||
if ((pos & 0x80000000) == 0) {
|
||||
int start = startPos(i);
|
||||
if ( ( pos & 0x80000000 ) == 0 )
|
||||
{
|
||||
int start = startPos( i );
|
||||
int end = fapos[i];
|
||||
int len = end - start;
|
||||
System.arraycopy(ab, start, nab, nab_off, len);
|
||||
System.arraycopy( ab, start, nab, nab_off, len );
|
||||
nfaid[idx] = faid[i];
|
||||
nab_off += len;
|
||||
nfapos[idx] = nab_off;
|
||||
|
|
@ -161,15 +185,17 @@ public class MicroCache extends ByteDataWriter {
|
|||
ab = nab;
|
||||
}
|
||||
int deleted = delbytes;
|
||||
init(nsize);
|
||||
init( nsize );
|
||||
return deleted;
|
||||
}
|
||||
|
||||
public final void unGhost() {
|
||||
public final void unGhost()
|
||||
{
|
||||
ghost = false;
|
||||
delcount = 0;
|
||||
delbytes = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
for ( int i = 0; i < size; i++ )
|
||||
{
|
||||
fapos[i] &= 0x7fffffff; // clear deleted flags
|
||||
}
|
||||
}
|
||||
|
|
@ -177,166 +203,201 @@ public class MicroCache extends ByteDataWriter {
|
|||
/**
|
||||
* @return the 64-bit global id for the given cache-position
|
||||
*/
|
||||
public final long getIdForIndex(int i) {
|
||||
public final long getIdForIndex( int i )
|
||||
{
|
||||
int id32 = faid[i];
|
||||
return expandId(id32);
|
||||
return expandId( id32 );
|
||||
}
|
||||
|
||||
/**
|
||||
* expand a 32-bit micro-cache-internal id into a 64-bit (lon|lat) global-id
|
||||
*
|
||||
*
|
||||
* @see #shrinkId
|
||||
*/
|
||||
public long expandId(int id32) {
|
||||
throw new IllegalArgumentException("expandId for empty cache");
|
||||
public long expandId( int id32 )
|
||||
{
|
||||
throw new IllegalArgumentException( "expandId for empty cache" );
|
||||
}
|
||||
|
||||
/**
|
||||
* shrink a 64-bit (lon|lat) global-id into a a 32-bit micro-cache-internal id
|
||||
*
|
||||
*
|
||||
* @see #expandId
|
||||
*/
|
||||
public int shrinkId(long id64) {
|
||||
throw new IllegalArgumentException("shrinkId for empty cache");
|
||||
public int shrinkId( long id64 )
|
||||
{
|
||||
throw new IllegalArgumentException( "shrinkId for empty cache" );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the given lon/lat position is internal for that micro-cache
|
||||
*/
|
||||
public boolean isInternal(int ilon, int ilat) {
|
||||
throw new IllegalArgumentException("isInternal for empty cache");
|
||||
public boolean isInternal( int ilon, int ilat )
|
||||
{
|
||||
throw new IllegalArgumentException( "isInternal for empty cache" );
|
||||
}
|
||||
|
||||
/**
|
||||
* (stasticially) encode the micro-cache into the format used in the datafiles
|
||||
*
|
||||
* @param buffer byte array to encode into (considered big enough)
|
||||
*
|
||||
* @param buffer
|
||||
* byte array to encode into (considered big enough)
|
||||
* @return the size of the encoded data
|
||||
*/
|
||||
public int encodeMicroCache(byte[] buffer) {
|
||||
throw new IllegalArgumentException("encodeMicroCache for empty cache");
|
||||
public int encodeMicroCache( byte[] buffer )
|
||||
{
|
||||
throw new IllegalArgumentException( "encodeMicroCache for empty cache" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the content of this microcache to another
|
||||
*
|
||||
*
|
||||
* @return null if equals, else a diff-report
|
||||
*/
|
||||
public String compareWith(MicroCache mc) {
|
||||
String msg = _compareWith(mc);
|
||||
if (msg != null) {
|
||||
StringBuilder sb = new StringBuilder(msg);
|
||||
sb.append("\nencode cache:\n").append(summary());
|
||||
sb.append("\ndecode cache:\n").append(mc.summary());
|
||||
public String compareWith( MicroCache mc )
|
||||
{
|
||||
String msg = _compareWith( mc );
|
||||
if ( msg != null )
|
||||
{
|
||||
StringBuilder sb = new StringBuilder( msg );
|
||||
sb.append( "\nencode cache:\n" ).append( summary() );
|
||||
sb.append( "\ndecode cache:\n" ).append( mc.summary() );
|
||||
return sb.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String summary() {
|
||||
StringBuilder sb = new StringBuilder("size=" + size + " aboffset=" + aboffset);
|
||||
for (int i = 0; i < size; i++) {
|
||||
sb.append("\nidx=" + i + " faid=" + faid[i] + " fapos=" + fapos[i]);
|
||||
private String summary()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder( "size=" + size + " aboffset=" + aboffset );
|
||||
for ( int i = 0; i < size; i++ )
|
||||
{
|
||||
sb.append( "\nidx=" + i + " faid=" + faid[i] + " fapos=" + fapos[i] );
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String _compareWith(MicroCache mc) {
|
||||
if (size != mc.size) {
|
||||
return "size mismatch: " + size + "->" + mc.size;
|
||||
private String _compareWith( MicroCache mc )
|
||||
{
|
||||
if ( size != mc.size )
|
||||
{
|
||||
return "size missmatch: " + size + "->" + mc.size;
|
||||
}
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (faid[i] != mc.faid[i]) {
|
||||
return "faid mismatch at index " + i + ":" + faid[i] + "->" + mc.faid[i];
|
||||
for ( int i = 0; i < size; i++ )
|
||||
{
|
||||
if ( faid[i] != mc.faid[i] )
|
||||
{
|
||||
return "faid missmatch at index " + i + ":" + faid[i] + "->" + mc.faid[i];
|
||||
}
|
||||
int start = i > 0 ? fapos[i - 1] : 0;
|
||||
int end = fapos[i] < mc.fapos[i] ? fapos[i] : mc.fapos[i];
|
||||
int len = end - start;
|
||||
for (int offset = 0; offset < len; offset++) {
|
||||
if (mc.ab.length <= start + offset) {
|
||||
for ( int offset = 0; offset < len; offset++ )
|
||||
{
|
||||
if ( mc.ab.length <= start + offset )
|
||||
{
|
||||
return "data buffer too small";
|
||||
}
|
||||
if (ab[start + offset] != mc.ab[start + offset]) {
|
||||
return "data mismatch at index " + i + " offset=" + offset;
|
||||
if ( ab[start + offset] != mc.ab[start + offset] )
|
||||
{
|
||||
return "data missmatch at index " + i + " offset=" + offset;
|
||||
}
|
||||
}
|
||||
if (fapos[i] != mc.fapos[i]) {
|
||||
return "fapos mismatch at index " + i + ":" + fapos[i] + "->" + mc.fapos[i];
|
||||
if ( fapos[i] != mc.fapos[i] )
|
||||
{
|
||||
return "fapos missmatch at index " + i + ":" + fapos[i] + "->" + mc.fapos[i];
|
||||
}
|
||||
}
|
||||
if (aboffset != mc.aboffset) {
|
||||
return "datasize mismatch: " + aboffset + "->" + mc.aboffset;
|
||||
if ( aboffset != mc.aboffset )
|
||||
{
|
||||
return "datasize missmatch: " + aboffset + "->" + mc.aboffset;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void calcDelta(MicroCache mc1, MicroCache mc2) {
|
||||
int idx1 = 0;
|
||||
int idx2 = 0;
|
||||
public void calcDelta( MicroCache mc1, MicroCache mc2 )
|
||||
{
|
||||
int idx1 = 0;
|
||||
int idx2 = 0;
|
||||
|
||||
while (idx1 < mc1.size || idx2 < mc2.size) {
|
||||
int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE;
|
||||
int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE;
|
||||
int id;
|
||||
if (id1 >= id2) {
|
||||
id = id2;
|
||||
int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
|
||||
int len2 = mc2.fapos[idx2++] - start2;
|
||||
while( idx1 < mc1.size || idx2 < mc2.size )
|
||||
{
|
||||
int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE;
|
||||
int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE;
|
||||
int id;
|
||||
if ( id1 >= id2 )
|
||||
{
|
||||
id = id2;
|
||||
int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
|
||||
int len2 = mc2.fapos[idx2++] - start2;
|
||||
|
||||
if (id1 == id2) {
|
||||
// id exists in both caches, compare data
|
||||
int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
|
||||
int len1 = mc1.fapos[idx1++] - start1;
|
||||
if (len1 == len2) {
|
||||
int i = 0;
|
||||
while (i < len1) {
|
||||
if (mc1.ab[start1 + i] != mc2.ab[start2 + i]) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (i == len1) {
|
||||
continue; // same data -> do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
write(mc2.ab, start2, len2);
|
||||
} else {
|
||||
idx1++;
|
||||
id = id1; // deleted node
|
||||
}
|
||||
fapos[size] = aboffset;
|
||||
faid[size] = id;
|
||||
size++;
|
||||
}
|
||||
if ( id1 == id2 )
|
||||
{
|
||||
// id exists in both caches, compare data
|
||||
int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
|
||||
int len1 = mc1.fapos[idx1++] - start1;
|
||||
if ( len1 == len2 )
|
||||
{
|
||||
int i = 0;
|
||||
while( i<len1 )
|
||||
{
|
||||
if ( mc1.ab[start1+i] != mc2.ab[start2+i] )
|
||||
{
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if ( i == len1 )
|
||||
{
|
||||
continue; // same data -> do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
write( mc2.ab, start2, len2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
idx1++;
|
||||
id = id1; // deleted node
|
||||
}
|
||||
fapos[size] = aboffset;
|
||||
faid[size] = id;
|
||||
size++;
|
||||
}
|
||||
}
|
||||
|
||||
public void addDelta(MicroCache mc1, MicroCache mc2, boolean keepEmptyNodes) {
|
||||
int idx1 = 0;
|
||||
int idx2 = 0;
|
||||
public void addDelta( MicroCache mc1, MicroCache mc2, boolean keepEmptyNodes )
|
||||
{
|
||||
int idx1 = 0;
|
||||
int idx2 = 0;
|
||||
|
||||
while (idx1 < mc1.size || idx2 < mc2.size) {
|
||||
int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE;
|
||||
int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE;
|
||||
if (id1 >= id2) { // data from diff file wins
|
||||
int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
|
||||
int len2 = mc2.fapos[idx2++] - start2;
|
||||
if (keepEmptyNodes || len2 > 0) {
|
||||
write(mc2.ab, start2, len2);
|
||||
fapos[size] = aboffset;
|
||||
faid[size++] = id2;
|
||||
}
|
||||
if (id1 == id2) { // // id exists in both caches
|
||||
idx1++;
|
||||
}
|
||||
} else // use data from base file
|
||||
{
|
||||
int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
|
||||
int len1 = mc1.fapos[idx1++] - start1;
|
||||
write(mc1.ab, start1, len1);
|
||||
fapos[size] = aboffset;
|
||||
faid[size++] = id1;
|
||||
}
|
||||
}
|
||||
while( idx1 < mc1.size || idx2 < mc2.size )
|
||||
{
|
||||
int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE;
|
||||
int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE;
|
||||
if ( id1 >= id2 ) // data from diff file wins
|
||||
{
|
||||
int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
|
||||
int len2 = mc2.fapos[idx2++] - start2;
|
||||
if ( keepEmptyNodes || len2 > 0 )
|
||||
{
|
||||
write( mc2.ab, start2, len2 );
|
||||
fapos[size] = aboffset;
|
||||
faid[size++] = id2;
|
||||
}
|
||||
if ( id1 == id2 ) // // id exists in both caches
|
||||
{
|
||||
idx1++;
|
||||
}
|
||||
}
|
||||
else // use data from base file
|
||||
{
|
||||
int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
|
||||
int len1 = mc1.fapos[idx1++] - start1;
|
||||
write( mc1.ab, start1, len1 );
|
||||
fapos[size] = aboffset;
|
||||
faid[size++] = id1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package btools.codec;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import btools.util.ByteDataReader;
|
||||
import btools.util.IByteArrayUnifier;
|
||||
|
|
@ -10,198 +9,222 @@ import btools.util.IByteArrayUnifier;
|
|||
* MicroCache2 is the new format that uses statistical encoding and
|
||||
* is able to do access filtering and waypoint matching during encoding
|
||||
*/
|
||||
public final class MicroCache2 extends MicroCache {
|
||||
public final class MicroCache2 extends MicroCache
|
||||
{
|
||||
private int lonBase;
|
||||
private int latBase;
|
||||
private int cellsize;
|
||||
|
||||
public MicroCache2(int size, byte[] databuffer, int lonIdx, int latIdx, int divisor) {
|
||||
super(databuffer); // sets ab=databuffer, aboffset=0
|
||||
public MicroCache2( int size, byte[] databuffer, int lonIdx, int latIdx, int divisor ) throws Exception
|
||||
{
|
||||
super( databuffer ); // sets ab=databuffer, aboffset=0
|
||||
|
||||
faid = new int[size];
|
||||
fapos = new int[size];
|
||||
this.size = 0;
|
||||
cellsize = 1000000 / divisor;
|
||||
lonBase = lonIdx * cellsize;
|
||||
latBase = latIdx * cellsize;
|
||||
lonBase = lonIdx*cellsize;
|
||||
latBase = latIdx*cellsize;
|
||||
}
|
||||
|
||||
public byte[] readUnified( int len, IByteArrayUnifier u )
|
||||
{
|
||||
byte[] b = u.unify( ab, aboffset, len );
|
||||
aboffset += len;
|
||||
return b;
|
||||
}
|
||||
|
||||
public byte[] readUnified(int len, IByteArrayUnifier u) {
|
||||
byte[] b = u.unify(ab, aboffset, len);
|
||||
aboffset += len;
|
||||
return b;
|
||||
}
|
||||
|
||||
public MicroCache2(StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher) {
|
||||
super(null);
|
||||
public MicroCache2( StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher ) throws Exception
|
||||
{
|
||||
super( null );
|
||||
cellsize = 1000000 / divisor;
|
||||
lonBase = lonIdx * cellsize;
|
||||
latBase = latIdx * cellsize;
|
||||
lonBase = lonIdx*cellsize;
|
||||
latBase = latIdx*cellsize;
|
||||
|
||||
TagValueCoder wayTagCoder = new TagValueCoder(bc, dataBuffers, wayValidator);
|
||||
TagValueCoder nodeTagCoder = new TagValueCoder(bc, dataBuffers, null);
|
||||
NoisyDiffCoder nodeIdxDiff = new NoisyDiffCoder(bc);
|
||||
NoisyDiffCoder nodeEleDiff = new NoisyDiffCoder(bc);
|
||||
TagValueCoder wayTagCoder = new TagValueCoder( bc, dataBuffers, wayValidator );
|
||||
TagValueCoder nodeTagCoder = new TagValueCoder( bc, dataBuffers, null );
|
||||
NoisyDiffCoder nodeIdxDiff = new NoisyDiffCoder( bc );
|
||||
NoisyDiffCoder nodeEleDiff = new NoisyDiffCoder( bc );
|
||||
NoisyDiffCoder extLonDiff = new NoisyDiffCoder(bc);
|
||||
NoisyDiffCoder extLatDiff = new NoisyDiffCoder(bc);
|
||||
NoisyDiffCoder transEleDiff = new NoisyDiffCoder(bc);
|
||||
NoisyDiffCoder transEleDiff = new NoisyDiffCoder( bc );
|
||||
|
||||
size = bc.decodeNoisyNumber(5);
|
||||
size = bc.decodeNoisyNumber( 5 );
|
||||
faid = size > dataBuffers.ibuf2.length ? new int[size] : dataBuffers.ibuf2;
|
||||
fapos = size > dataBuffers.ibuf3.length ? new int[size] : dataBuffers.ibuf3;
|
||||
|
||||
|
||||
|
||||
int[] alon = size > dataBuffers.alon.length ? new int[size] : dataBuffers.alon;
|
||||
int[] alat = size > dataBuffers.alat.length ? new int[size] : dataBuffers.alat;
|
||||
|
||||
if ( debug ) System.out.println( "*** decoding cache of size=" + size + " for lonIdx=" + lonIdx + " latIdx=" + latIdx );
|
||||
|
||||
int[] alon = size > dataBuffers.alon.length ? new int[size] : dataBuffers.alon;
|
||||
int[] alat = size > dataBuffers.alat.length ? new int[size] : dataBuffers.alat;
|
||||
|
||||
if (debug)
|
||||
System.out.println("*** decoding cache of size=" + size + " for lonIdx=" + lonIdx + " latIdx=" + latIdx);
|
||||
|
||||
bc.decodeSortedArray(faid, 0, size, 29, 0);
|
||||
|
||||
for (int n = 0; n < size; n++) {
|
||||
long id64 = expandId(faid[n]);
|
||||
alon[n] = (int) (id64 >> 32);
|
||||
alat[n] = (int) (id64 & 0xffffffff);
|
||||
bc.decodeSortedArray( faid, 0, size, 29, 0 );
|
||||
|
||||
for( int n = 0; n<size; n++ )
|
||||
{
|
||||
long id64 = expandId( faid[n] );
|
||||
alon[n] = (int)(id64 >> 32);
|
||||
alat[n] = (int)(id64 & 0xffffffff);
|
||||
}
|
||||
|
||||
int netdatasize = bc.decodeNoisyNumber(10);
|
||||
|
||||
int netdatasize = bc.decodeNoisyNumber( 10 );
|
||||
ab = netdatasize > dataBuffers.bbuf1.length ? new byte[netdatasize] : dataBuffers.bbuf1;
|
||||
aboffset = 0;
|
||||
|
||||
int[] validBits = new int[(size + 31) >> 5];
|
||||
int[] validBits = new int[(size+31)>>5];
|
||||
|
||||
int finaldatasize = 0;
|
||||
|
||||
LinkedListContainer reverseLinks = new LinkedListContainer(size, dataBuffers.ibuf1);
|
||||
LinkedListContainer reverseLinks = new LinkedListContainer( size, dataBuffers.ibuf1 );
|
||||
|
||||
int selev = 0;
|
||||
for (int n = 0; n < size; n++) { // loop over nodes
|
||||
for( int n=0; n<size; n++ ) // loop over nodes
|
||||
{
|
||||
int ilon = alon[n];
|
||||
int ilat = alat[n];
|
||||
|
||||
|
||||
// future escapes (turn restrictions?)
|
||||
short trExceptions = 0;
|
||||
int featureId = bc.decodeVarBits();
|
||||
if (featureId == 13) {
|
||||
if ( featureId == 13 )
|
||||
{
|
||||
fapos[n] = aboffset;
|
||||
validBits[n >> 5] |= 1 << n; // mark dummy-node valid
|
||||
validBits[ n >> 5 ] |= 1 << n; // mark dummy-node valid
|
||||
continue; // empty node escape (delta files only)
|
||||
}
|
||||
while (featureId != 0) {
|
||||
int bitsize = bc.decodeNoisyNumber(5);
|
||||
while( featureId != 0 )
|
||||
{
|
||||
int bitsize = bc.decodeNoisyNumber( 5 );
|
||||
|
||||
if (featureId == 2) { // exceptions to turn-restriction
|
||||
trExceptions = (short) bc.decodeBounded(1023);
|
||||
} else if (featureId == 1) { // turn-restriction
|
||||
writeBoolean(true);
|
||||
writeShort(trExceptions); // exceptions from previous feature
|
||||
if ( featureId == 2 ) // exceptions to turn-restriction
|
||||
{
|
||||
trExceptions = (short)bc.decodeBounded( 1023 );
|
||||
}
|
||||
else if ( featureId == 1 ) // turn-restriction
|
||||
{
|
||||
writeBoolean( true );
|
||||
writeShort( trExceptions ); // exceptions from previous feature
|
||||
trExceptions = 0;
|
||||
|
||||
writeBoolean(bc.decodeBit()); // isPositive
|
||||
writeInt(ilon + bc.decodeNoisyDiff(10)); // fromLon
|
||||
writeInt(ilat + bc.decodeNoisyDiff(10)); // fromLat
|
||||
writeInt(ilon + bc.decodeNoisyDiff(10)); // toLon
|
||||
writeInt(ilat + bc.decodeNoisyDiff(10)); // toLat
|
||||
} else {
|
||||
for (int i = 0; i < bitsize; i++) bc.decodeBit(); // unknown feature, just skip
|
||||
writeBoolean( bc.decodeBit() ); // isPositive
|
||||
writeInt( ilon + bc.decodeNoisyDiff( 10 ) ); // fromLon
|
||||
writeInt( ilat + bc.decodeNoisyDiff( 10 ) ); // fromLat
|
||||
writeInt( ilon + bc.decodeNoisyDiff( 10 ) ); // toLon
|
||||
writeInt( ilat + bc.decodeNoisyDiff( 10 ) ); // toLat
|
||||
}
|
||||
else
|
||||
{
|
||||
for( int i=0; i< bitsize; i++ ) bc.decodeBit(); // unknown feature, just skip
|
||||
}
|
||||
featureId = bc.decodeVarBits();
|
||||
}
|
||||
writeBoolean(false);
|
||||
writeBoolean( false );
|
||||
|
||||
selev += nodeEleDiff.decodeSignedValue();
|
||||
writeShort((short) selev);
|
||||
writeShort( (short) selev );
|
||||
TagValueWrapper nodeTags = nodeTagCoder.decodeTagValueSet();
|
||||
writeVarBytes(nodeTags == null ? null : nodeTags.data);
|
||||
writeVarBytes( nodeTags == null ? null : nodeTags.data );
|
||||
|
||||
int links = bc.decodeNoisyNumber(1);
|
||||
if (debug)
|
||||
System.out.println("*** decoding node " + ilon + "/" + ilat + " with links=" + links);
|
||||
for (int li = 0; li < links; li++) {
|
||||
int links = bc.decodeNoisyNumber( 1 );
|
||||
if ( debug ) System.out.println( "*** decoding node " + ilon + "/" + ilat + " with links=" + links );
|
||||
for( int li=0; li<links; li++ )
|
||||
{
|
||||
int sizeoffset = 0;
|
||||
int nodeIdx = n + nodeIdxDiff.decodeSignedValue();
|
||||
|
||||
|
||||
int dlon_remaining;
|
||||
int dlat_remaining;
|
||||
|
||||
boolean isReverse = false;
|
||||
if (nodeIdx != n) { // internal (forward-) link
|
||||
if ( nodeIdx != n ) // internal (forward-) link
|
||||
{
|
||||
dlon_remaining = alon[nodeIdx] - ilon;
|
||||
dlat_remaining = alat[nodeIdx] - ilat;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
isReverse = bc.decodeBit();
|
||||
dlon_remaining = extLonDiff.decodeSignedValue();
|
||||
dlat_remaining = extLatDiff.decodeSignedValue();
|
||||
}
|
||||
if (debug)
|
||||
System.out.println("*** decoding link to " + (ilon + dlon_remaining) + "/" + (ilat + dlat_remaining) + " extern=" + (nodeIdx == n));
|
||||
if ( debug ) System.out.println( "*** decoding link to " + (ilon+dlon_remaining) + "/" + (ilat+dlat_remaining) + " extern=" + (nodeIdx == n) );
|
||||
|
||||
TagValueWrapper wayTags = wayTagCoder.decodeTagValueSet();
|
||||
|
||||
boolean linkValid = wayTags != null || wayValidator == null;
|
||||
if (linkValid) {
|
||||
if ( linkValid )
|
||||
{
|
||||
int startPointer = aboffset;
|
||||
sizeoffset = writeSizePlaceHolder();
|
||||
|
||||
writeVarLengthSigned(dlon_remaining);
|
||||
writeVarLengthSigned(dlat_remaining);
|
||||
writeVarLengthSigned( dlon_remaining );
|
||||
writeVarLengthSigned( dlat_remaining );
|
||||
|
||||
validBits[n >> 5] |= 1 << n; // mark source-node valid
|
||||
if (nodeIdx != n) { // valid internal (forward-) link
|
||||
reverseLinks.addDataElement(nodeIdx, n); // register reverse link
|
||||
finaldatasize += 1 + aboffset - startPointer; // reserve place for reverse
|
||||
validBits[nodeIdx >> 5] |= 1 << nodeIdx; // mark target-node valid
|
||||
validBits[ n >> 5 ] |= 1 << n; // mark source-node valid
|
||||
if ( nodeIdx != n ) // valid internal (forward-) link
|
||||
{
|
||||
reverseLinks.addDataElement( nodeIdx, n ); // register reverse link
|
||||
finaldatasize += 1 + aboffset-startPointer; // reserve place for reverse
|
||||
validBits[ nodeIdx >> 5 ] |= 1 << nodeIdx; // mark target-node valid
|
||||
}
|
||||
writeModeAndDesc(isReverse, wayTags == null ? null : wayTags.data);
|
||||
writeModeAndDesc( isReverse, wayTags == null ? null : wayTags.data );
|
||||
}
|
||||
|
||||
if (!isReverse) { // write geometry for forward links only
|
||||
if ( !isReverse ) // write geometry for forward links only
|
||||
{
|
||||
WaypointMatcher matcher = wayTags == null || wayTags.accessType < 2 ? null : waypointMatcher;
|
||||
int ilontarget = ilon + dlon_remaining;
|
||||
int ilattarget = ilat + dlat_remaining;
|
||||
if (matcher != null) {
|
||||
if (!matcher.start(ilon, ilat, ilontarget, ilattarget)) {
|
||||
if ( matcher != null )
|
||||
{
|
||||
if ( !matcher.start( ilon, ilat, ilontarget, ilattarget ) )
|
||||
{
|
||||
matcher = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int transcount = bc.decodeVarBits();
|
||||
if (debug) System.out.println("*** decoding geometry with count=" + transcount);
|
||||
int count = transcount + 1;
|
||||
for (int i = 0; i < transcount; i++) {
|
||||
int dlon = bc.decodePredictedValue(dlon_remaining / count);
|
||||
int dlat = bc.decodePredictedValue(dlat_remaining / count);
|
||||
if ( debug ) System.out.println( "*** decoding geometry with count=" + transcount );
|
||||
int count = transcount+1;
|
||||
for( int i=0; i<transcount; i++ )
|
||||
{
|
||||
int dlon = bc.decodePredictedValue( dlon_remaining/count );
|
||||
int dlat = bc.decodePredictedValue( dlat_remaining/count );
|
||||
dlon_remaining -= dlon;
|
||||
dlat_remaining -= dlat;
|
||||
count--;
|
||||
int elediff = transEleDiff.decodeSignedValue();
|
||||
if (wayTags != null) {
|
||||
writeVarLengthSigned(dlon);
|
||||
writeVarLengthSigned(dlat);
|
||||
writeVarLengthSigned(elediff);
|
||||
if ( wayTags != null )
|
||||
{
|
||||
writeVarLengthSigned( dlon );
|
||||
writeVarLengthSigned( dlat );
|
||||
writeVarLengthSigned( elediff );
|
||||
}
|
||||
|
||||
if (matcher != null)
|
||||
matcher.transferNode(ilontarget - dlon_remaining, ilattarget - dlat_remaining);
|
||||
|
||||
if ( matcher != null ) matcher.transferNode( ilontarget - dlon_remaining, ilattarget - dlat_remaining );
|
||||
}
|
||||
if (matcher != null) matcher.end();
|
||||
if ( matcher != null ) matcher.end();
|
||||
}
|
||||
if (linkValid) {
|
||||
injectSize(sizeoffset);
|
||||
if ( linkValid )
|
||||
{
|
||||
injectSize( sizeoffset );
|
||||
}
|
||||
}
|
||||
fapos[n] = aboffset;
|
||||
}
|
||||
|
||||
|
||||
// calculate final data size
|
||||
int finalsize = 0;
|
||||
int startpos = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
for( int i=0; i<size; i++ )
|
||||
{
|
||||
int endpos = fapos[i];
|
||||
if ((validBits[i >> 5] & (1 << i)) != 0) {
|
||||
finaldatasize += endpos - startpos;
|
||||
finalsize++;
|
||||
if ( ( validBits[ i >> 5 ] & (1 << i ) ) != 0 )
|
||||
{
|
||||
finaldatasize += endpos-startpos;
|
||||
finalsize++;
|
||||
}
|
||||
startpos = endpos;
|
||||
}
|
||||
|
|
@ -217,26 +240,29 @@ public final class MicroCache2 extends MicroCache {
|
|||
size = 0;
|
||||
|
||||
startpos = 0;
|
||||
for (int n = 0; n < sizeOld; n++) {
|
||||
for ( int n = 0; n < sizeOld; n++ )
|
||||
{
|
||||
int endpos = faposOld[n];
|
||||
if ((validBits[n >> 5] & (1 << n)) != 0) {
|
||||
if ( ( validBits[ n >> 5 ] & (1 << n ) ) != 0 )
|
||||
{
|
||||
int len = endpos - startpos;
|
||||
System.arraycopy(abOld, startpos, ab, aboffset, len);
|
||||
if (debug)
|
||||
System.out.println("*** copied " + len + " bytes from " + aboffset + " for node " + n);
|
||||
System.arraycopy( abOld, startpos, ab, aboffset, len );
|
||||
if ( debug )
|
||||
System.out.println( "*** copied " + len + " bytes from " + aboffset + " for node " + n );
|
||||
aboffset += len;
|
||||
|
||||
int cnt = reverseLinks.initList(n);
|
||||
if (debug)
|
||||
System.out.println("*** appending " + cnt + " reverse links for node " + n);
|
||||
int cnt = reverseLinks.initList( n );
|
||||
if ( debug )
|
||||
System.out.println( "*** appending " + cnt + " reverse links for node " + n );
|
||||
|
||||
for (int ri = 0; ri < cnt; ri++) {
|
||||
for ( int ri = 0; ri < cnt; ri++ )
|
||||
{
|
||||
int nodeIdx = reverseLinks.getDataElement();
|
||||
int sizeoffset = writeSizePlaceHolder();
|
||||
writeVarLengthSigned(alon[nodeIdx] - alon[n]);
|
||||
writeVarLengthSigned(alat[nodeIdx] - alat[n]);
|
||||
writeModeAndDesc(true, null);
|
||||
injectSize(sizeoffset);
|
||||
writeVarLengthSigned( alon[nodeIdx] - alon[n] );
|
||||
writeVarLengthSigned( alat[nodeIdx] - alat[n] );
|
||||
writeModeAndDesc( true, null );
|
||||
injectSize( sizeoffset );
|
||||
}
|
||||
faid[size] = faidOld[n];
|
||||
fapos[size] = aboffset;
|
||||
|
|
@ -244,58 +270,65 @@ public final class MicroCache2 extends MicroCache {
|
|||
}
|
||||
startpos = endpos;
|
||||
}
|
||||
init(size);
|
||||
init( size );
|
||||
}
|
||||
|
||||
@Override
|
||||
public long expandId(int id32) {
|
||||
public long expandId( int id32 )
|
||||
{
|
||||
int dlon = 0;
|
||||
int dlat = 0;
|
||||
|
||||
for (int bm = 1; bm < 0x8000; bm <<= 1) {
|
||||
if ((id32 & 1) != 0) dlon |= bm;
|
||||
if ((id32 & 2) != 0) dlat |= bm;
|
||||
for( int bm = 1; bm < 0x8000; bm <<= 1 )
|
||||
{
|
||||
if ( (id32 & 1) != 0 ) dlon |= bm;
|
||||
if ( (id32 & 2) != 0 ) dlat |= bm;
|
||||
id32 >>= 2;
|
||||
}
|
||||
|
||||
int lon32 = lonBase + dlon;
|
||||
int lat32 = latBase + dlat;
|
||||
|
||||
return ((long) lon32) << 32 | lat32;
|
||||
return ((long)lon32)<<32 | lat32;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int shrinkId(long id64) {
|
||||
int lon32 = (int) (id64 >> 32);
|
||||
int lat32 = (int) (id64 & 0xffffffff);
|
||||
public int shrinkId( long id64 )
|
||||
{
|
||||
int lon32 = (int)(id64 >> 32);
|
||||
int lat32 = (int)(id64 & 0xffffffff);
|
||||
int dlon = lon32 - lonBase;
|
||||
int dlat = lat32 - latBase;
|
||||
int id32 = 0;
|
||||
|
||||
for (int bm = 0x4000; bm > 0; bm >>= 1) {
|
||||
for( int bm = 0x4000; bm > 0; bm >>= 1 )
|
||||
{
|
||||
id32 <<= 2;
|
||||
if ((dlon & bm) != 0) id32 |= 1;
|
||||
if ((dlat & bm) != 0) id32 |= 2;
|
||||
if ( ( dlon & bm ) != 0 ) id32 |= 1;
|
||||
if ( ( dlat & bm ) != 0 ) id32 |= 2;
|
||||
}
|
||||
return id32;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInternal(int ilon, int ilat) {
|
||||
public boolean isInternal( int ilon, int ilat )
|
||||
{
|
||||
return ilon >= lonBase && ilon < lonBase + cellsize
|
||||
&& ilat >= latBase && ilat < latBase + cellsize;
|
||||
&& ilat >= latBase && ilat < latBase + cellsize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int encodeMicroCache(byte[] buffer) {
|
||||
Map<Long, Integer> idMap = new HashMap<>();
|
||||
for (int n = 0; n < size; n++) { // loop over nodes
|
||||
idMap.put(expandId(faid[n]), n);
|
||||
public int encodeMicroCache( byte[] buffer )
|
||||
{
|
||||
HashMap<Long,Integer> idMap = new HashMap<Long,Integer>();
|
||||
for( int n=0; n<size; n++ ) // loop over nodes
|
||||
{
|
||||
idMap.put( Long.valueOf( expandId( faid[n] ) ), Integer.valueOf( n ) );
|
||||
}
|
||||
|
||||
IntegerFifo3Pass linkCounts = new IntegerFifo3Pass(256);
|
||||
IntegerFifo3Pass transCounts = new IntegerFifo3Pass(256);
|
||||
IntegerFifo3Pass restrictionBits = new IntegerFifo3Pass(16);
|
||||
IntegerFifo3Pass linkCounts = new IntegerFifo3Pass( 256 );
|
||||
IntegerFifo3Pass transCounts = new IntegerFifo3Pass( 256 );
|
||||
IntegerFifo3Pass restrictionBits = new IntegerFifo3Pass( 16 );
|
||||
|
||||
TagValueCoder wayTagCoder = new TagValueCoder();
|
||||
TagValueCoder nodeTagCoder = new TagValueCoder();
|
||||
|
|
@ -304,170 +337,182 @@ public final class MicroCache2 extends MicroCache {
|
|||
NoisyDiffCoder extLonDiff = new NoisyDiffCoder();
|
||||
NoisyDiffCoder extLatDiff = new NoisyDiffCoder();
|
||||
NoisyDiffCoder transEleDiff = new NoisyDiffCoder();
|
||||
|
||||
|
||||
int netdatasize = 0;
|
||||
|
||||
for (int pass = 1; ; pass++) { // 3 passes: counters, stat-collection, encoding
|
||||
for(int pass=1;; pass++) // 3 passes: counters, stat-collection, encoding
|
||||
{
|
||||
boolean dostats = pass == 3;
|
||||
boolean dodebug = debug && pass == 3;
|
||||
|
||||
if (pass < 3) netdatasize = fapos[size - 1];
|
||||
|
||||
StatCoderContext bc = new StatCoderContext(buffer);
|
||||
|
||||
if ( pass < 3 ) netdatasize = fapos[size-1];
|
||||
|
||||
StatCoderContext bc = new StatCoderContext( buffer );
|
||||
|
||||
linkCounts.init();
|
||||
transCounts.init();
|
||||
restrictionBits.init();
|
||||
|
||||
wayTagCoder.encodeDictionary(bc);
|
||||
if (dostats) bc.assignBits("wayTagDictionary");
|
||||
nodeTagCoder.encodeDictionary(bc);
|
||||
if (dostats) bc.assignBits("nodeTagDictionary");
|
||||
nodeIdxDiff.encodeDictionary(bc);
|
||||
nodeEleDiff.encodeDictionary(bc);
|
||||
extLonDiff.encodeDictionary(bc);
|
||||
extLatDiff.encodeDictionary(bc);
|
||||
transEleDiff.encodeDictionary(bc);
|
||||
if (dostats) bc.assignBits("noisebits");
|
||||
bc.encodeNoisyNumber(size, 5);
|
||||
if (dostats) bc.assignBits("nodecount");
|
||||
bc.encodeSortedArray(faid, 0, size, 0x20000000, 0);
|
||||
if (dostats) bc.assignBits("node-positions");
|
||||
bc.encodeNoisyNumber(netdatasize, 10); // net-size
|
||||
if (dostats) bc.assignBits("netdatasize");
|
||||
if (dodebug) System.out.println("*** encoding cache of size=" + size);
|
||||
wayTagCoder.encodeDictionary( bc );
|
||||
if ( dostats ) bc.assignBits( "wayTagDictionary" );
|
||||
nodeTagCoder.encodeDictionary( bc );
|
||||
if ( dostats ) bc.assignBits( "nodeTagDictionary" );
|
||||
nodeIdxDiff.encodeDictionary( bc );
|
||||
nodeEleDiff.encodeDictionary( bc );
|
||||
extLonDiff.encodeDictionary( bc );
|
||||
extLatDiff.encodeDictionary( bc );
|
||||
transEleDiff.encodeDictionary( bc );
|
||||
if ( dostats ) bc.assignBits( "noisebits" );
|
||||
bc.encodeNoisyNumber( size, 5 );
|
||||
if ( dostats ) bc.assignBits( "nodecount" );
|
||||
bc.encodeSortedArray( faid, 0, size, 0x20000000, 0 );
|
||||
if ( dostats ) bc.assignBits( "node-positions" );
|
||||
bc.encodeNoisyNumber( netdatasize, 10 ); // net-size
|
||||
if ( dostats ) bc.assignBits( "netdatasize" );
|
||||
if ( dodebug ) System.out.println( "*** encoding cache of size=" + size );
|
||||
int lastSelev = 0;
|
||||
|
||||
for (int n = 0; n < size; n++) { // loop over nodes
|
||||
aboffset = startPos(n);
|
||||
|
||||
for( int n=0; n<size; n++ ) // loop over nodes
|
||||
{
|
||||
aboffset = startPos( n );
|
||||
aboffsetEnd = fapos[n];
|
||||
if (dodebug)
|
||||
System.out.println("*** encoding node " + n + " from " + aboffset + " to " + aboffsetEnd);
|
||||
if ( dodebug ) System.out.println( "*** encoding node " + n + " from " + aboffset + " to " + aboffsetEnd );
|
||||
|
||||
long id64 = expandId(faid[n]);
|
||||
int ilon = (int) (id64 >> 32);
|
||||
int ilat = (int) (id64 & 0xffffffff);
|
||||
long id64 = expandId( faid[n] );
|
||||
int ilon = (int)(id64 >> 32);
|
||||
int ilat = (int)(id64 & 0xffffffff);
|
||||
|
||||
if (aboffset == aboffsetEnd) {
|
||||
bc.encodeVarBits(13); // empty node escape (delta files only)
|
||||
if ( aboffset == aboffsetEnd )
|
||||
{
|
||||
bc.encodeVarBits( 13 ); // empty node escape (delta files only)
|
||||
continue;
|
||||
}
|
||||
|
||||
// write turn restrictions
|
||||
while (readBoolean()) {
|
||||
while( readBoolean() )
|
||||
{
|
||||
short exceptions = readShort(); // except bikes, psv, ...
|
||||
if (exceptions != 0) {
|
||||
bc.encodeVarBits(2); // 2 = tr exceptions
|
||||
bc.encodeNoisyNumber(10, 5); // bit-count
|
||||
bc.encodeBounded(1023, exceptions & 1023);
|
||||
if ( exceptions != 0 )
|
||||
{
|
||||
bc.encodeVarBits( 2 ); // 2 = tr exceptions
|
||||
bc.encodeNoisyNumber( 10 , 5 ); // bit-count
|
||||
bc.encodeBounded( 1023 , exceptions & 1023 );
|
||||
}
|
||||
bc.encodeVarBits(1); // 1 = turn restriction
|
||||
bc.encodeNoisyNumber(restrictionBits.getNext(), 5); // bit-count using look-ahead fifo
|
||||
bc.encodeVarBits( 1 ); // 1 = turn restriction
|
||||
bc.encodeNoisyNumber( restrictionBits.getNext(), 5 ); // bit-count using look-ahead fifo
|
||||
long b0 = bc.getWritingBitPosition();
|
||||
bc.encodeBit(readBoolean()); // isPositive
|
||||
bc.encodeNoisyDiff(readInt() - ilon, 10); // fromLon
|
||||
bc.encodeNoisyDiff(readInt() - ilat, 10); // fromLat
|
||||
bc.encodeNoisyDiff(readInt() - ilon, 10); // toLon
|
||||
bc.encodeNoisyDiff(readInt() - ilat, 10); // toLat
|
||||
restrictionBits.add((int) (bc.getWritingBitPosition() - b0));
|
||||
bc.encodeBit( readBoolean() ); // isPositive
|
||||
bc.encodeNoisyDiff( readInt() - ilon, 10 ); // fromLon
|
||||
bc.encodeNoisyDiff( readInt() - ilat, 10 ); // fromLat
|
||||
bc.encodeNoisyDiff( readInt() - ilon, 10 ); // toLon
|
||||
bc.encodeNoisyDiff( readInt() - ilat, 10 ); // toLat
|
||||
restrictionBits.add( (int)( bc.getWritingBitPosition() - b0 ) );
|
||||
}
|
||||
bc.encodeVarBits(0); // end of extra data
|
||||
bc.encodeVarBits( 0 ); // end of extra data
|
||||
|
||||
if (dostats) bc.assignBits("extradata");
|
||||
if ( dostats ) bc.assignBits( "extradata" );
|
||||
|
||||
int selev = readShort();
|
||||
nodeEleDiff.encodeSignedValue(selev - lastSelev);
|
||||
if (dostats) bc.assignBits("nodeele");
|
||||
nodeEleDiff.encodeSignedValue( selev - lastSelev );
|
||||
if ( dostats ) bc.assignBits( "nodeele" );
|
||||
lastSelev = selev;
|
||||
nodeTagCoder.encodeTagValueSet(readVarBytes());
|
||||
if (dostats) bc.assignBits("nodeTagIdx");
|
||||
nodeTagCoder.encodeTagValueSet( readVarBytes() );
|
||||
if ( dostats ) bc.assignBits( "nodeTagIdx" );
|
||||
int nlinks = linkCounts.getNext();
|
||||
if (dodebug) System.out.println("*** nlinks=" + nlinks);
|
||||
bc.encodeNoisyNumber(nlinks, 1);
|
||||
if (dostats) bc.assignBits("link-counts");
|
||||
|
||||
if ( dodebug ) System.out.println( "*** nlinks=" + nlinks );
|
||||
bc.encodeNoisyNumber( nlinks, 1 );
|
||||
if ( dostats ) bc.assignBits( "link-counts" );
|
||||
|
||||
nlinks = 0;
|
||||
while (hasMoreData()) { // loop over links
|
||||
while( hasMoreData() ) // loop over links
|
||||
{
|
||||
// read link data
|
||||
int startPointer = aboffset;
|
||||
int endPointer = getEndPointer();
|
||||
|
||||
|
||||
int ilonlink = ilon + readVarLengthSigned();
|
||||
int ilatlink = ilat + readVarLengthSigned();
|
||||
|
||||
|
||||
int sizecode = readVarLengthUnsigned();
|
||||
boolean isReverse = (sizecode & 1) != 0;
|
||||
boolean isReverse = ( sizecode & 1 ) != 0;
|
||||
int descSize = sizecode >> 1;
|
||||
byte[] description = null;
|
||||
if (descSize > 0) {
|
||||
if ( descSize > 0 )
|
||||
{
|
||||
description = new byte[descSize];
|
||||
readFully(description);
|
||||
readFully( description );
|
||||
}
|
||||
|
||||
long link64 = ((long) ilonlink) << 32 | ilatlink;
|
||||
Integer idx = idMap.get(link64);
|
||||
|
||||
long link64 = ((long)ilonlink)<<32 | ilatlink;
|
||||
Integer idx = idMap.get( Long.valueOf( link64 ) );
|
||||
boolean isInternal = idx != null;
|
||||
|
||||
if (isReverse && isInternal) {
|
||||
if (dodebug)
|
||||
System.out.println("*** NOT encoding link reverse=" + isReverse + " internal=" + isInternal);
|
||||
netdatasize -= aboffset - startPointer;
|
||||
if ( isReverse && isInternal )
|
||||
{
|
||||
if ( dodebug ) System.out.println( "*** NOT encoding link reverse=" + isReverse + " internal=" + isInternal );
|
||||
netdatasize -= aboffset-startPointer;
|
||||
continue; // do not encode internal reverse links
|
||||
}
|
||||
if (dodebug)
|
||||
System.out.println("*** encoding link reverse=" + isReverse + " internal=" + isInternal);
|
||||
if ( dodebug ) System.out.println( "*** encoding link reverse=" + isReverse + " internal=" + isInternal );
|
||||
nlinks++;
|
||||
|
||||
if (isInternal) {
|
||||
int nodeIdx = idx;
|
||||
if (dodebug) System.out.println("*** target nodeIdx=" + nodeIdx);
|
||||
if (nodeIdx == n) throw new RuntimeException("ups: self ref?");
|
||||
nodeIdxDiff.encodeSignedValue(nodeIdx - n);
|
||||
if (dostats) bc.assignBits("nodeIdx");
|
||||
} else {
|
||||
nodeIdxDiff.encodeSignedValue(0);
|
||||
bc.encodeBit(isReverse);
|
||||
extLonDiff.encodeSignedValue(ilonlink - ilon);
|
||||
extLatDiff.encodeSignedValue(ilatlink - ilat);
|
||||
if (dostats) bc.assignBits("externalNode");
|
||||
|
||||
if ( isInternal )
|
||||
{
|
||||
int nodeIdx = idx.intValue();
|
||||
if ( dodebug ) System.out.println( "*** target nodeIdx=" + nodeIdx );
|
||||
if ( nodeIdx == n ) throw new RuntimeException( "ups: self ref?" );
|
||||
nodeIdxDiff.encodeSignedValue( nodeIdx - n );
|
||||
if ( dostats ) bc.assignBits( "nodeIdx" );
|
||||
}
|
||||
wayTagCoder.encodeTagValueSet(description);
|
||||
if (dostats) bc.assignBits("wayDescIdx");
|
||||
|
||||
if (!isReverse) {
|
||||
byte[] geometry = readDataUntil(endPointer);
|
||||
else
|
||||
{
|
||||
nodeIdxDiff.encodeSignedValue( 0 );
|
||||
bc.encodeBit( isReverse );
|
||||
extLonDiff.encodeSignedValue( ilonlink - ilon );
|
||||
extLatDiff.encodeSignedValue( ilatlink - ilat );
|
||||
if ( dostats ) bc.assignBits( "externalNode" );
|
||||
}
|
||||
wayTagCoder.encodeTagValueSet( description );
|
||||
if ( dostats ) bc.assignBits( "wayDescIdx" );
|
||||
|
||||
if ( !isReverse )
|
||||
{
|
||||
byte[] geometry = readDataUntil( endPointer );
|
||||
// write transition nodes
|
||||
int count = transCounts.getNext();
|
||||
if (dodebug) System.out.println("*** encoding geometry with count=" + count);
|
||||
bc.encodeVarBits(count++);
|
||||
if (dostats) bc.assignBits("transcount");
|
||||
if ( dodebug ) System.out.println( "*** encoding geometry with count=" + count );
|
||||
bc.encodeVarBits( count++ );
|
||||
if ( dostats ) bc.assignBits( "transcount" );
|
||||
int transcount = 0;
|
||||
if (geometry != null) {
|
||||
if ( geometry != null )
|
||||
{
|
||||
int dlon_remaining = ilonlink - ilon;
|
||||
int dlat_remaining = ilatlink - ilat;
|
||||
|
||||
ByteDataReader r = new ByteDataReader(geometry);
|
||||
while (r.hasMoreData()) {
|
||||
|
||||
ByteDataReader r = new ByteDataReader( geometry );
|
||||
while ( r.hasMoreData() )
|
||||
{
|
||||
transcount++;
|
||||
|
||||
|
||||
int dlon = r.readVarLengthSigned();
|
||||
int dlat = r.readVarLengthSigned();
|
||||
bc.encodePredictedValue(dlon, dlon_remaining / count);
|
||||
bc.encodePredictedValue(dlat, dlat_remaining / count);
|
||||
bc.encodePredictedValue( dlon, dlon_remaining/count );
|
||||
bc.encodePredictedValue( dlat, dlat_remaining/count );
|
||||
dlon_remaining -= dlon;
|
||||
dlat_remaining -= dlat;
|
||||
if (count > 1) count--;
|
||||
if (dostats) bc.assignBits("transpos");
|
||||
transEleDiff.encodeSignedValue(r.readVarLengthSigned());
|
||||
if (dostats) bc.assignBits("transele");
|
||||
if ( count > 1 ) count--;
|
||||
if ( dostats ) bc.assignBits( "transpos" );
|
||||
transEleDiff.encodeSignedValue( r.readVarLengthSigned() );
|
||||
if ( dostats ) bc.assignBits( "transele" );
|
||||
}
|
||||
}
|
||||
transCounts.add(transcount);
|
||||
transCounts.add( transcount );
|
||||
}
|
||||
}
|
||||
linkCounts.add(nlinks);
|
||||
linkCounts.add( nlinks );
|
||||
}
|
||||
if (pass == 3) {
|
||||
if ( pass == 3 )
|
||||
{
|
||||
return bc.closeAndGetEncodedLength();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,12 @@ package btools.codec;
|
|||
* Encoder/Decoder for signed integers that automatically detects the typical
|
||||
* range of these numbers to determine a noisy-bit count as a very simple
|
||||
* dictionary
|
||||
* <p>
|
||||
*
|
||||
* Adapted for 3-pass encoding (counters -> statistics -> encoding )
|
||||
* but doesn't do anything at pass1
|
||||
*/
|
||||
public final class NoisyDiffCoder {
|
||||
public final class NoisyDiffCoder
|
||||
{
|
||||
private int tot;
|
||||
private int[] freqs;
|
||||
private int noisybits;
|
||||
|
|
@ -18,7 +19,8 @@ public final class NoisyDiffCoder {
|
|||
/**
|
||||
* Create a decoder and read the noisy-bit count from the gibe context
|
||||
*/
|
||||
public NoisyDiffCoder(StatCoderContext bc) {
|
||||
public NoisyDiffCoder( StatCoderContext bc )
|
||||
{
|
||||
noisybits = bc.decodeVarBits();
|
||||
this.bc = bc;
|
||||
}
|
||||
|
|
@ -26,49 +28,60 @@ public final class NoisyDiffCoder {
|
|||
/**
|
||||
* Create an encoder for 3-pass-encoding
|
||||
*/
|
||||
public NoisyDiffCoder() {
|
||||
public NoisyDiffCoder()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* encodes a signed int (pass3 only, stats collection in pass2)
|
||||
*/
|
||||
public void encodeSignedValue(int value) {
|
||||
if (pass == 3) {
|
||||
bc.encodeNoisyDiff(value, noisybits);
|
||||
} else if (pass == 2) {
|
||||
count(value < 0 ? -value : value);
|
||||
public void encodeSignedValue( int value )
|
||||
{
|
||||
if ( pass == 3 )
|
||||
{
|
||||
bc.encodeNoisyDiff( value, noisybits );
|
||||
}
|
||||
else if ( pass == 2 )
|
||||
{
|
||||
count( value < 0 ? -value : value );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* decodes a signed int
|
||||
*/
|
||||
public int decodeSignedValue() {
|
||||
return bc.decodeNoisyDiff(noisybits);
|
||||
public int decodeSignedValue()
|
||||
{
|
||||
return bc.decodeNoisyDiff( noisybits );
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new encoding pass and (in pass3) calculates the noisy-bit count
|
||||
* from the stats collected in pass2 and writes that to the given context
|
||||
*/
|
||||
public void encodeDictionary(StatCoderContext bc) {
|
||||
if (++pass == 3) {
|
||||
public void encodeDictionary( StatCoderContext bc )
|
||||
{
|
||||
if ( ++pass == 3 )
|
||||
{
|
||||
// how many noisy bits?
|
||||
for (noisybits = 0; noisybits < 14 && tot > 0; noisybits++) {
|
||||
if (freqs[noisybits] < (tot >> 1))
|
||||
for ( noisybits = 0; noisybits < 14 && tot > 0; noisybits++ )
|
||||
{
|
||||
if ( freqs[noisybits] < ( tot >> 1 ) )
|
||||
break;
|
||||
}
|
||||
bc.encodeVarBits(noisybits);
|
||||
bc.encodeVarBits( noisybits );
|
||||
}
|
||||
this.bc = bc;
|
||||
}
|
||||
|
||||
private void count(int value) {
|
||||
if (freqs == null)
|
||||
private void count( int value )
|
||||
{
|
||||
if ( freqs == null )
|
||||
freqs = new int[14];
|
||||
int bm = 1;
|
||||
for (int i = 0; i < 14; i++) {
|
||||
if (value < bm)
|
||||
for ( int i = 0; i < 14; i++ )
|
||||
{
|
||||
if ( value < bm )
|
||||
break;
|
||||
else
|
||||
freqs[i]++;
|
||||
|
|
|
|||
|
|
@ -1,23 +1,26 @@
|
|||
package btools.codec;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import btools.util.BitCoderContext;
|
||||
|
||||
public final class StatCoderContext extends BitCoderContext {
|
||||
private static Map<String, long[]> statsPerName;
|
||||
public final class StatCoderContext extends BitCoderContext
|
||||
{
|
||||
private static TreeMap<String, long[]> statsPerName;
|
||||
private long lastbitpos = 0;
|
||||
|
||||
|
||||
private static final int[] noisy_bits = new int[1024];
|
||||
|
||||
static {
|
||||
static
|
||||
{
|
||||
// noisybits lookup
|
||||
for (int i = 0; i < 1024; i++) {
|
||||
for( int i=0; i<1024; i++ )
|
||||
{
|
||||
int p = i;
|
||||
int noisybits = 0;
|
||||
while (p > 2) {
|
||||
while (p > 2)
|
||||
{
|
||||
noisybits++;
|
||||
p >>= 1;
|
||||
}
|
||||
|
|
@ -26,25 +29,29 @@ public final class StatCoderContext extends BitCoderContext {
|
|||
}
|
||||
|
||||
|
||||
public StatCoderContext(byte[] ab) {
|
||||
super(ab);
|
||||
public StatCoderContext( byte[] ab )
|
||||
{
|
||||
super( ab );
|
||||
}
|
||||
|
||||
/**
|
||||
* assign the de-/encoded bits since the last call assignBits to the given
|
||||
* name. Used for encoding statistics
|
||||
*
|
||||
*
|
||||
* @see #getBitReport
|
||||
*/
|
||||
public void assignBits(String name) {
|
||||
public void assignBits( String name )
|
||||
{
|
||||
long bitpos = getWritingBitPosition();
|
||||
if (statsPerName == null) {
|
||||
statsPerName = new TreeMap<>();
|
||||
if ( statsPerName == null )
|
||||
{
|
||||
statsPerName = new TreeMap<String, long[]>();
|
||||
}
|
||||
long[] stats = statsPerName.get(name);
|
||||
if (stats == null) {
|
||||
long[] stats = statsPerName.get( name );
|
||||
if ( stats == null )
|
||||
{
|
||||
stats = new long[2];
|
||||
statsPerName.put(name, stats);
|
||||
statsPerName.put( name, stats );
|
||||
}
|
||||
stats[0] += bitpos - lastbitpos;
|
||||
stats[1] += 1;
|
||||
|
|
@ -53,17 +60,20 @@ public final class StatCoderContext extends BitCoderContext {
|
|||
|
||||
/**
|
||||
* Get a textual report on the bit-statistics
|
||||
*
|
||||
*
|
||||
* @see #assignBits
|
||||
*/
|
||||
public static String getBitReport() {
|
||||
if (statsPerName == null) {
|
||||
public static String getBitReport()
|
||||
{
|
||||
if ( statsPerName == null )
|
||||
{
|
||||
return "<empty bit report>";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String name : statsPerName.keySet()) {
|
||||
long[] stats = statsPerName.get(name);
|
||||
sb.append(name + " count=" + stats[1] + " bits=" + stats[0] + "\n");
|
||||
for ( String name : statsPerName.keySet() )
|
||||
{
|
||||
long[] stats = statsPerName.get( name );
|
||||
sb.append( name + " count=" + stats[1] + " bits=" + stats[0] + "\n" );
|
||||
}
|
||||
statsPerName = null;
|
||||
return sb.toString();
|
||||
|
|
@ -72,65 +82,76 @@ public final class StatCoderContext extends BitCoderContext {
|
|||
/**
|
||||
* encode an unsigned integer with some of of least significant bits
|
||||
* considered noisy
|
||||
*
|
||||
*
|
||||
* @see #decodeNoisyNumber
|
||||
*/
|
||||
public void encodeNoisyNumber(int value, int noisybits) {
|
||||
if (value < 0) {
|
||||
throw new IllegalArgumentException("encodeVarBits expects positive value");
|
||||
public void encodeNoisyNumber( int value, int noisybits )
|
||||
{
|
||||
if ( value < 0 )
|
||||
{
|
||||
throw new IllegalArgumentException( "encodeVarBits expects positive value" );
|
||||
}
|
||||
if (noisybits > 0) {
|
||||
int mask = 0xffffffff >>> (32 - noisybits);
|
||||
encodeBounded(mask, value & mask);
|
||||
if ( noisybits > 0 )
|
||||
{
|
||||
int mask = 0xffffffff >>> ( 32 - noisybits );
|
||||
encodeBounded( mask, value & mask );
|
||||
value >>= noisybits;
|
||||
}
|
||||
encodeVarBits(value);
|
||||
encodeVarBits( value );
|
||||
}
|
||||
|
||||
/**
|
||||
* decode an unsigned integer with some of of least significant bits
|
||||
* considered noisy
|
||||
*
|
||||
*
|
||||
* @see #encodeNoisyNumber
|
||||
*/
|
||||
public int decodeNoisyNumber(int noisybits) {
|
||||
int value = decodeBits(noisybits);
|
||||
return value | (decodeVarBits() << noisybits);
|
||||
public int decodeNoisyNumber( int noisybits )
|
||||
{
|
||||
int value = decodeBits( noisybits );
|
||||
return value | ( decodeVarBits() << noisybits );
|
||||
}
|
||||
|
||||
/**
|
||||
* encode a signed integer with some of of least significant bits considered
|
||||
* noisy
|
||||
*
|
||||
*
|
||||
* @see #decodeNoisyDiff
|
||||
*/
|
||||
public void encodeNoisyDiff(int value, int noisybits) {
|
||||
if (noisybits > 0) {
|
||||
value += 1 << (noisybits - 1);
|
||||
int mask = 0xffffffff >>> (32 - noisybits);
|
||||
encodeBounded(mask, value & mask);
|
||||
public void encodeNoisyDiff( int value, int noisybits )
|
||||
{
|
||||
if ( noisybits > 0 )
|
||||
{
|
||||
value += 1 << ( noisybits - 1 );
|
||||
int mask = 0xffffffff >>> ( 32 - noisybits );
|
||||
encodeBounded( mask, value & mask );
|
||||
value >>= noisybits;
|
||||
}
|
||||
encodeVarBits(value < 0 ? -value : value);
|
||||
if (value != 0) {
|
||||
encodeBit(value < 0);
|
||||
encodeVarBits( value < 0 ? -value : value );
|
||||
if ( value != 0 )
|
||||
{
|
||||
encodeBit( value < 0 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* decode a signed integer with some of of least significant bits considered
|
||||
* noisy
|
||||
*
|
||||
*
|
||||
* @see #encodeNoisyDiff
|
||||
*/
|
||||
public int decodeNoisyDiff(int noisybits) {
|
||||
public int decodeNoisyDiff( int noisybits )
|
||||
{
|
||||
int value = 0;
|
||||
if (noisybits > 0) {
|
||||
value = decodeBits(noisybits) - (1 << (noisybits - 1));
|
||||
if ( noisybits > 0 )
|
||||
{
|
||||
value = decodeBits( noisybits ) - ( 1 << ( noisybits - 1 ) );
|
||||
}
|
||||
int val2 = decodeVarBits() << noisybits;
|
||||
if (val2 != 0) {
|
||||
if (decodeBit()) {
|
||||
if ( val2 != 0 )
|
||||
{
|
||||
if ( decodeBit() )
|
||||
{
|
||||
val2 = -val2;
|
||||
}
|
||||
}
|
||||
|
|
@ -140,34 +161,38 @@ public final class StatCoderContext extends BitCoderContext {
|
|||
/**
|
||||
* encode a signed integer with the typical range and median taken from the
|
||||
* predicted value
|
||||
*
|
||||
*
|
||||
* @see #decodePredictedValue
|
||||
*/
|
||||
public void encodePredictedValue(int value, int predictor) {
|
||||
public void encodePredictedValue( int value, int predictor )
|
||||
{
|
||||
int p = predictor < 0 ? -predictor : predictor;
|
||||
int noisybits = 0;
|
||||
|
||||
while (p > 2) {
|
||||
while (p > 2)
|
||||
{
|
||||
noisybits++;
|
||||
p >>= 1;
|
||||
}
|
||||
encodeNoisyDiff(value - predictor, noisybits);
|
||||
encodeNoisyDiff( value - predictor, noisybits );
|
||||
}
|
||||
|
||||
/**
|
||||
* decode a signed integer with the typical range and median taken from the
|
||||
* predicted value
|
||||
*
|
||||
*
|
||||
* @see #encodePredictedValue
|
||||
*/
|
||||
public int decodePredictedValue(int predictor) {
|
||||
public int decodePredictedValue( int predictor )
|
||||
{
|
||||
int p = predictor < 0 ? -predictor : predictor;
|
||||
int noisybits = 0;
|
||||
while (p > 1023) {
|
||||
while (p > 1023)
|
||||
{
|
||||
noisybits++;
|
||||
p >>= 1;
|
||||
}
|
||||
return predictor + decodeNoisyDiff(noisybits + noisy_bits[p]);
|
||||
return predictor + decodeNoisyDiff( noisybits + noisy_bits[p] );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -176,21 +201,30 @@ public final class StatCoderContext extends BitCoderContext {
|
|||
* number of values with the current bit being 0. This yields an number of
|
||||
* bits per value that only depends on the typical distance between subsequent
|
||||
* values and also benefits
|
||||
*
|
||||
* @param values the array to encode
|
||||
* @param offset position in this array where to start
|
||||
* @param subsize number of values to encode
|
||||
* @param nextbit bitmask with the most significant bit set to 1
|
||||
* @param mask should be 0
|
||||
*
|
||||
* @param values
|
||||
* the array to encode
|
||||
* @param offset
|
||||
* position in this array where to start
|
||||
* @param subsize
|
||||
* number of values to encode
|
||||
* @param nextbit
|
||||
* bitmask with the most significant bit set to 1
|
||||
* @param mask
|
||||
* should be 0
|
||||
*/
|
||||
public void encodeSortedArray(int[] values, int offset, int subsize, int nextbit, int mask) {
|
||||
if (subsize == 1) { // last-choice shortcut
|
||||
while (nextbit != 0) {
|
||||
encodeBit((values[offset] & nextbit) != 0);
|
||||
public void encodeSortedArray( int[] values, int offset, int subsize, int nextbit, int mask )
|
||||
{
|
||||
if ( subsize == 1 ) // last-choice shortcut
|
||||
{
|
||||
while (nextbit != 0)
|
||||
{
|
||||
encodeBit( ( values[offset] & nextbit ) != 0 );
|
||||
nextbit >>= 1;
|
||||
}
|
||||
}
|
||||
if (nextbit == 0) {
|
||||
if ( nextbit == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -200,54 +234,71 @@ public final class StatCoderContext extends BitCoderContext {
|
|||
// count 0-bit-fraction
|
||||
int i = offset;
|
||||
int end = subsize + offset;
|
||||
for (; i < end; i++) {
|
||||
if ((values[i] & mask) != data) {
|
||||
for ( ; i < end; i++ )
|
||||
{
|
||||
if ( ( values[i] & mask ) != data )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
int size1 = i - offset;
|
||||
int size2 = subsize - size1;
|
||||
|
||||
encodeBounded(subsize, size1);
|
||||
if (size1 > 0) {
|
||||
encodeSortedArray(values, offset, size1, nextbit >> 1, mask);
|
||||
encodeBounded( subsize, size1 );
|
||||
if ( size1 > 0 )
|
||||
{
|
||||
encodeSortedArray( values, offset, size1, nextbit >> 1, mask );
|
||||
}
|
||||
if (size2 > 0) {
|
||||
encodeSortedArray(values, i, size2, nextbit >> 1, mask);
|
||||
if ( size2 > 0 )
|
||||
{
|
||||
encodeSortedArray( values, i, size2, nextbit >> 1, mask );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param values the array to encode
|
||||
* @param offset position in this array where to start
|
||||
* @param subsize number of values to encode
|
||||
* @param nextbit bitmask with the most significant bit set to 1
|
||||
* @param value should be 0
|
||||
* @see #encodeSortedArray
|
||||
*
|
||||
* @param values
|
||||
* the array to encode
|
||||
* @param offset
|
||||
* position in this array where to start
|
||||
* @param subsize
|
||||
* number of values to encode
|
||||
* @param nextbit
|
||||
* bitmask with the most significant bit set to 1
|
||||
* @param value
|
||||
* should be 0
|
||||
*/
|
||||
public void decodeSortedArray(int[] values, int offset, int subsize, int nextbitpos, int value) {
|
||||
if (subsize == 1) { // last-choice shortcut
|
||||
if (nextbitpos >= 0) {
|
||||
value |= decodeBitsReverse(nextbitpos + 1);
|
||||
public void decodeSortedArray( int[] values, int offset, int subsize, int nextbitpos, int value )
|
||||
{
|
||||
if ( subsize == 1 ) // last-choice shortcut
|
||||
{
|
||||
if ( nextbitpos >= 0 )
|
||||
{
|
||||
value |= decodeBitsReverse( nextbitpos+1 );
|
||||
}
|
||||
values[offset] = value;
|
||||
return;
|
||||
}
|
||||
if (nextbitpos < 0) {
|
||||
while (subsize-- > 0) {
|
||||
if ( nextbitpos < 0 )
|
||||
{
|
||||
while (subsize-- > 0)
|
||||
{
|
||||
values[offset++] = value;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int size1 = decodeBounded(subsize);
|
||||
int size1 = decodeBounded( subsize );
|
||||
int size2 = subsize - size1;
|
||||
|
||||
if (size1 > 0) {
|
||||
decodeSortedArray(values, offset, size1, nextbitpos - 1, value);
|
||||
if ( size1 > 0 )
|
||||
{
|
||||
decodeSortedArray( values, offset, size1, nextbitpos-1, value );
|
||||
}
|
||||
if (size2 > 0) {
|
||||
decodeSortedArray(values, offset + size1, size2, nextbitpos - 1, value | (1 << nextbitpos));
|
||||
if ( size2 > 0 )
|
||||
{
|
||||
decodeSortedArray( values, offset + size1, size2, nextbitpos-1, value | (1 << nextbitpos) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,50 +2,57 @@ package btools.codec;
|
|||
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.Queue;
|
||||
|
||||
import btools.util.BitCoderContext;
|
||||
|
||||
/**
|
||||
* Encoder/Decoder for way-/node-descriptions
|
||||
* <p>
|
||||
*
|
||||
* It detects identical descriptions and sorts them
|
||||
* into a huffman-tree according to their frequencies
|
||||
* <p>
|
||||
*
|
||||
* Adapted for 3-pass encoding (counters -> statistics -> encoding )
|
||||
* but doesn't do anything at pass1
|
||||
*/
|
||||
public final class TagValueCoder {
|
||||
private Map<TagValueSet, TagValueSet> identityMap;
|
||||
public final class TagValueCoder
|
||||
{
|
||||
private HashMap<TagValueSet, TagValueSet> identityMap;
|
||||
private Object tree;
|
||||
private BitCoderContext bc;
|
||||
private int pass;
|
||||
private int nextTagValueSetId;
|
||||
|
||||
public void encodeTagValueSet(byte[] data) {
|
||||
if (pass == 1) {
|
||||
public void encodeTagValueSet( byte[] data )
|
||||
{
|
||||
if ( pass == 1 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
TagValueSet tvsProbe = new TagValueSet(nextTagValueSetId);
|
||||
tvsProbe.data = data;
|
||||
TagValueSet tvs = identityMap.get(tvsProbe);
|
||||
if (pass == 3) {
|
||||
bc.encodeBounded(tvs.range - 1, tvs.code);
|
||||
} else if (pass == 2) {
|
||||
if (tvs == null) {
|
||||
TagValueSet tvs = identityMap.get( tvsProbe );
|
||||
if ( pass == 3 )
|
||||
{
|
||||
bc.encodeBounded( tvs.range - 1, tvs.code );
|
||||
}
|
||||
else if ( pass == 2 )
|
||||
{
|
||||
if ( tvs == null )
|
||||
{
|
||||
tvs = tvsProbe;
|
||||
nextTagValueSetId++;
|
||||
identityMap.put(tvs, tvs);
|
||||
identityMap.put( tvs, tvs );
|
||||
}
|
||||
tvs.frequency++;
|
||||
}
|
||||
}
|
||||
|
||||
public TagValueWrapper decodeTagValueSet() {
|
||||
public TagValueWrapper decodeTagValueSet()
|
||||
{
|
||||
Object node = tree;
|
||||
while (node instanceof TreeNode) {
|
||||
while (node instanceof TreeNode)
|
||||
{
|
||||
TreeNode tn = (TreeNode) node;
|
||||
boolean nextBit = bc.decodeBit();
|
||||
node = nextBit ? tn.child2 : tn.child1;
|
||||
|
|
@ -53,87 +60,104 @@ public final class TagValueCoder {
|
|||
return (TagValueWrapper) node;
|
||||
}
|
||||
|
||||
public void encodeDictionary(BitCoderContext bc) {
|
||||
if (++pass == 3) {
|
||||
if (identityMap.size() == 0) {
|
||||
public void encodeDictionary( BitCoderContext bc )
|
||||
{
|
||||
if ( ++pass == 3 )
|
||||
{
|
||||
if ( identityMap.size() == 0 )
|
||||
{
|
||||
TagValueSet dummy = new TagValueSet(nextTagValueSetId++);
|
||||
identityMap.put(dummy, dummy);
|
||||
identityMap.put( dummy, dummy );
|
||||
}
|
||||
Queue<TagValueSet> queue = new PriorityQueue<>(2 * identityMap.size(), new TagValueSet.FrequencyComparator());
|
||||
PriorityQueue<TagValueSet> queue = new PriorityQueue<TagValueSet>(2*identityMap.size(), new TagValueSet.FrequencyComparator());
|
||||
queue.addAll(identityMap.values());
|
||||
while (queue.size() > 1) {
|
||||
while (queue.size() > 1)
|
||||
{
|
||||
TagValueSet node = new TagValueSet(nextTagValueSetId++);
|
||||
node.child1 = queue.poll();
|
||||
node.child2 = queue.poll();
|
||||
node.frequency = node.child1.frequency + node.child2.frequency;
|
||||
queue.add(node);
|
||||
queue.add( node );
|
||||
}
|
||||
TagValueSet root = queue.poll();
|
||||
root.encode(bc, 1, 0);
|
||||
root.encode( bc, 1, 0 );
|
||||
}
|
||||
this.bc = bc;
|
||||
}
|
||||
|
||||
public TagValueCoder(BitCoderContext bc, DataBuffers buffers, TagValueValidator validator) {
|
||||
tree = decodeTree(bc, buffers, validator);
|
||||
public TagValueCoder( BitCoderContext bc, DataBuffers buffers, TagValueValidator validator )
|
||||
{
|
||||
tree = decodeTree( bc, buffers, validator );
|
||||
this.bc = bc;
|
||||
}
|
||||
|
||||
public TagValueCoder() {
|
||||
identityMap = new HashMap<>();
|
||||
public TagValueCoder()
|
||||
{
|
||||
identityMap = new HashMap<TagValueSet, TagValueSet>();
|
||||
}
|
||||
|
||||
private Object decodeTree(BitCoderContext bc, DataBuffers buffers, TagValueValidator validator) {
|
||||
private Object decodeTree( BitCoderContext bc, DataBuffers buffers, TagValueValidator validator )
|
||||
{
|
||||
boolean isNode = bc.decodeBit();
|
||||
if (isNode) {
|
||||
if ( isNode )
|
||||
{
|
||||
TreeNode node = new TreeNode();
|
||||
node.child1 = decodeTree(bc, buffers, validator);
|
||||
node.child2 = decodeTree(bc, buffers, validator);
|
||||
node.child1 = decodeTree( bc, buffers, validator );
|
||||
node.child2 = decodeTree( bc, buffers, validator );
|
||||
return node;
|
||||
}
|
||||
|
||||
byte[] buffer = buffers.tagbuf1;
|
||||
BitCoderContext ctx = buffers.bctx1;
|
||||
ctx.reset(buffer);
|
||||
BitCoderContext ctx = buffers.bctx1;
|
||||
ctx.reset( buffer );
|
||||
|
||||
int inum = 0;
|
||||
int lastEncodedInum = 0;
|
||||
|
||||
boolean hasdata = false;
|
||||
for (; ; ) {
|
||||
for ( ;; )
|
||||
{
|
||||
int delta = bc.decodeVarBits();
|
||||
if (!hasdata) {
|
||||
if (delta == 0) {
|
||||
if ( !hasdata )
|
||||
{
|
||||
if ( delta == 0 )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (delta == 0) {
|
||||
ctx.encodeVarBits(0);
|
||||
if ( delta == 0 )
|
||||
{
|
||||
ctx.encodeVarBits( 0 );
|
||||
break;
|
||||
}
|
||||
inum += delta;
|
||||
|
||||
int data = bc.decodeVarBits();
|
||||
|
||||
if (validator == null || validator.isLookupIdxUsed(inum)) {
|
||||
if ( validator == null || validator.isLookupIdxUsed( inum ) )
|
||||
{
|
||||
hasdata = true;
|
||||
ctx.encodeVarBits(inum - lastEncodedInum);
|
||||
ctx.encodeVarBits(data);
|
||||
ctx.encodeVarBits( inum - lastEncodedInum );
|
||||
ctx.encodeVarBits( data );
|
||||
lastEncodedInum = inum;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] res;
|
||||
int len = ctx.closeAndGetEncodedLength();
|
||||
if (validator == null) {
|
||||
if ( validator == null )
|
||||
{
|
||||
res = new byte[len];
|
||||
System.arraycopy(buffer, 0, res, 0, len);
|
||||
} else {
|
||||
res = validator.unify(buffer, 0, len);
|
||||
System.arraycopy( buffer, 0, res, 0, len );
|
||||
}
|
||||
else
|
||||
{
|
||||
res = validator.unify( buffer, 0, len );
|
||||
}
|
||||
|
||||
int accessType = validator == null ? 2 : validator.accessType(res);
|
||||
if (accessType > 0) {
|
||||
int accessType = validator == null ? 2 : validator.accessType( res );
|
||||
if ( accessType > 0 )
|
||||
{
|
||||
TagValueWrapper w = new TagValueWrapper();
|
||||
w.data = res;
|
||||
w.accessType = accessType;
|
||||
|
|
@ -142,12 +166,14 @@ public final class TagValueCoder {
|
|||
return null;
|
||||
}
|
||||
|
||||
public static final class TreeNode {
|
||||
public static final class TreeNode
|
||||
{
|
||||
public Object child1;
|
||||
public Object child2;
|
||||
}
|
||||
|
||||
public static final class TagValueSet {
|
||||
public static final class TagValueSet
|
||||
{
|
||||
public byte[] data;
|
||||
public int frequency;
|
||||
public int code;
|
||||
|
|
@ -156,51 +182,66 @@ public final class TagValueCoder {
|
|||
public TagValueSet child2;
|
||||
private int id; // serial number to make the comparator well defined in case of equal frequencies
|
||||
|
||||
public TagValueSet(int id) {
|
||||
public TagValueSet( int id )
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void encode(BitCoderContext bc, int range, int code) {
|
||||
public void encode( BitCoderContext bc, int range, int code )
|
||||
{
|
||||
this.range = range;
|
||||
this.code = code;
|
||||
boolean isNode = child1 != null;
|
||||
bc.encodeBit(isNode);
|
||||
if (isNode) {
|
||||
child1.encode(bc, range << 1, code);
|
||||
child2.encode(bc, range << 1, code + range);
|
||||
} else {
|
||||
if (data == null) {
|
||||
bc.encodeVarBits(0);
|
||||
bc.encodeBit( isNode );
|
||||
if ( isNode )
|
||||
{
|
||||
child1.encode( bc, range << 1, code );
|
||||
child2.encode( bc, range << 1, code + range );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( data == null )
|
||||
{
|
||||
bc.encodeVarBits( 0 );
|
||||
return;
|
||||
}
|
||||
BitCoderContext src = new BitCoderContext(data);
|
||||
for (; ; ) {
|
||||
BitCoderContext src = new BitCoderContext( data );
|
||||
for ( ;; )
|
||||
{
|
||||
int delta = src.decodeVarBits();
|
||||
bc.encodeVarBits(delta);
|
||||
if (delta == 0) {
|
||||
bc.encodeVarBits( delta );
|
||||
if ( delta == 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
int data = src.decodeVarBits();
|
||||
bc.encodeVarBits(data);
|
||||
bc.encodeVarBits( data );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof TagValueSet) {
|
||||
public boolean equals( Object o )
|
||||
{
|
||||
if ( o instanceof TagValueSet )
|
||||
{
|
||||
TagValueSet tvs = (TagValueSet) o;
|
||||
if (data == null) {
|
||||
if ( data == null )
|
||||
{
|
||||
return tvs.data == null;
|
||||
}
|
||||
if (tvs.data == null) {
|
||||
if ( tvs.data == null )
|
||||
{
|
||||
return data == null;
|
||||
}
|
||||
if (data.length != tvs.data.length) {
|
||||
if ( data.length != tvs.data.length )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
if (data[i] != tvs.data[i]) {
|
||||
for ( int i = 0; i < data.length; i++ )
|
||||
{
|
||||
if ( data[i] != tvs.data[i] )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -210,34 +251,39 @@ public final class TagValueCoder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (data == null) {
|
||||
public int hashCode()
|
||||
{
|
||||
if ( data == null )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int h = 17;
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
h = (h << 8) + data[i];
|
||||
for ( int i = 0; i < data.length; i++ )
|
||||
{
|
||||
h = ( h << 8 ) + data[i];
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
public static class FrequencyComparator implements Comparator<TagValueSet> {
|
||||
public static class FrequencyComparator implements Comparator<TagValueSet>
|
||||
{
|
||||
|
||||
@Override
|
||||
public int compare(TagValueSet tvs1, TagValueSet tvs2) {
|
||||
if (tvs1.frequency < tvs2.frequency)
|
||||
if ( tvs1.frequency < tvs2.frequency )
|
||||
return -1;
|
||||
if (tvs1.frequency > tvs2.frequency)
|
||||
if ( tvs1.frequency > tvs2.frequency )
|
||||
return 1;
|
||||
|
||||
// to avoid ordering instability, decide on the id if frequency is equal
|
||||
if (tvs1.id < tvs2.id)
|
||||
if ( tvs1.id < tvs2.id )
|
||||
return -1;
|
||||
if (tvs1.id > tvs2.id)
|
||||
if ( tvs1.id > tvs2.id )
|
||||
return 1;
|
||||
|
||||
if (tvs1 != tvs2) {
|
||||
throw new RuntimeException("identity corruption!");
|
||||
if ( tvs1 != tvs2 )
|
||||
{
|
||||
throw new RuntimeException( "identity corruption!" );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
package btools.codec;
|
||||
|
||||
|
||||
public interface TagValueValidator {
|
||||
public interface TagValueValidator
|
||||
{
|
||||
/**
|
||||
* @param tagValueSet the way description to check
|
||||
* @return 0 = nothing, 1=no matching, 2=normal
|
||||
*/
|
||||
int accessType(byte[] tagValueSet);
|
||||
public int accessType( byte[] tagValueSet );
|
||||
|
||||
byte[] unify(byte[] tagValueSet, int offset, int len);
|
||||
public byte[] unify( byte[] tagValueSet, int offset, int len );
|
||||
|
||||
boolean isLookupIdxUsed(int idx);
|
||||
public boolean isLookupIdxUsed( int idx );
|
||||
|
||||
void setDecodeForbidden(boolean decodeForbidden);
|
||||
public void setDecodeForbidden( boolean decodeForbidden );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ package btools.codec;
|
|||
* TagValueWrapper wrapps a description bitmap
|
||||
* to add the access-type
|
||||
*/
|
||||
public final class TagValueWrapper {
|
||||
public final class TagValueWrapper
|
||||
{
|
||||
public byte[] data;
|
||||
public int accessType;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,9 @@ package btools.codec;
|
|||
* from the decoder to find the closest
|
||||
* matches to the waypoints
|
||||
*/
|
||||
public interface WaypointMatcher {
|
||||
boolean start(int ilonStart, int ilatStart, int ilonTarget, int ilatTarget);
|
||||
|
||||
void transferNode(int ilon, int ilat);
|
||||
|
||||
public interface WaypointMatcher
|
||||
{
|
||||
boolean start( int ilonStart, int ilatStart, int ilonTarget, int ilatTarget );
|
||||
void transferNode( int ilon, int ilat );
|
||||
void end();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,35 +3,50 @@ package btools.codec;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class LinkedListContainerTest {
|
||||
public class LinkedListContainerTest
|
||||
{
|
||||
@Test
|
||||
public void linkedListTest1() {
|
||||
public void linkedListTest1()
|
||||
{
|
||||
int nlists = 553;
|
||||
|
||||
LinkedListContainer llc = new LinkedListContainer(nlists, null);
|
||||
LinkedListContainer llc = new LinkedListContainer( nlists, null );
|
||||
|
||||
for (int ln = 0; ln < nlists; ln++) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
llc.addDataElement(ln, ln * i);
|
||||
for ( int ln = 0; ln < nlists; ln++ )
|
||||
{
|
||||
for ( int i = 0; i < 10; i++ )
|
||||
{
|
||||
llc.addDataElement( ln, ln * i );
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int ln = 0; ln < nlists; ln++) {
|
||||
llc.addDataElement(ln, ln * i);
|
||||
for ( int i = 0; i < 10; i++ )
|
||||
{
|
||||
for ( int ln = 0; ln < nlists; ln++ )
|
||||
{
|
||||
llc.addDataElement( ln, ln * i );
|
||||
}
|
||||
}
|
||||
|
||||
for (int ln = 0; ln < nlists; ln++) {
|
||||
int cnt = llc.initList(ln);
|
||||
Assert.assertEquals("list size test", 20, cnt);
|
||||
for ( int ln = 0; ln < nlists; ln++ )
|
||||
{
|
||||
int cnt = llc.initList( ln );
|
||||
Assert.assertTrue( "list size test", cnt == 20 );
|
||||
|
||||
for (int i = 19; i >= 0; i--) {
|
||||
for ( int i = 19; i >= 0; i-- )
|
||||
{
|
||||
int data = llc.getDataElement();
|
||||
Assert.assertEquals("data value test", ln * (i % 10), data);
|
||||
Assert.assertTrue( "data value test", data == ln * ( i % 10 ) );
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertThrows("no more elements expected", IllegalArgumentException.class, () -> llc.getDataElement());
|
||||
try
|
||||
{
|
||||
llc.getDataElement();
|
||||
Assert.fail( "no more elements expected" );
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,79 +6,100 @@ import java.util.Random;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class StatCoderContextTest {
|
||||
public class StatCoderContextTest
|
||||
{
|
||||
@Test
|
||||
public void noisyVarBitsEncodeDecodeTest() {
|
||||
public void noisyVarBitsEncodeDecodeTest()
|
||||
{
|
||||
byte[] ab = new byte[40000];
|
||||
StatCoderContext ctx = new StatCoderContext(ab);
|
||||
for (int noisybits = 1; noisybits < 12; noisybits++) {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
ctx.encodeNoisyNumber(i, noisybits);
|
||||
StatCoderContext ctx = new StatCoderContext( ab );
|
||||
for ( int noisybits = 1; noisybits < 12; noisybits++ )
|
||||
{
|
||||
for ( int i = 0; i < 1000; i++ )
|
||||
{
|
||||
ctx.encodeNoisyNumber( i, noisybits );
|
||||
}
|
||||
}
|
||||
ctx.closeAndGetEncodedLength();
|
||||
ctx = new StatCoderContext(ab);
|
||||
ctx = new StatCoderContext( ab );
|
||||
|
||||
for (int noisybits = 1; noisybits < 12; noisybits++) {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
int value = ctx.decodeNoisyNumber(noisybits);
|
||||
if (value != i) {
|
||||
Assert.fail("value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value);
|
||||
for ( int noisybits = 1; noisybits < 12; noisybits++ )
|
||||
{
|
||||
for ( int i = 0; i < 1000; i++ )
|
||||
{
|
||||
int value = ctx.decodeNoisyNumber( noisybits );
|
||||
if ( value != i )
|
||||
{
|
||||
Assert.fail( "value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noisySignedVarBitsEncodeDecodeTest() {
|
||||
public void noisySignedVarBitsEncodeDecodeTest()
|
||||
{
|
||||
byte[] ab = new byte[80000];
|
||||
StatCoderContext ctx = new StatCoderContext(ab);
|
||||
for (int noisybits = 0; noisybits < 12; noisybits++) {
|
||||
for (int i = -1000; i < 1000; i++) {
|
||||
ctx.encodeNoisyDiff(i, noisybits);
|
||||
StatCoderContext ctx = new StatCoderContext( ab );
|
||||
for ( int noisybits = 0; noisybits < 12; noisybits++ )
|
||||
{
|
||||
for ( int i = -1000; i < 1000; i++ )
|
||||
{
|
||||
ctx.encodeNoisyDiff( i, noisybits );
|
||||
}
|
||||
}
|
||||
ctx.closeAndGetEncodedLength();
|
||||
ctx = new StatCoderContext(ab);
|
||||
ctx = new StatCoderContext( ab );
|
||||
|
||||
for (int noisybits = 0; noisybits < 12; noisybits++) {
|
||||
for (int i = -1000; i < 1000; i++) {
|
||||
int value = ctx.decodeNoisyDiff(noisybits);
|
||||
if (value != i) {
|
||||
Assert.fail("value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value);
|
||||
for ( int noisybits = 0; noisybits < 12; noisybits++ )
|
||||
{
|
||||
for ( int i = -1000; i < 1000; i++ )
|
||||
{
|
||||
int value = ctx.decodeNoisyDiff( noisybits );
|
||||
if ( value != i )
|
||||
{
|
||||
Assert.fail( "value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void predictedValueEncodeDecodeTest() {
|
||||
public void predictedValueEncodeDecodeTest()
|
||||
{
|
||||
byte[] ab = new byte[80000];
|
||||
StatCoderContext ctx = new StatCoderContext(ab);
|
||||
for (int value = -100; value < 100; value += 5) {
|
||||
for (int predictor = -200; predictor < 200; predictor += 7) {
|
||||
ctx.encodePredictedValue(value, predictor);
|
||||
StatCoderContext ctx = new StatCoderContext( ab );
|
||||
for ( int value = -100; value < 100; value += 5 )
|
||||
{
|
||||
for ( int predictor = -200; predictor < 200; predictor += 7 )
|
||||
{
|
||||
ctx.encodePredictedValue( value, predictor );
|
||||
}
|
||||
}
|
||||
ctx.closeAndGetEncodedLength();
|
||||
ctx = new StatCoderContext(ab);
|
||||
ctx = new StatCoderContext( ab );
|
||||
|
||||
for (int value = -100; value < 100; value += 5) {
|
||||
for (int predictor = -200; predictor < 200; predictor += 7) {
|
||||
int decodedValue = ctx.decodePredictedValue(predictor);
|
||||
if (value != decodedValue) {
|
||||
Assert.fail("value mismatch: value=" + value + " predictor=" + predictor + " decodedValue=" + decodedValue);
|
||||
for ( int value = -100; value < 100; value += 5 )
|
||||
{
|
||||
for ( int predictor = -200; predictor < 200; predictor += 7 )
|
||||
{
|
||||
int decodedValue = ctx.decodePredictedValue( predictor );
|
||||
if ( value != decodedValue )
|
||||
{
|
||||
Assert.fail( "value mismatch: value=" + value + " predictor=" + predictor + " decodedValue=" + decodedValue );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sortedArrayEncodeDecodeTest() {
|
||||
public void sortedArrayEncodeDecodeTest()
|
||||
{
|
||||
Random rand = new Random();
|
||||
int size = 1000000;
|
||||
int[] values = new int[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
for ( int i = 0; i < size; i++ )
|
||||
{
|
||||
values[i] = rand.nextInt() & 0x0fffffff;
|
||||
}
|
||||
values[5] = 175384; // force collision
|
||||
|
|
@ -87,21 +108,23 @@ public class StatCoderContextTest {
|
|||
values[15] = 275384; // force neighbours
|
||||
values[18] = 275385;
|
||||
|
||||
Arrays.sort(values);
|
||||
Arrays.sort( values );
|
||||
|
||||
byte[] ab = new byte[3000000];
|
||||
StatCoderContext ctx = new StatCoderContext(ab);
|
||||
ctx.encodeSortedArray(values, 0, size, 0x08000000, 0);
|
||||
StatCoderContext ctx = new StatCoderContext( ab );
|
||||
ctx.encodeSortedArray( values, 0, size, 0x08000000, 0 );
|
||||
|
||||
ctx.closeAndGetEncodedLength();
|
||||
ctx = new StatCoderContext(ab);
|
||||
ctx = new StatCoderContext( ab );
|
||||
|
||||
int[] decodedValues = new int[size];
|
||||
ctx.decodeSortedArray(decodedValues, 0, size, 27, 0);
|
||||
ctx.decodeSortedArray( decodedValues, 0, size, 27, 0 );
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (values[i] != decodedValues[i]) {
|
||||
Assert.fail("mismatch at i=" + i + " " + values[i] + "<>" + decodedValues[i]);
|
||||
for ( int i = 0; i < size; i++ )
|
||||
{
|
||||
if ( values[i] != decodedValues[i] )
|
||||
{
|
||||
Assert.fail( "mismatch at i=" + i + " " + values[i] + "<>" + decodedValues[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1
brouter-core/.gitignore
vendored
Normal file
1
brouter-core/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build/
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
plugins {
|
||||
id 'brouter.library-conventions'
|
||||
id 'java-library'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation project(':brouter-mapaccess')
|
||||
implementation project(':brouter-util')
|
||||
implementation project(':brouter-expressions')
|
||||
implementation project(':brouter-codec')
|
||||
}
|
||||
testImplementation 'junit:junit:4.13.1'
|
||||
|
||||
// MapcreatorTest generates segments which are used in tests
|
||||
test.dependsOn ':brouter-map-creator:test'
|
||||
}
|
||||
|
|
|
|||
3
brouter-core/src/main/AndroidManifest.xml
Normal file
3
brouter-core/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<manifest package="btools.router" />
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -11,12 +11,15 @@ import btools.expressions.BExpressionContextNode;
|
|||
import btools.expressions.BExpressionContextWay;
|
||||
|
||||
|
||||
final class KinematicModel extends OsmPathModel {
|
||||
public OsmPrePath createPrePath() {
|
||||
final class KinematicModel extends OsmPathModel
|
||||
{
|
||||
public OsmPrePath createPrePath()
|
||||
{
|
||||
return new KinematicPrePath();
|
||||
}
|
||||
|
||||
public OsmPath createPath() {
|
||||
public OsmPath createPath()
|
||||
{
|
||||
return new KinematicPath();
|
||||
}
|
||||
|
||||
|
|
@ -35,7 +38,7 @@ final class KinematicModel extends OsmPathModel {
|
|||
// derived values
|
||||
public double pw; // balance power
|
||||
public double cost0; // minimum possible cost per meter
|
||||
|
||||
|
||||
private int wayIdxMaxspeed;
|
||||
private int wayIdxMaxspeedExplicit;
|
||||
private int wayIdxMinspeed;
|
||||
|
|
@ -44,7 +47,7 @@ final class KinematicModel extends OsmPathModel {
|
|||
|
||||
protected BExpressionContextWay ctxWay;
|
||||
protected BExpressionContextNode ctxNode;
|
||||
protected Map<String, String> params;
|
||||
protected Map<String,String> params;
|
||||
|
||||
private boolean initDone = false;
|
||||
|
||||
|
|
@ -52,67 +55,77 @@ final class KinematicModel extends OsmPathModel {
|
|||
private double lastBreakingSpeed;
|
||||
|
||||
@Override
|
||||
public void init(BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String, String> extraParams) {
|
||||
if (!initDone) {
|
||||
public void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String,String> extraParams )
|
||||
{
|
||||
if ( !initDone )
|
||||
{
|
||||
ctxWay = expctxWay;
|
||||
ctxNode = expctxNode;
|
||||
wayIdxMaxspeed = ctxWay.getOutputVariableIndex("maxspeed", false);
|
||||
wayIdxMaxspeedExplicit = ctxWay.getOutputVariableIndex("maxspeed_explicit", false);
|
||||
wayIdxMinspeed = ctxWay.getOutputVariableIndex("minspeed", false);
|
||||
nodeIdxMaxspeed = ctxNode.getOutputVariableIndex("maxspeed", false);
|
||||
wayIdxMaxspeed = ctxWay.getOutputVariableIndex( "maxspeed", false );
|
||||
wayIdxMaxspeedExplicit = ctxWay.getOutputVariableIndex( "maxspeed_explicit", false );
|
||||
wayIdxMinspeed = ctxWay.getOutputVariableIndex( "minspeed", false );
|
||||
nodeIdxMaxspeed = ctxNode.getOutputVariableIndex( "maxspeed", false );
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
params = extraParams;
|
||||
|
||||
turnAngleDecayTime = getParam("turnAngleDecayTime", 5.f);
|
||||
f_roll = getParam("f_roll", 232.f);
|
||||
f_air = getParam("f_air", 0.4f);
|
||||
f_recup = getParam("f_recup", 400.f);
|
||||
p_standby = getParam("p_standby", 250.f);
|
||||
outside_temp = getParam("outside_temp", 20.f);
|
||||
recup_efficiency = getParam("recup_efficiency", 0.7f);
|
||||
totalweight = getParam("totalweight", 1640.f);
|
||||
vmax = getParam("vmax", 80.f) / 3.6;
|
||||
leftWaySpeed = getParam("leftWaySpeed", 12.f) / 3.6;
|
||||
rightWaySpeed = getParam("rightWaySpeed", 12.f) / 3.6;
|
||||
|
||||
turnAngleDecayTime = getParam( "turnAngleDecayTime", 5.f );
|
||||
f_roll = getParam( "f_roll", 232.f );
|
||||
f_air = getParam( "f_air", 0.4f );
|
||||
f_recup = getParam( "f_recup", 400.f );
|
||||
p_standby = getParam( "p_standby", 250.f );
|
||||
outside_temp = getParam( "outside_temp", 20.f );
|
||||
recup_efficiency = getParam( "recup_efficiency", 0.7f );
|
||||
totalweight = getParam( "totalweight", 1640.f );
|
||||
vmax = getParam( "vmax", 80.f ) / 3.6;
|
||||
leftWaySpeed = getParam( "leftWaySpeed", 12.f ) / 3.6;
|
||||
rightWaySpeed = getParam( "rightWaySpeed", 12.f ) / 3.6;
|
||||
|
||||
pw = 2. * f_air * vmax * vmax * vmax - p_standby;
|
||||
cost0 = (pw + p_standby) / vmax + f_roll + f_air * vmax * vmax;
|
||||
cost0 = (pw+p_standby)/vmax + f_roll + f_air*vmax*vmax;
|
||||
}
|
||||
|
||||
protected float getParam(String name, float defaultValue) {
|
||||
String sval = params == null ? null : params.get(name);
|
||||
if (sval != null) {
|
||||
return Float.parseFloat(sval);
|
||||
protected float getParam( String name, float defaultValue )
|
||||
{
|
||||
String sval = params == null ? null : params.get( name );
|
||||
if ( sval != null )
|
||||
{
|
||||
return Float.parseFloat( sval );
|
||||
}
|
||||
float v = ctxWay.getVariableValue(name, defaultValue);
|
||||
if (params != null) {
|
||||
params.put(name, "" + v);
|
||||
float v = ctxWay.getVariableValue( name, defaultValue );
|
||||
if ( params != null )
|
||||
{
|
||||
params.put( name, "" + v );
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
public float getWayMaxspeed() {
|
||||
return ctxWay.getBuildInVariable(wayIdxMaxspeed) / 3.6f;
|
||||
|
||||
public float getWayMaxspeed()
|
||||
{
|
||||
return ctxWay.getBuildInVariable( wayIdxMaxspeed ) / 3.6f;
|
||||
}
|
||||
|
||||
public float getWayMaxspeedExplicit() {
|
||||
return ctxWay.getBuildInVariable(wayIdxMaxspeedExplicit) / 3.6f;
|
||||
public float getWayMaxspeedExplicit()
|
||||
{
|
||||
return ctxWay.getBuildInVariable( wayIdxMaxspeedExplicit ) / 3.6f;
|
||||
}
|
||||
|
||||
public float getWayMinspeed() {
|
||||
return ctxWay.getBuildInVariable(wayIdxMinspeed) / 3.6f;
|
||||
public float getWayMinspeed()
|
||||
{
|
||||
return ctxWay.getBuildInVariable( wayIdxMinspeed ) / 3.6f;
|
||||
}
|
||||
|
||||
public float getNodeMaxspeed() {
|
||||
return ctxNode.getBuildInVariable(nodeIdxMaxspeed) / 3.6f;
|
||||
public float getNodeMaxspeed()
|
||||
{
|
||||
return ctxNode.getBuildInVariable( nodeIdxMaxspeed ) / 3.6f;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the effective speed limit from the way-limit and vmax/vmin
|
||||
*/
|
||||
public double getEffectiveSpeedLimit() {
|
||||
/**
|
||||
* get the effective speed limit from the way-limit and vmax/vmin
|
||||
*/
|
||||
public double getEffectiveSpeedLimit( )
|
||||
{
|
||||
// performance related inline coding
|
||||
double minspeed = getWayMinspeed();
|
||||
double espeed = minspeed > vmax ? minspeed : vmax;
|
||||
|
|
@ -120,27 +133,30 @@ final class KinematicModel extends OsmPathModel {
|
|||
return maxspeed < espeed ? maxspeed : espeed;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the breaking speed for current balance-power (pw) and effective speed limit (vl)
|
||||
*/
|
||||
public double getBreakingSpeed(double vl) {
|
||||
if (vl == lastEffectiveLimit) {
|
||||
/**
|
||||
* get the breaking speed for current balance-power (pw) and effective speed limit (vl)
|
||||
*/
|
||||
public double getBreakingSpeed( double vl )
|
||||
{
|
||||
if ( vl == lastEffectiveLimit )
|
||||
{
|
||||
return lastBreakingSpeed;
|
||||
}
|
||||
|
||||
double v = vl * 0.8;
|
||||
double pw2 = pw + p_standby;
|
||||
double v = vl*0.8;
|
||||
double pw2 = pw+p_standby;
|
||||
double e = recup_efficiency;
|
||||
double x0 = pw2 / vl + f_air * e * vl * vl + (1. - e) * f_roll;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
double v2 = v * v;
|
||||
double x = pw2 / v + f_air * e * v2 - x0;
|
||||
double dx = 2. * e * f_air * v - pw2 / v2;
|
||||
v -= x / dx;
|
||||
double x0 = pw2/vl+f_air*e*vl*vl+(1.-e)*f_roll;
|
||||
for(int i=0;i<5;i++)
|
||||
{
|
||||
double v2 = v*v;
|
||||
double x = pw2/v+f_air*e*v2 - x0;
|
||||
double dx = 2.*e*f_air*v - pw2/v2;
|
||||
v -= x/dx;
|
||||
}
|
||||
lastEffectiveLimit = vl;
|
||||
lastBreakingSpeed = v;
|
||||
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,11 @@
|
|||
*/
|
||||
package btools.router;
|
||||
|
||||
final class KinematicPath extends OsmPath {
|
||||
import btools.util.FastMath;
|
||||
|
||||
|
||||
final class KinematicPath extends OsmPath
|
||||
{
|
||||
private double ekin; // kinetic energy (Joule)
|
||||
private double totalTime; // travel time (seconds)
|
||||
private double totalEnergy; // total route energy (Joule)
|
||||
|
|
@ -13,17 +17,20 @@ final class KinematicPath extends OsmPath {
|
|||
private float floatingAngleRight; // sliding average right bend (degree)
|
||||
|
||||
@Override
|
||||
protected void init(OsmPath orig) {
|
||||
KinematicPath origin = (KinematicPath) orig;
|
||||
protected void init( OsmPath orig )
|
||||
{
|
||||
KinematicPath origin = (KinematicPath)orig;
|
||||
ekin = origin.ekin;
|
||||
totalTime = origin.totalTime;
|
||||
totalEnergy = origin.totalEnergy;
|
||||
floatingAngleLeft = origin.floatingAngleLeft;
|
||||
floatingAngleRight = origin.floatingAngleRight;
|
||||
priorityclassifier = origin.priorityclassifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetState() {
|
||||
protected void resetState()
|
||||
{
|
||||
ekin = 0.;
|
||||
totalTime = 0.;
|
||||
totalEnergy = 0.;
|
||||
|
|
@ -32,233 +39,267 @@ final class KinematicPath extends OsmPath {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected double processWaySection(RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier) {
|
||||
KinematicModel km = (KinematicModel) rc.pm;
|
||||
protected double processWaySection( RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier )
|
||||
{
|
||||
KinematicModel km = (KinematicModel)rc.pm;
|
||||
|
||||
double cost = 0.;
|
||||
double extraTime = 0.;
|
||||
|
||||
if (isStartpoint) {
|
||||
if ( isStartpoint )
|
||||
{
|
||||
// for forward direction, we start with target speed
|
||||
if (!rc.inverseDirection) {
|
||||
extraTime = 0.5 * (1. - cosangle) * 40.; // 40 seconds turn penalty
|
||||
if ( !rc.inverseDirection )
|
||||
{
|
||||
extraTime = 0.5 * (1. - cosangle ) * 40.; // 40 seconds turn penalty
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
double turnspeed = 999.; // just high
|
||||
|
||||
if (km.turnAngleDecayTime != 0.) { // process turn-angle slowdown
|
||||
if (angle < 0) floatingAngleLeft -= (float) angle;
|
||||
else floatingAngleRight += (float) angle;
|
||||
float aa = Math.max(floatingAngleLeft, floatingAngleRight);
|
||||
if ( km.turnAngleDecayTime != 0. ) // process turn-angle slowdown
|
||||
{
|
||||
if ( angle < 0 ) floatingAngleLeft -= (float)angle;
|
||||
else floatingAngleRight += (float)angle;
|
||||
float aa = Math.max( floatingAngleLeft, floatingAngleRight );
|
||||
|
||||
double curveSpeed = aa > 10. ? 200. / aa : 20.;
|
||||
double curveSpeed = aa > 10. ? 200. / aa : 20.;
|
||||
double distanceTime = dist / curveSpeed;
|
||||
double decayFactor = Math.exp(-distanceTime / km.turnAngleDecayTime);
|
||||
floatingAngleLeft = (float) (floatingAngleLeft * decayFactor);
|
||||
floatingAngleRight = (float) (floatingAngleRight * decayFactor);
|
||||
double decayFactor = FastMath.exp( - distanceTime / km.turnAngleDecayTime );
|
||||
floatingAngleLeft = (float)( floatingAngleLeft * decayFactor );
|
||||
floatingAngleRight = (float)( floatingAngleRight * decayFactor );
|
||||
|
||||
if (curveSpeed < 20.) {
|
||||
if ( curveSpeed < 20. )
|
||||
{
|
||||
turnspeed = curveSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
if (nsection == 0) { // process slowdown by crossing geometry
|
||||
if ( nsection == 0 ) // process slowdown by crossing geometry
|
||||
{
|
||||
double junctionspeed = 999.; // just high
|
||||
|
||||
int classifiermask = (int) rc.expctxWay.getClassifierMask();
|
||||
int classifiermask = (int)rc.expctxWay.getClassifierMask();
|
||||
|
||||
// penalty for equal priority crossing
|
||||
boolean hasLeftWay = false;
|
||||
boolean hasRightWay = false;
|
||||
boolean hasResidential = false;
|
||||
for (OsmPrePath prePath = rc.firstPrePath; prePath != null; prePath = prePath.next) {
|
||||
KinematicPrePath pp = (KinematicPrePath) prePath;
|
||||
for( OsmPrePath prePath = rc.firstPrePath; prePath != null; prePath = prePath.next )
|
||||
{
|
||||
KinematicPrePath pp = (KinematicPrePath)prePath;
|
||||
|
||||
if (((pp.classifiermask ^ classifiermask) & 8) != 0) { // exactly one is linktype
|
||||
if ( ( (pp.classifiermask ^ classifiermask) & 8 ) != 0 ) // exactly one is linktype
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((pp.classifiermask & 32) != 0) { // touching a residential?
|
||||
if ( ( pp.classifiermask & 32 ) != 0 ) // touching a residential?
|
||||
{
|
||||
hasResidential = true;
|
||||
}
|
||||
|
||||
if (pp.priorityclassifier > priorityclassifier || pp.priorityclassifier == priorityclassifier && priorityclassifier < 20) {
|
||||
if ( pp.priorityclassifier > priorityclassifier || pp.priorityclassifier == priorityclassifier && priorityclassifier < 20 )
|
||||
{
|
||||
double diff = pp.angle - angle;
|
||||
if (diff < -40. && diff > -140.) hasLeftWay = true;
|
||||
if (diff > 40. && diff < 140.) hasRightWay = true;
|
||||
if ( diff < -40. && diff > -140.) hasLeftWay = true;
|
||||
if ( diff > 40. && diff < 140. ) hasRightWay = true;
|
||||
}
|
||||
}
|
||||
double residentialSpeed = 13.;
|
||||
|
||||
if (hasLeftWay && junctionspeed > km.leftWaySpeed) junctionspeed = km.leftWaySpeed;
|
||||
if (hasRightWay && junctionspeed > km.rightWaySpeed) junctionspeed = km.rightWaySpeed;
|
||||
if (hasResidential && junctionspeed > residentialSpeed) junctionspeed = residentialSpeed;
|
||||
if ( hasLeftWay && junctionspeed > km.leftWaySpeed ) junctionspeed = km.leftWaySpeed;
|
||||
if ( hasRightWay && junctionspeed > km.rightWaySpeed ) junctionspeed = km.rightWaySpeed;
|
||||
if ( hasResidential && junctionspeed > residentialSpeed ) junctionspeed = residentialSpeed;
|
||||
|
||||
if ((lastpriorityclassifier < 20) ^ (priorityclassifier < 20)) {
|
||||
if ( (lastpriorityclassifier < 20) ^ (priorityclassifier < 20) )
|
||||
{
|
||||
extraTime += 10.;
|
||||
junctionspeed = 0; // full stop for entering or leaving road network
|
||||
}
|
||||
|
||||
if (lastpriorityclassifier != priorityclassifier && (classifiermask & 8) != 0) {
|
||||
if ( lastpriorityclassifier != priorityclassifier && (classifiermask & 8) != 0 )
|
||||
{
|
||||
extraTime += 2.; // two seconds for entering a link-type
|
||||
}
|
||||
turnspeed = turnspeed > junctionspeed ? junctionspeed : turnspeed;
|
||||
|
||||
if (message != null) {
|
||||
message.vnode0 = (int) (junctionspeed * 3.6 + 0.5);
|
||||
if ( message != null )
|
||||
{
|
||||
message.vnode0 = (int) ( junctionspeed * 3.6 + 0.5 );
|
||||
}
|
||||
}
|
||||
cutEkin(km.totalweight, turnspeed); // apply turnspeed
|
||||
cutEkin( km.totalweight, turnspeed ); // apply turnspeed
|
||||
}
|
||||
|
||||
// linear temperature correction
|
||||
double tcorr = (20. - km.outside_temp) * 0.0035;
|
||||
double tcorr = (20.-km.outside_temp)*0.0035;
|
||||
|
||||
// air_pressure down 1mb/8m
|
||||
double ecorr = 0.0001375 * (elevation - 100.);
|
||||
|
||||
double f_air = km.f_air * (1. + tcorr - ecorr);
|
||||
double f_air = km.f_air * ( 1. + tcorr - ecorr );
|
||||
|
||||
double distanceCost = evolveDistance(km, dist, delta_h, f_air);
|
||||
double distanceCost = evolveDistance( km, dist, delta_h, f_air );
|
||||
|
||||
if (message != null) {
|
||||
message.costfactor = (float) (distanceCost / dist);
|
||||
message.vmax = (int) (km.getWayMaxspeed() * 3.6 + 0.5);
|
||||
message.vmaxExplicit = (int) (km.getWayMaxspeedExplicit() * 3.6 + 0.5);
|
||||
message.vmin = (int) (km.getWayMinspeed() * 3.6 + 0.5);
|
||||
message.extraTime = (int) (extraTime * 1000);
|
||||
if ( message != null )
|
||||
{
|
||||
message.costfactor = (float)(distanceCost/dist);
|
||||
message.vmax = (int) ( km.getWayMaxspeed() * 3.6 + 0.5 );
|
||||
message.vmaxExplicit = (int) ( km.getWayMaxspeedExplicit() * 3.6 + 0.5 );
|
||||
message.vmin = (int) ( km.getWayMinspeed() * 3.6 + 0.5 );
|
||||
message.extraTime = (int)(extraTime*1000);
|
||||
}
|
||||
|
||||
cost += extraTime * km.pw / km.cost0;
|
||||
cost += extraTime * km.pw / km.cost0;
|
||||
totalTime += extraTime;
|
||||
|
||||
return cost + distanceCost;
|
||||
}
|
||||
|
||||
|
||||
protected double evolveDistance(KinematicModel km, double dist, double delta_h, double f_air) {
|
||||
protected double evolveDistance( KinematicModel km, double dist, double delta_h, double f_air )
|
||||
{
|
||||
// elevation force
|
||||
double fh = delta_h * km.totalweight * 9.81 / dist;
|
||||
|
||||
double effectiveSpeedLimit = km.getEffectiveSpeedLimit();
|
||||
double emax = 0.5 * km.totalweight * effectiveSpeedLimit * effectiveSpeedLimit;
|
||||
if (emax <= 0.) {
|
||||
double emax = 0.5*km.totalweight*effectiveSpeedLimit*effectiveSpeedLimit;
|
||||
if ( emax <= 0. )
|
||||
{
|
||||
return -1.;
|
||||
}
|
||||
double vb = km.getBreakingSpeed(effectiveSpeedLimit);
|
||||
double elow = 0.5 * km.totalweight * vb * vb;
|
||||
double vb = km.getBreakingSpeed( effectiveSpeedLimit );
|
||||
double elow = 0.5*km.totalweight*vb*vb;
|
||||
|
||||
double elapsedTime = 0.;
|
||||
double dissipatedEnergy = 0.;
|
||||
|
||||
double v = Math.sqrt(2. * ekin / km.totalweight);
|
||||
double v = Math.sqrt( 2. * ekin / km.totalweight );
|
||||
double d = dist;
|
||||
while (d > 0.) {
|
||||
while( d > 0. )
|
||||
{
|
||||
boolean slow = ekin < elow;
|
||||
boolean fast = ekin >= emax;
|
||||
double etarget = slow ? elow : emax;
|
||||
double f = km.f_roll + f_air * v * v + fh;
|
||||
double f_recup = Math.max(0., fast ? -f : (slow ? km.f_recup : 0) - fh); // additional recup for slow part
|
||||
double f = km.f_roll + f_air*v*v + fh;
|
||||
double f_recup = Math.max( 0., fast ? -f : (slow ? km.f_recup :0 ) -fh ); // additional recup for slow part
|
||||
f += f_recup;
|
||||
|
||||
double delta_ekin;
|
||||
double timeStep;
|
||||
double x;
|
||||
if (fast) {
|
||||
if ( fast )
|
||||
{
|
||||
x = d;
|
||||
delta_ekin = x * f;
|
||||
timeStep = x / v;
|
||||
delta_ekin = x*f;
|
||||
timeStep = x/v;
|
||||
ekin = etarget;
|
||||
} else {
|
||||
delta_ekin = etarget - ekin;
|
||||
double b = 2. * f_air / km.totalweight;
|
||||
double x0 = delta_ekin / f;
|
||||
double x0b = x0 * b;
|
||||
x = x0 * (1. - x0b * (0.5 + x0b * (0.333333333 - x0b * 0.25))); // = ln( delta_ekin*b/f + 1.) / b;
|
||||
double maxstep = Math.min(50., d);
|
||||
if (x >= maxstep) {
|
||||
}
|
||||
else
|
||||
{
|
||||
delta_ekin = etarget-ekin;
|
||||
double b = 2.*f_air / km.totalweight;
|
||||
double x0 = delta_ekin/f;
|
||||
double x0b = x0*b;
|
||||
x = x0*(1. - x0b*(0.5 + x0b*(0.333333333-x0b*0.25 ) ) ); // = ln( delta_ekin*b/f + 1.) / b;
|
||||
double maxstep = Math.min( 50., d );
|
||||
if ( x >= maxstep )
|
||||
{
|
||||
x = maxstep;
|
||||
double xb = x * b;
|
||||
delta_ekin = x * f * (1. + xb * (0.5 + xb * (0.166666667 + xb * 0.0416666667))); // = f/b* exp(xb-1)
|
||||
double xb = x*b;
|
||||
delta_ekin = x*f*(1.+xb*(0.5+xb*(0.166666667+xb*0.0416666667 ) ) ); // = f/b* exp(xb-1)
|
||||
ekin += delta_ekin;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ekin = etarget;
|
||||
}
|
||||
double v2 = Math.sqrt(2. * ekin / km.totalweight);
|
||||
double v2 = Math.sqrt( 2. * ekin / km.totalweight );
|
||||
double a = f / km.totalweight; // TODO: average force?
|
||||
timeStep = (v2 - v) / a;
|
||||
timeStep = (v2-v)/a;
|
||||
v = v2;
|
||||
}
|
||||
d -= x;
|
||||
elapsedTime += timeStep;
|
||||
|
||||
// dissipated energy does not contain elevation and efficient recup
|
||||
dissipatedEnergy += delta_ekin - x * (fh + f_recup * km.recup_efficiency);
|
||||
dissipatedEnergy += delta_ekin - x*(fh + f_recup*km.recup_efficiency);
|
||||
|
||||
// correction: inefficient recup going into heating is half efficient
|
||||
double ieRecup = x * f_recup * (1. - km.recup_efficiency);
|
||||
double eaux = timeStep * km.p_standby;
|
||||
dissipatedEnergy -= Math.max(ieRecup, eaux) * 0.5;
|
||||
double ieRecup = x*f_recup*(1.-km.recup_efficiency);
|
||||
double eaux = timeStep*km.p_standby;
|
||||
dissipatedEnergy -= Math.max( ieRecup, eaux ) * 0.5;
|
||||
}
|
||||
|
||||
dissipatedEnergy += elapsedTime * km.p_standby;
|
||||
|
||||
totalTime += elapsedTime;
|
||||
totalEnergy += dissipatedEnergy + dist * fh;
|
||||
totalEnergy += dissipatedEnergy + dist*fh;
|
||||
|
||||
return (km.pw * elapsedTime + dissipatedEnergy) / km.cost0; // =cost
|
||||
return (km.pw * elapsedTime + dissipatedEnergy)/km.cost0; // =cost
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double processTargetNode(RoutingContext rc) {
|
||||
KinematicModel km = (KinematicModel) rc.pm;
|
||||
protected double processTargetNode( RoutingContext rc )
|
||||
{
|
||||
KinematicModel km = (KinematicModel)rc.pm;
|
||||
|
||||
// finally add node-costs for target node
|
||||
if (targetNode.nodeDescription != null) {
|
||||
rc.expctxNode.evaluate(false, targetNode.nodeDescription);
|
||||
if ( targetNode.nodeDescription != null )
|
||||
{
|
||||
rc.expctxNode.evaluate( false , targetNode.nodeDescription );
|
||||
float initialcost = rc.expctxNode.getInitialcost();
|
||||
if (initialcost >= 1000000.) {
|
||||
if ( initialcost >= 1000000. )
|
||||
{
|
||||
return -1.;
|
||||
}
|
||||
cutEkin(km.totalweight, km.getNodeMaxspeed()); // apply node maxspeed
|
||||
cutEkin( km.totalweight, km.getNodeMaxspeed() ); // apply node maxspeed
|
||||
|
||||
if (message != null) {
|
||||
message.linknodecost += (int) initialcost;
|
||||
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription(false, targetNode.nodeDescription);
|
||||
if ( message != null )
|
||||
{
|
||||
message.linknodecost += (int)initialcost;
|
||||
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription( false, targetNode.nodeDescription );
|
||||
|
||||
message.vnode1 = (int) (km.getNodeMaxspeed() * 3.6 + 0.5);
|
||||
message.vnode1 = (int) ( km.getNodeMaxspeed() * 3.6 + 0.5 );
|
||||
}
|
||||
return initialcost;
|
||||
}
|
||||
return 0.;
|
||||
}
|
||||
|
||||
private void cutEkin(double weight, double speed) {
|
||||
double e = 0.5 * weight * speed * speed;
|
||||
if (ekin > e) ekin = e;
|
||||
private void cutEkin( double weight, double speed )
|
||||
{
|
||||
double e = 0.5*weight*speed*speed;
|
||||
if ( ekin > e ) ekin = e;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int elevationCorrection() {
|
||||
public int elevationCorrection( RoutingContext rc )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean definitlyWorseThan(OsmPath path) {
|
||||
KinematicPath p = (KinematicPath) path;
|
||||
public boolean definitlyWorseThan( OsmPath path, RoutingContext rc )
|
||||
{
|
||||
KinematicPath p = (KinematicPath)path;
|
||||
|
||||
int c = p.cost;
|
||||
return cost > c + 100;
|
||||
int c = p.cost;
|
||||
return cost > c + 100;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getTotalTime() {
|
||||
public double getTotalTime()
|
||||
{
|
||||
return totalTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getTotalEnergy() {
|
||||
public double getTotalEnergy()
|
||||
{
|
||||
return totalEnergy;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,14 +8,16 @@ package btools.router;
|
|||
import btools.mapaccess.OsmNode;
|
||||
import btools.mapaccess.OsmTransferNode;
|
||||
|
||||
final class KinematicPrePath extends OsmPrePath {
|
||||
final class KinematicPrePath extends OsmPrePath
|
||||
{
|
||||
public double angle;
|
||||
public int priorityclassifier;
|
||||
public int classifiermask;
|
||||
|
||||
protected void initPrePath(OsmPath origin, RoutingContext rc) {
|
||||
protected void initPrePath(OsmPath origin, RoutingContext rc )
|
||||
{
|
||||
byte[] description = link.descriptionBitmap;
|
||||
if (description == null) throw new IllegalArgumentException("null description for: " + link);
|
||||
if ( description == null ) throw new IllegalArgumentException( "null description for: " + link );
|
||||
|
||||
// extract the 3 positions of the first section
|
||||
int lon0 = origin.originLon;
|
||||
|
|
@ -25,29 +27,32 @@ final class KinematicPrePath extends OsmPrePath {
|
|||
int lon1 = p1.getILon();
|
||||
int lat1 = p1.getILat();
|
||||
|
||||
boolean isReverse = link.isReverse(sourceNode);
|
||||
boolean isReverse = link.isReverse( sourceNode );
|
||||
|
||||
// evaluate the way tags
|
||||
rc.expctxWay.evaluate(rc.inverseDirection ^ isReverse, description);
|
||||
rc.expctxWay.evaluate( rc.inverseDirection ^ isReverse, description );
|
||||
|
||||
OsmTransferNode transferNode = link.geometry == null ? null
|
||||
: rc.geometryDecoder.decodeGeometry(link.geometry, p1, targetNode, isReverse);
|
||||
: rc.geometryDecoder.decodeGeometry( link.geometry, p1, targetNode, isReverse );
|
||||
|
||||
int lon2;
|
||||
int lat2;
|
||||
|
||||
if (transferNode == null) {
|
||||
if ( transferNode == null )
|
||||
{
|
||||
lon2 = targetNode.ilon;
|
||||
lat2 = targetNode.ilat;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
lon2 = transferNode.ilon;
|
||||
lat2 = transferNode.ilat;
|
||||
}
|
||||
|
||||
int dist = rc.calcDistance(lon1, lat1, lon2, lat2);
|
||||
int dist = rc.calcDistance( lon1, lat1, lon2, lat2 );
|
||||
|
||||
angle = rc.anglemeter.calcAngle(lon0, lat0, lon1, lat1, lon2, lat2);
|
||||
priorityclassifier = (int) rc.expctxWay.getPriorityClassifier();
|
||||
classifiermask = (int) rc.expctxWay.getClassifierMask();
|
||||
angle = rc.anglemeter.calcAngle( lon0, lat0, lon1, lat1, lon2, lat2 );
|
||||
priorityclassifier = (int)rc.expctxWay.getPriorityClassifier();
|
||||
classifiermask = (int)rc.expctxWay.getClassifierMask();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,13 +6,15 @@
|
|||
package btools.router;
|
||||
|
||||
|
||||
final class MessageData implements Cloneable {
|
||||
|
||||
final class MessageData implements Cloneable
|
||||
{
|
||||
int linkdist = 0;
|
||||
int linkelevationcost = 0;
|
||||
int linkturncost = 0;
|
||||
int linknodecost = 0;
|
||||
int linkinitcost = 0;
|
||||
|
||||
|
||||
float costfactor;
|
||||
int priorityclassifier;
|
||||
int classifiermask;
|
||||
|
|
@ -23,7 +25,7 @@ final class MessageData implements Cloneable {
|
|||
int lon;
|
||||
int lat;
|
||||
short ele;
|
||||
|
||||
|
||||
float time;
|
||||
float energy;
|
||||
|
||||
|
|
@ -35,70 +37,84 @@ final class MessageData implements Cloneable {
|
|||
int vnode1 = 999;
|
||||
int extraTime = 0;
|
||||
|
||||
String toMessage() {
|
||||
if (wayKeyValues == null) {
|
||||
String toMessage()
|
||||
{
|
||||
if ( wayKeyValues == null )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int iCost = (int) (costfactor * 1000 + 0.5f);
|
||||
return (lon - 180000000) + "\t"
|
||||
+ (lat - 90000000) + "\t"
|
||||
+ ele / 4 + "\t"
|
||||
+ linkdist + "\t"
|
||||
+ iCost + "\t"
|
||||
+ linkelevationcost
|
||||
+ "\t" + linkturncost
|
||||
+ "\t" + linknodecost
|
||||
+ "\t" + linkinitcost
|
||||
+ "\t" + wayKeyValues
|
||||
+ "\t" + (nodeKeyValues == null ? "" : nodeKeyValues)
|
||||
+ "\t" + ((int) time)
|
||||
+ "\t" + ((int) energy);
|
||||
|
||||
int iCost = (int)(costfactor*1000 + 0.5f);
|
||||
return (lon-180000000) + "\t"
|
||||
+ (lat-90000000) + "\t"
|
||||
+ ele/4 + "\t"
|
||||
+ linkdist + "\t"
|
||||
+ iCost + "\t"
|
||||
+ linkelevationcost
|
||||
+ "\t" + linkturncost
|
||||
+ "\t" + linknodecost
|
||||
+ "\t" + linkinitcost
|
||||
+ "\t" + wayKeyValues
|
||||
+ "\t" + ( nodeKeyValues == null ? "" : nodeKeyValues )
|
||||
+ "\t" + ((int)time)
|
||||
+ "\t" + ((int)energy);
|
||||
}
|
||||
|
||||
void add(MessageData d) {
|
||||
void add( MessageData d )
|
||||
{
|
||||
linkdist += d.linkdist;
|
||||
linkelevationcost += d.linkelevationcost;
|
||||
linkturncost += d.linkturncost;
|
||||
linknodecost += d.linknodecost;
|
||||
linkinitcost += d.linkinitcost;
|
||||
linkinitcost+= d.linkinitcost;
|
||||
}
|
||||
|
||||
MessageData copy() {
|
||||
try {
|
||||
return (MessageData) clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new RuntimeException(e);
|
||||
MessageData copy()
|
||||
{
|
||||
try
|
||||
{
|
||||
return (MessageData)clone();
|
||||
}
|
||||
catch( CloneNotSupportedException e )
|
||||
{
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
public String toString()
|
||||
{
|
||||
return "dist=" + linkdist + " prio=" + priorityclassifier + " turn=" + turnangle;
|
||||
}
|
||||
|
||||
public int getPrio() {
|
||||
public int getPrio()
|
||||
{
|
||||
return priorityclassifier;
|
||||
}
|
||||
|
||||
public boolean isBadOneway() {
|
||||
return (classifiermask & 1) != 0;
|
||||
public boolean isBadOneway()
|
||||
{
|
||||
return ( classifiermask & 1 ) != 0;
|
||||
}
|
||||
|
||||
public boolean isGoodOneway() {
|
||||
return (classifiermask & 2) != 0;
|
||||
public boolean isGoodOneway()
|
||||
{
|
||||
return ( classifiermask & 2 ) != 0;
|
||||
}
|
||||
|
||||
public boolean isRoundabout() {
|
||||
return (classifiermask & 4) != 0;
|
||||
public boolean isRoundabout()
|
||||
{
|
||||
return ( classifiermask & 4 ) != 0;
|
||||
}
|
||||
|
||||
public boolean isLinktType() {
|
||||
return (classifiermask & 8) != 0;
|
||||
public boolean isLinktType()
|
||||
{
|
||||
return ( classifiermask & 8 ) != 0;
|
||||
}
|
||||
|
||||
public boolean isGoodForCars() {
|
||||
return (classifiermask & 16) != 0;
|
||||
public boolean isGoodForCars()
|
||||
{
|
||||
return ( classifiermask & 16 ) != 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,99 +1,103 @@
|
|||
/**
|
||||
* Container for an osm node
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
package btools.router;
|
||||
|
||||
import btools.mapaccess.OsmNode;
|
||||
import btools.util.CheapRuler;
|
||||
|
||||
public class OsmNodeNamed extends OsmNode {
|
||||
public String name;
|
||||
public double radius; // radius of nogopoint (in meters)
|
||||
public double nogoWeight; // weight for nogopoint
|
||||
public boolean isNogo = false;
|
||||
public boolean direct = false; // mark direct routing
|
||||
|
||||
public OsmNodeNamed() {
|
||||
}
|
||||
|
||||
public OsmNodeNamed(OsmNode n) {
|
||||
super(n.ilon, n.ilat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (Double.isNaN(nogoWeight)) {
|
||||
return ilon + "," + ilat + "," + name;
|
||||
} else {
|
||||
return ilon + "," + ilat + "," + name + "," + nogoWeight;
|
||||
}
|
||||
}
|
||||
|
||||
public double distanceWithinRadius(int lon1, int lat1, int lon2, int lat2, double totalSegmentLength) {
|
||||
double[] lonlat2m = CheapRuler.getLonLatToMeterScales((lat1 + lat2) >> 1);
|
||||
|
||||
boolean isFirstPointWithinCircle = CheapRuler.distance(lon1, lat1, ilon, ilat) < radius;
|
||||
boolean isLastPointWithinCircle = CheapRuler.distance(lon2, lat2, ilon, ilat) < radius;
|
||||
// First point is within the circle
|
||||
if (isFirstPointWithinCircle) {
|
||||
// Last point is within the circle
|
||||
if (isLastPointWithinCircle) {
|
||||
return totalSegmentLength;
|
||||
}
|
||||
// Last point is not within the circle
|
||||
// Just swap points and go on with first first point not within the
|
||||
// circle now.
|
||||
// Swap longitudes
|
||||
int tmp = lon2;
|
||||
lon2 = lon1;
|
||||
lon1 = tmp;
|
||||
// Swap latitudes
|
||||
tmp = lat2;
|
||||
lat2 = lat1;
|
||||
lat1 = tmp;
|
||||
// Fix boolean values
|
||||
isLastPointWithinCircle = isFirstPointWithinCircle;
|
||||
isFirstPointWithinCircle = false;
|
||||
}
|
||||
// Distance between the initial point and projection of center of
|
||||
// the circle on the current segment.
|
||||
double initialToProject = (
|
||||
(lon2 - lon1) * (ilon - lon1) * lonlat2m[0] * lonlat2m[0]
|
||||
+ (lat2 - lat1) * (ilat - lat1) * lonlat2m[1] * lonlat2m[1]
|
||||
) / totalSegmentLength;
|
||||
// Distance between the initial point and the center of the circle.
|
||||
double initialToCenter = CheapRuler.distance(ilon, ilat, lon1, lat1);
|
||||
// Half length of the segment within the circle
|
||||
double halfDistanceWithin = Math.sqrt(
|
||||
radius * radius - (
|
||||
initialToCenter * initialToCenter -
|
||||
initialToProject * initialToProject
|
||||
)
|
||||
);
|
||||
// Last point is within the circle
|
||||
if (isLastPointWithinCircle) {
|
||||
return halfDistanceWithin + (totalSegmentLength - initialToProject);
|
||||
}
|
||||
return 2 * halfDistanceWithin;
|
||||
}
|
||||
|
||||
public static OsmNodeNamed decodeNogo(String s) {
|
||||
OsmNodeNamed n = new OsmNodeNamed();
|
||||
int idx1 = s.indexOf(',');
|
||||
n.ilon = Integer.parseInt(s.substring(0, idx1));
|
||||
int idx2 = s.indexOf(',', idx1 + 1);
|
||||
n.ilat = Integer.parseInt(s.substring(idx1 + 1, idx2));
|
||||
int idx3 = s.indexOf(',', idx2 + 1);
|
||||
if (idx3 == -1) {
|
||||
n.name = s.substring(idx2 + 1);
|
||||
n.nogoWeight = Double.NaN;
|
||||
} else {
|
||||
n.name = s.substring(idx2 + 1, idx3);
|
||||
n.nogoWeight = Double.parseDouble(s.substring(idx3 + 1));
|
||||
}
|
||||
n.isNogo = true;
|
||||
return n;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Container for an osm node
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
package btools.router;
|
||||
|
||||
import btools.mapaccess.OsmNode;
|
||||
import btools.util.CheapRuler;
|
||||
|
||||
public class OsmNodeNamed extends OsmNode
|
||||
{
|
||||
public String name;
|
||||
public double radius; // radius of nogopoint (in meters)
|
||||
public double nogoWeight; // weight for nogopoint
|
||||
public boolean isNogo = false;
|
||||
|
||||
public OsmNodeNamed()
|
||||
{
|
||||
}
|
||||
|
||||
public OsmNodeNamed( OsmNode n)
|
||||
{
|
||||
super( n.ilon, n.ilat );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
if ( Double.isNaN(nogoWeight ) ) {
|
||||
return ilon + "," + ilat + "," + name;
|
||||
} else {
|
||||
return ilon + "," + ilat + "," + name + "," + nogoWeight;
|
||||
}
|
||||
}
|
||||
|
||||
public double distanceWithinRadius(int lon1, int lat1, int lon2, int lat2, double totalSegmentLength) {
|
||||
double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (lat1 + lat2) >> 1 );
|
||||
|
||||
boolean isFirstPointWithinCircle = CheapRuler.distance(lon1, lat1, ilon, ilat) < radius;
|
||||
boolean isLastPointWithinCircle = CheapRuler.distance(lon2, lat2, ilon, ilat) < radius;
|
||||
// First point is within the circle
|
||||
if (isFirstPointWithinCircle) {
|
||||
// Last point is within the circle
|
||||
if (isLastPointWithinCircle) {
|
||||
return totalSegmentLength;
|
||||
}
|
||||
// Last point is not within the circle
|
||||
// Just swap points and go on with first first point not within the
|
||||
// circle now.
|
||||
// Swap longitudes
|
||||
int tmp = lon2;
|
||||
lon2 = lon1;
|
||||
lon1 = tmp;
|
||||
// Swap latitudes
|
||||
tmp = lat2;
|
||||
lat2 = lat1;
|
||||
lat1 = tmp;
|
||||
// Fix boolean values
|
||||
isLastPointWithinCircle = isFirstPointWithinCircle;
|
||||
isFirstPointWithinCircle = false;
|
||||
}
|
||||
// Distance between the initial point and projection of center of
|
||||
// the circle on the current segment.
|
||||
double initialToProject = (
|
||||
(lon2 - lon1) * (ilon - lon1) * lonlat2m[0] * lonlat2m[0]
|
||||
+ (lat2 - lat1) * (ilat - lat1) * lonlat2m[1] * lonlat2m[1]
|
||||
) / totalSegmentLength;
|
||||
// Distance between the initial point and the center of the circle.
|
||||
double initialToCenter = CheapRuler.distance(ilon, ilat, lon1, lat1);
|
||||
// Half length of the segment within the circle
|
||||
double halfDistanceWithin = Math.sqrt(
|
||||
radius*radius - (
|
||||
initialToCenter*initialToCenter -
|
||||
initialToProject*initialToProject
|
||||
)
|
||||
);
|
||||
// Last point is within the circle
|
||||
if (isLastPointWithinCircle) {
|
||||
return halfDistanceWithin + (totalSegmentLength - initialToProject);
|
||||
}
|
||||
return 2 * halfDistanceWithin;
|
||||
}
|
||||
|
||||
public static OsmNodeNamed decodeNogo( String s )
|
||||
{
|
||||
OsmNodeNamed n = new OsmNodeNamed();
|
||||
int idx1 = s.indexOf( ',' );
|
||||
n.ilon = Integer.parseInt( s.substring( 0, idx1 ) );
|
||||
int idx2 = s.indexOf( ',', idx1+1 );
|
||||
n.ilat = Integer.parseInt( s.substring( idx1+1, idx2 ) );
|
||||
int idx3 = s.indexOf( ',', idx2+1 );
|
||||
if ( idx3 == -1) {
|
||||
n.name = s.substring( idx2 + 1 );
|
||||
n.nogoWeight = Double.NaN;
|
||||
} else {
|
||||
n.name = s.substring( idx2+1, idx3 );
|
||||
n.nogoWeight = Double.parseDouble( s.substring( idx3 + 1 ) );
|
||||
}
|
||||
n.isNogo = true;
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,430 +1,524 @@
|
|||
/**
|
||||
* Container for link between two Osm nodes
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
package btools.router;
|
||||
|
||||
import btools.mapaccess.OsmLink;
|
||||
import btools.mapaccess.OsmLinkHolder;
|
||||
import btools.mapaccess.OsmNode;
|
||||
import btools.mapaccess.OsmTransferNode;
|
||||
import btools.mapaccess.TurnRestriction;
|
||||
import btools.util.CheapRuler;
|
||||
|
||||
abstract class OsmPath implements OsmLinkHolder {
|
||||
/**
|
||||
* The cost of that path (a modified distance)
|
||||
*/
|
||||
public int cost = 0;
|
||||
|
||||
// the elevation assumed for that path can have a value
|
||||
// if the corresponding node has not
|
||||
public short selev;
|
||||
|
||||
public int airdistance = 0; // distance to endpos
|
||||
|
||||
protected OsmNode sourceNode;
|
||||
protected OsmNode targetNode;
|
||||
|
||||
protected OsmLink link;
|
||||
public OsmPathElement originElement;
|
||||
public OsmPathElement myElement;
|
||||
|
||||
private OsmLinkHolder nextForLink = null;
|
||||
|
||||
public int treedepth = 0;
|
||||
|
||||
// the position of the waypoint just before
|
||||
// this path position (for angle calculation)
|
||||
public int originLon;
|
||||
public int originLat;
|
||||
|
||||
// the classifier of the segment just before this paths position
|
||||
protected float lastClassifier;
|
||||
protected float lastInitialCost;
|
||||
|
||||
protected int priorityclassifier;
|
||||
|
||||
private static final int PATH_START_BIT = 1;
|
||||
private static final int CAN_LEAVE_DESTINATION_BIT = 2;
|
||||
private static final int IS_ON_DESTINATION_BIT = 4;
|
||||
private static final int HAD_DESTINATION_START_BIT = 8;
|
||||
protected int bitfield = PATH_START_BIT;
|
||||
|
||||
private boolean getBit(int mask) {
|
||||
return (bitfield & mask) != 0;
|
||||
}
|
||||
|
||||
private void setBit(int mask, boolean bit) {
|
||||
if (getBit(mask) != bit) {
|
||||
bitfield ^= mask;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean didEnterDestinationArea() {
|
||||
return !getBit(HAD_DESTINATION_START_BIT) && getBit(IS_ON_DESTINATION_BIT);
|
||||
}
|
||||
|
||||
public MessageData message;
|
||||
|
||||
public void init(OsmLink link) {
|
||||
this.link = link;
|
||||
targetNode = link.getTarget(null);
|
||||
selev = targetNode.getSElev();
|
||||
|
||||
originLon = -1;
|
||||
originLat = -1;
|
||||
}
|
||||
|
||||
public void init(OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc) {
|
||||
if (origin.myElement == null) {
|
||||
origin.myElement = OsmPathElement.create(origin);
|
||||
}
|
||||
this.originElement = origin.myElement;
|
||||
this.link = link;
|
||||
this.sourceNode = origin.targetNode;
|
||||
this.targetNode = link.getTarget(sourceNode);
|
||||
this.cost = origin.cost;
|
||||
this.lastClassifier = origin.lastClassifier;
|
||||
this.lastInitialCost = origin.lastInitialCost;
|
||||
this.bitfield = origin.bitfield;
|
||||
this.priorityclassifier = origin.priorityclassifier;
|
||||
init(origin);
|
||||
addAddionalPenalty(refTrack, detailMode, origin, link, rc);
|
||||
}
|
||||
|
||||
protected abstract void init(OsmPath orig);
|
||||
|
||||
protected abstract void resetState();
|
||||
|
||||
static int seg = 1;
|
||||
|
||||
protected void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc) {
|
||||
byte[] description = link.descriptionBitmap;
|
||||
if (description == null) { // could be a beeline path
|
||||
message = new MessageData();
|
||||
if (message != null) {
|
||||
message.turnangle = 0;
|
||||
message.time = (float) 1;
|
||||
message.energy = (float) 0;
|
||||
message.priorityclassifier = 0;
|
||||
message.classifiermask = 0;
|
||||
message.lon = targetNode.getILon();
|
||||
message.lat = targetNode.getILat();
|
||||
message.ele = Short.MIN_VALUE;
|
||||
message.linkdist = sourceNode.calcDistance(targetNode);
|
||||
message.wayKeyValues = "direct_segment=" + seg;
|
||||
seg++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
boolean recordTransferNodes = detailMode;
|
||||
|
||||
rc.nogoCost = 0.;
|
||||
|
||||
// extract the 3 positions of the first section
|
||||
int lon0 = origin.originLon;
|
||||
int lat0 = origin.originLat;
|
||||
|
||||
int lon1 = sourceNode.getILon();
|
||||
int lat1 = sourceNode.getILat();
|
||||
short ele1 = origin.selev;
|
||||
|
||||
int linkdisttotal = 0;
|
||||
|
||||
message = detailMode ? new MessageData() : null;
|
||||
|
||||
boolean isReverse = link.isReverse(sourceNode);
|
||||
|
||||
// evaluate the way tags
|
||||
rc.expctxWay.evaluate(rc.inverseDirection ^ isReverse, description);
|
||||
|
||||
|
||||
// calculate the costfactor inputs
|
||||
float costfactor = rc.expctxWay.getCostfactor();
|
||||
boolean isTrafficBackbone = cost == 0 && rc.expctxWay.getIsTrafficBackbone() > 0.f;
|
||||
int lastpriorityclassifier = priorityclassifier;
|
||||
priorityclassifier = (int) rc.expctxWay.getPriorityClassifier();
|
||||
|
||||
// *** add initial cost if the classifier changed
|
||||
float newClassifier = rc.expctxWay.getInitialClassifier();
|
||||
float newInitialCost = rc.expctxWay.getInitialcost();
|
||||
float classifierDiff = newClassifier - lastClassifier;
|
||||
if (newClassifier != 0. && lastClassifier != 0. && (classifierDiff > 0.0005 || classifierDiff < -0.0005)) {
|
||||
float initialcost = rc.inverseDirection ? lastInitialCost : newInitialCost;
|
||||
if (initialcost >= 1000000.) {
|
||||
cost = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
int iicost = (int) initialcost;
|
||||
if (message != null) {
|
||||
message.linkinitcost += iicost;
|
||||
}
|
||||
cost += iicost;
|
||||
}
|
||||
lastClassifier = newClassifier;
|
||||
lastInitialCost = newInitialCost;
|
||||
|
||||
// *** destination logic: no destination access in between
|
||||
int classifiermask = (int) rc.expctxWay.getClassifierMask();
|
||||
boolean newDestination = (classifiermask & 64) != 0;
|
||||
boolean oldDestination = getBit(IS_ON_DESTINATION_BIT);
|
||||
if (getBit(PATH_START_BIT)) {
|
||||
setBit(PATH_START_BIT, false);
|
||||
setBit(CAN_LEAVE_DESTINATION_BIT, newDestination);
|
||||
setBit(HAD_DESTINATION_START_BIT, newDestination);
|
||||
} else {
|
||||
if (oldDestination && !newDestination) {
|
||||
if (getBit(CAN_LEAVE_DESTINATION_BIT)) {
|
||||
setBit(CAN_LEAVE_DESTINATION_BIT, false);
|
||||
} else {
|
||||
cost = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
setBit(IS_ON_DESTINATION_BIT, newDestination);
|
||||
|
||||
|
||||
OsmTransferNode transferNode = link.geometry == null ? null
|
||||
: rc.geometryDecoder.decodeGeometry(link.geometry, sourceNode, targetNode, isReverse);
|
||||
|
||||
for (int nsection = 0; ; nsection++) {
|
||||
|
||||
originLon = lon1;
|
||||
originLat = lat1;
|
||||
|
||||
int lon2;
|
||||
int lat2;
|
||||
short ele2;
|
||||
short originEle2;
|
||||
|
||||
if (transferNode == null) {
|
||||
lon2 = targetNode.ilon;
|
||||
lat2 = targetNode.ilat;
|
||||
originEle2 = targetNode.selev;
|
||||
} else {
|
||||
lon2 = transferNode.ilon;
|
||||
lat2 = transferNode.ilat;
|
||||
originEle2 = transferNode.selev;
|
||||
}
|
||||
ele2 = originEle2;
|
||||
|
||||
boolean isStartpoint = lon0 == -1 && lat0 == -1;
|
||||
|
||||
// check turn restrictions (n detail mode (=final pass) no TR to not mess up voice hints)
|
||||
if (nsection == 0 && rc.considerTurnRestrictions && !detailMode && !isStartpoint) {
|
||||
if (rc.inverseDirection
|
||||
? TurnRestriction.isTurnForbidden(sourceNode.firstRestriction, lon2, lat2, lon0, lat0, rc.bikeMode || rc.footMode, rc.carMode)
|
||||
: TurnRestriction.isTurnForbidden(sourceNode.firstRestriction, lon0, lat0, lon2, lat2, rc.bikeMode || rc.footMode, rc.carMode)) {
|
||||
cost = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if recording, new MessageData for each section (needed for turn-instructions)
|
||||
if (message != null && message.wayKeyValues != null) {
|
||||
originElement.message = message;
|
||||
message = new MessageData();
|
||||
}
|
||||
|
||||
int dist = rc.calcDistance(lon1, lat1, lon2, lat2);
|
||||
|
||||
boolean stopAtEndpoint = false;
|
||||
if (rc.shortestmatch) {
|
||||
if (rc.isEndpoint) {
|
||||
stopAtEndpoint = true;
|
||||
ele2 = interpolateEle(ele1, ele2, rc.wayfraction);
|
||||
} else {
|
||||
// we just start here, reset everything
|
||||
cost = 0;
|
||||
resetState();
|
||||
lon0 = -1; // reset turncost-pipe
|
||||
lat0 = -1;
|
||||
isStartpoint = true;
|
||||
|
||||
if (recordTransferNodes) {
|
||||
if (rc.wayfraction > 0.) {
|
||||
ele1 = interpolateEle(ele1, ele2, 1. - rc.wayfraction);
|
||||
originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, ele1, null);
|
||||
} else {
|
||||
originElement = null; // prevent duplicate point
|
||||
}
|
||||
}
|
||||
|
||||
if (rc.checkPendingEndpoint()) {
|
||||
dist = rc.calcDistance(rc.ilonshortest, rc.ilatshortest, lon2, lat2);
|
||||
if (rc.shortestmatch) {
|
||||
stopAtEndpoint = true;
|
||||
ele2 = interpolateEle(ele1, ele2, rc.wayfraction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (message != null) {
|
||||
message.linkdist += dist;
|
||||
}
|
||||
linkdisttotal += dist;
|
||||
|
||||
// apply a start-direction if appropriate (by faking the origin position)
|
||||
if (isStartpoint) {
|
||||
if (rc.startDirectionValid) {
|
||||
double dir = rc.startDirection * CheapRuler.DEG_TO_RAD;
|
||||
double[] lonlat2m = CheapRuler.getLonLatToMeterScales((lon0 + lat1) >> 1);
|
||||
lon0 = lon1 - (int) (1000. * Math.sin(dir) / lonlat2m[0]);
|
||||
lat0 = lat1 - (int) (1000. * Math.cos(dir) / lonlat2m[1]);
|
||||
} else {
|
||||
lon0 = lon1 - (lon2 - lon1);
|
||||
lat0 = lat1 - (lat2 - lat1);
|
||||
}
|
||||
}
|
||||
double angle = rc.anglemeter.calcAngle(lon0, lat0, lon1, lat1, lon2, lat2);
|
||||
double cosangle = rc.anglemeter.getCosAngle();
|
||||
|
||||
// *** elevation stuff
|
||||
double delta_h = 0.;
|
||||
if (ele2 == Short.MIN_VALUE) ele2 = ele1;
|
||||
if (ele1 != Short.MIN_VALUE) {
|
||||
delta_h = (ele2 - ele1) / 4.;
|
||||
if (rc.inverseDirection) {
|
||||
delta_h = -delta_h;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double elevation = ele2 == Short.MIN_VALUE ? 100. : ele2 / 4.;
|
||||
|
||||
double sectionCost = processWaySection(rc, dist, delta_h, elevation, angle, cosangle, isStartpoint, nsection, lastpriorityclassifier);
|
||||
if ((sectionCost < 0. || costfactor > 9998. && !detailMode) || sectionCost + cost >= 2000000000.) {
|
||||
cost = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isTrafficBackbone) {
|
||||
sectionCost = 0.;
|
||||
}
|
||||
|
||||
cost += (int) sectionCost;
|
||||
|
||||
// compute kinematic
|
||||
computeKinematic(rc, dist, delta_h, detailMode);
|
||||
|
||||
if (message != null) {
|
||||
message.turnangle = (float) angle;
|
||||
message.time = (float) getTotalTime();
|
||||
message.energy = (float) getTotalEnergy();
|
||||
message.priorityclassifier = priorityclassifier;
|
||||
message.classifiermask = classifiermask;
|
||||
message.lon = lon2;
|
||||
message.lat = lat2;
|
||||
message.ele = originEle2;
|
||||
message.wayKeyValues = rc.expctxWay.getKeyValueDescription(isReverse, description);
|
||||
}
|
||||
|
||||
if (stopAtEndpoint) {
|
||||
if (recordTransferNodes) {
|
||||
originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, originEle2, originElement);
|
||||
originElement.cost = cost;
|
||||
if (message != null) {
|
||||
originElement.message = message;
|
||||
}
|
||||
}
|
||||
if (rc.nogoCost < 0) {
|
||||
cost = -1;
|
||||
} else {
|
||||
cost += rc.nogoCost;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (transferNode == null) {
|
||||
// *** penalty for being part of the reference track
|
||||
if (refTrack != null && refTrack.containsNode(targetNode) && refTrack.containsNode(sourceNode)) {
|
||||
int reftrackcost = linkdisttotal;
|
||||
cost += reftrackcost;
|
||||
}
|
||||
selev = ele2;
|
||||
break;
|
||||
}
|
||||
transferNode = transferNode.next;
|
||||
|
||||
if (recordTransferNodes) {
|
||||
originElement = OsmPathElement.create(lon2, lat2, originEle2, originElement);
|
||||
originElement.cost = cost;
|
||||
}
|
||||
lon0 = lon1;
|
||||
lat0 = lat1;
|
||||
lon1 = lon2;
|
||||
lat1 = lat2;
|
||||
ele1 = ele2;
|
||||
}
|
||||
|
||||
// check for nogo-matches (after the *actual* start of segment)
|
||||
if (rc.nogoCost < 0) {
|
||||
cost = -1;
|
||||
return;
|
||||
} else {
|
||||
cost += rc.nogoCost;
|
||||
}
|
||||
|
||||
// add target-node costs
|
||||
double targetCost = processTargetNode(rc);
|
||||
if (targetCost < 0. || targetCost + cost >= 2000000000.) {
|
||||
cost = -1;
|
||||
return;
|
||||
}
|
||||
cost += (int) targetCost;
|
||||
}
|
||||
|
||||
|
||||
public short interpolateEle(short e1, short e2, double fraction) {
|
||||
if (e1 == Short.MIN_VALUE || e2 == Short.MIN_VALUE) {
|
||||
return Short.MIN_VALUE;
|
||||
}
|
||||
return (short) (e1 * (1. - fraction) + e2 * fraction);
|
||||
}
|
||||
|
||||
protected abstract double processWaySection(RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier);
|
||||
|
||||
protected abstract double processTargetNode(RoutingContext rc);
|
||||
|
||||
protected void computeKinematic(RoutingContext rc, double dist, double delta_h, boolean detailMode) {
|
||||
}
|
||||
|
||||
public abstract int elevationCorrection();
|
||||
|
||||
public abstract boolean definitlyWorseThan(OsmPath p);
|
||||
|
||||
public OsmNode getSourceNode() {
|
||||
return sourceNode;
|
||||
}
|
||||
|
||||
public OsmNode getTargetNode() {
|
||||
return targetNode;
|
||||
}
|
||||
|
||||
public OsmLink getLink() {
|
||||
return link;
|
||||
}
|
||||
|
||||
|
||||
public void setNextForLink(OsmLinkHolder holder) {
|
||||
nextForLink = holder;
|
||||
}
|
||||
|
||||
public OsmLinkHolder getNextForLink() {
|
||||
return nextForLink;
|
||||
}
|
||||
|
||||
public double getTotalTime() {
|
||||
return 0.;
|
||||
}
|
||||
|
||||
public double getTotalEnergy() {
|
||||
return 0.;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Container for link between two Osm nodes
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
package btools.router;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import btools.mapaccess.OsmLink;
|
||||
import btools.mapaccess.OsmLinkHolder;
|
||||
import btools.mapaccess.OsmNode;
|
||||
import btools.mapaccess.OsmTransferNode;
|
||||
import btools.mapaccess.TurnRestriction;
|
||||
import btools.util.CheapRuler;
|
||||
|
||||
abstract class OsmPath implements OsmLinkHolder
|
||||
{
|
||||
/**
|
||||
* The cost of that path (a modified distance)
|
||||
*/
|
||||
public int cost = 0;
|
||||
|
||||
// the elevation assumed for that path can have a value
|
||||
// if the corresponding node has not
|
||||
public short selev;
|
||||
|
||||
public int airdistance = 0; // distance to endpos
|
||||
|
||||
protected OsmNode sourceNode;
|
||||
protected OsmNode targetNode;
|
||||
|
||||
protected OsmLink link;
|
||||
public OsmPathElement originElement;
|
||||
public OsmPathElement myElement;
|
||||
|
||||
protected float traffic;
|
||||
|
||||
private OsmLinkHolder nextForLink = null;
|
||||
|
||||
public int treedepth = 0;
|
||||
|
||||
// the position of the waypoint just before
|
||||
// this path position (for angle calculation)
|
||||
public int originLon;
|
||||
public int originLat;
|
||||
|
||||
// the classifier of the segment just before this paths position
|
||||
protected float lastClassifier;
|
||||
protected float lastInitialCost;
|
||||
|
||||
protected int priorityclassifier;
|
||||
|
||||
private static final int PATH_START_BIT = 1;
|
||||
private static final int CAN_LEAVE_DESTINATION_BIT = 2;
|
||||
private static final int IS_ON_DESTINATION_BIT = 4;
|
||||
private static final int HAD_DESTINATION_START_BIT = 8;
|
||||
protected int bitfield = PATH_START_BIT;
|
||||
|
||||
private boolean getBit( int mask )
|
||||
{
|
||||
return (bitfield & mask ) != 0;
|
||||
}
|
||||
|
||||
private void setBit( int mask, boolean bit )
|
||||
{
|
||||
if ( getBit( mask ) != bit )
|
||||
{
|
||||
bitfield ^= mask;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean didEnterDestinationArea()
|
||||
{
|
||||
return !getBit( HAD_DESTINATION_START_BIT ) && getBit( IS_ON_DESTINATION_BIT );
|
||||
}
|
||||
|
||||
public MessageData message;
|
||||
|
||||
public void unregisterUpTree( RoutingContext rc )
|
||||
{
|
||||
try
|
||||
{
|
||||
OsmPathElement pe = originElement;
|
||||
while( pe instanceof OsmPathElementWithTraffic && ((OsmPathElementWithTraffic)pe).unregister(rc) )
|
||||
{
|
||||
pe = pe.origin;
|
||||
}
|
||||
}
|
||||
catch( IOException ioe )
|
||||
{
|
||||
throw new RuntimeException( ioe );
|
||||
}
|
||||
}
|
||||
|
||||
public void registerUpTree()
|
||||
{
|
||||
if ( originElement instanceof OsmPathElementWithTraffic )
|
||||
{
|
||||
OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic)originElement;
|
||||
ot.register();
|
||||
ot.addTraffic( traffic );
|
||||
}
|
||||
}
|
||||
|
||||
public void init( OsmLink link )
|
||||
{
|
||||
this.link = link;
|
||||
targetNode = link.getTarget( null );
|
||||
selev = targetNode.getSElev();
|
||||
|
||||
originLon = -1;
|
||||
originLat = -1;
|
||||
}
|
||||
|
||||
public void init( OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc )
|
||||
{
|
||||
if ( origin.myElement == null )
|
||||
{
|
||||
origin.myElement = OsmPathElement.create( origin, rc.countTraffic );
|
||||
}
|
||||
this.originElement = origin.myElement;
|
||||
this.link = link;
|
||||
this.sourceNode = origin.targetNode;
|
||||
this.targetNode = link.getTarget( sourceNode );
|
||||
this.cost = origin.cost;
|
||||
this.lastClassifier = origin.lastClassifier;
|
||||
this.lastInitialCost = origin.lastInitialCost;
|
||||
this.bitfield = origin.bitfield;
|
||||
init( origin );
|
||||
addAddionalPenalty(refTrack, detailMode, origin, link, rc );
|
||||
}
|
||||
|
||||
protected abstract void init( OsmPath orig );
|
||||
|
||||
protected abstract void resetState();
|
||||
|
||||
|
||||
protected void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc )
|
||||
{
|
||||
byte[] description = link.descriptionBitmap;
|
||||
if ( description == null )
|
||||
{
|
||||
return; // could be a beeline path
|
||||
}
|
||||
|
||||
boolean recordTransferNodes = detailMode || rc.countTraffic;
|
||||
|
||||
rc.nogoCost = 0.;
|
||||
|
||||
// extract the 3 positions of the first section
|
||||
int lon0 = origin.originLon;
|
||||
int lat0 = origin.originLat;
|
||||
|
||||
int lon1 = sourceNode.getILon();
|
||||
int lat1 = sourceNode.getILat();
|
||||
short ele1 = origin.selev;
|
||||
|
||||
int linkdisttotal = 0;
|
||||
|
||||
message = detailMode ? new MessageData() : null;
|
||||
|
||||
boolean isReverse = link.isReverse( sourceNode );
|
||||
|
||||
// evaluate the way tags
|
||||
rc.expctxWay.evaluate( rc.inverseDirection ^ isReverse, description );
|
||||
|
||||
|
||||
// calculate the costfactor inputs
|
||||
float costfactor = rc.expctxWay.getCostfactor();
|
||||
boolean isTrafficBackbone = cost == 0 && rc.expctxWay.getIsTrafficBackbone() > 0.f;
|
||||
int lastpriorityclassifier = priorityclassifier;
|
||||
priorityclassifier = (int)rc.expctxWay.getPriorityClassifier();
|
||||
|
||||
// *** add initial cost if the classifier changed
|
||||
float newClassifier = rc.expctxWay.getInitialClassifier();
|
||||
float newInitialCost = rc.expctxWay.getInitialcost();
|
||||
float classifierDiff = newClassifier - lastClassifier;
|
||||
if ( newClassifier != 0. && lastClassifier != 0. && ( classifierDiff > 0.0005 || classifierDiff < -0.0005 ) )
|
||||
{
|
||||
float initialcost = rc.inverseDirection ? lastInitialCost : newInitialCost;
|
||||
if ( initialcost >= 1000000. )
|
||||
{
|
||||
cost = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
int iicost = (int)initialcost;
|
||||
if ( message != null )
|
||||
{
|
||||
message.linkinitcost += iicost;
|
||||
}
|
||||
cost += iicost;
|
||||
}
|
||||
lastClassifier = newClassifier;
|
||||
lastInitialCost = newInitialCost;
|
||||
|
||||
// *** destination logic: no destination access in between
|
||||
int classifiermask = (int)rc.expctxWay.getClassifierMask();
|
||||
boolean newDestination = (classifiermask & 64) != 0;
|
||||
boolean oldDestination = getBit( IS_ON_DESTINATION_BIT );
|
||||
if ( getBit( PATH_START_BIT ) )
|
||||
{
|
||||
setBit( PATH_START_BIT, false );
|
||||
setBit( CAN_LEAVE_DESTINATION_BIT, newDestination );
|
||||
setBit( HAD_DESTINATION_START_BIT, newDestination );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( oldDestination && !newDestination )
|
||||
{
|
||||
if ( getBit( CAN_LEAVE_DESTINATION_BIT ) )
|
||||
{
|
||||
setBit( CAN_LEAVE_DESTINATION_BIT, false );
|
||||
}
|
||||
else
|
||||
{
|
||||
cost = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
setBit( IS_ON_DESTINATION_BIT, newDestination );
|
||||
|
||||
|
||||
OsmTransferNode transferNode = link.geometry == null ? null
|
||||
: rc.geometryDecoder.decodeGeometry( link.geometry, sourceNode, targetNode, isReverse );
|
||||
|
||||
for(int nsection=0; ;nsection++)
|
||||
{
|
||||
|
||||
originLon = lon1;
|
||||
originLat = lat1;
|
||||
|
||||
int lon2;
|
||||
int lat2;
|
||||
short ele2;
|
||||
|
||||
if ( transferNode == null )
|
||||
{
|
||||
lon2 = targetNode.ilon;
|
||||
lat2 = targetNode.ilat;
|
||||
ele2 = targetNode.selev;
|
||||
}
|
||||
else
|
||||
{
|
||||
lon2 = transferNode.ilon;
|
||||
lat2 = transferNode.ilat;
|
||||
ele2 = transferNode.selev;
|
||||
}
|
||||
|
||||
boolean isStartpoint = lon0 == -1 && lat0 == -1;
|
||||
|
||||
// check turn restrictions (n detail mode (=final pass) no TR to not mess up voice hints)
|
||||
if ( nsection == 0 && rc.considerTurnRestrictions && !detailMode&& !isStartpoint )
|
||||
{
|
||||
if ( rc.inverseDirection
|
||||
? TurnRestriction.isTurnForbidden( sourceNode.firstRestriction, lon2, lat2, lon0, lat0, rc.bikeMode, rc.carMode )
|
||||
: TurnRestriction.isTurnForbidden( sourceNode.firstRestriction, lon0, lat0, lon2, lat2, rc.bikeMode, rc.carMode ) )
|
||||
{
|
||||
cost = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if recording, new MessageData for each section (needed for turn-instructions)
|
||||
if ( message != null && message.wayKeyValues != null )
|
||||
{
|
||||
originElement.message = message;
|
||||
message = new MessageData();
|
||||
}
|
||||
|
||||
int dist = rc.calcDistance( lon1, lat1, lon2, lat2 );
|
||||
|
||||
boolean stopAtEndpoint = false;
|
||||
if ( rc.shortestmatch )
|
||||
{
|
||||
if ( rc.isEndpoint )
|
||||
{
|
||||
stopAtEndpoint = true;
|
||||
ele2 = interpolateEle( ele1, ele2, rc.wayfraction );
|
||||
}
|
||||
else
|
||||
{
|
||||
// we just start here, reset everything
|
||||
cost = 0;
|
||||
resetState();
|
||||
lon0 = -1; // reset turncost-pipe
|
||||
lat0 = -1;
|
||||
isStartpoint = true;
|
||||
|
||||
if ( recordTransferNodes )
|
||||
{
|
||||
if ( rc.wayfraction > 0. )
|
||||
{
|
||||
ele1 = interpolateEle( ele1, ele2, 1. - rc.wayfraction );
|
||||
originElement = OsmPathElement.create( rc.ilonshortest, rc.ilatshortest, ele1, null, rc.countTraffic );
|
||||
}
|
||||
else
|
||||
{
|
||||
originElement = null; // prevent duplicate point
|
||||
}
|
||||
}
|
||||
|
||||
if ( rc.checkPendingEndpoint() )
|
||||
{
|
||||
dist = rc.calcDistance( rc.ilonshortest, rc.ilatshortest, lon2, lat2 );
|
||||
if ( rc.shortestmatch )
|
||||
{
|
||||
stopAtEndpoint = true;
|
||||
ele2 = interpolateEle( ele1, ele2, rc.wayfraction );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( message != null )
|
||||
{
|
||||
message.linkdist += dist;
|
||||
}
|
||||
linkdisttotal += dist;
|
||||
|
||||
// apply a start-direction if appropriate (by faking the origin position)
|
||||
if ( isStartpoint )
|
||||
{
|
||||
if ( rc.startDirectionValid )
|
||||
{
|
||||
double dir = rc.startDirection.intValue() * CheapRuler.DEG_TO_RAD;
|
||||
double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (lon0 + lat1) >> 1 );
|
||||
lon0 = lon1 - (int) ( 1000. * Math.sin( dir ) / lonlat2m[0] );
|
||||
lat0 = lat1 - (int) ( 1000. * Math.cos( dir ) / lonlat2m[1] );
|
||||
}
|
||||
else
|
||||
{
|
||||
lon0 = lon1 - (lon2-lon1);
|
||||
lat0 = lat1 - (lat2-lat1);
|
||||
}
|
||||
}
|
||||
double angle = rc.anglemeter.calcAngle( lon0, lat0, lon1, lat1, lon2, lat2 );
|
||||
double cosangle = rc.anglemeter.getCosAngle();
|
||||
|
||||
// *** elevation stuff
|
||||
double delta_h = 0.;
|
||||
if ( ele2 == Short.MIN_VALUE ) ele2 = ele1;
|
||||
if ( ele1 != Short.MIN_VALUE )
|
||||
{
|
||||
delta_h = (ele2 - ele1)/4.;
|
||||
if ( rc.inverseDirection )
|
||||
{
|
||||
delta_h = -delta_h;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
double elevation = ele2 == Short.MIN_VALUE ? 100. : ele2/4.;
|
||||
|
||||
double sectionCost = processWaySection( rc, dist, delta_h, elevation, angle, cosangle, isStartpoint, nsection, lastpriorityclassifier );
|
||||
if ( ( sectionCost < 0. || costfactor > 9998. && !detailMode ) || sectionCost + cost >= 2000000000. )
|
||||
{
|
||||
cost = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isTrafficBackbone )
|
||||
{
|
||||
sectionCost = 0.;
|
||||
}
|
||||
|
||||
cost += (int)sectionCost;
|
||||
|
||||
// calculate traffic
|
||||
if ( rc.countTraffic )
|
||||
{
|
||||
int minDist = (int)rc.trafficSourceMinDist;
|
||||
int cost2 = cost < minDist ? minDist : cost;
|
||||
traffic += dist*rc.expctxWay.getTrafficSourceDensity()*Math.pow(cost2/10000.f,rc.trafficSourceExponent);
|
||||
}
|
||||
|
||||
// compute kinematic
|
||||
computeKinematic( rc, dist, delta_h, detailMode );
|
||||
|
||||
if ( message != null )
|
||||
{
|
||||
message.turnangle = (float)angle;
|
||||
message.time = (float)getTotalTime();
|
||||
message.energy = (float)getTotalEnergy();
|
||||
message.priorityclassifier = priorityclassifier;
|
||||
message.classifiermask = classifiermask;
|
||||
message.lon = lon2;
|
||||
message.lat = lat2;
|
||||
message.ele = ele2;
|
||||
message.wayKeyValues = rc.expctxWay.getKeyValueDescription( isReverse, description );
|
||||
}
|
||||
|
||||
if ( stopAtEndpoint )
|
||||
{
|
||||
if ( recordTransferNodes )
|
||||
{
|
||||
originElement = OsmPathElement.create( rc.ilonshortest, rc.ilatshortest, ele2, originElement, rc.countTraffic );
|
||||
originElement.cost = cost;
|
||||
if ( message != null )
|
||||
{
|
||||
originElement.message = message;
|
||||
}
|
||||
}
|
||||
if ( rc.nogoCost < 0)
|
||||
{
|
||||
cost = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
cost += rc.nogoCost;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ( transferNode == null )
|
||||
{
|
||||
// *** penalty for being part of the reference track
|
||||
if ( refTrack != null && refTrack.containsNode( targetNode ) && refTrack.containsNode( sourceNode ) )
|
||||
{
|
||||
int reftrackcost = linkdisttotal;
|
||||
cost += reftrackcost;
|
||||
}
|
||||
selev = ele2;
|
||||
break;
|
||||
}
|
||||
transferNode = transferNode.next;
|
||||
|
||||
if ( recordTransferNodes )
|
||||
{
|
||||
originElement = OsmPathElement.create( lon2, lat2, ele2, originElement, rc.countTraffic );
|
||||
originElement.cost = cost;
|
||||
originElement.addTraffic( traffic );
|
||||
traffic = 0;
|
||||
}
|
||||
lon0 = lon1;
|
||||
lat0 = lat1;
|
||||
lon1 = lon2;
|
||||
lat1 = lat2;
|
||||
ele1 = ele2;
|
||||
}
|
||||
|
||||
// check for nogo-matches (after the *actual* start of segment)
|
||||
if ( rc.nogoCost < 0)
|
||||
{
|
||||
cost = -1;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
cost += rc.nogoCost;
|
||||
}
|
||||
|
||||
// add target-node costs
|
||||
double targetCost = processTargetNode( rc );
|
||||
if ( targetCost < 0. || targetCost + cost >= 2000000000. )
|
||||
{
|
||||
cost = -1;
|
||||
return;
|
||||
}
|
||||
cost += (int)targetCost;
|
||||
}
|
||||
|
||||
|
||||
public short interpolateEle( short e1, short e2, double fraction )
|
||||
{
|
||||
if ( e1 == Short.MIN_VALUE || e2 == Short.MIN_VALUE )
|
||||
{
|
||||
return Short.MIN_VALUE;
|
||||
}
|
||||
return (short)( e1*(1.-fraction) + e2*fraction );
|
||||
}
|
||||
|
||||
protected abstract double processWaySection( RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier );
|
||||
|
||||
protected abstract double processTargetNode( RoutingContext rc );
|
||||
|
||||
protected void computeKinematic( RoutingContext rc, double dist, double delta_h, boolean detailMode )
|
||||
{
|
||||
}
|
||||
|
||||
public abstract int elevationCorrection( RoutingContext rc );
|
||||
|
||||
public abstract boolean definitlyWorseThan( OsmPath p, RoutingContext rc );
|
||||
|
||||
public OsmNode getSourceNode()
|
||||
{
|
||||
return sourceNode;
|
||||
}
|
||||
|
||||
public OsmNode getTargetNode()
|
||||
{
|
||||
return targetNode;
|
||||
}
|
||||
|
||||
public OsmLink getLink()
|
||||
{
|
||||
return link;
|
||||
}
|
||||
|
||||
|
||||
public void setNextForLink( OsmLinkHolder holder )
|
||||
{
|
||||
nextForLink = holder;
|
||||
}
|
||||
|
||||
public OsmLinkHolder getNextForLink()
|
||||
{
|
||||
return nextForLink;
|
||||
}
|
||||
|
||||
public double getTotalTime()
|
||||
{
|
||||
return 0.;
|
||||
}
|
||||
|
||||
public double getTotalEnergy()
|
||||
{
|
||||
return 0.;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,127 +1,136 @@
|
|||
package btools.router;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
import btools.mapaccess.OsmNode;
|
||||
import btools.mapaccess.OsmPos;
|
||||
import btools.util.CheapRuler;
|
||||
|
||||
/**
|
||||
* Container for link between two Osm nodes
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
|
||||
public class OsmPathElement implements OsmPos {
|
||||
private int ilat; // latitude
|
||||
private int ilon; // longitude
|
||||
private short selev; // longitude
|
||||
|
||||
public MessageData message = null; // description
|
||||
|
||||
public int cost;
|
||||
|
||||
// interface OsmPos
|
||||
public final int getILat() {
|
||||
return ilat;
|
||||
}
|
||||
|
||||
public final int getILon() {
|
||||
return ilon;
|
||||
}
|
||||
|
||||
public final short getSElev() {
|
||||
return selev;
|
||||
}
|
||||
|
||||
public final void setSElev(short s) {
|
||||
selev = s;
|
||||
}
|
||||
|
||||
public final double getElev() {
|
||||
return selev / 4.;
|
||||
}
|
||||
|
||||
public final float getTime() {
|
||||
return message == null ? 0.f : message.time;
|
||||
}
|
||||
|
||||
public final void setTime(float t) {
|
||||
if (message != null) {
|
||||
message.time = t;
|
||||
}
|
||||
}
|
||||
|
||||
public final float getEnergy() {
|
||||
return message == null ? 0.f : message.energy;
|
||||
}
|
||||
|
||||
public final void setEnergy(float e) {
|
||||
if (message != null) {
|
||||
message.energy = e;
|
||||
}
|
||||
}
|
||||
|
||||
public final void setAngle(float e) {
|
||||
if (message != null) {
|
||||
message.turnangle = e;
|
||||
}
|
||||
}
|
||||
|
||||
public final long getIdFromPos() {
|
||||
return ((long) ilon) << 32 | ilat;
|
||||
}
|
||||
|
||||
public final int calcDistance(OsmPos p) {
|
||||
return (int) Math.max(1.0, Math.round(CheapRuler.distance(ilon, ilat, p.getILon(), p.getILat())));
|
||||
}
|
||||
|
||||
public OsmPathElement origin;
|
||||
|
||||
// construct a path element from a path
|
||||
public static final OsmPathElement create(OsmPath path) {
|
||||
OsmNode n = path.getTargetNode();
|
||||
OsmPathElement pe = create(n.getILon(), n.getILat(), n.getSElev(), path.originElement);
|
||||
pe.cost = path.cost;
|
||||
pe.message = path.message;
|
||||
return pe;
|
||||
}
|
||||
|
||||
public static final OsmPathElement create(int ilon, int ilat, short selev, OsmPathElement origin) {
|
||||
OsmPathElement pe = new OsmPathElement();
|
||||
pe.ilon = ilon;
|
||||
pe.ilat = ilat;
|
||||
pe.selev = selev;
|
||||
pe.origin = origin;
|
||||
return pe;
|
||||
}
|
||||
|
||||
protected OsmPathElement() {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return ilon + "_" + ilat;
|
||||
}
|
||||
|
||||
public boolean positionEquals(OsmPathElement e) {
|
||||
return this.ilat == e.ilat && this.ilon == e.ilon;
|
||||
}
|
||||
|
||||
public void writeToStream(DataOutput dos) throws IOException {
|
||||
dos.writeInt(ilat);
|
||||
dos.writeInt(ilon);
|
||||
dos.writeShort(selev);
|
||||
dos.writeInt(cost);
|
||||
}
|
||||
|
||||
public static OsmPathElement readFromStream(DataInput dis) throws IOException {
|
||||
OsmPathElement pe = new OsmPathElement();
|
||||
pe.ilat = dis.readInt();
|
||||
pe.ilon = dis.readInt();
|
||||
pe.selev = dis.readShort();
|
||||
pe.cost = dis.readInt();
|
||||
return pe;
|
||||
}
|
||||
}
|
||||
package btools.router;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
import btools.mapaccess.OsmNode;
|
||||
import btools.mapaccess.OsmPos;
|
||||
import btools.util.CheapRuler;
|
||||
|
||||
/**
|
||||
* Container for link between two Osm nodes
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
|
||||
public class OsmPathElement implements OsmPos
|
||||
{
|
||||
private int ilat; // latitude
|
||||
private int ilon; // longitude
|
||||
private short selev; // longitude
|
||||
|
||||
public MessageData message = null; // description
|
||||
|
||||
public int cost;
|
||||
|
||||
// interface OsmPos
|
||||
public final int getILat()
|
||||
{
|
||||
return ilat;
|
||||
}
|
||||
|
||||
public final int getILon()
|
||||
{
|
||||
return ilon;
|
||||
}
|
||||
|
||||
public final short getSElev()
|
||||
{
|
||||
return selev;
|
||||
}
|
||||
|
||||
public final double getElev()
|
||||
{
|
||||
return selev / 4.;
|
||||
}
|
||||
|
||||
public final float getTime()
|
||||
{
|
||||
return message == null ? 0.f : message.time;
|
||||
}
|
||||
|
||||
public final void setTime( float t )
|
||||
{
|
||||
if ( message != null )
|
||||
{
|
||||
message.time = t;
|
||||
}
|
||||
}
|
||||
|
||||
public final float getEnergy()
|
||||
{
|
||||
return message == null ? 0.f : message.energy;
|
||||
}
|
||||
|
||||
public final void setEnergy( float e )
|
||||
{
|
||||
if ( message != null )
|
||||
{
|
||||
message.energy = e;
|
||||
}
|
||||
}
|
||||
|
||||
public final long getIdFromPos()
|
||||
{
|
||||
return ((long)ilon)<<32 | ilat;
|
||||
}
|
||||
|
||||
public final int calcDistance( OsmPos p )
|
||||
{
|
||||
return (int)(CheapRuler.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0 );
|
||||
}
|
||||
|
||||
public OsmPathElement origin;
|
||||
|
||||
// construct a path element from a path
|
||||
public static final OsmPathElement create( OsmPath path, boolean countTraffic )
|
||||
{
|
||||
OsmNode n = path.getTargetNode();
|
||||
OsmPathElement pe = create( n.getILon(), n.getILat(), path.selev, path.originElement, countTraffic );
|
||||
pe.cost = path.cost;
|
||||
pe.message = path.message;
|
||||
return pe;
|
||||
}
|
||||
|
||||
public static final OsmPathElement create( int ilon, int ilat, short selev, OsmPathElement origin, boolean countTraffic )
|
||||
{
|
||||
OsmPathElement pe = countTraffic ? new OsmPathElementWithTraffic() : new OsmPathElement();
|
||||
pe.ilon = ilon;
|
||||
pe.ilat = ilat;
|
||||
pe.selev = selev;
|
||||
pe.origin = origin;
|
||||
return pe;
|
||||
}
|
||||
|
||||
protected OsmPathElement()
|
||||
{
|
||||
}
|
||||
|
||||
public void addTraffic( float traffic )
|
||||
{
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return ilon + "_" + ilat;
|
||||
}
|
||||
|
||||
public void writeToStream( DataOutput dos ) throws IOException
|
||||
{
|
||||
dos.writeInt( ilat );
|
||||
dos.writeInt( ilon );
|
||||
dos.writeShort( selev );
|
||||
dos.writeInt( cost );
|
||||
}
|
||||
|
||||
public static OsmPathElement readFromStream( DataInput dis ) throws IOException
|
||||
{
|
||||
OsmPathElement pe = new OsmPathElement();
|
||||
pe.ilat = dis.readInt();
|
||||
pe.ilon = dis.readInt();
|
||||
pe.selev = dis.readShort();
|
||||
pe.cost = dis.readInt();
|
||||
return pe;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -11,10 +11,11 @@ import btools.expressions.BExpressionContextNode;
|
|||
import btools.expressions.BExpressionContextWay;
|
||||
|
||||
|
||||
abstract class OsmPathModel {
|
||||
abstract class OsmPathModel
|
||||
{
|
||||
public abstract OsmPrePath createPrePath();
|
||||
|
||||
public abstract OsmPath createPath();
|
||||
|
||||
public abstract void init(BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String, String> keyValues);
|
||||
public abstract void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String,String> keyValues );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,20 +7,23 @@ package btools.router;
|
|||
|
||||
import btools.mapaccess.OsmLink;
|
||||
import btools.mapaccess.OsmNode;
|
||||
import btools.mapaccess.OsmTransferNode;
|
||||
|
||||
public abstract class OsmPrePath {
|
||||
public abstract class OsmPrePath
|
||||
{
|
||||
protected OsmNode sourceNode;
|
||||
protected OsmNode targetNode;
|
||||
protected OsmLink link;
|
||||
|
||||
|
||||
public OsmPrePath next;
|
||||
|
||||
public void init(OsmPath origin, OsmLink link, RoutingContext rc) {
|
||||
public void init( OsmPath origin, OsmLink link, RoutingContext rc )
|
||||
{
|
||||
this.link = link;
|
||||
this.sourceNode = origin.getTargetNode();
|
||||
this.targetNode = link.getTarget(sourceNode);
|
||||
initPrePath(origin, rc);
|
||||
this.targetNode = link.getTarget( sourceNode );
|
||||
initPrePath(origin, rc );
|
||||
}
|
||||
|
||||
protected abstract void initPrePath(OsmPath origin, RoutingContext rc);
|
||||
protected abstract void initPrePath(OsmPath origin, RoutingContext rc );
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -11,125 +11,146 @@ import btools.expressions.BExpressionContextNode;
|
|||
import btools.expressions.BExpressionContextWay;
|
||||
import btools.expressions.BExpressionMetaData;
|
||||
|
||||
public final class ProfileCache {
|
||||
|
||||
public final class ProfileCache
|
||||
{
|
||||
|
||||
private static File lastLookupFile;
|
||||
private static long lastLookupTimestamp;
|
||||
|
||||
private BExpressionContextWay expctxWay;
|
||||
private BExpressionContextNode expctxNode;
|
||||
private File lastProfileFile;
|
||||
private long lastProfileTimestamp;
|
||||
private long lastProfileTimestamp;
|
||||
private boolean profilesBusy;
|
||||
private long lastUseTime;
|
||||
|
||||
|
||||
private static ProfileCache[] apc = new ProfileCache[1];
|
||||
private static boolean debug = Boolean.getBoolean("debugProfileCache");
|
||||
private static boolean debug = Boolean.getBoolean( "debugProfileCache" );
|
||||
|
||||
public static synchronized void setSize(int size) {
|
||||
public static synchronized void setSize( int size )
|
||||
{
|
||||
apc = new ProfileCache[size];
|
||||
}
|
||||
|
||||
public static synchronized boolean parseProfile(RoutingContext rc) {
|
||||
String profileBaseDir = System.getProperty("profileBaseDir");
|
||||
File profileDir;
|
||||
File profileFile;
|
||||
if (profileBaseDir == null) {
|
||||
profileDir = new File(rc.localFunction).getParentFile();
|
||||
profileFile = new File(rc.localFunction);
|
||||
} else {
|
||||
profileDir = new File(profileBaseDir);
|
||||
profileFile = new File(profileDir, rc.localFunction + ".brf");
|
||||
}
|
||||
|
||||
rc.profileTimestamp = profileFile.lastModified() + rc.getKeyValueChecksum() << 24;
|
||||
File lookupFile = new File(profileDir, "lookups.dat");
|
||||
|
||||
// invalidate cache at lookup-table update
|
||||
if (!(lookupFile.equals(lastLookupFile) && lookupFile.lastModified() == lastLookupTimestamp)) {
|
||||
if (lastLookupFile != null) {
|
||||
System.out.println("******** invalidating profile-cache after lookup-file update ******** ");
|
||||
public static synchronized boolean parseProfile( RoutingContext rc )
|
||||
{
|
||||
String profileBaseDir = System.getProperty( "profileBaseDir" );
|
||||
File profileDir;
|
||||
File profileFile;
|
||||
if ( profileBaseDir == null )
|
||||
{
|
||||
profileDir = new File( rc.localFunction ).getParentFile();
|
||||
profileFile = new File( rc.localFunction ) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
profileDir = new File( profileBaseDir );
|
||||
profileFile = new File( profileDir, rc.localFunction + ".brf" ) ;
|
||||
}
|
||||
apc = new ProfileCache[apc.length];
|
||||
lastLookupFile = lookupFile;
|
||||
lastLookupTimestamp = lookupFile.lastModified();
|
||||
}
|
||||
|
||||
ProfileCache lru = null;
|
||||
int unusedSlot = -1;
|
||||
rc.profileTimestamp = profileFile.lastModified() + rc.getKeyValueChecksum()<<24;
|
||||
File lookupFile = new File( profileDir, "lookups.dat" );
|
||||
|
||||
// invalidate cache at lookup-table update
|
||||
if ( !(lookupFile.equals( lastLookupFile ) && lookupFile.lastModified() == lastLookupTimestamp ) )
|
||||
{
|
||||
if ( lastLookupFile != null )
|
||||
{
|
||||
System.out.println( "******** invalidating profile-cache after lookup-file update ******** " );
|
||||
}
|
||||
apc = new ProfileCache[apc.length];
|
||||
lastLookupFile = lookupFile;
|
||||
lastLookupTimestamp = lookupFile.lastModified();
|
||||
}
|
||||
|
||||
ProfileCache lru = null;
|
||||
int unusedSlot =-1;
|
||||
|
||||
// check for re-use
|
||||
for (int i = 0; i < apc.length; i++) {
|
||||
ProfileCache pc = apc[i];
|
||||
|
||||
if (pc != null) {
|
||||
if ((!pc.profilesBusy) && profileFile.equals(pc.lastProfileFile)) {
|
||||
if (rc.profileTimestamp == pc.lastProfileTimestamp) {
|
||||
rc.expctxWay = pc.expctxWay;
|
||||
rc.expctxNode = pc.expctxNode;
|
||||
rc.readGlobalConfig();
|
||||
pc.profilesBusy = true;
|
||||
return true;
|
||||
// check for re-use
|
||||
for( int i=0; i<apc.length; i++)
|
||||
{
|
||||
ProfileCache pc = apc[i];
|
||||
|
||||
if ( pc != null )
|
||||
{
|
||||
if ( (!pc.profilesBusy) && profileFile.equals( pc.lastProfileFile ) )
|
||||
{
|
||||
if ( rc.profileTimestamp == pc.lastProfileTimestamp )
|
||||
{
|
||||
rc.expctxWay = pc.expctxWay;
|
||||
rc.expctxNode = pc.expctxNode;
|
||||
rc.readGlobalConfig();
|
||||
pc.profilesBusy = true;
|
||||
return true;
|
||||
}
|
||||
lru = pc; // name-match but timestamp-mismatch -> we overide this one
|
||||
unusedSlot = -1;
|
||||
break;
|
||||
}
|
||||
if ( lru == null || lru.lastUseTime > pc.lastUseTime )
|
||||
{
|
||||
lru = pc;
|
||||
}
|
||||
lru = pc; // name-match but timestamp-mismatch -> we overide this one
|
||||
unusedSlot = -1;
|
||||
break;
|
||||
}
|
||||
if (lru == null || lru.lastUseTime > pc.lastUseTime) {
|
||||
lru = pc;
|
||||
else if ( unusedSlot < 0 )
|
||||
{
|
||||
unusedSlot = i;
|
||||
}
|
||||
} else if (unusedSlot < 0) {
|
||||
unusedSlot = i;
|
||||
}
|
||||
}
|
||||
|
||||
BExpressionMetaData meta = new BExpressionMetaData();
|
||||
|
||||
rc.expctxWay = new BExpressionContextWay( rc.memoryclass * 512, meta );
|
||||
rc.expctxNode = new BExpressionContextNode( 0, meta );
|
||||
rc.expctxNode.setForeignContext( rc.expctxWay );
|
||||
|
||||
meta.readMetaData( new File( profileDir, "lookups.dat" ) );
|
||||
|
||||
BExpressionMetaData meta = new BExpressionMetaData();
|
||||
rc.expctxWay.parseFile( profileFile, "global" );
|
||||
rc.expctxNode.parseFile( profileFile, "global" );
|
||||
|
||||
rc.expctxWay = new BExpressionContextWay(rc.memoryclass * 512, meta);
|
||||
rc.expctxNode = new BExpressionContextNode(0, meta);
|
||||
rc.expctxNode.setForeignContext(rc.expctxWay);
|
||||
|
||||
meta.readMetaData(new File(profileDir, "lookups.dat"));
|
||||
|
||||
rc.expctxWay.parseFile(profileFile, "global", rc.keyValues);
|
||||
rc.expctxNode.parseFile(profileFile, "global", rc.keyValues);
|
||||
|
||||
rc.readGlobalConfig();
|
||||
|
||||
if (rc.processUnusedTags) {
|
||||
rc.expctxWay.setAllTagsUsed();
|
||||
}
|
||||
|
||||
if (lru == null || unusedSlot >= 0) {
|
||||
lru = new ProfileCache();
|
||||
if (unusedSlot >= 0) {
|
||||
apc[unusedSlot] = lru;
|
||||
if (debug)
|
||||
System.out.println("******* adding new profile at idx=" + unusedSlot + " for " + profileFile);
|
||||
rc.readGlobalConfig();
|
||||
|
||||
if ( rc.processUnusedTags )
|
||||
{
|
||||
rc.expctxWay.setAllTagsUsed();
|
||||
}
|
||||
}
|
||||
|
||||
if (lru.lastProfileFile != null) {
|
||||
if (debug)
|
||||
System.out.println("******* replacing profile of age " + ((System.currentTimeMillis() - lru.lastUseTime) / 1000L) + " sec " + lru.lastProfileFile + "->" + profileFile);
|
||||
}
|
||||
if ( lru == null || unusedSlot >= 0 )
|
||||
{
|
||||
lru = new ProfileCache();
|
||||
if ( unusedSlot >= 0 )
|
||||
{
|
||||
apc[unusedSlot] = lru;
|
||||
if ( debug ) System.out.println( "******* adding new profile at idx=" + unusedSlot + " for " + profileFile );
|
||||
}
|
||||
}
|
||||
|
||||
lru.lastProfileTimestamp = rc.profileTimestamp;
|
||||
lru.lastProfileFile = profileFile;
|
||||
lru.expctxWay = rc.expctxWay;
|
||||
lru.expctxNode = rc.expctxNode;
|
||||
lru.profilesBusy = true;
|
||||
lru.lastUseTime = System.currentTimeMillis();
|
||||
return false;
|
||||
if ( lru.lastProfileFile != null )
|
||||
{
|
||||
if ( debug ) System.out.println( "******* replacing profile of age " + ((System.currentTimeMillis()-lru.lastUseTime)/1000L) + " sec " + lru.lastProfileFile + "->" + profileFile );
|
||||
}
|
||||
|
||||
lru.lastProfileTimestamp = rc.profileTimestamp;
|
||||
lru.lastProfileFile = profileFile;
|
||||
lru.expctxWay = rc.expctxWay;
|
||||
lru.expctxNode = rc.expctxNode;
|
||||
lru.profilesBusy = true;
|
||||
lru.lastUseTime = System.currentTimeMillis();
|
||||
return false;
|
||||
}
|
||||
|
||||
public static synchronized void releaseProfile(RoutingContext rc) {
|
||||
for (int i = 0; i < apc.length; i++) {
|
||||
public static synchronized void releaseProfile( RoutingContext rc )
|
||||
{
|
||||
for( int i=0; i<apc.length; i++)
|
||||
{
|
||||
ProfileCache pc = apc[i];
|
||||
|
||||
if (pc != null) {
|
||||
|
||||
if ( pc != null )
|
||||
{
|
||||
// only the thread that holds the cached instance can release it
|
||||
if (rc.expctxWay == pc.expctxWay && rc.expctxNode == pc.expctxNode) {
|
||||
if ( rc.expctxWay == pc.expctxWay && rc.expctxNode == pc.expctxNode )
|
||||
{
|
||||
pc.profilesBusy = false;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -9,33 +9,41 @@ import java.io.File;
|
|||
|
||||
import btools.mapaccess.StorageConfigHelper;
|
||||
|
||||
public final class RoutingHelper {
|
||||
public static File getAdditionalMaptoolDir(File segmentDir) {
|
||||
return StorageConfigHelper.getAdditionalMaptoolDir(segmentDir);
|
||||
}
|
||||
|
||||
public static File getSecondarySegmentDir(File segmentDir) {
|
||||
return StorageConfigHelper.getSecondarySegmentDir(segmentDir);
|
||||
}
|
||||
|
||||
|
||||
public static boolean hasDirectoryAnyDatafiles(File segmentDir) {
|
||||
if (hasAnyDatafiles(segmentDir)) {
|
||||
return true;
|
||||
public final class RoutingHelper
|
||||
{
|
||||
public static File getAdditionalMaptoolDir( File segmentDir )
|
||||
{
|
||||
return StorageConfigHelper.getAdditionalMaptoolDir(segmentDir);
|
||||
}
|
||||
// check secondary, too
|
||||
File secondary = StorageConfigHelper.getSecondarySegmentDir(segmentDir);
|
||||
if (secondary != null) {
|
||||
return hasAnyDatafiles(secondary);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean hasAnyDatafiles(File dir) {
|
||||
String[] fileNames = dir.list();
|
||||
for (String fileName : fileNames) {
|
||||
if (fileName.endsWith(".rd5")) return true;
|
||||
public static File getSecondarySegmentDir( File segmentDir )
|
||||
{
|
||||
return StorageConfigHelper.getSecondarySegmentDir(segmentDir);
|
||||
}
|
||||
|
||||
|
||||
public static boolean hasDirectoryAnyDatafiles( File segmentDir )
|
||||
{
|
||||
if ( hasAnyDatafiles( segmentDir ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// check secondary, too
|
||||
File secondary = StorageConfigHelper.getSecondarySegmentDir( segmentDir );
|
||||
if ( secondary != null )
|
||||
{
|
||||
return hasAnyDatafiles( secondary );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean hasAnyDatafiles( File dir )
|
||||
{
|
||||
String[] fileNames = dir.list();
|
||||
for( String fileName : fileNames )
|
||||
{
|
||||
if ( fileName.endsWith( ".rd5" ) ) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
package btools.router;
|
||||
|
||||
public class RoutingIslandException extends RuntimeException {
|
||||
public class RoutingIslandException extends RuntimeException
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -8,80 +8,83 @@ package btools.router;
|
|||
import btools.mapaccess.OsmNode;
|
||||
|
||||
|
||||
public final class SearchBoundary {
|
||||
public final class SearchBoundary
|
||||
{
|
||||
|
||||
private int minlon0;
|
||||
private int minlat0;
|
||||
private int maxlon0;
|
||||
private int maxlat0;
|
||||
private int minlon0;
|
||||
private int minlat0;
|
||||
private int maxlon0;
|
||||
private int maxlat0;
|
||||
|
||||
private int minlon;
|
||||
private int minlat;
|
||||
private int maxlon;
|
||||
private int maxlat;
|
||||
private int radius;
|
||||
private OsmNode p;
|
||||
private int minlon;
|
||||
private int minlat;
|
||||
private int maxlon;
|
||||
private int maxlat;
|
||||
private int radius;
|
||||
private OsmNode p;
|
||||
|
||||
int direction;
|
||||
int direction;
|
||||
|
||||
/**
|
||||
* @param radius Search radius in meters.
|
||||
*/
|
||||
public SearchBoundary(OsmNode n, int radius, int direction) {
|
||||
this.radius = radius;
|
||||
this.direction = direction;
|
||||
/**
|
||||
* @param radius Search radius in meters.
|
||||
*/
|
||||
public SearchBoundary( OsmNode n, int radius, int direction )
|
||||
{
|
||||
this.radius = radius;
|
||||
this.direction = direction;
|
||||
|
||||
p = new OsmNode(n.ilon, n.ilat);
|
||||
p = new OsmNode( n.ilon, n.ilat );
|
||||
|
||||
int lon = (n.ilon / 5000000) * 5000000;
|
||||
int lat = (n.ilat / 5000000) * 5000000;
|
||||
int lon = (n.ilon / 5000000 ) * 5000000;
|
||||
int lat = (n.ilat / 5000000 ) * 5000000;
|
||||
|
||||
minlon0 = lon - 5000000;
|
||||
minlat0 = lat - 5000000;
|
||||
maxlon0 = lon + 10000000;
|
||||
maxlat0 = lat + 10000000;
|
||||
minlon0 = lon - 5000000;
|
||||
minlat0 = lat - 5000000;
|
||||
maxlon0 = lon + 10000000;
|
||||
maxlat0 = lat + 10000000;
|
||||
|
||||
minlon = lon - 1000000;
|
||||
minlat = lat - 1000000;
|
||||
maxlon = lon + 6000000;
|
||||
maxlat = lat + 6000000;
|
||||
}
|
||||
|
||||
public static String getFileName(OsmNode n) {
|
||||
int lon = (n.ilon / 5000000) * 5000000;
|
||||
int lat = (n.ilat / 5000000) * 5000000;
|
||||
|
||||
int dlon = lon / 1000000 - 180;
|
||||
int dlat = lat / 1000000 - 90;
|
||||
|
||||
String slon = dlon < 0 ? "W" + (-dlon) : "E" + dlon;
|
||||
String slat = dlat < 0 ? "S" + (-dlat) : "N" + dlat;
|
||||
return slon + "_" + slat + ".trf";
|
||||
}
|
||||
|
||||
public boolean isInBoundary(OsmNode n, int cost) {
|
||||
if (radius > 0) {
|
||||
return n.calcDistance(p) < radius;
|
||||
minlon = lon - 1000000;
|
||||
minlat = lat - 1000000;
|
||||
maxlon = lon + 6000000;
|
||||
maxlat = lat + 6000000;
|
||||
}
|
||||
if (cost == 0) {
|
||||
return n.ilon > minlon0 && n.ilon < maxlon0 && n.ilat > minlat0 && n.ilat < maxlat0;
|
||||
}
|
||||
return n.ilon > minlon && n.ilon < maxlon && n.ilat > minlat && n.ilat < maxlat;
|
||||
}
|
||||
|
||||
public int getBoundaryDistance(OsmNode n) {
|
||||
switch (direction) {
|
||||
case 0:
|
||||
return n.calcDistance(new OsmNode(n.ilon, minlat));
|
||||
case 1:
|
||||
return n.calcDistance(new OsmNode(minlon, n.ilat));
|
||||
case 2:
|
||||
return n.calcDistance(new OsmNode(n.ilon, maxlat));
|
||||
case 3:
|
||||
return n.calcDistance(new OsmNode(maxlon, n.ilat));
|
||||
default:
|
||||
throw new IllegalArgumentException("undefined direction: " + direction);
|
||||
public static String getFileName( OsmNode n )
|
||||
{
|
||||
int lon = (n.ilon / 5000000 ) * 5000000;
|
||||
int lat = (n.ilat / 5000000 ) * 5000000;
|
||||
|
||||
int dlon = lon / 1000000 -180;
|
||||
int dlat = lat / 1000000 - 90;
|
||||
|
||||
String slon = dlon < 0 ? "W" + (-dlon) : "E" + dlon;
|
||||
String slat = dlat < 0 ? "S" + (-dlat) : "N" + dlat;
|
||||
return slon + "_" + slat + ".trf";
|
||||
}
|
||||
|
||||
public boolean isInBoundary( OsmNode n, int cost )
|
||||
{
|
||||
if ( radius > 0 )
|
||||
{
|
||||
return n.calcDistance( p ) < radius;
|
||||
}
|
||||
if ( cost == 0 )
|
||||
{
|
||||
return n.ilon > minlon0 && n.ilon < maxlon0 && n.ilat > minlat0 && n.ilat < maxlat0;
|
||||
}
|
||||
return n.ilon > minlon && n.ilon < maxlon && n.ilat > minlat && n.ilat < maxlat;
|
||||
}
|
||||
|
||||
public int getBoundaryDistance( OsmNode n )
|
||||
{
|
||||
switch( direction )
|
||||
{
|
||||
case 0: return n.calcDistance( new OsmNode( n.ilon, minlat ) );
|
||||
case 1: return n.calcDistance( new OsmNode( minlon, n.ilat ) );
|
||||
case 2: return n.calcDistance( new OsmNode( n.ilon, maxlat ) );
|
||||
case 3: return n.calcDistance( new OsmNode( maxlon, n.ilat ) );
|
||||
default: throw new IllegalArgumentException( "undefined direction: "+ direction );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,12 +12,15 @@ import btools.expressions.BExpressionContextNode;
|
|||
import btools.expressions.BExpressionContextWay;
|
||||
|
||||
|
||||
final class StdModel extends OsmPathModel {
|
||||
public OsmPrePath createPrePath() {
|
||||
final class StdModel extends OsmPathModel
|
||||
{
|
||||
public OsmPrePath createPrePath()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public OsmPath createPath() {
|
||||
public OsmPath createPath()
|
||||
{
|
||||
return new StdPath();
|
||||
}
|
||||
|
||||
|
|
@ -26,10 +29,11 @@ final class StdModel extends OsmPathModel {
|
|||
|
||||
|
||||
@Override
|
||||
public void init(BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String, String> keyValues) {
|
||||
public void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String,String> keyValues )
|
||||
{
|
||||
ctxWay = expctxWay;
|
||||
ctxNode = expctxNode;
|
||||
|
||||
|
||||
BExpressionContext expctxGlobal = expctxWay; // just one of them...
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@
|
|||
*/
|
||||
package btools.router;
|
||||
|
||||
final class StdPath extends OsmPath {
|
||||
import btools.util.FastMath;
|
||||
|
||||
final class StdPath extends OsmPath
|
||||
{
|
||||
/**
|
||||
* The elevation-hysteresis-buffer (0-10 m)
|
||||
*/
|
||||
|
|
@ -16,15 +19,13 @@ final class StdPath extends OsmPath {
|
|||
private float totalEnergy; // total route energy (Joule)
|
||||
private float elevation_buffer; // just another elevation buffer (for travel time)
|
||||
|
||||
private int uphillcostdiv;
|
||||
private int downhillcostdiv;
|
||||
|
||||
// Gravitational constant, g
|
||||
private static final double GRAVITY = 9.81; // in meters per second^(-2)
|
||||
|
||||
@Override
|
||||
public void init(OsmPath orig) {
|
||||
StdPath origin = (StdPath) orig;
|
||||
public void init( OsmPath orig )
|
||||
{
|
||||
StdPath origin = (StdPath)orig;
|
||||
this.ehbd = origin.ehbd;
|
||||
this.ehbu = origin.ehbu;
|
||||
this.totalTime = origin.totalTime;
|
||||
|
|
@ -33,63 +34,34 @@ final class StdPath extends OsmPath {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void resetState() {
|
||||
protected void resetState()
|
||||
{
|
||||
ehbd = 0;
|
||||
ehbu = 0;
|
||||
totalTime = 0.f;
|
||||
totalEnergy = 0.f;
|
||||
uphillcostdiv = 0;
|
||||
downhillcostdiv = 0;
|
||||
elevation_buffer = 0.f;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double processWaySection(RoutingContext rc, double distance, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier) {
|
||||
protected double processWaySection( RoutingContext rc, double distance, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier )
|
||||
{
|
||||
// calculate the costfactor inputs
|
||||
float turncostbase = rc.expctxWay.getTurncost();
|
||||
float uphillcutoff = rc.expctxWay.getUphillcutoff() * 10000;
|
||||
float downhillcutoff = rc.expctxWay.getDownhillcutoff() * 10000;
|
||||
float uphillmaxslope = rc.expctxWay.getUphillmaxslope() * 10000;
|
||||
float downhillmaxslope = rc.expctxWay.getDownhillmaxslope() * 10000;
|
||||
float cfup = rc.expctxWay.getUphillCostfactor();
|
||||
float cfdown = rc.expctxWay.getDownhillCostfactor();
|
||||
float cf = rc.expctxWay.getCostfactor();
|
||||
cfup = cfup == 0.f ? cf : cfup;
|
||||
cfdown = cfdown == 0.f ? cf : cfdown;
|
||||
|
||||
downhillcostdiv = (int) rc.expctxWay.getDownhillcost();
|
||||
if (downhillcostdiv > 0) {
|
||||
downhillcostdiv = 1000000 / downhillcostdiv;
|
||||
}
|
||||
|
||||
int downhillmaxslopecostdiv = (int) rc.expctxWay.getDownhillmaxslopecost();
|
||||
if (downhillmaxslopecostdiv > 0) {
|
||||
downhillmaxslopecostdiv = 1000000 / downhillmaxslopecostdiv;
|
||||
} else {
|
||||
// if not given, use legacy behavior
|
||||
downhillmaxslopecostdiv = downhillcostdiv;
|
||||
}
|
||||
|
||||
uphillcostdiv = (int) rc.expctxWay.getUphillcost();
|
||||
if (uphillcostdiv > 0) {
|
||||
uphillcostdiv = 1000000 / uphillcostdiv;
|
||||
}
|
||||
|
||||
int uphillmaxslopecostdiv = (int) rc.expctxWay.getUphillmaxslopecost();
|
||||
if (uphillmaxslopecostdiv > 0) {
|
||||
uphillmaxslopecostdiv = 1000000 / uphillmaxslopecostdiv;
|
||||
} else {
|
||||
// if not given, use legacy behavior
|
||||
uphillmaxslopecostdiv = uphillcostdiv;
|
||||
}
|
||||
|
||||
int dist = (int) distance; // legacy arithmetics needs int
|
||||
int dist = (int)distance; // legacy arithmetics needs int
|
||||
|
||||
// penalty for turning angle
|
||||
int turncost = (int) ((1. - cosangle) * turncostbase + 0.2); // e.g. turncost=90 -> 90 degree = 90m penalty
|
||||
if (message != null) {
|
||||
int turncost = (int)((1.-cosangle) * turncostbase + 0.2 ); // e.g. turncost=90 -> 90 degree = 90m penalty
|
||||
if ( message != null )
|
||||
{
|
||||
message.linkturncost += turncost;
|
||||
message.turnangle = (float) angle;
|
||||
message.turnangle = (float)angle;
|
||||
}
|
||||
|
||||
double sectionCost = turncost;
|
||||
|
|
@ -98,78 +70,81 @@ final class StdPath extends OsmPath {
|
|||
// only the part of the descend that does not fit into the elevation-hysteresis-buffers
|
||||
// leads to an immediate penalty
|
||||
|
||||
int delta_h_micros = (int) (1000000. * delta_h);
|
||||
ehbd += -delta_h_micros - dist * downhillcutoff;
|
||||
ehbu += delta_h_micros - dist * uphillcutoff;
|
||||
int delta_h_micros = (int)(1000000. * delta_h);
|
||||
ehbd += -delta_h_micros - dist * rc.downhillcutoff;
|
||||
ehbu += delta_h_micros - dist * rc.uphillcutoff;
|
||||
|
||||
float downweight = 0.f;
|
||||
if (ehbd > rc.elevationpenaltybuffer) {
|
||||
if ( ehbd > rc.elevationpenaltybuffer )
|
||||
{
|
||||
downweight = 1.f;
|
||||
|
||||
int excess = ehbd - rc.elevationpenaltybuffer;
|
||||
int reduce = dist * rc.elevationbufferreduce;
|
||||
if (reduce > excess) {
|
||||
downweight = ((float) excess) / reduce;
|
||||
if ( reduce > excess )
|
||||
{
|
||||
downweight = ((float)excess)/reduce;
|
||||
reduce = excess;
|
||||
}
|
||||
excess = ehbd - rc.elevationmaxbuffer;
|
||||
if (reduce < excess) {
|
||||
if ( reduce < excess )
|
||||
{
|
||||
reduce = excess;
|
||||
}
|
||||
ehbd -= reduce;
|
||||
float elevationCost = 0.f;
|
||||
if (downhillcostdiv > 0) {
|
||||
elevationCost += Math.min(reduce, dist * downhillmaxslope) / downhillcostdiv;
|
||||
}
|
||||
if (downhillmaxslopecostdiv > 0) {
|
||||
elevationCost += Math.max(0, reduce - dist * downhillmaxslope) / downhillmaxslopecostdiv;
|
||||
}
|
||||
if (elevationCost > 0) {
|
||||
if ( rc.downhillcostdiv > 0 )
|
||||
{
|
||||
int elevationCost = reduce/rc.downhillcostdiv;
|
||||
sectionCost += elevationCost;
|
||||
if (message != null) {
|
||||
if ( message != null )
|
||||
{
|
||||
message.linkelevationcost += elevationCost;
|
||||
}
|
||||
}
|
||||
} else if (ehbd < 0) {
|
||||
}
|
||||
else if ( ehbd < 0 )
|
||||
{
|
||||
ehbd = 0;
|
||||
}
|
||||
|
||||
float upweight = 0.f;
|
||||
if (ehbu > rc.elevationpenaltybuffer) {
|
||||
if ( ehbu > rc.elevationpenaltybuffer )
|
||||
{
|
||||
upweight = 1.f;
|
||||
|
||||
int excess = ehbu - rc.elevationpenaltybuffer;
|
||||
int reduce = dist * rc.elevationbufferreduce;
|
||||
if (reduce > excess) {
|
||||
upweight = ((float) excess) / reduce;
|
||||
if ( reduce > excess )
|
||||
{
|
||||
upweight = ((float)excess)/reduce;
|
||||
reduce = excess;
|
||||
}
|
||||
excess = ehbu - rc.elevationmaxbuffer;
|
||||
if (reduce < excess) {
|
||||
if ( reduce < excess )
|
||||
{
|
||||
reduce = excess;
|
||||
}
|
||||
ehbu -= reduce;
|
||||
float elevationCost = 0.f;
|
||||
if (uphillcostdiv > 0) {
|
||||
elevationCost += Math.min(reduce, dist * uphillmaxslope) / uphillcostdiv;
|
||||
}
|
||||
if (uphillmaxslopecostdiv > 0) {
|
||||
elevationCost += Math.max(0, reduce - dist * uphillmaxslope) / uphillmaxslopecostdiv;
|
||||
}
|
||||
if (elevationCost > 0) {
|
||||
if ( rc.uphillcostdiv > 0 )
|
||||
{
|
||||
int elevationCost = reduce/rc.uphillcostdiv;
|
||||
sectionCost += elevationCost;
|
||||
if (message != null) {
|
||||
if ( message != null )
|
||||
{
|
||||
message.linkelevationcost += elevationCost;
|
||||
}
|
||||
}
|
||||
} else if (ehbu < 0) {
|
||||
}
|
||||
else if ( ehbu < 0 )
|
||||
{
|
||||
ehbu = 0;
|
||||
}
|
||||
|
||||
// get the effective costfactor (slope dependent)
|
||||
float costfactor = cfup * upweight + cf * (1.f - upweight - downweight) + cfdown * downweight;
|
||||
float costfactor = cfup*upweight + cf*(1.f - upweight - downweight) + cfdown*downweight;
|
||||
|
||||
if (message != null) {
|
||||
if ( message != null )
|
||||
{
|
||||
message.costfactor = costfactor;
|
||||
}
|
||||
|
||||
|
|
@ -179,18 +154,22 @@ final class StdPath extends OsmPath {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected double processTargetNode(RoutingContext rc) {
|
||||
protected double processTargetNode( RoutingContext rc )
|
||||
{
|
||||
// finally add node-costs for target node
|
||||
if (targetNode.nodeDescription != null) {
|
||||
if ( targetNode.nodeDescription != null )
|
||||
{
|
||||
boolean nodeAccessGranted = rc.expctxWay.getNodeAccessGranted() != 0.;
|
||||
rc.expctxNode.evaluate(nodeAccessGranted, targetNode.nodeDescription);
|
||||
rc.expctxNode.evaluate( nodeAccessGranted , targetNode.nodeDescription );
|
||||
float initialcost = rc.expctxNode.getInitialcost();
|
||||
if (initialcost >= 1000000.) {
|
||||
if ( initialcost >= 1000000. )
|
||||
{
|
||||
return -1.;
|
||||
}
|
||||
if (message != null) {
|
||||
message.linknodecost += (int) initialcost;
|
||||
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription(nodeAccessGranted, targetNode.nodeDescription);
|
||||
if ( message != null )
|
||||
{
|
||||
message.linknodecost += (int)initialcost;
|
||||
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription( nodeAccessGranted, targetNode.nodeDescription );
|
||||
}
|
||||
return initialcost;
|
||||
}
|
||||
|
|
@ -198,88 +177,118 @@ final class StdPath extends OsmPath {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int elevationCorrection() {
|
||||
return (downhillcostdiv > 0 ? ehbd / downhillcostdiv : 0)
|
||||
+ (uphillcostdiv > 0 ? ehbu / uphillcostdiv : 0);
|
||||
public int elevationCorrection( RoutingContext rc )
|
||||
{
|
||||
return ( rc.downhillcostdiv > 0 ? ehbd/rc.downhillcostdiv : 0 )
|
||||
+ ( rc.uphillcostdiv > 0 ? ehbu/rc.uphillcostdiv : 0 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean definitlyWorseThan(OsmPath path) {
|
||||
StdPath p = (StdPath) path;
|
||||
public boolean definitlyWorseThan( OsmPath path, RoutingContext rc )
|
||||
{
|
||||
StdPath p = (StdPath)path;
|
||||
|
||||
int c = p.cost;
|
||||
if (p.downhillcostdiv > 0) {
|
||||
int delta = p.ehbd / p.downhillcostdiv - (downhillcostdiv > 0 ? ehbd / downhillcostdiv : 0);
|
||||
if (delta > 0) c += delta;
|
||||
}
|
||||
if (p.uphillcostdiv > 0) {
|
||||
int delta = p.ehbu / p.uphillcostdiv - (uphillcostdiv > 0 ? ehbu / uphillcostdiv : 0);
|
||||
if (delta > 0) c += delta;
|
||||
}
|
||||
int c = p.cost;
|
||||
if ( rc.downhillcostdiv > 0 )
|
||||
{
|
||||
int delta = p.ehbd - ehbd;
|
||||
if ( delta > 0 ) c += delta/rc.downhillcostdiv;
|
||||
}
|
||||
if ( rc.uphillcostdiv > 0 )
|
||||
{
|
||||
int delta = p.ehbu - ehbu;
|
||||
if ( delta > 0 ) c += delta/rc.uphillcostdiv;
|
||||
}
|
||||
|
||||
return cost > c;
|
||||
return cost > c;
|
||||
}
|
||||
|
||||
private double calcIncline(double dist) {
|
||||
private double calcIncline( double dist )
|
||||
{
|
||||
double min_delta = 3.;
|
||||
double shift = 0.;
|
||||
if (elevation_buffer > min_delta) {
|
||||
double shift;
|
||||
if ( elevation_buffer > min_delta )
|
||||
{
|
||||
shift = -min_delta;
|
||||
} else if (elevation_buffer < -min_delta) {
|
||||
shift = min_delta;
|
||||
}
|
||||
double decayFactor = Math.exp(-dist / 100.);
|
||||
float new_elevation_buffer = (float) ((elevation_buffer + shift) * decayFactor - shift);
|
||||
double incline = (elevation_buffer - new_elevation_buffer) / dist;
|
||||
else if ( elevation_buffer < min_delta )
|
||||
{
|
||||
shift = -min_delta;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.;
|
||||
}
|
||||
double decayFactor = FastMath.exp( - dist / 100. );
|
||||
float new_elevation_buffer = (float)( (elevation_buffer+shift) * decayFactor - shift);
|
||||
double incline = ( elevation_buffer - new_elevation_buffer ) / dist;
|
||||
elevation_buffer = new_elevation_buffer;
|
||||
return incline;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void computeKinematic(RoutingContext rc, double dist, double delta_h, boolean detailMode) {
|
||||
if (!detailMode) {
|
||||
protected void computeKinematic( RoutingContext rc, double dist, double delta_h, boolean detailMode )
|
||||
{
|
||||
if ( !detailMode )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// compute incline
|
||||
elevation_buffer += delta_h;
|
||||
double incline = calcIncline(dist);
|
||||
double incline = calcIncline( dist );
|
||||
|
||||
double maxSpeed = rc.maxSpeed;
|
||||
double speedLimit = rc.expctxWay.getMaxspeed() / 3.6f;
|
||||
if (speedLimit > 0) {
|
||||
maxSpeed = Math.min(maxSpeed, speedLimit);
|
||||
double wayMaxspeed;
|
||||
|
||||
wayMaxspeed = rc.expctxWay.getMaxspeed() / 3.6f;
|
||||
if (wayMaxspeed == 0)
|
||||
{
|
||||
wayMaxspeed = rc.maxSpeed;
|
||||
}
|
||||
|
||||
double speed = maxSpeed; // Travel speed
|
||||
double f_roll = rc.totalMass * GRAVITY * (rc.defaultC_r + incline);
|
||||
if (rc.footMode) {
|
||||
wayMaxspeed = Math.min(wayMaxspeed,rc.maxSpeed);
|
||||
|
||||
double speed; // Travel speed
|
||||
double f_roll = rc.totalMass * GRAVITY * ( rc.defaultC_r + incline );
|
||||
if (rc.footMode || rc.expctxWay.getCostfactor() > 4.9 )
|
||||
{
|
||||
// Use Tobler's hiking function for walking sections
|
||||
speed = rc.maxSpeed * Math.exp(-3.5 * Math.abs(incline + 0.05));
|
||||
} else if (rc.bikeMode) {
|
||||
speed = solveCubic(rc.S_C_x, f_roll, rc.bikerPower);
|
||||
speed = Math.min(speed, maxSpeed);
|
||||
speed = rc.maxSpeed * 3.6;
|
||||
speed = (speed * FastMath.exp(-3.5 * Math.abs( incline + 0.05))) / 3.6;
|
||||
}
|
||||
float dt = (float) (dist / speed);
|
||||
else if (rc.bikeMode)
|
||||
{
|
||||
speed = solveCubic( rc.S_C_x, f_roll, rc.bikerPower );
|
||||
speed = Math.min(speed, wayMaxspeed);
|
||||
}
|
||||
else // all other
|
||||
{
|
||||
speed = wayMaxspeed;
|
||||
}
|
||||
float dt = (float) ( dist / speed );
|
||||
totalTime += dt;
|
||||
// Calc energy assuming biking (no good model yet for hiking)
|
||||
// (Count only positive, negative would mean breaking to enforce maxspeed)
|
||||
double energy = dist * (rc.S_C_x * speed * speed + f_roll);
|
||||
if (energy > 0.) {
|
||||
double energy = dist*(rc.S_C_x*speed*speed + f_roll);
|
||||
if ( energy > 0. )
|
||||
{
|
||||
totalEnergy += energy;
|
||||
}
|
||||
}
|
||||
|
||||
private static double solveCubic(double a, double c, double d) {
|
||||
private static double solveCubic( double a, double c, double d )
|
||||
{
|
||||
// Solves a * v^3 + c * v = d with a Newton method
|
||||
// to get the speed v for the section.
|
||||
|
||||
double v = 8.;
|
||||
boolean findingStartvalue = true;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
double y = (a * v * v + c) * v - d;
|
||||
if (y < .1) {
|
||||
if (findingStartvalue) {
|
||||
for ( int i = 0; i < 10; i++ )
|
||||
{
|
||||
double y = ( a * v * v + c ) * v - d;
|
||||
if ( y < .1 )
|
||||
{
|
||||
if ( findingStartvalue )
|
||||
{
|
||||
v *= 2.;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -293,12 +302,14 @@ final class StdPath extends OsmPath {
|
|||
}
|
||||
|
||||
@Override
|
||||
public double getTotalTime() {
|
||||
public double getTotalTime()
|
||||
{
|
||||
return totalTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getTotalEnergy() {
|
||||
public double getTotalEnergy()
|
||||
{
|
||||
return totalEnergy;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -9,7 +9,8 @@ package btools.router;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class VoiceHint {
|
||||
public class VoiceHint
|
||||
{
|
||||
static final int C = 1; // continue (go straight)
|
||||
static final int TL = 2; // turn left
|
||||
static final int TSLL = 3; // turn slightly left
|
||||
|
|
@ -19,13 +20,11 @@ public class VoiceHint {
|
|||
static final int TSHR = 7; // turn sharply right
|
||||
static final int KL = 8; // keep left
|
||||
static final int KR = 9; // keep right
|
||||
static final int TLU = 10; // U-turn
|
||||
static final int TU = 10; // U-turn
|
||||
static final int TRU = 11; // Right U-turn
|
||||
static final int OFFR = 12; // Off route
|
||||
static final int RNDB = 13; // Roundabout
|
||||
static final int RNLB = 14; // Roundabout left
|
||||
static final int TU = 15; // 180 degree u-turn
|
||||
static final int BL = 16; // Beeline routing
|
||||
|
||||
int ilon;
|
||||
int ilat;
|
||||
|
|
@ -37,566 +36,280 @@ public class VoiceHint {
|
|||
double distanceToNext;
|
||||
int indexInTrack;
|
||||
|
||||
public float getTime() {
|
||||
public float getTime()
|
||||
{
|
||||
return oldWay == null ? 0.f : oldWay.time;
|
||||
}
|
||||
|
||||
float angle = Float.MAX_VALUE;
|
||||
float angle;
|
||||
boolean turnAngleConsumed;
|
||||
boolean needsRealTurn;
|
||||
int maxBadPrio = -1;
|
||||
|
||||
int roundaboutExit;
|
||||
|
||||
boolean isRoundabout() {
|
||||
boolean isRoundabout()
|
||||
{
|
||||
return roundaboutExit != 0;
|
||||
}
|
||||
|
||||
public void addBadWay(MessageData badWay) {
|
||||
if (badWay == null) {
|
||||
|
||||
public void addBadWay( MessageData badWay )
|
||||
{
|
||||
if ( badWay == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (badWays == null) {
|
||||
badWays = new ArrayList<>();
|
||||
if ( badWays == null )
|
||||
{
|
||||
badWays = new ArrayList<MessageData>();
|
||||
}
|
||||
badWays.add(badWay);
|
||||
badWays.add( badWay );
|
||||
}
|
||||
|
||||
public int getJsonCommandIndex() {
|
||||
switch (cmd) {
|
||||
case TLU:
|
||||
return 10;
|
||||
case TU:
|
||||
return 15;
|
||||
case TSHL:
|
||||
return 4;
|
||||
case TL:
|
||||
return 2;
|
||||
case TSLL:
|
||||
return 3;
|
||||
case KL:
|
||||
return 8;
|
||||
case C:
|
||||
return 1;
|
||||
case KR:
|
||||
return 9;
|
||||
case TSLR:
|
||||
return 6;
|
||||
case TR:
|
||||
return 5;
|
||||
case TSHR:
|
||||
return 7;
|
||||
case TRU:
|
||||
return 11;
|
||||
case RNDB:
|
||||
return 13;
|
||||
case RNLB:
|
||||
return 14;
|
||||
case BL:
|
||||
return 16;
|
||||
case OFFR:
|
||||
return 12;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown command: " + cmd);
|
||||
}
|
||||
public int getCommand()
|
||||
{
|
||||
return cmd;
|
||||
}
|
||||
|
||||
public int getExitNumber() {
|
||||
public int getExitNumber()
|
||||
{
|
||||
return roundaboutExit;
|
||||
}
|
||||
|
||||
/*
|
||||
* used by comment style, osmand style
|
||||
*/
|
||||
public String getCommandString() {
|
||||
switch (cmd) {
|
||||
case TLU:
|
||||
return "TU"; // should be changed to TLU when osmand uses new voice hint constants
|
||||
case TU:
|
||||
return "TU";
|
||||
case TSHL:
|
||||
return "TSHL";
|
||||
case TL:
|
||||
return "TL";
|
||||
case TSLL:
|
||||
return "TSLL";
|
||||
case KL:
|
||||
return "KL";
|
||||
case C:
|
||||
return "C";
|
||||
case KR:
|
||||
return "KR";
|
||||
case TSLR:
|
||||
return "TSLR";
|
||||
case TR:
|
||||
return "TR";
|
||||
case TSHR:
|
||||
return "TSHR";
|
||||
case TRU:
|
||||
return "TRU";
|
||||
case RNDB:
|
||||
return "RNDB" + roundaboutExit;
|
||||
case RNLB:
|
||||
return "RNLB" + (-roundaboutExit);
|
||||
case BL:
|
||||
return "BL";
|
||||
case OFFR:
|
||||
return "OFFR";
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown command: " + cmd);
|
||||
public String getCommandString()
|
||||
{
|
||||
switch ( cmd )
|
||||
{
|
||||
case TU : return "TU";
|
||||
case TSHL : return "TSHL";
|
||||
case TL : return "TL";
|
||||
case TSLL : return "TSLL";
|
||||
case KL : return "KL";
|
||||
case C : return "C";
|
||||
case KR : return "KR";
|
||||
case TSLR : return "TSLR";
|
||||
case TR : return "TR";
|
||||
case TSHR : return "TSHR";
|
||||
case TRU : return "TRU";
|
||||
case RNDB : return "RNDB" + roundaboutExit;
|
||||
case RNLB : return "RNLB" + (-roundaboutExit);
|
||||
default : throw new IllegalArgumentException( "unknown command: " + cmd );
|
||||
}
|
||||
}
|
||||
|
||||
public String getSymbolString()
|
||||
{
|
||||
switch ( cmd )
|
||||
{
|
||||
case TU : return "TU";
|
||||
case TSHL : return "TSHL";
|
||||
case TL : return "Left";
|
||||
case TSLL : return "TSLL";
|
||||
case KL : return "TSLL"; // ?
|
||||
case C : return "Straight";
|
||||
case KR : return "TSLR"; // ?
|
||||
case TSLR : return "TSLR";
|
||||
case TR : return "Right";
|
||||
case TSHR : return "TSHR";
|
||||
case TRU : return "TU";
|
||||
case RNDB : return "RNDB" + roundaboutExit;
|
||||
case RNLB : return "RNLB" + (-roundaboutExit);
|
||||
default : throw new IllegalArgumentException( "unknown command: " + cmd );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* used by trkpt/sym style
|
||||
*/
|
||||
public String getCommandString(int c) {
|
||||
switch (c) {
|
||||
case TLU:
|
||||
return "TLU";
|
||||
case TU:
|
||||
return "TU";
|
||||
case TSHL:
|
||||
return "TSHL";
|
||||
case TL:
|
||||
return "TL";
|
||||
case TSLL:
|
||||
return "TSLL";
|
||||
case KL:
|
||||
return "KL";
|
||||
case C:
|
||||
return "C";
|
||||
case KR:
|
||||
return "KR";
|
||||
case TSLR:
|
||||
return "TSLR";
|
||||
case TR:
|
||||
return "TR";
|
||||
case TSHR:
|
||||
return "TSHR";
|
||||
case TRU:
|
||||
return "TRU";
|
||||
case RNDB:
|
||||
return "RNDB" + roundaboutExit;
|
||||
case RNLB:
|
||||
return "RNLB" + (-roundaboutExit);
|
||||
case BL:
|
||||
return "BL";
|
||||
case OFFR:
|
||||
return "OFFR";
|
||||
default:
|
||||
return "unknown command: " + c;
|
||||
public String getMessageString()
|
||||
{
|
||||
switch ( cmd )
|
||||
{
|
||||
case TU : return "u-turn";
|
||||
case TSHL : return "sharp left";
|
||||
case TL : return "left";
|
||||
case TSLL : return "slight left";
|
||||
case KL : return "keep left";
|
||||
case C : return "straight";
|
||||
case KR : return "keep right";
|
||||
case TSLR : return "slight right";
|
||||
case TR : return "right";
|
||||
case TSHR : return "sharp right";
|
||||
case TRU : return "u-turn";
|
||||
case RNDB : return "Take exit " + roundaboutExit;
|
||||
case RNLB : return "Take exit " + (-roundaboutExit);
|
||||
default : throw new IllegalArgumentException( "unknown command: " + cmd );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* used by gpsies style
|
||||
*/
|
||||
public String getSymbolString() {
|
||||
switch (cmd) {
|
||||
case TLU:
|
||||
return "TU";
|
||||
case TU:
|
||||
return "TU";
|
||||
case TSHL:
|
||||
return "TSHL";
|
||||
case TL:
|
||||
return "Left";
|
||||
case TSLL:
|
||||
return "TSLL";
|
||||
case KL:
|
||||
return "TSLL"; // ?
|
||||
case C:
|
||||
return "Straight";
|
||||
case KR:
|
||||
return "TSLR"; // ?
|
||||
case TSLR:
|
||||
return "TSLR";
|
||||
case TR:
|
||||
return "Right";
|
||||
case TSHR:
|
||||
return "TSHR";
|
||||
case TRU:
|
||||
return "TU";
|
||||
case RNDB:
|
||||
return "RNDB" + roundaboutExit;
|
||||
case RNLB:
|
||||
return "RNLB" + (-roundaboutExit);
|
||||
case BL:
|
||||
return "BL";
|
||||
case OFFR:
|
||||
return "OFFR";
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown command: " + cmd);
|
||||
public int getLocusAction()
|
||||
{
|
||||
switch ( cmd )
|
||||
{
|
||||
case TU : return 13;
|
||||
case TSHL : return 5;
|
||||
case TL : return 4;
|
||||
case TSLL : return 3;
|
||||
case KL : return 9; // ?
|
||||
case C : return 1;
|
||||
case KR : return 10; // ?
|
||||
case TSLR : return 6;
|
||||
case TR : return 7;
|
||||
case TSHR : return 8;
|
||||
case TRU : return 14;
|
||||
case RNDB : return 26 + roundaboutExit;
|
||||
case RNLB : return 26 - roundaboutExit;
|
||||
default : throw new IllegalArgumentException( "unknown command: " + cmd );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* used by new locus trkpt style
|
||||
*/
|
||||
public String getLocusSymbolString() {
|
||||
switch (cmd) {
|
||||
case TLU:
|
||||
return "u-turn_left";
|
||||
case TU:
|
||||
return "u-turn";
|
||||
case TSHL:
|
||||
return "left_sharp";
|
||||
case TL:
|
||||
return "left";
|
||||
case TSLL:
|
||||
return "left_slight";
|
||||
case KL:
|
||||
return "stay_left"; // ?
|
||||
case C:
|
||||
return "straight";
|
||||
case KR:
|
||||
return "stay_right"; // ?
|
||||
case TSLR:
|
||||
return "right_slight";
|
||||
case TR:
|
||||
return "right";
|
||||
case TSHR:
|
||||
return "right_sharp";
|
||||
case TRU:
|
||||
return "u-turn_right";
|
||||
case RNDB:
|
||||
return "roundabout_e" + roundaboutExit;
|
||||
case RNLB:
|
||||
return "roundabout_e" + (-roundaboutExit);
|
||||
case BL:
|
||||
return "beeline";
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown command: " + cmd);
|
||||
}
|
||||
|
||||
public int getOruxAction()
|
||||
{
|
||||
switch ( cmd )
|
||||
{
|
||||
case TU : return 1003;
|
||||
case TSHL : return 1019;
|
||||
case TL : return 1000;
|
||||
case TSLL : return 1017;
|
||||
case KL : return 1015; // ?
|
||||
case C : return 1002;
|
||||
case KR : return 1014; // ?
|
||||
case TSLR : return 1016;
|
||||
case TR : return 1001;
|
||||
case TSHR : return 1018;
|
||||
case TRU : return 1003;
|
||||
case RNDB : return 1008 + roundaboutExit;
|
||||
case RNLB : return 1008 + roundaboutExit;
|
||||
default : throw new IllegalArgumentException( "unknown command: " + cmd );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* used by osmand style
|
||||
*/
|
||||
public String getMessageString() {
|
||||
switch (cmd) {
|
||||
case TLU:
|
||||
return "u-turn"; // should be changed to u-turn-left when osmand uses new voice hint constants
|
||||
case TU:
|
||||
return "u-turn";
|
||||
case TSHL:
|
||||
return "sharp left";
|
||||
case TL:
|
||||
return "left";
|
||||
case TSLL:
|
||||
return "slight left";
|
||||
case KL:
|
||||
return "keep left";
|
||||
case C:
|
||||
return "straight";
|
||||
case KR:
|
||||
return "keep right";
|
||||
case TSLR:
|
||||
return "slight right";
|
||||
case TR:
|
||||
return "right";
|
||||
case TSHR:
|
||||
return "sharp right";
|
||||
case TRU:
|
||||
return "u-turn"; // should be changed to u-turn-right when osmand uses new voice hint constants
|
||||
case RNDB:
|
||||
return "Take exit " + roundaboutExit;
|
||||
case RNLB:
|
||||
return "Take exit " + (-roundaboutExit);
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown command: " + cmd);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* used by old locus style
|
||||
*/
|
||||
public int getLocusAction() {
|
||||
switch (cmd) {
|
||||
case TLU:
|
||||
return 13;
|
||||
case TU:
|
||||
return 12;
|
||||
case TSHL:
|
||||
return 5;
|
||||
case TL:
|
||||
return 4;
|
||||
case TSLL:
|
||||
return 3;
|
||||
case KL:
|
||||
return 9; // ?
|
||||
case C:
|
||||
return 1;
|
||||
case KR:
|
||||
return 10; // ?
|
||||
case TSLR:
|
||||
return 6;
|
||||
case TR:
|
||||
return 7;
|
||||
case TSHR:
|
||||
return 8;
|
||||
case TRU:
|
||||
return 14;
|
||||
case RNDB:
|
||||
return 26 + roundaboutExit;
|
||||
case RNLB:
|
||||
return 26 - roundaboutExit;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown command: " + cmd);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* used by orux style
|
||||
*/
|
||||
public int getOruxAction() {
|
||||
switch (cmd) {
|
||||
case TLU:
|
||||
return 1003;
|
||||
case TU:
|
||||
return 1003;
|
||||
case TSHL:
|
||||
return 1019;
|
||||
case TL:
|
||||
return 1000;
|
||||
case TSLL:
|
||||
return 1017;
|
||||
case KL:
|
||||
return 1015; // ?
|
||||
case C:
|
||||
return 1002;
|
||||
case KR:
|
||||
return 1014; // ?
|
||||
case TSLR:
|
||||
return 1016;
|
||||
case TR:
|
||||
return 1001;
|
||||
case TSHR:
|
||||
return 1018;
|
||||
case TRU:
|
||||
return 1003;
|
||||
case RNDB:
|
||||
return 1008 + roundaboutExit;
|
||||
case RNLB:
|
||||
return 1008 + roundaboutExit;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown command: " + cmd);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* used by cruiser, equivalent to getCommandString() - osmand style - when osmand changes the voice hint constants
|
||||
*/
|
||||
public String getCruiserCommandString() {
|
||||
switch (cmd) {
|
||||
case TLU:
|
||||
return "TLU";
|
||||
case TU:
|
||||
return "TU";
|
||||
case TSHL:
|
||||
return "TSHL";
|
||||
case TL:
|
||||
return "TL";
|
||||
case TSLL:
|
||||
return "TSLL";
|
||||
case KL:
|
||||
return "KL";
|
||||
case C:
|
||||
return "C";
|
||||
case KR:
|
||||
return "KR";
|
||||
case TSLR:
|
||||
return "TSLR";
|
||||
case TR:
|
||||
return "TR";
|
||||
case TSHR:
|
||||
return "TSHR";
|
||||
case TRU:
|
||||
return "TRU";
|
||||
case RNDB:
|
||||
return "RNDB" + roundaboutExit;
|
||||
case RNLB:
|
||||
return "RNLB" + (-roundaboutExit);
|
||||
case BL:
|
||||
return "BL";
|
||||
case OFFR:
|
||||
return "OFFR";
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown command: " + cmd);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* used by cruiser, equivalent to getMessageString() - osmand style - when osmand changes the voice hint constants
|
||||
*/
|
||||
public String getCruiserMessageString() {
|
||||
switch (cmd) {
|
||||
case TLU:
|
||||
return "u-turn left";
|
||||
case TU:
|
||||
return "u-turn";
|
||||
case TSHL:
|
||||
return "sharp left";
|
||||
case TL:
|
||||
return "left";
|
||||
case TSLL:
|
||||
return "slight left";
|
||||
case KL:
|
||||
return "keep left";
|
||||
case C:
|
||||
return "straight";
|
||||
case KR:
|
||||
return "keep right";
|
||||
case TSLR:
|
||||
return "slight right";
|
||||
case TR:
|
||||
return "right";
|
||||
case TSHR:
|
||||
return "sharp right";
|
||||
case TRU:
|
||||
return "u-turn right";
|
||||
case RNDB:
|
||||
return "take exit " + roundaboutExit;
|
||||
case RNLB:
|
||||
return "take exit " + (-roundaboutExit);
|
||||
case BL:
|
||||
return "beeline";
|
||||
case OFFR:
|
||||
return "offroad";
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown command: " + cmd);
|
||||
}
|
||||
}
|
||||
|
||||
public void calcCommand() {
|
||||
public void calcCommand()
|
||||
{
|
||||
float lowerBadWayAngle = -181;
|
||||
float higherBadWayAngle = 181;
|
||||
if (badWays != null) {
|
||||
for (MessageData badWay : badWays) {
|
||||
if (badWay.isBadOneway()) {
|
||||
if ( badWays != null )
|
||||
{
|
||||
for ( MessageData badWay : badWays )
|
||||
{
|
||||
if ( badWay.isBadOneway() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (lowerBadWayAngle < badWay.turnangle && badWay.turnangle < goodWay.turnangle) {
|
||||
if ( lowerBadWayAngle < badWay.turnangle && badWay.turnangle < goodWay.turnangle )
|
||||
{
|
||||
lowerBadWayAngle = badWay.turnangle;
|
||||
}
|
||||
if (higherBadWayAngle > badWay.turnangle && badWay.turnangle > goodWay.turnangle) {
|
||||
if ( higherBadWayAngle > badWay.turnangle && badWay.turnangle > goodWay.turnangle )
|
||||
{
|
||||
higherBadWayAngle = badWay.turnangle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float cmdAngle = angle;
|
||||
float cmdAngle= angle;
|
||||
|
||||
// fall back to local angle if otherwise inconsistent
|
||||
//if ( lowerBadWayAngle > angle || higherBadWayAngle < angle )
|
||||
//{
|
||||
//cmdAngle = goodWay.turnangle;
|
||||
//}
|
||||
if (angle == Float.MAX_VALUE) {
|
||||
if ( lowerBadWayAngle > angle || higherBadWayAngle < angle )
|
||||
{
|
||||
cmdAngle = goodWay.turnangle;
|
||||
}
|
||||
if (cmd == BL) return;
|
||||
|
||||
if (roundaboutExit > 0) {
|
||||
if (roundaboutExit > 0)
|
||||
{
|
||||
cmd = RNDB;
|
||||
} else if (roundaboutExit < 0) {
|
||||
}
|
||||
else if (roundaboutExit < 0)
|
||||
{
|
||||
cmd = RNLB;
|
||||
} else if (is180DegAngle(cmdAngle) && cmdAngle <= -179.f && higherBadWayAngle == 181.f && lowerBadWayAngle == -181.f) {
|
||||
}
|
||||
else if ( cmdAngle < -159. )
|
||||
{
|
||||
cmd = TU;
|
||||
} else if (cmdAngle < -159.f) {
|
||||
cmd = TLU;
|
||||
} else if (cmdAngle < -135.f) {
|
||||
}
|
||||
else if ( cmdAngle < -135. )
|
||||
{
|
||||
cmd = TSHL;
|
||||
} else if (cmdAngle < -45.f) {
|
||||
}
|
||||
else if ( cmdAngle < -45. )
|
||||
{
|
||||
// a TL can be pushed in either direction by a close-by alternative
|
||||
if (cmdAngle < -95.f && higherBadWayAngle < -30.f && lowerBadWayAngle < -180.f) {
|
||||
if ( higherBadWayAngle > -90. && higherBadWayAngle < -15. && lowerBadWayAngle < -180. )
|
||||
{
|
||||
cmd = TSHL;
|
||||
} else if (cmdAngle > -85.f && lowerBadWayAngle > -180.f && higherBadWayAngle > -10.f) {
|
||||
cmd = TSLL;
|
||||
} else {
|
||||
if (cmdAngle < -110.f) {
|
||||
cmd = TSHL;
|
||||
} else if (cmdAngle > -60.f) {
|
||||
cmd = TSLL;
|
||||
} else {
|
||||
cmd = TL;
|
||||
}
|
||||
}
|
||||
} else if (cmdAngle < -21.f) {
|
||||
if (cmd != KR) { // don't overwrite KR with TSLL
|
||||
else if ( lowerBadWayAngle > -180. && lowerBadWayAngle < -90. && higherBadWayAngle > 0. )
|
||||
{
|
||||
cmd = TSLL;
|
||||
}
|
||||
} else if (cmdAngle < -5.f) {
|
||||
if (lowerBadWayAngle < -100.f && higherBadWayAngle < 45.f) {
|
||||
else
|
||||
{
|
||||
cmd = TL;
|
||||
}
|
||||
}
|
||||
else if ( cmdAngle < -21. )
|
||||
{
|
||||
if ( cmd != KR ) // don't overwrite KR with TSLL
|
||||
{
|
||||
cmd = TSLL;
|
||||
} else if (lowerBadWayAngle >= -100.f && higherBadWayAngle < 45.f) {
|
||||
cmd = KL;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
else if ( cmdAngle < 21. )
|
||||
{
|
||||
if ( cmd != KR && cmd != KL ) // don't overwrite KL/KR hints!
|
||||
{
|
||||
cmd = C;
|
||||
}
|
||||
} else if (cmdAngle < 5.f) {
|
||||
if (lowerBadWayAngle > -30.f) {
|
||||
cmd = KR;
|
||||
} else if (higherBadWayAngle < 30.f) {
|
||||
cmd = KL;
|
||||
} else {
|
||||
cmd = C;
|
||||
}
|
||||
else if ( cmdAngle < 45. )
|
||||
{
|
||||
if ( cmd != KL ) // don't overwrite KL with TSLR
|
||||
{
|
||||
cmd = TSLR;
|
||||
}
|
||||
} else if (cmdAngle < 21.f) {
|
||||
}
|
||||
else if ( cmdAngle < 135. )
|
||||
{
|
||||
// a TR can be pushed in either direction by a close-by alternative
|
||||
if (lowerBadWayAngle > -45.f && higherBadWayAngle > 100.f) {
|
||||
if ( higherBadWayAngle > 90. && higherBadWayAngle < 180. && lowerBadWayAngle < 0. )
|
||||
{
|
||||
cmd = TSLR;
|
||||
} else if (lowerBadWayAngle > -45.f && higherBadWayAngle <= 100.f) {
|
||||
cmd = KR;
|
||||
} else {
|
||||
cmd = C;
|
||||
}
|
||||
} else if (cmdAngle < 45.f) {
|
||||
cmd = TSLR;
|
||||
} else if (cmdAngle < 135.f) {
|
||||
if (cmdAngle < 85.f && higherBadWayAngle < 180.f && lowerBadWayAngle < 10.f) {
|
||||
cmd = TSLR;
|
||||
} else if (cmdAngle > 95.f && lowerBadWayAngle > 30.f && higherBadWayAngle > 180.f) {
|
||||
else if ( lowerBadWayAngle > 15. && lowerBadWayAngle < 90. && higherBadWayAngle > 180. )
|
||||
{
|
||||
cmd = TSHR;
|
||||
} else {
|
||||
if (cmdAngle > 110.) {
|
||||
cmd = TSHR;
|
||||
} else if (cmdAngle < 60.) {
|
||||
cmd = TSLR;
|
||||
} else {
|
||||
cmd = TR;
|
||||
}
|
||||
}
|
||||
} else if (cmdAngle < 159.f) {
|
||||
else
|
||||
{
|
||||
cmd = TR;
|
||||
}
|
||||
}
|
||||
else if ( cmdAngle < 159. )
|
||||
{
|
||||
cmd = TSHR;
|
||||
} else if (is180DegAngle(cmdAngle) && cmdAngle >= 179.f && higherBadWayAngle == 181.f && lowerBadWayAngle == -181.f) {
|
||||
cmd = TU;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd = TRU;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean is180DegAngle(float angle) {
|
||||
return (Math.abs(angle) <= 180.f && Math.abs(angle) >= 179.f);
|
||||
}
|
||||
|
||||
public String formatGeometry() {
|
||||
public String formatGeometry()
|
||||
{
|
||||
float oldPrio = oldWay == null ? 0.f : oldWay.priorityclassifier;
|
||||
StringBuilder sb = new StringBuilder(30);
|
||||
sb.append(' ').append((int) oldPrio);
|
||||
appendTurnGeometry(sb, goodWay);
|
||||
if (badWays != null) {
|
||||
for (MessageData badWay : badWays) {
|
||||
sb.append(" ");
|
||||
appendTurnGeometry(sb, badWay);
|
||||
sb.append( ' ' ).append( (int)oldPrio );
|
||||
appendTurnGeometry(sb,goodWay);
|
||||
if ( badWays != null )
|
||||
{
|
||||
for ( MessageData badWay : badWays )
|
||||
{
|
||||
sb.append( " " );
|
||||
appendTurnGeometry( sb, badWay );
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void appendTurnGeometry(StringBuilder sb, MessageData msg) {
|
||||
sb.append("(").append((int) (msg.turnangle + 0.5)).append(")").append((int) (msg.priorityclassifier));
|
||||
private void appendTurnGeometry( StringBuilder sb, MessageData msg )
|
||||
{
|
||||
sb.append( "(" ).append( (int)(msg.turnangle+0.5) ).append( ")" ).append( (int)(msg.priorityclassifier) );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,51 +9,30 @@ package btools.router;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class VoiceHintList {
|
||||
|
||||
static final int TRANS_MODE_NONE = 0;
|
||||
static final int TRANS_MODE_FOOT = 1;
|
||||
static final int TRANS_MODE_BIKE = 2;
|
||||
static final int TRANS_MODE_CAR = 3;
|
||||
|
||||
private int transportMode = TRANS_MODE_BIKE;
|
||||
public class VoiceHintList
|
||||
{
|
||||
private String transportMode;
|
||||
int turnInstructionMode;
|
||||
List<VoiceHint> list = new ArrayList<>();
|
||||
ArrayList<VoiceHint> list = new ArrayList<VoiceHint>();
|
||||
|
||||
public void setTransportMode(boolean isCar, boolean isBike) {
|
||||
transportMode = isCar ? TRANS_MODE_CAR : (isBike ? TRANS_MODE_BIKE : TRANS_MODE_FOOT);
|
||||
public void setTransportMode( boolean isCar, boolean isBike )
|
||||
{
|
||||
transportMode = isCar ? "car" : ( isBike ? "bike" : "foot" );
|
||||
}
|
||||
|
||||
public void setTransportMode(int mode) {
|
||||
transportMode = mode;
|
||||
}
|
||||
|
||||
public String getTransportMode() {
|
||||
String ret;
|
||||
switch (transportMode) {
|
||||
case TRANS_MODE_FOOT:
|
||||
ret = "foot";
|
||||
break;
|
||||
case TRANS_MODE_CAR:
|
||||
ret = "car";
|
||||
break;
|
||||
case TRANS_MODE_BIKE:
|
||||
default:
|
||||
ret = "bike";
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public int transportMode() {
|
||||
public String getTransportMode()
|
||||
{
|
||||
return transportMode;
|
||||
}
|
||||
|
||||
public int getLocusRouteType() {
|
||||
if (transportMode == TRANS_MODE_CAR) {
|
||||
public int getLocusRouteType()
|
||||
{
|
||||
if ( "car".equals( transportMode ) )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (transportMode == TRANS_MODE_BIKE) {
|
||||
if ( "bike".equals( transportMode ) )
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
return 3; // foot
|
||||
|
|
|
|||
|
|
@ -8,27 +8,26 @@ package btools.router;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class VoiceHintProcessor {
|
||||
|
||||
double SIGNIFICANT_ANGLE = 22.5;
|
||||
double INTERNAL_CATCHING_RANGE = 2.;
|
||||
|
||||
// private double catchingRange; // range to catch angles and merge turns
|
||||
public final class VoiceHintProcessor
|
||||
{
|
||||
private double catchingRange; // range to catch angles and merge turns
|
||||
private boolean explicitRoundabouts;
|
||||
private int transportMode;
|
||||
|
||||
public VoiceHintProcessor(double catchingRange, boolean explicitRoundabouts, int transportMode) {
|
||||
// this.catchingRange = catchingRange;
|
||||
public VoiceHintProcessor( double catchingRange, boolean explicitRoundabouts )
|
||||
{
|
||||
this.catchingRange = catchingRange;
|
||||
this.explicitRoundabouts = explicitRoundabouts;
|
||||
this.transportMode = transportMode;
|
||||
}
|
||||
|
||||
private float sumNonConsumedWithinCatchingRange(List<VoiceHint> inputs, int offset) {
|
||||
private float sumNonConsumedWithinCatchingRange( List<VoiceHint> inputs, int offset )
|
||||
{
|
||||
double distance = 0.;
|
||||
float angle = 0.f;
|
||||
while (offset >= 0 && distance < INTERNAL_CATCHING_RANGE) {
|
||||
VoiceHint input = inputs.get(offset--);
|
||||
if (input.turnAngleConsumed) {
|
||||
while( offset >= 0 && distance < catchingRange )
|
||||
{
|
||||
VoiceHint input = inputs.get( offset-- );
|
||||
if ( input.turnAngleConsumed )
|
||||
{
|
||||
break;
|
||||
}
|
||||
angle += input.goodWay.turnangle;
|
||||
|
|
@ -45,10 +44,10 @@ public final class VoiceHintProcessor {
|
|||
* order (from target to start), but output is
|
||||
* returned in travel-direction and only for
|
||||
* those nodes that trigger a voice hint.
|
||||
* <p>
|
||||
*
|
||||
* Input objects are expected for every segment
|
||||
* of the track, also for those without a junction
|
||||
* <p>
|
||||
*
|
||||
* VoiceHint objects in the output list are enriched
|
||||
* by the voice-command, the total angle and the distance
|
||||
* to the next hint
|
||||
|
|
@ -56,91 +55,56 @@ public final class VoiceHintProcessor {
|
|||
* @param inputs tracknodes, un reverse order
|
||||
* @return voice hints, in forward order
|
||||
*/
|
||||
public List<VoiceHint> process(List<VoiceHint> inputs) {
|
||||
List<VoiceHint> results = new ArrayList<>();
|
||||
public List<VoiceHint> process( List<VoiceHint> inputs )
|
||||
{
|
||||
List<VoiceHint> results = new ArrayList<VoiceHint>();
|
||||
double distance = 0.;
|
||||
float roundAboutTurnAngle = 0.f; // sums up angles in roundabout
|
||||
|
||||
int roundaboutExit = 0;
|
||||
int roundaboudStartIdx = -1;
|
||||
|
||||
for (int hintIdx = 0; hintIdx < inputs.size(); hintIdx++) {
|
||||
VoiceHint input = inputs.get(hintIdx);
|
||||
for ( int hintIdx = 0; hintIdx < inputs.size(); hintIdx++ )
|
||||
{
|
||||
VoiceHint input = inputs.get( hintIdx );
|
||||
|
||||
if (input.cmd == VoiceHint.BL) {
|
||||
results.add(input);
|
||||
continue;
|
||||
}
|
||||
float turnAngle = input.goodWay.turnangle;
|
||||
distance += input.goodWay.linkdist;
|
||||
int currentPrio = input.goodWay.getPrio();
|
||||
int oldPrio = input.oldWay.getPrio();
|
||||
int minPrio = Math.min(oldPrio, currentPrio);
|
||||
int minPrio = Math.min( oldPrio, currentPrio );
|
||||
|
||||
boolean isLink2Highway = input.oldWay.isLinktType() && !input.goodWay.isLinktType();
|
||||
boolean isHighway2Link = !input.oldWay.isLinktType() && input.goodWay.isLinktType();
|
||||
|
||||
if (explicitRoundabouts && input.oldWay.isRoundabout()) {
|
||||
if (roundaboudStartIdx == -1) roundaboudStartIdx = hintIdx;
|
||||
roundAboutTurnAngle += sumNonConsumedWithinCatchingRange(inputs, hintIdx);
|
||||
if (roundaboudStartIdx == hintIdx) {
|
||||
if (input.badWays != null) {
|
||||
// remove goodWay
|
||||
roundAboutTurnAngle -= input.goodWay.turnangle;
|
||||
// add a badWay
|
||||
for (MessageData badWay : input.badWays) {
|
||||
if (!badWay.isBadOneway()) roundAboutTurnAngle += badWay.turnangle;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( input.oldWay.isRoundabout() )
|
||||
{
|
||||
roundAboutTurnAngle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
|
||||
boolean isExit = roundaboutExit == 0; // exit point is always exit
|
||||
if (input.badWays != null) {
|
||||
for (MessageData badWay : input.badWays) {
|
||||
if (!badWay.isBadOneway() &&
|
||||
badWay.isGoodForCars()) {
|
||||
if ( input.badWays != null )
|
||||
{
|
||||
for ( MessageData badWay : input.badWays )
|
||||
{
|
||||
if ( !badWay.isBadOneway() && badWay.isGoodForCars() && Math.abs( badWay.turnangle ) < 120. )
|
||||
{
|
||||
isExit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isExit) {
|
||||
if ( isExit )
|
||||
{
|
||||
roundaboutExit++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (roundaboutExit > 0) {
|
||||
//roundAboutTurnAngle += sumNonConsumedWithinCatchingRange(inputs, hintIdx);
|
||||
//double startTurn = (roundaboudStartIdx != -1 ? inputs.get(roundaboudStartIdx + 1).goodWay.turnangle : turnAngle);
|
||||
if ( roundaboutExit > 0 )
|
||||
{
|
||||
roundAboutTurnAngle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
|
||||
input.angle = roundAboutTurnAngle;
|
||||
input.goodWay.turnangle = roundAboutTurnAngle;
|
||||
input.distanceToNext = distance;
|
||||
//input.roundaboutExit = startTurn < 0 ? roundaboutExit : -roundaboutExit;
|
||||
input.roundaboutExit = roundAboutTurnAngle < 0 ? roundaboutExit : -roundaboutExit;
|
||||
float tmpangle = 0;
|
||||
VoiceHint tmpRndAbt = new VoiceHint();
|
||||
tmpRndAbt.badWays = new ArrayList<>();
|
||||
for (int i = hintIdx-1; i > roundaboudStartIdx; i--) {
|
||||
VoiceHint vh = inputs.get(i);
|
||||
tmpangle += inputs.get(i).goodWay.turnangle;
|
||||
if (vh.badWays != null) {
|
||||
for (MessageData badWay : vh.badWays) {
|
||||
if (!badWay.isBadOneway()) {
|
||||
MessageData md = new MessageData();
|
||||
md.linkdist = vh.goodWay.linkdist;
|
||||
md.priorityclassifier = vh.goodWay.priorityclassifier;
|
||||
md.turnangle = tmpangle;
|
||||
tmpRndAbt.badWays.add(md);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
input.roundaboutExit = turnAngle < 0 ? -roundaboutExit : roundaboutExit;
|
||||
distance = 0.;
|
||||
|
||||
input.badWays = tmpRndAbt.badWays;
|
||||
|
||||
results.add(input);
|
||||
results.add( input );
|
||||
roundAboutTurnAngle = 0.f;
|
||||
roundaboutExit = 0;
|
||||
roundaboudStartIdx = -1;
|
||||
continue;
|
||||
}
|
||||
int maxPrioAll = -1; // max prio of all detours
|
||||
|
|
@ -150,265 +114,134 @@ public final class VoiceHintProcessor {
|
|||
float minAngle = 180.f;
|
||||
float minAbsAngeRaw = 180.f;
|
||||
|
||||
boolean isBadwayLink = false;
|
||||
|
||||
if (input.badWays != null) {
|
||||
for (MessageData badWay : input.badWays) {
|
||||
if ( input.badWays != null )
|
||||
{
|
||||
for ( MessageData badWay : input.badWays )
|
||||
{
|
||||
int badPrio = badWay.getPrio();
|
||||
float badTurn = badWay.turnangle;
|
||||
if (badWay.isLinktType()) {
|
||||
isBadwayLink = true;
|
||||
}
|
||||
boolean isBadHighway2Link = !input.oldWay.isLinktType() && badWay.isLinktType();
|
||||
|
||||
if (badPrio > maxPrioAll && !isBadHighway2Link) {
|
||||
boolean isHighway2Link = !input.oldWay.isLinktType() && badWay.isLinktType();
|
||||
|
||||
if ( badPrio > maxPrioAll && !isHighway2Link )
|
||||
{
|
||||
maxPrioAll = badPrio;
|
||||
input.maxBadPrio = Math.max(input.maxBadPrio, badPrio);
|
||||
}
|
||||
|
||||
if (badPrio < minPrio) {
|
||||
if ( badWay.costfactor < 20.f && Math.abs( badTurn ) < minAbsAngeRaw )
|
||||
{
|
||||
minAbsAngeRaw = Math.abs( badTurn );
|
||||
}
|
||||
|
||||
if ( badPrio < minPrio )
|
||||
{
|
||||
continue; // ignore low prio ways
|
||||
}
|
||||
|
||||
if (badWay.isBadOneway()) {
|
||||
if ( badWay.isBadOneway() )
|
||||
{
|
||||
continue; // ignore wrong oneways
|
||||
}
|
||||
|
||||
if (Math.abs(badTurn) - Math.abs(turnAngle) > 80.f) {
|
||||
if ( Math.abs( badTurn ) - Math.abs( turnAngle ) > 80.f )
|
||||
{
|
||||
continue; // ways from the back should not trigger a slight turn
|
||||
}
|
||||
|
||||
if (badWay.costfactor < 20.f && Math.abs(badTurn) < minAbsAngeRaw) {
|
||||
minAbsAngeRaw = Math.abs(badTurn);
|
||||
}
|
||||
|
||||
if (badPrio > maxPrioCandidates) {
|
||||
if ( badPrio > maxPrioCandidates )
|
||||
{
|
||||
maxPrioCandidates = badPrio;
|
||||
input.maxBadPrio = Math.max(input.maxBadPrio, badPrio);
|
||||
}
|
||||
if (badTurn > maxAngle) {
|
||||
if ( badTurn > maxAngle )
|
||||
{
|
||||
maxAngle = badTurn;
|
||||
}
|
||||
if (badTurn < minAngle) {
|
||||
if ( badTurn < minAngle )
|
||||
{
|
||||
minAngle = badTurn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// boolean hasSomethingMoreStraight = (Math.abs(turnAngle) - minAbsAngeRaw) > 20.;
|
||||
boolean hasSomethingMoreStraight = (Math.abs(turnAngle - minAbsAngeRaw)) > 20. && input.badWays != null; // && !ignoreBadway;
|
||||
boolean hasSomethingMoreStraight = Math.abs( turnAngle ) - minAbsAngeRaw > 20.;
|
||||
|
||||
// unconditional triggers are all junctions with
|
||||
// - higher detour prios than the minimum route prio (except link->highway junctions)
|
||||
// - or candidate detours with higher prio then the route exit leg
|
||||
boolean unconditionalTrigger = hasSomethingMoreStraight ||
|
||||
(maxPrioAll > minPrio && !isLink2Highway) ||
|
||||
(maxPrioCandidates > currentPrio) ||
|
||||
VoiceHint.is180DegAngle(turnAngle) ||
|
||||
(!isHighway2Link && isBadwayLink && Math.abs(turnAngle) > 5.f) ||
|
||||
(isHighway2Link && !isBadwayLink && Math.abs(turnAngle) < 5.f);
|
||||
boolean unconditionalTrigger = hasSomethingMoreStraight || ( maxPrioAll > minPrio && !isLink2Highway ) || ( maxPrioCandidates > currentPrio );
|
||||
|
||||
// conditional triggers (=real turning angle required) are junctions
|
||||
// with candidate detours equal in priority than the route exit leg
|
||||
boolean conditionalTrigger = maxPrioCandidates >= minPrio;
|
||||
|
||||
if (unconditionalTrigger || conditionalTrigger) {
|
||||
if ( unconditionalTrigger || conditionalTrigger )
|
||||
{
|
||||
input.angle = turnAngle;
|
||||
input.calcCommand();
|
||||
boolean isStraight = input.cmd == VoiceHint.C;
|
||||
input.needsRealTurn = (!unconditionalTrigger) && isStraight;
|
||||
|
||||
// check for KR/KL
|
||||
if (Math.abs(turnAngle) > 5.) { // don't use too small angles
|
||||
if (maxAngle < turnAngle && maxAngle > turnAngle - 45.f - (Math.max(turnAngle, 0.f))) {
|
||||
input.cmd = VoiceHint.KR;
|
||||
}
|
||||
if (minAngle > turnAngle && minAngle < turnAngle + 45.f - (Math.min(turnAngle, 0.f))) {
|
||||
input.cmd = VoiceHint.KL;
|
||||
}
|
||||
if ( maxAngle < turnAngle && maxAngle > turnAngle - 45.f - (turnAngle > 0.f ? turnAngle : 0.f ) )
|
||||
{
|
||||
input.cmd = VoiceHint.KR;
|
||||
}
|
||||
if ( minAngle > turnAngle && minAngle < turnAngle + 45.f - (turnAngle < 0.f ? turnAngle : 0.f ) )
|
||||
{
|
||||
input.cmd = VoiceHint.KL;
|
||||
}
|
||||
|
||||
input.angle = sumNonConsumedWithinCatchingRange(inputs, hintIdx);
|
||||
input.angle = sumNonConsumedWithinCatchingRange( inputs, hintIdx );
|
||||
input.distanceToNext = distance;
|
||||
distance = 0.;
|
||||
results.add(input);
|
||||
results.add( input );
|
||||
}
|
||||
if (results.size() > 0 && distance < INTERNAL_CATCHING_RANGE) { //catchingRange
|
||||
results.get(results.size() - 1).angle += sumNonConsumedWithinCatchingRange(inputs, hintIdx);
|
||||
if ( results.size() > 0 && distance < catchingRange )
|
||||
{
|
||||
results.get( results.size()-1 ).angle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
|
||||
}
|
||||
}
|
||||
|
||||
// go through the hint list again in reverse order (=travel direction)
|
||||
// and filter out non-significant hints and hints too close to its predecessor
|
||||
// and filter out non-signficant hints and hints too close to it's predecessor
|
||||
|
||||
List<VoiceHint> results2 = new ArrayList<>();
|
||||
List<VoiceHint> results2 = new ArrayList<VoiceHint>();
|
||||
int i = results.size();
|
||||
while (i > 0) {
|
||||
while( i > 0 )
|
||||
{
|
||||
VoiceHint hint = results.get(--i);
|
||||
if (hint.cmd == 0) {
|
||||
if ( hint.cmd == 0 )
|
||||
{
|
||||
hint.calcCommand();
|
||||
}
|
||||
if (!(hint.needsRealTurn && (hint.cmd == VoiceHint.C || hint.cmd == VoiceHint.BL))) {
|
||||
if ( ! ( hint.needsRealTurn && hint.cmd == VoiceHint.C ) )
|
||||
{
|
||||
double dist = hint.distanceToNext;
|
||||
// sum up other hints within the catching range (e.g. 40m)
|
||||
while (dist < INTERNAL_CATCHING_RANGE && i > 0) {
|
||||
VoiceHint h2 = results.get(i - 1);
|
||||
while( dist < catchingRange && i > 0 )
|
||||
{
|
||||
VoiceHint h2 = results.get(i-1);
|
||||
dist = h2.distanceToNext;
|
||||
hint.distanceToNext += dist;
|
||||
hint.distanceToNext+= dist;
|
||||
hint.angle += h2.angle;
|
||||
i--;
|
||||
if (h2.isRoundabout()) { // if we hit a roundabout, use that as the trigger
|
||||
if ( h2.isRoundabout() ) // if we hit a roundabout, use that as the trigger
|
||||
{
|
||||
h2.angle = hint.angle;
|
||||
hint = h2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!explicitRoundabouts) {
|
||||
if ( !explicitRoundabouts )
|
||||
{
|
||||
hint.roundaboutExit = 0; // use an angular hint instead
|
||||
}
|
||||
hint.calcCommand();
|
||||
results2.add(hint);
|
||||
} else if (hint.cmd == VoiceHint.BL) {
|
||||
results2.add(hint);
|
||||
} else {
|
||||
if (results2.size() > 0)
|
||||
results2.get(results2.size() - 1).distanceToNext += hint.distanceToNext;
|
||||
results2.add( hint );
|
||||
}
|
||||
}
|
||||
return results2;
|
||||
}
|
||||
|
||||
public List<VoiceHint> postProcess(List<VoiceHint> inputs, double catchingRange, double minRange) {
|
||||
List<VoiceHint> results = new ArrayList<>();
|
||||
double distance = 0;
|
||||
VoiceHint inputLast = null;
|
||||
VoiceHint inputLastSaved = null;
|
||||
for (int hintIdx = 0; hintIdx < inputs.size(); hintIdx++) {
|
||||
VoiceHint input = inputs.get(hintIdx);
|
||||
VoiceHint nextInput = null;
|
||||
if (hintIdx + 1 < inputs.size()) {
|
||||
nextInput = inputs.get(hintIdx + 1);
|
||||
}
|
||||
|
||||
if (nextInput == null) {
|
||||
if (input.cmd == VoiceHint.C && !input.goodWay.isLinktType()) {
|
||||
if (input.goodWay.getPrio() < input.maxBadPrio && (inputLastSaved != null && inputLastSaved.distanceToNext > catchingRange)) {
|
||||
results.add(input);
|
||||
} else {
|
||||
if (inputLast != null) { // when drop add distance to last
|
||||
inputLast.distanceToNext += input.distanceToNext;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
results.add(input);
|
||||
}
|
||||
} else {
|
||||
if ((inputLastSaved != null && inputLastSaved.distanceToNext > catchingRange) || input.distanceToNext > catchingRange) {
|
||||
if (input.cmd == VoiceHint.C && !input.goodWay.isLinktType()) {
|
||||
if (input.goodWay.getPrio() < input.maxBadPrio
|
||||
&& (inputLastSaved != null && inputLastSaved.distanceToNext > minRange)
|
||||
&& (input.distanceToNext > minRange)) {
|
||||
// add only on prio
|
||||
results.add(input);
|
||||
inputLastSaved = input;
|
||||
} else {
|
||||
if (inputLastSaved != null) { // when drop add distance to last
|
||||
inputLastSaved.distanceToNext += input.distanceToNext;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// add all others
|
||||
// ignore motorway / primary continue
|
||||
if (((input.goodWay.getPrio() != 28) &&
|
||||
(input.goodWay.getPrio() != 30) &&
|
||||
(input.goodWay.getPrio() != 26))
|
||||
|| input.isRoundabout()
|
||||
|| Math.abs(input.angle) > 21.f) {
|
||||
results.add(input);
|
||||
inputLastSaved = input;
|
||||
} else {
|
||||
if (inputLastSaved != null) { // when drop add distance to last
|
||||
inputLastSaved.distanceToNext += input.distanceToNext;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (input.distanceToNext < catchingRange) {
|
||||
double dist = input.distanceToNext;
|
||||
float angles = input.angle;
|
||||
int i = 1;
|
||||
boolean save = false;
|
||||
|
||||
dist += nextInput.distanceToNext;
|
||||
angles += nextInput.angle;
|
||||
|
||||
if (input.cmd == VoiceHint.C && !input.goodWay.isLinktType()) {
|
||||
if (input.goodWay.getPrio() < input.maxBadPrio) {
|
||||
if (inputLastSaved != null && inputLastSaved.cmd != VoiceHint.C
|
||||
&& (inputLastSaved != null && inputLastSaved.distanceToNext > minRange)
|
||||
&& transportMode != VoiceHintList.TRANS_MODE_CAR) {
|
||||
// add when straight and not linktype
|
||||
// and last vh not straight
|
||||
save = true;
|
||||
// remove when next straight and not linktype
|
||||
if (nextInput != null &&
|
||||
nextInput.cmd == VoiceHint.C &&
|
||||
!nextInput.goodWay.isLinktType()) {
|
||||
input.distanceToNext += nextInput.distanceToNext;
|
||||
hintIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (inputLastSaved != null) { // when drop add distance to last
|
||||
inputLastSaved.distanceToNext += input.distanceToNext;
|
||||
}
|
||||
}
|
||||
} else if (VoiceHint.is180DegAngle(input.angle)) {
|
||||
// add u-turn, 180 degree
|
||||
save = true;
|
||||
} else if (transportMode == VoiceHintList.TRANS_MODE_CAR && Math.abs(angles) > 180 - SIGNIFICANT_ANGLE) {
|
||||
// add when inc car mode and u-turn, collects e.g. two left turns in range
|
||||
input.angle = angles;
|
||||
input.calcCommand();
|
||||
input.distanceToNext += nextInput.distanceToNext;
|
||||
save = true;
|
||||
hintIdx++;
|
||||
} else if (Math.abs(angles) < SIGNIFICANT_ANGLE && input.distanceToNext < minRange) {
|
||||
input.angle = angles;
|
||||
input.calcCommand();
|
||||
input.distanceToNext += nextInput.distanceToNext;
|
||||
save = true;
|
||||
hintIdx++;
|
||||
} else if (Math.abs(input.angle) > SIGNIFICANT_ANGLE) {
|
||||
// add when angle above 22.5 deg
|
||||
save = true;
|
||||
} else if (Math.abs(input.angle) < SIGNIFICANT_ANGLE) {
|
||||
// add when angle below 22.5 deg ???
|
||||
// save = true;
|
||||
} else {
|
||||
// otherwise ignore but add distance to next
|
||||
if (nextInput != null) { // when drop add distance to last
|
||||
nextInput.distanceToNext += input.distanceToNext;
|
||||
}
|
||||
save = false;
|
||||
}
|
||||
|
||||
if (save) {
|
||||
results.add(input); // add when last
|
||||
inputLastSaved = input;
|
||||
}
|
||||
} else {
|
||||
results.add(input);
|
||||
inputLastSaved = input;
|
||||
}
|
||||
}
|
||||
inputLast = input;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,22 @@
|
|||
package btools.router;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import btools.util.CheapRuler;
|
||||
|
||||
public class OsmNodeNamedTest {
|
||||
static int toOsmLon(double lon) {
|
||||
return (int) ((lon + 180.) / CheapRuler.ILATLNG_TO_LATLNG + 0.5);
|
||||
return (int)( ( lon + 180. ) / CheapRuler.ILATLNG_TO_LATLNG + 0.5);
|
||||
}
|
||||
|
||||
static int toOsmLat(double lat) {
|
||||
return (int) ((lat + 90.) / CheapRuler.ILATLNG_TO_LATLNG + 0.5);
|
||||
return (int)( ( lat + 90. ) / CheapRuler.ILATLNG_TO_LATLNG + 0.5);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/**********************************************************************************************
|
||||
Copyright (C) 2018 Norbert Truchsess norbert.truchsess@t-online.de
|
||||
**********************************************************************************************/
|
||||
Copyright (C) 2018 Norbert Truchsess norbert.truchsess@t-online.de
|
||||
**********************************************************************************************/
|
||||
package btools.router;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
|
@ -11,6 +11,7 @@ import org.junit.AfterClass;
|
|||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import btools.router.OsmNogoPolygon.Point;
|
||||
import btools.util.CheapRuler;
|
||||
|
||||
public class OsmNogoPolygonTest {
|
||||
|
|
@ -21,26 +22,26 @@ public class OsmNogoPolygonTest {
|
|||
static OsmNogoPolygon polygon;
|
||||
static OsmNogoPolygon polyline;
|
||||
|
||||
static final double[] lons = {1.0, 1.0, 0.5, 0.5, 1.0, 1.0, -1.1, -1.0};
|
||||
static final double[] lats = {-1.0, -0.1, -0.1, 0.1, 0.1, 1.0, 1.1, -1.0};
|
||||
static final double[] lons = { 1.0, 1.0, 0.5, 0.5, 1.0, 1.0, -1.1, -1.0 };
|
||||
static final double[] lats = { -1.0, -0.1, -0.1, 0.1, 0.1, 1.0, 1.1, -1.0 };
|
||||
|
||||
static int toOsmLon(double lon, int offset_x) {
|
||||
return (int) ((lon + 180.) * 1000000. + 0.5) + offset_x; // see ServerHandler.readPosition()
|
||||
return (int)( ( lon + 180. ) *1000000. + 0.5)+offset_x; // see ServerHandler.readPosition()
|
||||
}
|
||||
|
||||
static int toOsmLat(double lat, int offset_y) {
|
||||
return (int) ((lat + 90.) * 1000000. + 0.5) + offset_y;
|
||||
return (int)( ( lat + 90. ) *1000000. + 0.5)+offset_y;
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() throws Exception {
|
||||
polygon = new OsmNogoPolygon(true);
|
||||
for (int i = 0; i < lons.length; i++) {
|
||||
polygon.addVertex(toOsmLon(lons[i], OFFSET_X), toOsmLat(lats[i], OFFSET_Y));
|
||||
for (int i = 0; i<lons.length; i++) {
|
||||
polygon.addVertex(toOsmLon(lons[i], OFFSET_X),toOsmLat(lats[i], OFFSET_Y));
|
||||
}
|
||||
polyline = new OsmNogoPolygon(false);
|
||||
for (int i = 0; i < lons.length; i++) {
|
||||
polyline.addVertex(toOsmLon(lons[i], OFFSET_X), toOsmLat(lats[i], OFFSET_Y));
|
||||
for (int i = 0; i<lons.length; i++) {
|
||||
polyline.addVertex(toOsmLon(lons[i], OFFSET_X),toOsmLat(lats[i], OFFSET_Y));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -50,162 +51,162 @@ public class OsmNogoPolygonTest {
|
|||
|
||||
@Test
|
||||
public void testCalcBoundingCircle() {
|
||||
double[] lonlat2m = CheapRuler.getLonLatToMeterScales(polygon.ilat);
|
||||
double[] lonlat2m = CheapRuler.getLonLatToMeterScales( polygon.ilat );
|
||||
double dlon2m = lonlat2m[0];
|
||||
double dlat2m = lonlat2m[1];
|
||||
|
||||
polygon.calcBoundingCircle();
|
||||
double r = polygon.radius;
|
||||
for (int i = 0; i < lons.length; i++) {
|
||||
for (int i=0; i<lons.length; i++) {
|
||||
double dpx = (toOsmLon(lons[i], OFFSET_X) - polygon.ilon) * dlon2m;
|
||||
double dpy = (toOsmLat(lats[i], OFFSET_Y) - polygon.ilat) * dlat2m;
|
||||
double r1 = Math.sqrt(dpx * dpx + dpy * dpy);
|
||||
double diff = r - r1;
|
||||
assertTrue("i: " + i + " r(" + r + ") >= r1(" + r1 + ")", diff >= 0);
|
||||
double diff = r-r1;
|
||||
assertTrue("i: "+i+" r("+r+") >= r1("+r1+")", diff >= 0);
|
||||
}
|
||||
polyline.calcBoundingCircle();
|
||||
r = polyline.radius;
|
||||
for (int i = 0; i < lons.length; i++) {
|
||||
for (int i=0; i<lons.length; i++) {
|
||||
double dpx = (toOsmLon(lons[i], OFFSET_X) - polyline.ilon) * dlon2m;
|
||||
double dpy = (toOsmLat(lats[i], OFFSET_Y) - polyline.ilat) * dlat2m;
|
||||
double r1 = Math.sqrt(dpx * dpx + dpy * dpy);
|
||||
double diff = r - r1;
|
||||
assertTrue("i: " + i + " r(" + r + ") >= r1(" + r1 + ")", diff >= 0);
|
||||
double diff = r-r1;
|
||||
assertTrue("i: "+i+" r("+r+") >= r1("+r1+")", diff >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsWithin() {
|
||||
double[] plons = {0.0, 0.5, 1.0, -1.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5};
|
||||
double[] plats = {0.0, 1.5, 0.0, 0.5, -1.5, -1.0, -0.1, -0.1, 0.0, 0.1};
|
||||
boolean[] within = {true, false, false, false, false, true, true, true, true, true};
|
||||
double[] plons = { 0.0, 0.5, 1.0, -1.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5, };
|
||||
double[] plats = { 0.0, 1.5, 0.0, 0.5, -1.5, -1.0, -0.1, -0.1, 0.0, 0.1, };
|
||||
boolean[] within = { true, false, false, false, false, true, true, true, true, true, };
|
||||
|
||||
for (int i = 0; i < plons.length; i++) {
|
||||
assertEquals("(" + plons[i] + "," + plats[i] + ")", within[i], polygon.isWithin(toOsmLon(plons[i], OFFSET_X), toOsmLat(plats[i], OFFSET_Y)));
|
||||
for (int i=0; i<plons.length; i++) {
|
||||
assertEquals("("+plons[i]+","+plats[i]+")",within[i],polygon.isWithin(toOsmLon(plons[i], OFFSET_X), toOsmLat(plats[i], OFFSET_Y)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntersectsPolygon() {
|
||||
double[] p0lons = {0.0, 1.0, -0.5, 0.5, 0.7, 0.7, 0.7, -1.5, -1.5, 0.0};
|
||||
double[] p0lats = {0.0, 0.0, 0.5, 0.5, 0.5, 0.05, 0.05, -1.5, 0.2, 0.0};
|
||||
double[] p1lons = {0.0, 1.0, 0.5, 1.0, 0.7, 0.7, 0.7, -0.5, -0.2, 0.5};
|
||||
double[] p1lats = {0.0, 0.0, 0.5, 0.5, -0.5, -0.5, -0.05, -0.5, 1.5, -1.5};
|
||||
boolean[] within = {false, false, false, true, true, true, false, true, true, true};
|
||||
double[] p0lons = { 0.0, 1.0, -0.5, 0.5, 0.7, 0.7, 0.7, -1.5, -1.5, 0.0 };
|
||||
double[] p0lats = { 0.0, 0.0, 0.5, 0.5, 0.5, 0.05, 0.05, -1.5, 0.2, 0.0 };
|
||||
double[] p1lons = { 0.0, 1.0, 0.5, 1.0, 0.7, 0.7, 0.7, -0.5, -0.2, 0.5 };
|
||||
double[] p1lats = { 0.0, 0.0, 0.5, 0.5, -0.5, -0.5, -0.05, -0.5, 1.5, -1.5 };
|
||||
boolean[] within = { false, false, false, true, true, true, false, true, true, true };
|
||||
|
||||
for (int i = 0; i < p0lons.length; i++) {
|
||||
assertEquals("(" + p0lons[i] + "," + p0lats[i] + ")-(" + p1lons[i] + "," + p1lats[i] + ")", within[i], polygon.intersects(toOsmLon(p0lons[i], OFFSET_X), toOsmLat(p0lats[i], OFFSET_Y), toOsmLon(p1lons[i], OFFSET_X), toOsmLat(p1lats[i], OFFSET_Y)));
|
||||
for (int i=0; i<p0lons.length; i++) {
|
||||
assertEquals("("+p0lons[i]+","+p0lats[i]+")-("+p1lons[i]+","+p1lats[i]+")",within[i],polygon.intersects(toOsmLon(p0lons[i], OFFSET_X), toOsmLat(p0lats[i], OFFSET_Y), toOsmLon(p1lons[i], OFFSET_X), toOsmLat(p1lats[i], OFFSET_Y)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntersectsPolyline() {
|
||||
double[] p0lons = {0.0, 1.0, -0.5, 0.5, 0.7, 0.7, 0.7, -1.5, -1.5, 0.0};
|
||||
double[] p0lats = {0.0, 0.0, 0.5, 0.5, 0.5, 0.05, 0.05, -1.5, 0.2, 0.0};
|
||||
double[] p1lons = {0.0, 1.0, 0.5, 1.0, 0.7, 0.7, 0.7, -0.5, -0.2, 0.5};
|
||||
double[] p1lats = {0.0, 0.0, 0.5, 0.5, -0.5, -0.5, -0.05, -0.5, 1.5, -1.5};
|
||||
boolean[] within = {false, false, false, true, true, true, false, true, true, false};
|
||||
double[] p0lons = { 0.0, 1.0, -0.5, 0.5, 0.7, 0.7, 0.7, -1.5, -1.5, 0.0 };
|
||||
double[] p0lats = { 0.0, 0.0, 0.5, 0.5, 0.5, 0.05, 0.05, -1.5, 0.2, 0.0 };
|
||||
double[] p1lons = { 0.0, 1.0, 0.5, 1.0, 0.7, 0.7, 0.7, -0.5, -0.2, 0.5 };
|
||||
double[] p1lats = { 0.0, 0.0, 0.5, 0.5, -0.5, -0.5, -0.05, -0.5, 1.5, -1.5 };
|
||||
boolean[] within = { false, false, false, true, true, true, false, true, true, false };
|
||||
|
||||
for (int i = 0; i < p0lons.length; i++) {
|
||||
assertEquals("(" + p0lons[i] + "," + p0lats[i] + ")-(" + p1lons[i] + "," + p1lats[i] + ")", within[i], polyline.intersects(toOsmLon(p0lons[i], OFFSET_X), toOsmLat(p0lats[i], OFFSET_Y), toOsmLon(p1lons[i], OFFSET_X), toOsmLat(p1lats[i], OFFSET_Y)));
|
||||
for (int i=0; i<p0lons.length; i++) {
|
||||
assertEquals("("+p0lons[i]+","+p0lats[i]+")-("+p1lons[i]+","+p1lats[i]+")",within[i],polyline.intersects(toOsmLon(p0lons[i], OFFSET_X), toOsmLat(p0lats[i], OFFSET_Y), toOsmLon(p1lons[i], OFFSET_X), toOsmLat(p1lats[i], OFFSET_Y)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBelongsToLine() {
|
||||
assertTrue(OsmNogoPolygon.isOnLine(10, 10, 10, 10, 10, 20));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(10, 10, 10, 10, 20, 10));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(10, 10, 20, 10, 10, 10));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(10, 10, 10, 20, 10, 10));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(10, 15, 10, 10, 10, 20));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(15, 10, 10, 10, 20, 10));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(10, 10, 10, 10, 20, 30));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(20, 30, 10, 10, 20, 30));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(15, 20, 10, 10, 20, 30));
|
||||
assertFalse(OsmNogoPolygon.isOnLine(11, 11, 10, 10, 10, 20));
|
||||
assertFalse(OsmNogoPolygon.isOnLine(11, 11, 10, 10, 20, 10));
|
||||
assertFalse(OsmNogoPolygon.isOnLine(15, 21, 10, 10, 20, 30));
|
||||
assertFalse(OsmNogoPolygon.isOnLine(15, 19, 10, 10, 20, 30));
|
||||
assertFalse(OsmNogoPolygon.isOnLine(0, -10, 10, 10, 20, 30));
|
||||
assertFalse(OsmNogoPolygon.isOnLine(30, 50, 10, 10, 20, 30));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(10,10, 10,10, 10,20));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(10,10, 10,10, 20,10));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(10,10, 20,10, 10,10));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(10,10, 10,20, 10,10));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(10,15, 10,10, 10,20));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(15,10, 10,10, 20,10));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(10,10, 10,10, 20,30));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(20,30, 10,10, 20,30));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(15,20, 10,10, 20,30));
|
||||
assertFalse(OsmNogoPolygon.isOnLine(11,11, 10,10, 10,20));
|
||||
assertFalse(OsmNogoPolygon.isOnLine(11,11, 10,10, 20,10));
|
||||
assertFalse(OsmNogoPolygon.isOnLine(15,21, 10,10, 20,30));
|
||||
assertFalse(OsmNogoPolygon.isOnLine(15,19, 10,10, 20,30));
|
||||
assertFalse(OsmNogoPolygon.isOnLine(0,-10, 10,10, 20,30));
|
||||
assertFalse(OsmNogoPolygon.isOnLine(30,50, 10,10, 20,30));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDistanceWithinPolygon() {
|
||||
// Testing polygon
|
||||
final double[] lons = {2.333523, 2.333432, 2.333833, 2.333983, 2.334815, 2.334766};
|
||||
final double[] lats = {48.823778, 48.824091, 48.82389, 48.824165, 48.824232, 48.82384};
|
||||
OsmNogoPolygon polygon = new OsmNogoPolygon(true);
|
||||
for (int i = 0; i < lons.length; i++) {
|
||||
polygon.addVertex(toOsmLon(lons[i], 0), toOsmLat(lats[i], 0));
|
||||
}
|
||||
OsmNogoPolygon polyline = new OsmNogoPolygon(false);
|
||||
for (int i = 0; i < lons.length; i++) {
|
||||
polyline.addVertex(toOsmLon(lons[i], 0), toOsmLat(lats[i], 0));
|
||||
}
|
||||
// Testing polygon
|
||||
final double[] lons = { 2.333523, 2.333432, 2.333833, 2.333983, 2.334815, 2.334766 };
|
||||
final double[] lats = { 48.823778, 48.824091, 48.82389, 48.824165, 48.824232, 48.82384 };
|
||||
OsmNogoPolygon polygon = new OsmNogoPolygon(true);
|
||||
for (int i = 0; i < lons.length; i++) {
|
||||
polygon.addVertex(toOsmLon(lons[i], 0), toOsmLat(lats[i], 0));
|
||||
}
|
||||
OsmNogoPolygon polyline = new OsmNogoPolygon(false);
|
||||
for (int i = 0; i < lons.length; i++) {
|
||||
polyline.addVertex(toOsmLon(lons[i], 0), toOsmLat(lats[i], 0));
|
||||
}
|
||||
|
||||
// Check with a segment with a single intersection with the polygon
|
||||
int lon1 = toOsmLon(2.33308732509613, 0);
|
||||
int lat1 = toOsmLat(48.8238790443901, 0);
|
||||
int lon2 = toOsmLon(2.33378201723099, 0);
|
||||
int lat2 = toOsmLat(48.8239585098974, 0);
|
||||
assertEquals(
|
||||
"Should give the correct length for a segment with a single intersection",
|
||||
17.5,
|
||||
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
|
||||
0.05 * 17.5
|
||||
);
|
||||
// Check with a segment with a single intersection with the polygon
|
||||
int lon1 = toOsmLon(2.33308732509613, 0);
|
||||
int lat1 = toOsmLat(48.8238790443901, 0);
|
||||
int lon2 = toOsmLon(2.33378201723099, 0);
|
||||
int lat2 = toOsmLat(48.8239585098974, 0);
|
||||
assertEquals(
|
||||
"Should give the correct length for a segment with a single intersection",
|
||||
17.5,
|
||||
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
|
||||
0.05 * 17.5
|
||||
);
|
||||
|
||||
// Check with a segment crossing multiple times the polygon
|
||||
lon2 = toOsmLon(2.33488172292709, 0);
|
||||
lat2 = toOsmLat(48.8240891862353, 0);
|
||||
assertEquals(
|
||||
"Should give the correct length for a segment with multiple intersections",
|
||||
85,
|
||||
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
|
||||
0.05 * 85
|
||||
);
|
||||
// Check with a segment crossing multiple times the polygon
|
||||
lon2 = toOsmLon(2.33488172292709, 0);
|
||||
lat2 = toOsmLat(48.8240891862353, 0);
|
||||
assertEquals(
|
||||
"Should give the correct length for a segment with multiple intersections",
|
||||
85,
|
||||
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
|
||||
0.05 * 85
|
||||
);
|
||||
|
||||
// Check that it works when a point is within the polygon
|
||||
lon2 = toOsmLon(2.33433187007904, 0);
|
||||
lat2 = toOsmLat(48.8240238480664, 0);
|
||||
assertEquals(
|
||||
"Should give the correct length when last point is within the polygon",
|
||||
50,
|
||||
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
|
||||
0.05 * 50
|
||||
);
|
||||
lon1 = toOsmLon(2.33433187007904, 0);
|
||||
lat1 = toOsmLat(48.8240238480664, 0);
|
||||
lon2 = toOsmLon(2.33488172292709, 0);
|
||||
lat2 = toOsmLat(48.8240891862353, 0);
|
||||
assertEquals(
|
||||
"Should give the correct length when first point is within the polygon",
|
||||
35,
|
||||
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
|
||||
0.05 * 35
|
||||
);
|
||||
// Check that it works when a point is within the polygon
|
||||
lon2 = toOsmLon(2.33433187007904, 0);
|
||||
lat2 = toOsmLat(48.8240238480664, 0);
|
||||
assertEquals(
|
||||
"Should give the correct length when last point is within the polygon",
|
||||
50,
|
||||
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
|
||||
0.05 * 50
|
||||
);
|
||||
lon1 = toOsmLon(2.33433187007904, 0);
|
||||
lat1 = toOsmLat(48.8240238480664, 0);
|
||||
lon2 = toOsmLon(2.33488172292709, 0);
|
||||
lat2 = toOsmLat(48.8240891862353, 0);
|
||||
assertEquals(
|
||||
"Should give the correct length when first point is within the polygon",
|
||||
35,
|
||||
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
|
||||
0.05 * 35
|
||||
);
|
||||
|
||||
lon1 = toOsmLon(2.333523, 0);
|
||||
lat1 = toOsmLat(48.823778, 0);
|
||||
lon2 = toOsmLon(2.333432, 0);
|
||||
lat2 = toOsmLat(48.824091, 0);
|
||||
assertEquals(
|
||||
"Should give the correct length if the segment overlaps with an edge of the polygon",
|
||||
CheapRuler.distance(lon1, lat1, lon2, lat2),
|
||||
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
|
||||
0.05 * CheapRuler.distance(lon1, lat1, lon2, lat2)
|
||||
);
|
||||
lon1 = toOsmLon(2.333523, 0);
|
||||
lat1 = toOsmLat(48.823778, 0);
|
||||
lon2 = toOsmLon(2.333432, 0);
|
||||
lat2 = toOsmLat(48.824091, 0);
|
||||
assertEquals(
|
||||
"Should give the correct length if the segment overlaps with an edge of the polygon",
|
||||
CheapRuler.distance(lon1, lat1, lon2, lat2),
|
||||
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
|
||||
0.05 * CheapRuler.distance(lon1, lat1, lon2, lat2)
|
||||
);
|
||||
|
||||
lon1 = toOsmLon(2.333523, 0);
|
||||
lat1 = toOsmLat(48.823778, 0);
|
||||
lon2 = toOsmLon(2.3334775, 0);
|
||||
lat2 = toOsmLat(48.8239345, 0);
|
||||
assertEquals(
|
||||
"Should give the correct length if the segment overlaps with a polyline",
|
||||
CheapRuler.distance(lon1, lat1, lon2, lat2),
|
||||
polyline.distanceWithinPolygon(lon1, lat1, lon2, lat2),
|
||||
0.05 * CheapRuler.distance(lon1, lat1, lon2, lat2)
|
||||
);
|
||||
lon1 = toOsmLon(2.333523, 0);
|
||||
lat1 = toOsmLat(48.823778, 0);
|
||||
lon2 = toOsmLon(2.3334775, 0);
|
||||
lat2 = toOsmLat(48.8239345, 0);
|
||||
assertEquals(
|
||||
"Should give the correct length if the segment overlaps with a polyline",
|
||||
CheapRuler.distance(lon1, lat1, lon2, lat2),
|
||||
polyline.distanceWithinPolygon(lon1, lat1, lon2, lat2),
|
||||
0.05 * CheapRuler.distance(lon1, lat1, lon2, lat2)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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
1
brouter-expressions/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build/
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
plugins {
|
||||
id 'brouter.library-conventions'
|
||||
id 'java-library'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':brouter-util')
|
||||
implementation project(':brouter-codec')
|
||||
testImplementation 'junit:junit:4.13.1'
|
||||
}
|
||||
|
|
|
|||
3
brouter-expressions/src/main/AndroidManifest.xml
Normal file
3
brouter-expressions/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<manifest package="btools.expressions" />
|
||||
|
|
@ -1,406 +1,300 @@
|
|||
package btools.expressions;
|
||||
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
final class BExpression {
|
||||
private static final int OR_EXP = 10;
|
||||
private static final int AND_EXP = 11;
|
||||
private static final int NOT_EXP = 12;
|
||||
|
||||
private static final int ADD_EXP = 20;
|
||||
private static final int MULTIPLY_EXP = 21;
|
||||
private static final int DIVIDE_EXP = 22;
|
||||
private static final int MAX_EXP = 23;
|
||||
private static final int EQUAL_EXP = 24;
|
||||
private static final int GREATER_EXP = 25;
|
||||
private static final int MIN_EXP = 26;
|
||||
|
||||
private static final int SUB_EXP = 27;
|
||||
private static final int LESSER_EXP = 28;
|
||||
private static final int XOR_EXP = 29;
|
||||
|
||||
private static final int SWITCH_EXP = 30;
|
||||
private static final int ASSIGN_EXP = 31;
|
||||
private static final int LOOKUP_EXP = 32;
|
||||
private static final int NUMBER_EXP = 33;
|
||||
private static final int VARIABLE_EXP = 34;
|
||||
private static final int FOREIGN_VARIABLE_EXP = 35;
|
||||
private static final int VARIABLE_GET_EXP = 36;
|
||||
|
||||
private int typ;
|
||||
private BExpression op1;
|
||||
private BExpression op2;
|
||||
private BExpression op3;
|
||||
private float numberValue;
|
||||
private int variableIdx;
|
||||
private int lookupNameIdx = -1;
|
||||
private int[] lookupValueIdxArray;
|
||||
private boolean doNotChange;
|
||||
|
||||
// Parse the expression and all subexpression
|
||||
public static BExpression parse(BExpressionContext ctx, int level) throws Exception {
|
||||
return parse(ctx, level, null);
|
||||
}
|
||||
|
||||
private static BExpression parse(BExpressionContext ctx, int level, String optionalToken) throws Exception {
|
||||
BExpression e = parseRaw(ctx, level, optionalToken);
|
||||
if (e == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ASSIGN_EXP == e.typ) {
|
||||
// manage assined an injected values
|
||||
BExpression assignedBefore = ctx.lastAssignedExpression.get(e.variableIdx);
|
||||
if (assignedBefore != null && assignedBefore.doNotChange) {
|
||||
e.op1 = assignedBefore; // was injected as key-value
|
||||
e.op1.doNotChange = false; // protect just once, can be changed in second assignement
|
||||
}
|
||||
ctx.lastAssignedExpression.set(e.variableIdx, e.op1);
|
||||
}
|
||||
else if (!ctx.skipConstantExpressionOptimizations) {
|
||||
// try to simplify the expression
|
||||
if (VARIABLE_EXP == e.typ) {
|
||||
BExpression ae = ctx.lastAssignedExpression.get(e.variableIdx);
|
||||
if (ae != null && ae.typ == NUMBER_EXP) {
|
||||
e = ae;
|
||||
}
|
||||
} else {
|
||||
BExpression eCollapsed = e.tryCollapse();
|
||||
if (e != eCollapsed) {
|
||||
e = eCollapsed; // allow breakpoint..
|
||||
}
|
||||
BExpression eEvaluated = e.tryEvaluateConstant();
|
||||
if (e != eEvaluated) {
|
||||
e = eEvaluated; // allow breakpoint..
|
||||
}
|
||||
}
|
||||
}
|
||||
if (level == 0) {
|
||||
// mark the used lookups after the
|
||||
// expression is collapsed to not mark
|
||||
// lookups as used that appear in the profile
|
||||
// but are de-activated by constant expressions
|
||||
int nodeCount = e.markLookupIdxUsed(ctx);
|
||||
ctx.expressionNodeCount += nodeCount;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
private int markLookupIdxUsed(BExpressionContext ctx) {
|
||||
int nodeCount = 1;
|
||||
if (lookupNameIdx >= 0) {
|
||||
ctx.markLookupIdxUsed(lookupNameIdx);
|
||||
}
|
||||
if (op1 != null) {
|
||||
nodeCount += op1.markLookupIdxUsed(ctx);
|
||||
}
|
||||
if (op2 != null) {
|
||||
nodeCount += op2.markLookupIdxUsed(ctx);
|
||||
}
|
||||
if (op3 != null) {
|
||||
nodeCount += op3.markLookupIdxUsed(ctx);
|
||||
}
|
||||
return nodeCount;
|
||||
}
|
||||
|
||||
private static BExpression parseRaw(BExpressionContext ctx, int level, String optionalToken) throws Exception {
|
||||
|
||||
boolean brackets = false;
|
||||
String operator = ctx.parseToken();
|
||||
if (optionalToken != null && optionalToken.equals(operator)) {
|
||||
operator = ctx.parseToken();
|
||||
}
|
||||
if ("(".equals(operator)) {
|
||||
brackets = true;
|
||||
operator = ctx.parseToken();
|
||||
}
|
||||
|
||||
if (operator == null) {
|
||||
if (level == 0) return null;
|
||||
else throw new IllegalArgumentException("unexpected end of file");
|
||||
}
|
||||
|
||||
if (level == 0) {
|
||||
if (!"assign".equals(operator)) {
|
||||
throw new IllegalArgumentException("operator " + operator + " is invalid on toplevel (only 'assign' allowed)");
|
||||
}
|
||||
}
|
||||
|
||||
BExpression exp = new BExpression();
|
||||
int nops = 3;
|
||||
|
||||
boolean ifThenElse = false;
|
||||
|
||||
if ("switch".equals(operator)) {
|
||||
exp.typ = SWITCH_EXP;
|
||||
} else if ("if".equals(operator)) {
|
||||
exp.typ = SWITCH_EXP;
|
||||
ifThenElse = true;
|
||||
} else {
|
||||
nops = 2; // check binary expressions
|
||||
|
||||
if ("or".equals(operator)) {
|
||||
exp.typ = OR_EXP;
|
||||
} else if ("and".equals(operator)) {
|
||||
exp.typ = AND_EXP;
|
||||
} else if ("multiply".equals(operator)) {
|
||||
exp.typ = MULTIPLY_EXP;
|
||||
} else if ("divide".equals(operator)) {
|
||||
exp.typ = DIVIDE_EXP;
|
||||
} else if ("add".equals(operator)) {
|
||||
exp.typ = ADD_EXP;
|
||||
} else if ("max".equals(operator)) {
|
||||
exp.typ = MAX_EXP;
|
||||
} else if ("min".equals(operator)) {
|
||||
exp.typ = MIN_EXP;
|
||||
} else if ("equal".equals(operator)) {
|
||||
exp.typ = EQUAL_EXP;
|
||||
} else if ("greater".equals(operator)) {
|
||||
exp.typ = GREATER_EXP;
|
||||
} else if ("sub".equals(operator)) {
|
||||
exp.typ = SUB_EXP;
|
||||
} else if ("lesser".equals(operator)) {
|
||||
exp.typ = LESSER_EXP;
|
||||
} else if ("xor".equals(operator)) {
|
||||
exp.typ = XOR_EXP;
|
||||
} else {
|
||||
nops = 1; // check unary expressions
|
||||
if ("assign".equals(operator)) {
|
||||
if (level > 0) throw new IllegalArgumentException("assign operator within expression");
|
||||
exp.typ = ASSIGN_EXP;
|
||||
String variable = ctx.parseToken();
|
||||
if (variable == null) throw new IllegalArgumentException("unexpected end of file");
|
||||
if (variable.indexOf('=') >= 0)
|
||||
throw new IllegalArgumentException("variable name cannot contain '=': " + variable);
|
||||
if (variable.indexOf(':') >= 0)
|
||||
throw new IllegalArgumentException("cannot assign context-prefixed variable: " + variable);
|
||||
exp.variableIdx = ctx.getVariableIdx(variable, true);
|
||||
if (exp.variableIdx < ctx.getMinWriteIdx())
|
||||
throw new IllegalArgumentException("cannot assign to readonly variable " + variable);
|
||||
} else if ("not".equals(operator)) {
|
||||
exp.typ = NOT_EXP;
|
||||
} else {
|
||||
nops = 0; // check elemantary expressions
|
||||
int idx = operator.indexOf('=');
|
||||
if (idx >= 0) {
|
||||
exp.typ = LOOKUP_EXP;
|
||||
String name = operator.substring(0, idx);
|
||||
String values = operator.substring(idx + 1);
|
||||
|
||||
exp.lookupNameIdx = ctx.getLookupNameIdx(name);
|
||||
if (exp.lookupNameIdx < 0) {
|
||||
throw new IllegalArgumentException("unknown lookup name: " + name);
|
||||
}
|
||||
StringTokenizer tk = new StringTokenizer(values, "|");
|
||||
int nt = tk.countTokens();
|
||||
int nt2 = nt == 0 ? 1 : nt;
|
||||
exp.lookupValueIdxArray = new int[nt2];
|
||||
for (int ti = 0; ti < nt2; ti++) {
|
||||
String value = ti < nt ? tk.nextToken() : "";
|
||||
exp.lookupValueIdxArray[ti] = ctx.getLookupValueIdx(exp.lookupNameIdx, value);
|
||||
if (exp.lookupValueIdxArray[ti] < 0) {
|
||||
throw new IllegalArgumentException("unknown lookup value: " + value);
|
||||
}
|
||||
}
|
||||
} else if ((idx = operator.indexOf(':')) >= 0) {
|
||||
/*
|
||||
use of variable values
|
||||
assign no_height
|
||||
switch and not maxheight=
|
||||
lesser v:maxheight my_height true
|
||||
false
|
||||
*/
|
||||
if (operator.startsWith("v:")) {
|
||||
String name = operator.substring(2);
|
||||
exp.typ = VARIABLE_GET_EXP;
|
||||
exp.lookupNameIdx = ctx.getLookupNameIdx(name);
|
||||
} else {
|
||||
String context = operator.substring(0, idx);
|
||||
String varname = operator.substring(idx + 1);
|
||||
exp.typ = FOREIGN_VARIABLE_EXP;
|
||||
exp.variableIdx = ctx.getForeignVariableIdx(context, varname);
|
||||
}
|
||||
} else if ((idx = ctx.getVariableIdx(operator, false)) >= 0) {
|
||||
exp.typ = VARIABLE_EXP;
|
||||
exp.variableIdx = idx;
|
||||
} else if ("true".equals(operator)) {
|
||||
exp.numberValue = 1.f;
|
||||
exp.typ = NUMBER_EXP;
|
||||
} else if ("false".equals(operator)) {
|
||||
exp.numberValue = 0.f;
|
||||
exp.typ = NUMBER_EXP;
|
||||
} else {
|
||||
try {
|
||||
exp.numberValue = Float.parseFloat(operator);
|
||||
exp.typ = NUMBER_EXP;
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new IllegalArgumentException("unknown expression: " + operator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// parse operands
|
||||
if (nops > 0) {
|
||||
exp.op1 = parse(ctx, level + 1, exp.typ == ASSIGN_EXP ? "=" : null);
|
||||
}
|
||||
if (nops > 1) {
|
||||
if (ifThenElse) checkExpectedToken(ctx, "then");
|
||||
exp.op2 = parse(ctx, level + 1, null);
|
||||
}
|
||||
if (nops > 2) {
|
||||
if (ifThenElse) checkExpectedToken(ctx, "else");
|
||||
exp.op3 = parse(ctx, level + 1, null);
|
||||
}
|
||||
if (brackets) {
|
||||
checkExpectedToken(ctx, ")");
|
||||
}
|
||||
return exp;
|
||||
}
|
||||
|
||||
private static void checkExpectedToken(BExpressionContext ctx, String expected) throws Exception {
|
||||
String token = ctx.parseToken();
|
||||
if (!expected.equals(token)) {
|
||||
throw new IllegalArgumentException("unexpected token: " + token + ", expected: " + expected);
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate the expression
|
||||
public float evaluate(BExpressionContext ctx) {
|
||||
switch (typ) {
|
||||
case OR_EXP:
|
||||
return op1.evaluate(ctx) != 0.f ? 1.f : (op2.evaluate(ctx) != 0.f ? 1.f : 0.f);
|
||||
case XOR_EXP:
|
||||
return ((op1.evaluate(ctx) != 0.f) ^ (op2.evaluate(ctx) != 0.f) ? 1.f : 0.f);
|
||||
case AND_EXP:
|
||||
return op1.evaluate(ctx) != 0.f ? (op2.evaluate(ctx) != 0.f ? 1.f : 0.f) : 0.f;
|
||||
case ADD_EXP:
|
||||
return op1.evaluate(ctx) + op2.evaluate(ctx);
|
||||
case SUB_EXP:
|
||||
return op1.evaluate(ctx) - op2.evaluate(ctx);
|
||||
case MULTIPLY_EXP:
|
||||
return op1.evaluate(ctx) * op2.evaluate(ctx);
|
||||
case DIVIDE_EXP:
|
||||
return divide(op1.evaluate(ctx), op2.evaluate(ctx));
|
||||
case MAX_EXP:
|
||||
return max(op1.evaluate(ctx), op2.evaluate(ctx));
|
||||
case MIN_EXP:
|
||||
return min(op1.evaluate(ctx), op2.evaluate(ctx));
|
||||
case EQUAL_EXP:
|
||||
return op1.evaluate(ctx) == op2.evaluate(ctx) ? 1.f : 0.f;
|
||||
case GREATER_EXP:
|
||||
return op1.evaluate(ctx) > op2.evaluate(ctx) ? 1.f : 0.f;
|
||||
case LESSER_EXP:
|
||||
return op1.evaluate(ctx) < op2.evaluate(ctx) ? 1.f : 0.f;
|
||||
case SWITCH_EXP:
|
||||
return op1.evaluate(ctx) != 0.f ? op2.evaluate(ctx) : op3.evaluate(ctx);
|
||||
case ASSIGN_EXP:
|
||||
return ctx.assign(variableIdx, op1.evaluate(ctx));
|
||||
case LOOKUP_EXP:
|
||||
return ctx.getLookupMatch(lookupNameIdx, lookupValueIdxArray);
|
||||
case NUMBER_EXP:
|
||||
return numberValue;
|
||||
case VARIABLE_EXP:
|
||||
return ctx.getVariableValue(variableIdx);
|
||||
case FOREIGN_VARIABLE_EXP:
|
||||
return ctx.getForeignVariableValue(variableIdx);
|
||||
case VARIABLE_GET_EXP:
|
||||
return ctx.getLookupValue(lookupNameIdx);
|
||||
case NOT_EXP:
|
||||
return op1.evaluate(ctx) == 0.f ? 1.f : 0.f;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown op-code: " + typ);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to collapse the expression
|
||||
// if logically possible
|
||||
private BExpression tryCollapse() {
|
||||
switch (typ) {
|
||||
case OR_EXP:
|
||||
return NUMBER_EXP == op1.typ ?
|
||||
(op1.numberValue != 0.f ? op1 : op2)
|
||||
: (NUMBER_EXP == op2.typ ?
|
||||
(op2.numberValue != 0.f ? op2 : op1)
|
||||
: this);
|
||||
case AND_EXP:
|
||||
return NUMBER_EXP == op1.typ ?
|
||||
(op1.numberValue == 0.f ? op1 : op2)
|
||||
: (NUMBER_EXP == op2.typ ?
|
||||
(op2.numberValue == 0.f ? op2 : op1)
|
||||
: this);
|
||||
case ADD_EXP:
|
||||
return NUMBER_EXP == op1.typ ?
|
||||
(op1.numberValue == 0.f ? op2 : this)
|
||||
: (NUMBER_EXP == op2.typ ?
|
||||
(op2.numberValue == 0.f ? op1 : this)
|
||||
: this);
|
||||
case SWITCH_EXP:
|
||||
return NUMBER_EXP == op1.typ ?
|
||||
(op1.numberValue == 0.f ? op3 : op2) : this;
|
||||
default:
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to evaluate the expression
|
||||
// if all operands are constant
|
||||
private BExpression tryEvaluateConstant() {
|
||||
if (op1 != null && NUMBER_EXP == op1.typ
|
||||
&& (op2 == null || NUMBER_EXP == op2.typ)
|
||||
&& (op3 == null || NUMBER_EXP == op3.typ)) {
|
||||
BExpression exp = new BExpression();
|
||||
exp.typ = NUMBER_EXP;
|
||||
exp.numberValue = evaluate(null);
|
||||
return exp;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private float max(float v1, float v2) {
|
||||
return v1 > v2 ? v1 : v2;
|
||||
}
|
||||
|
||||
private float min(float v1, float v2) {
|
||||
return v1 < v2 ? v1 : v2;
|
||||
}
|
||||
|
||||
private float divide(float v1, float v2) {
|
||||
if (v2 == 0f) throw new IllegalArgumentException("div by zero");
|
||||
return v1 / v2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (typ == NUMBER_EXP) {
|
||||
return "" + numberValue;
|
||||
}
|
||||
if (typ == VARIABLE_EXP) {
|
||||
return "vidx=" + variableIdx;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder("typ=" + typ + " ops=(");
|
||||
addOp(sb, op1);
|
||||
addOp(sb, op2);
|
||||
addOp(sb, op3);
|
||||
sb.append(')');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void addOp(StringBuilder sb, BExpression e) {
|
||||
if (e != null) {
|
||||
sb.append('[').append(e.toString()).append(']');
|
||||
}
|
||||
}
|
||||
|
||||
static BExpression createAssignExpressionFromKeyValue(BExpressionContext ctx, String key, String value) {
|
||||
BExpression e = new BExpression();
|
||||
e.typ = ASSIGN_EXP;
|
||||
e.variableIdx = ctx.getVariableIdx(key, true);
|
||||
e.op1 = new BExpression();
|
||||
e.op1.typ = NUMBER_EXP;
|
||||
e.op1.numberValue = Float.parseFloat(value);
|
||||
e.op1.doNotChange = true;
|
||||
ctx.lastAssignedExpression.set(e.variableIdx, e.op1);
|
||||
return e;
|
||||
}
|
||||
}
|
||||
package btools.expressions;
|
||||
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
final class BExpression
|
||||
{
|
||||
private static final int OR_EXP = 10;
|
||||
private static final int AND_EXP = 11;
|
||||
private static final int NOT_EXP = 12;
|
||||
|
||||
private static final int ADD_EXP = 20;
|
||||
private static final int MULTIPLY_EXP = 21;
|
||||
private static final int MAX_EXP = 22;
|
||||
private static final int EQUAL_EXP = 23;
|
||||
private static final int GREATER_EXP = 24;
|
||||
private static final int MIN_EXP = 25;
|
||||
|
||||
private static final int SUB_EXP = 26;
|
||||
private static final int LESSER_EXP = 27;
|
||||
private static final int XOR_EXP = 28;
|
||||
|
||||
private static final int SWITCH_EXP = 30;
|
||||
private static final int ASSIGN_EXP = 31;
|
||||
private static final int LOOKUP_EXP = 32;
|
||||
private static final int NUMBER_EXP = 33;
|
||||
private static final int VARIABLE_EXP = 34;
|
||||
private static final int FOREIGN_VARIABLE_EXP = 35;
|
||||
private static final int VARIABLE_GET_EXP = 36;
|
||||
|
||||
private int typ;
|
||||
private BExpression op1;
|
||||
private BExpression op2;
|
||||
private BExpression op3;
|
||||
private float numberValue;
|
||||
private int variableIdx;
|
||||
private int lookupNameIdx;
|
||||
private int[] lookupValueIdxArray;
|
||||
|
||||
// Parse the expression and all subexpression
|
||||
public static BExpression parse( BExpressionContext ctx, int level ) throws Exception
|
||||
{
|
||||
return parse( ctx, level, null );
|
||||
}
|
||||
|
||||
private static BExpression parse( BExpressionContext ctx, int level, String optionalToken ) throws Exception
|
||||
{
|
||||
boolean brackets = false;
|
||||
String operator = ctx.parseToken();
|
||||
if ( optionalToken != null && optionalToken.equals( operator ) )
|
||||
{
|
||||
operator = ctx.parseToken();
|
||||
}
|
||||
if ( "(".equals( operator ) )
|
||||
{
|
||||
brackets = true;
|
||||
operator = ctx.parseToken();
|
||||
}
|
||||
|
||||
if ( operator == null )
|
||||
{
|
||||
if ( level == 0 ) return null;
|
||||
else throw new IllegalArgumentException( "unexpected end of file" );
|
||||
}
|
||||
|
||||
if ( level == 0 )
|
||||
{
|
||||
if ( !"assign".equals( operator ) )
|
||||
{
|
||||
throw new IllegalArgumentException( "operator " + operator + " is invalid on toplevel (only 'assign' allowed)" );
|
||||
}
|
||||
}
|
||||
|
||||
BExpression exp = new BExpression();
|
||||
int nops = 3;
|
||||
boolean ifThenElse = false;
|
||||
|
||||
if ( "switch".equals( operator ) )
|
||||
{
|
||||
exp.typ = SWITCH_EXP;
|
||||
}
|
||||
else if ( "if".equals( operator ) )
|
||||
{
|
||||
exp.typ = SWITCH_EXP;
|
||||
ifThenElse = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
nops = 2; // check binary expressions
|
||||
|
||||
if ( "or".equals( operator ) )
|
||||
{
|
||||
exp.typ = OR_EXP;
|
||||
}
|
||||
else if ( "and".equals( operator ) )
|
||||
{
|
||||
exp.typ = AND_EXP;
|
||||
}
|
||||
else if ( "multiply".equals( operator ) )
|
||||
{
|
||||
exp.typ = MULTIPLY_EXP;
|
||||
}
|
||||
else if ( "add".equals( operator ) )
|
||||
{
|
||||
exp.typ = ADD_EXP;
|
||||
}
|
||||
else if ( "max".equals( operator ) )
|
||||
{
|
||||
exp.typ = MAX_EXP;
|
||||
}
|
||||
else if ( "min".equals( operator ) )
|
||||
{
|
||||
exp.typ = MIN_EXP;
|
||||
}
|
||||
else if ( "equal".equals( operator ) )
|
||||
{
|
||||
exp.typ = EQUAL_EXP;
|
||||
}
|
||||
else if ( "greater".equals( operator ) )
|
||||
{
|
||||
exp.typ = GREATER_EXP;
|
||||
}
|
||||
else if ( "sub".equals( operator ) )
|
||||
{
|
||||
exp.typ = SUB_EXP;
|
||||
}
|
||||
else if ( "lesser".equals( operator ) )
|
||||
{
|
||||
exp.typ = LESSER_EXP;
|
||||
}
|
||||
else if ( "xor".equals( operator ) )
|
||||
{
|
||||
exp.typ = XOR_EXP;
|
||||
}
|
||||
else
|
||||
{
|
||||
nops = 1; // check unary expressions
|
||||
if ( "assign".equals( operator ) )
|
||||
{
|
||||
if ( level > 0 ) throw new IllegalArgumentException( "assign operator within expression" );
|
||||
exp.typ = ASSIGN_EXP;
|
||||
String variable = ctx.parseToken();
|
||||
if ( variable == null ) throw new IllegalArgumentException( "unexpected end of file" );
|
||||
if ( variable.indexOf( '=' ) >= 0 ) throw new IllegalArgumentException( "variable name cannot contain '=': " + variable );
|
||||
if ( variable.indexOf( ':' ) >= 0 ) throw new IllegalArgumentException( "cannot assign context-prefixed variable: " + variable );
|
||||
exp.variableIdx = ctx.getVariableIdx( variable, true );
|
||||
if ( exp.variableIdx < ctx.getMinWriteIdx() ) throw new IllegalArgumentException( "cannot assign to readonly variable " + variable );
|
||||
}
|
||||
else if ( "not".equals( operator ) )
|
||||
{
|
||||
exp.typ = NOT_EXP;
|
||||
}
|
||||
else
|
||||
{
|
||||
nops = 0; // check elemantary expressions
|
||||
int idx = operator.indexOf( '=' );
|
||||
if ( idx >= 0 )
|
||||
{
|
||||
exp.typ = LOOKUP_EXP;
|
||||
String name = operator.substring( 0, idx );
|
||||
String values = operator.substring( idx+1 );
|
||||
|
||||
exp.lookupNameIdx = ctx.getLookupNameIdx( name );
|
||||
if ( exp.lookupNameIdx < 0 )
|
||||
{
|
||||
throw new IllegalArgumentException( "unknown lookup name: " + name );
|
||||
}
|
||||
ctx.markLookupIdxUsed( exp.lookupNameIdx );
|
||||
StringTokenizer tk = new StringTokenizer( values, "|" );
|
||||
int nt = tk.countTokens();
|
||||
int nt2 = nt == 0 ? 1 : nt;
|
||||
exp.lookupValueIdxArray = new int[nt2];
|
||||
for( int ti=0; ti<nt2; ti++ )
|
||||
{
|
||||
String value = ti < nt ? tk.nextToken() : "";
|
||||
exp.lookupValueIdxArray[ti] = ctx.getLookupValueIdx( exp.lookupNameIdx, value );
|
||||
if ( exp.lookupValueIdxArray[ti] < 0 )
|
||||
{
|
||||
throw new IllegalArgumentException( "unknown lookup value: " + value );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( ( idx = operator.indexOf( ':' ) ) >= 0 )
|
||||
{
|
||||
/*
|
||||
use of variable values
|
||||
assign no_height
|
||||
switch and not maxheight=
|
||||
lesser v:maxheight my_height true
|
||||
false
|
||||
*/
|
||||
if (operator.startsWith("v:")) {
|
||||
String name = operator.substring(2);
|
||||
exp.typ = VARIABLE_GET_EXP;
|
||||
exp.lookupNameIdx = ctx.getLookupNameIdx( name );
|
||||
} else {
|
||||
String context = operator.substring( 0, idx );
|
||||
String varname = operator.substring( idx+1 );
|
||||
exp.typ = FOREIGN_VARIABLE_EXP;
|
||||
exp.variableIdx = ctx.getForeignVariableIdx( context, varname );
|
||||
}
|
||||
}
|
||||
else if ( (idx = ctx.getVariableIdx( operator, false )) >= 0 )
|
||||
{
|
||||
exp.typ = VARIABLE_EXP;
|
||||
exp.variableIdx = idx;
|
||||
}
|
||||
else if ( "true".equals( operator ) )
|
||||
{
|
||||
exp.numberValue = 1.f;
|
||||
exp.typ = NUMBER_EXP;
|
||||
}
|
||||
else if ( "false".equals( operator ) )
|
||||
{
|
||||
exp.numberValue = 0.f;
|
||||
exp.typ = NUMBER_EXP;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
exp.numberValue = Float.parseFloat( operator );
|
||||
exp.typ = NUMBER_EXP;
|
||||
}
|
||||
catch( NumberFormatException nfe )
|
||||
{
|
||||
throw new IllegalArgumentException( "unknown expression: " + operator );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// parse operands
|
||||
if ( nops > 0 )
|
||||
{
|
||||
exp.op1 = BExpression.parse( ctx, level+1, exp.typ == ASSIGN_EXP ? "=" : null );
|
||||
}
|
||||
if ( nops > 1 )
|
||||
{
|
||||
if ( ifThenElse ) checkExpectedToken( ctx, "then" );
|
||||
exp.op2 = BExpression.parse( ctx, level+1, null );
|
||||
}
|
||||
if ( nops > 2 )
|
||||
{
|
||||
if ( ifThenElse ) checkExpectedToken( ctx, "else" );
|
||||
exp.op3 = BExpression.parse( ctx, level+1, null );
|
||||
}
|
||||
if ( brackets )
|
||||
{
|
||||
checkExpectedToken( ctx, ")" );
|
||||
}
|
||||
return exp;
|
||||
}
|
||||
|
||||
private static void checkExpectedToken( BExpressionContext ctx, String expected ) throws Exception
|
||||
{
|
||||
String token = ctx.parseToken();
|
||||
if ( ! expected.equals( token ) )
|
||||
{
|
||||
throw new IllegalArgumentException( "unexpected token: " + token + ", expected: " + expected );
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate the expression
|
||||
public float evaluate( BExpressionContext ctx )
|
||||
{
|
||||
switch( typ )
|
||||
{
|
||||
case OR_EXP: return op1.evaluate(ctx) != 0.f ? 1.f : ( op2.evaluate(ctx) != 0.f ? 1.f : 0.f );
|
||||
case XOR_EXP: return ( (op1.evaluate(ctx) != 0.f) ^ ( op2.evaluate(ctx) != 0.f ) ? 1.f : 0.f );
|
||||
case AND_EXP: return op1.evaluate(ctx) != 0.f ? ( op2.evaluate(ctx) != 0.f ? 1.f : 0.f ) : 0.f;
|
||||
case ADD_EXP: return op1.evaluate(ctx) + op2.evaluate(ctx);
|
||||
case SUB_EXP: return op1.evaluate(ctx) - op2.evaluate(ctx);
|
||||
case MULTIPLY_EXP: return op1.evaluate(ctx) * op2.evaluate(ctx);
|
||||
case MAX_EXP: return max( op1.evaluate(ctx), op2.evaluate(ctx) );
|
||||
case MIN_EXP: return min( op1.evaluate(ctx), op2.evaluate(ctx) );
|
||||
case EQUAL_EXP: return op1.evaluate(ctx) == op2.evaluate(ctx) ? 1.f : 0.f;
|
||||
case GREATER_EXP: return op1.evaluate(ctx) > op2.evaluate(ctx) ? 1.f : 0.f;
|
||||
case LESSER_EXP: return op1.evaluate(ctx) < op2.evaluate(ctx) ? 1.f : 0.f;
|
||||
case SWITCH_EXP: return op1.evaluate(ctx) != 0.f ? op2.evaluate(ctx) : op3.evaluate(ctx);
|
||||
case ASSIGN_EXP: return ctx.assign( variableIdx, op1.evaluate(ctx) );
|
||||
case LOOKUP_EXP: return ctx.getLookupMatch( lookupNameIdx, lookupValueIdxArray );
|
||||
case NUMBER_EXP: return numberValue;
|
||||
case VARIABLE_EXP: return ctx.getVariableValue( variableIdx );
|
||||
case FOREIGN_VARIABLE_EXP: return ctx.getForeignVariableValue( variableIdx );
|
||||
case VARIABLE_GET_EXP: return ctx.getLookupValue(lookupNameIdx);
|
||||
case NOT_EXP: return op1.evaluate(ctx) == 0.f ? 1.f : 0.f;
|
||||
default: throw new IllegalArgumentException( "unknown op-code: " + typ );
|
||||
}
|
||||
}
|
||||
|
||||
private float max( float v1, float v2 )
|
||||
{
|
||||
return v1 > v2 ? v1 : v2;
|
||||
}
|
||||
|
||||
private float min( float v1, float v2 )
|
||||
{
|
||||
return v1 < v2 ? v1 : v2;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -7,29 +7,32 @@
|
|||
package btools.expressions;
|
||||
|
||||
|
||||
public final class BExpressionContextNode extends BExpressionContext {
|
||||
private static String[] buildInVariables =
|
||||
{"initialcost"};
|
||||
|
||||
protected String[] getBuildInVariableNames() {
|
||||
public final class BExpressionContextNode extends BExpressionContext
|
||||
{
|
||||
private static String[] buildInVariables =
|
||||
{ "initialcost" };
|
||||
|
||||
protected String[] getBuildInVariableNames()
|
||||
{
|
||||
return buildInVariables;
|
||||
}
|
||||
|
||||
public float getInitialcost() {
|
||||
return getBuildInVariable(0);
|
||||
}
|
||||
public float getInitialcost() { return getBuildInVariable(0); }
|
||||
|
||||
|
||||
public BExpressionContextNode(BExpressionMetaData meta) {
|
||||
super("node", meta);
|
||||
public BExpressionContextNode( BExpressionMetaData meta )
|
||||
{
|
||||
super( "node", meta );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Expression-Context for way context
|
||||
*
|
||||
* @param hashSize size of hashmap for result caching
|
||||
* @param hashSize size of hashmap for result caching
|
||||
*/
|
||||
public BExpressionContextNode(int hashSize, BExpressionMetaData meta) {
|
||||
super("node", hashSize, meta);
|
||||
public BExpressionContextNode( int hashSize, BExpressionMetaData meta )
|
||||
{
|
||||
super( "node", hashSize, meta );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,125 +8,66 @@ package btools.expressions;
|
|||
|
||||
import btools.codec.TagValueValidator;
|
||||
|
||||
public final class BExpressionContextWay extends BExpressionContext implements TagValueValidator {
|
||||
public final class BExpressionContextWay extends BExpressionContext implements TagValueValidator
|
||||
{
|
||||
private boolean decodeForbidden = true;
|
||||
|
||||
private static String[] buildInVariables =
|
||||
{"costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "classifiermask", "maxspeed", "uphillcost", "downhillcost", "uphillcutoff", "downhillcutoff", "uphillmaxslope", "downhillmaxslope", "uphillmaxslopecost", "downhillmaxslopecost"};
|
||||
|
||||
protected String[] getBuildInVariableNames() {
|
||||
{ "costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "classifiermask", "maxspeed" };
|
||||
|
||||
protected String[] getBuildInVariableNames()
|
||||
{
|
||||
return buildInVariables;
|
||||
}
|
||||
|
||||
public float getCostfactor() {
|
||||
return getBuildInVariable(0);
|
||||
}
|
||||
public float getCostfactor() { return getBuildInVariable(0); }
|
||||
public float getTurncost() { return getBuildInVariable(1); }
|
||||
public float getUphillCostfactor() { return getBuildInVariable(2); }
|
||||
public float getDownhillCostfactor() { return getBuildInVariable(3); }
|
||||
public float getInitialcost() { return getBuildInVariable(4); }
|
||||
public float getNodeAccessGranted() { return getBuildInVariable(5); }
|
||||
public float getInitialClassifier() { return getBuildInVariable(6); }
|
||||
public float getTrafficSourceDensity() { return getBuildInVariable(7); }
|
||||
public float getIsTrafficBackbone() { return getBuildInVariable(8); }
|
||||
public float getPriorityClassifier() { return getBuildInVariable(9); }
|
||||
public float getClassifierMask() { return getBuildInVariable(10); }
|
||||
public float getMaxspeed() { return getBuildInVariable(11); }
|
||||
|
||||
public float getTurncost() {
|
||||
return getBuildInVariable(1);
|
||||
}
|
||||
|
||||
public float getUphillCostfactor() {
|
||||
return getBuildInVariable(2);
|
||||
}
|
||||
|
||||
public float getDownhillCostfactor() {
|
||||
return getBuildInVariable(3);
|
||||
}
|
||||
|
||||
public float getInitialcost() {
|
||||
return getBuildInVariable(4);
|
||||
}
|
||||
|
||||
public float getNodeAccessGranted() {
|
||||
return getBuildInVariable(5);
|
||||
}
|
||||
|
||||
public float getInitialClassifier() {
|
||||
return getBuildInVariable(6);
|
||||
}
|
||||
|
||||
public float getTrafficSourceDensity() {
|
||||
return getBuildInVariable(7);
|
||||
}
|
||||
|
||||
public float getIsTrafficBackbone() {
|
||||
return getBuildInVariable(8);
|
||||
}
|
||||
|
||||
public float getPriorityClassifier() {
|
||||
return getBuildInVariable(9);
|
||||
}
|
||||
|
||||
public float getClassifierMask() {
|
||||
return getBuildInVariable(10);
|
||||
}
|
||||
|
||||
public float getMaxspeed() {
|
||||
return getBuildInVariable(11);
|
||||
}
|
||||
|
||||
public float getUphillcost() {
|
||||
return getBuildInVariable(12);
|
||||
}
|
||||
|
||||
public float getDownhillcost() {
|
||||
return getBuildInVariable(13);
|
||||
}
|
||||
|
||||
public float getUphillcutoff() {
|
||||
return getBuildInVariable(14);
|
||||
}
|
||||
|
||||
public float getDownhillcutoff() {
|
||||
return getBuildInVariable(15);
|
||||
}
|
||||
|
||||
public float getUphillmaxslope() {
|
||||
return getBuildInVariable(16);
|
||||
}
|
||||
|
||||
public float getDownhillmaxslope() {
|
||||
return getBuildInVariable(17);
|
||||
}
|
||||
|
||||
public float getUphillmaxslopecost() {
|
||||
return getBuildInVariable(18);
|
||||
}
|
||||
|
||||
public float getDownhillmaxslopecost() {
|
||||
return getBuildInVariable(19);
|
||||
}
|
||||
|
||||
public BExpressionContextWay(BExpressionMetaData meta) {
|
||||
super("way", meta);
|
||||
public BExpressionContextWay( BExpressionMetaData meta )
|
||||
{
|
||||
super( "way", meta );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Expression-Context for way context
|
||||
*
|
||||
* @param hashSize size of hashmap for result caching
|
||||
* @param hashSize size of hashmap for result caching
|
||||
*/
|
||||
public BExpressionContextWay(int hashSize, BExpressionMetaData meta) {
|
||||
super("way", hashSize, meta);
|
||||
public BExpressionContextWay( int hashSize, BExpressionMetaData meta )
|
||||
{
|
||||
super( "way", hashSize, meta );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int accessType(byte[] description) {
|
||||
evaluate(false, description);
|
||||
public int accessType( byte[] description )
|
||||
{
|
||||
evaluate( false, description );
|
||||
float minCostFactor = getCostfactor();
|
||||
if (minCostFactor >= 9999.f) {
|
||||
if ( minCostFactor >= 9999.f )
|
||||
{
|
||||
setInverseVars();
|
||||
float reverseCostFactor = getCostfactor();
|
||||
if (reverseCostFactor < minCostFactor) {
|
||||
if ( reverseCostFactor < minCostFactor )
|
||||
{
|
||||
minCostFactor = reverseCostFactor;
|
||||
}
|
||||
}
|
||||
return minCostFactor < 9999.f ? 2 : decodeForbidden ? (minCostFactor < 10000.f ? 1 : 0) : 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setDecodeForbidden(boolean decodeForbidden) {
|
||||
this.decodeForbidden = decodeForbidden;
|
||||
public void setDecodeForbidden( boolean decodeForbidden )
|
||||
{
|
||||
this.decodeForbidden= decodeForbidden;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,57 +1,66 @@
|
|||
/**
|
||||
* A lookup value with optional aliases
|
||||
* <p>
|
||||
* toString just gives the primary value,
|
||||
* equals just compares against primary value
|
||||
* matches() also compares aliases
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
package btools.expressions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
final class BExpressionLookupValue {
|
||||
String value;
|
||||
List<String> aliases;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public BExpressionLookupValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void addAlias(String alias) {
|
||||
if (aliases == null) aliases = new ArrayList<>();
|
||||
aliases.add(alias);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof String) {
|
||||
String v = (String) o;
|
||||
return value.equals(v);
|
||||
}
|
||||
if (o instanceof BExpressionLookupValue) {
|
||||
BExpressionLookupValue v = (BExpressionLookupValue) o;
|
||||
|
||||
return value.equals(v.value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean matches(String s) {
|
||||
if (value.equals(s)) return true;
|
||||
if (aliases != null) {
|
||||
for (String alias : aliases) {
|
||||
if (alias.equals(s)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* A lookup value with optional aliases
|
||||
*
|
||||
* toString just gives the primary value,
|
||||
* equals just compares against primary value
|
||||
* matches() also compares aliases
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
package btools.expressions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
final class BExpressionLookupValue
|
||||
{
|
||||
String value;
|
||||
ArrayList<String> aliases;
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
public BExpressionLookupValue( String value )
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void addAlias( String alias )
|
||||
{
|
||||
if ( aliases == null ) aliases = new ArrayList<String>();
|
||||
aliases.add( alias );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals( Object o )
|
||||
{
|
||||
if ( o instanceof String )
|
||||
{
|
||||
String v = (String)o;
|
||||
return value.equals( v );
|
||||
}
|
||||
if ( o instanceof BExpressionLookupValue )
|
||||
{
|
||||
BExpressionLookupValue v = (BExpressionLookupValue)o;
|
||||
|
||||
return value.equals( v.value );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean matches( String s )
|
||||
{
|
||||
if ( value.equals( s ) ) return true;
|
||||
if ( aliases != null )
|
||||
{
|
||||
for( String alias : aliases )
|
||||
{
|
||||
if ( alias.equals( s ) ) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,67 +9,81 @@ package btools.expressions;
|
|||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import btools.util.BitCoderContext;
|
||||
import btools.util.Crc32;
|
||||
|
||||
|
||||
public final class BExpressionMetaData {
|
||||
private static final String CONTEXT_TAG = "---context:";
|
||||
public final class BExpressionMetaData
|
||||
{
|
||||
private static final String CONTEXT_TAG = "---context:";
|
||||
private static final String VERSION_TAG = "---lookupversion:";
|
||||
private static final String MINOR_VERSION_TAG = "---minorversion:";
|
||||
private static final String VARLENGTH_TAG = "---readvarlength";
|
||||
private static final String MIN_APP_VERSION_TAG = "---minappversion:";
|
||||
|
||||
public short lookupVersion = -1;
|
||||
public short lookupMinorVersion = -1;
|
||||
public short minAppVersion = -1;
|
||||
|
||||
private Map<String, BExpressionContext> listeners = new HashMap<>();
|
||||
|
||||
public void registerListener(String context, BExpressionContext ctx) {
|
||||
listeners.put(context, ctx);
|
||||
private HashMap<String,BExpressionContext> listeners = new HashMap<String,BExpressionContext>();
|
||||
|
||||
public void registerListener( String context, BExpressionContext ctx )
|
||||
{
|
||||
listeners.put( context, ctx );
|
||||
}
|
||||
|
||||
public void readMetaData(File lookupsFile) {
|
||||
try {
|
||||
BufferedReader br = new BufferedReader(new FileReader(lookupsFile));
|
||||
|
||||
BExpressionContext ctx = null;
|
||||
|
||||
for (; ; ) {
|
||||
String line = br.readLine();
|
||||
if (line == null) break;
|
||||
line = line.trim();
|
||||
if (line.length() == 0 || line.startsWith("#")) continue;
|
||||
if (line.startsWith(CONTEXT_TAG)) {
|
||||
ctx = listeners.get(line.substring(CONTEXT_TAG.length()));
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith(VERSION_TAG)) {
|
||||
lookupVersion = Short.parseShort(line.substring(VERSION_TAG.length()));
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith(MINOR_VERSION_TAG)) {
|
||||
lookupMinorVersion = Short.parseShort(line.substring(MINOR_VERSION_TAG.length()));
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith(MIN_APP_VERSION_TAG)) {
|
||||
minAppVersion = Short.parseShort(line.substring(MIN_APP_VERSION_TAG.length()));
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith(VARLENGTH_TAG)) { // tag removed...
|
||||
continue;
|
||||
}
|
||||
if (ctx != null) ctx.parseMetaLine(line);
|
||||
|
||||
public void readMetaData( File lookupsFile )
|
||||
{
|
||||
try
|
||||
{
|
||||
BufferedReader br = new BufferedReader( new FileReader( lookupsFile ) );
|
||||
|
||||
BExpressionContext ctx = null;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
String line = br.readLine();
|
||||
if ( line == null ) break;
|
||||
line = line.trim();
|
||||
if ( line.length() == 0 || line.startsWith( "#" ) ) continue;
|
||||
if ( line.startsWith( CONTEXT_TAG ) )
|
||||
{
|
||||
ctx = listeners.get( line.substring( CONTEXT_TAG.length() ) );
|
||||
continue;
|
||||
}
|
||||
br.close();
|
||||
|
||||
for (BExpressionContext c : listeners.values()) {
|
||||
c.finishMetaParsing();
|
||||
if ( line.startsWith( VERSION_TAG ) )
|
||||
{
|
||||
lookupVersion = Short.parseShort( line.substring( VERSION_TAG.length() ) );
|
||||
continue;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
if ( line.startsWith( MINOR_VERSION_TAG ) )
|
||||
{
|
||||
lookupMinorVersion = Short.parseShort( line.substring( MINOR_VERSION_TAG.length() ) );
|
||||
continue;
|
||||
}
|
||||
if ( line.startsWith( VARLENGTH_TAG ) ) // tag removed...
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ( ctx != null ) ctx.parseMetaLine( line );
|
||||
}
|
||||
br.close();
|
||||
|
||||
for( BExpressionContext c : listeners.values() )
|
||||
{
|
||||
c.finishMetaParsing();
|
||||
}
|
||||
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
throw new RuntimeException( e );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,24 +4,29 @@ import java.util.Arrays;
|
|||
|
||||
import btools.util.LruMapNode;
|
||||
|
||||
public final class CacheNode extends LruMapNode {
|
||||
public final class CacheNode extends LruMapNode
|
||||
{
|
||||
byte[] ab;
|
||||
float[] vars;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
public int hashCode()
|
||||
{
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
public boolean equals( Object o )
|
||||
{
|
||||
CacheNode n = (CacheNode) o;
|
||||
if (hash != n.hash) {
|
||||
if ( hash != n.hash )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (ab == null) {
|
||||
if ( ab == null )
|
||||
{
|
||||
return true; // hack: null = crc match only
|
||||
}
|
||||
return Arrays.equals(ab, n.ab);
|
||||
return Arrays.equals( ab, n.ab );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -3,50 +3,45 @@ package btools.expressions;
|
|||
import java.io.File;
|
||||
import java.util.Random;
|
||||
|
||||
public final class ProfileComparator {
|
||||
public static void main(String[] args) {
|
||||
if (args.length != 4) {
|
||||
System.out.println("usage: java ProfileComparator <lookup-file> <profile1> <profile2> <nsamples>");
|
||||
public final class ProfileComparator
|
||||
{
|
||||
public static void main( String[] args )
|
||||
{
|
||||
if ( args.length != 4 )
|
||||
{
|
||||
System.out.println( "usage: java ProfileComparator <lookup-file> <profile1> <profile2> <nsamples>" );
|
||||
return;
|
||||
}
|
||||
|
||||
File lookupFile = new File(args[0]);
|
||||
File profile1File = new File(args[1]);
|
||||
File profile2File = new File(args[2]);
|
||||
int nsamples = Integer.parseInt(args[3]);
|
||||
testContext(lookupFile, profile1File, profile2File, nsamples, false);
|
||||
testContext(lookupFile, profile1File, profile2File, nsamples, true);
|
||||
File lookupFile = new File( args[0] );
|
||||
File profile1File = new File( args[1] );
|
||||
File profile2File = new File( args[2] );
|
||||
int nsamples = Integer.parseInt( args[3] );
|
||||
testContext( lookupFile, profile1File, profile2File, nsamples, false );
|
||||
testContext( lookupFile, profile1File, profile2File, nsamples, true );
|
||||
|
||||
}
|
||||
|
||||
private static void testContext(File lookupFile, File profile1File, File profile2File, int nsamples, boolean nodeContext) {
|
||||
|
||||
private static void testContext( File lookupFile, File profile1File, File profile2File, int nsamples, boolean nodeContext )
|
||||
{
|
||||
// read lookup.dat + profiles
|
||||
BExpressionMetaData meta1 = new BExpressionMetaData();
|
||||
BExpressionMetaData meta2 = new BExpressionMetaData();
|
||||
BExpressionContext expctx1 = nodeContext ? new BExpressionContextNode(meta1) : new BExpressionContextWay(meta1);
|
||||
BExpressionContext expctx2 = nodeContext ? new BExpressionContextNode(meta2) : new BExpressionContextWay(meta2);
|
||||
|
||||
// if same profiles, compare different optimization levels
|
||||
if (profile1File.getName().equals(profile2File.getName())) {
|
||||
expctx2.skipConstantExpressionOptimizations = true;
|
||||
}
|
||||
|
||||
meta1.readMetaData(lookupFile);
|
||||
meta2.readMetaData(lookupFile);
|
||||
expctx1.parseFile(profile1File, "global");
|
||||
System.out.println("usedTags1=" + expctx1.usedTagList());
|
||||
expctx2.parseFile(profile2File, "global");
|
||||
System.out.println("usedTags2=" + expctx2.usedTagList());
|
||||
|
||||
System.out.println("nodeContext=" + nodeContext + " nodeCount1=" + expctx1.expressionNodeCount + " nodeCount2=" + expctx2.expressionNodeCount);
|
||||
BExpressionContext expctx1 = nodeContext ? new BExpressionContextNode( meta1 ) : new BExpressionContextWay( meta1 );
|
||||
BExpressionContext expctx2 = nodeContext ? new BExpressionContextNode( meta2 ) : new BExpressionContextWay( meta2 );
|
||||
meta1.readMetaData( lookupFile );
|
||||
meta2.readMetaData( lookupFile );
|
||||
expctx1.parseFile( profile1File, "global" );
|
||||
expctx2.parseFile( profile2File, "global" );
|
||||
|
||||
Random rnd = new Random();
|
||||
for (int i = 0; i < nsamples; i++) {
|
||||
int[] data = expctx1.generateRandomValues(rnd);
|
||||
expctx1.evaluate(data);
|
||||
expctx2.evaluate(data);
|
||||
|
||||
expctx1.assertAllVariablesEqual(expctx2);
|
||||
for( int i=0; i<nsamples; i++ )
|
||||
{
|
||||
int[] data = expctx1.generateRandomValues( rnd );
|
||||
expctx1.evaluate( data );
|
||||
expctx2.evaluate( data );
|
||||
|
||||
expctx1.assertAllVariablesEqual( expctx2 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,20 +4,24 @@ import java.util.Arrays;
|
|||
|
||||
import btools.util.LruMapNode;
|
||||
|
||||
public final class VarWrapper extends LruMapNode {
|
||||
public final class VarWrapper extends LruMapNode
|
||||
{
|
||||
float[] vars;
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
public int hashCode()
|
||||
{
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
public boolean equals( Object o )
|
||||
{
|
||||
VarWrapper n = (VarWrapper) o;
|
||||
if (hash != n.hash) {
|
||||
if ( hash != n.hash )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Arrays.equals(vars, n.vars);
|
||||
return Arrays.equals( vars, n.vars );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +1,60 @@
|
|||
package btools.expressions;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
|
||||
public class EncodeDecodeTest {
|
||||
public class EncodeDecodeTest
|
||||
{
|
||||
@Test
|
||||
public void encodeDecodeTest() {
|
||||
URL testpurl = this.getClass().getResource("/dummy.txt");
|
||||
public void encodeDecodeTest()
|
||||
{
|
||||
URL testpurl = this.getClass().getResource( "/dummy.txt" );
|
||||
File workingDir = new File(testpurl.getFile()).getParentFile();
|
||||
File profileDir = new File(workingDir, "/../../../../misc/profiles2");
|
||||
File profileDir = new File( workingDir, "/../../../../misc/profiles2" );
|
||||
//File lookupFile = new File( profileDir, "lookups.dat" );
|
||||
// add a test lookup
|
||||
URL testlookup = this.getClass().getResource("/lookups_test.dat");
|
||||
File lookupFile = new File(testlookup.getPath());
|
||||
|
||||
// add a test lookup
|
||||
URL testlookup = this.getClass().getResource( "/lookups_test.dat" );
|
||||
File lookupFile = new File( testlookup.getPath() );
|
||||
|
||||
// read lookup.dat + trekking.brf
|
||||
BExpressionMetaData meta = new BExpressionMetaData();
|
||||
BExpressionContextWay expctxWay = new BExpressionContextWay(meta);
|
||||
meta.readMetaData(lookupFile);
|
||||
expctxWay.parseFile(new File(profileDir, "trekking.brf"), "global");
|
||||
BExpressionContextWay expctxWay = new BExpressionContextWay( meta );
|
||||
meta.readMetaData( lookupFile );
|
||||
expctxWay.parseFile( new File( profileDir, "trekking.brf" ), "global" );
|
||||
|
||||
String[] tags = {
|
||||
"highway=residential",
|
||||
"oneway=yes",
|
||||
"depth=1'6\"",
|
||||
// "depth=6 feet",
|
||||
"maxheight=5.1m",
|
||||
"maxdraft=~3 m - 4 m",
|
||||
"reversedirection=yes"
|
||||
"highway=residential",
|
||||
"oneway=yes",
|
||||
"depth=1'6\"",
|
||||
// "depth=6 feet",
|
||||
"maxheight=5.1m",
|
||||
"maxdraft=~3 mt",
|
||||
"reversedirection=yes"
|
||||
};
|
||||
|
||||
// encode the tags into 64 bit description word
|
||||
int[] lookupData = expctxWay.createNewLookupData();
|
||||
for (String arg : tags) {
|
||||
int idx = arg.indexOf('=');
|
||||
if (idx < 0)
|
||||
throw new IllegalArgumentException("bad argument (should be <tag>=<value>): " + arg);
|
||||
String key = arg.substring(0, idx);
|
||||
String value = arg.substring(idx + 1);
|
||||
|
||||
expctxWay.addLookupValue(key, value, lookupData);
|
||||
for( String arg: tags )
|
||||
{
|
||||
int idx = arg.indexOf( '=' );
|
||||
if ( idx < 0 ) throw new IllegalArgumentException( "bad argument (should be <tag>=<value>): " + arg );
|
||||
String key = arg.substring( 0, idx );
|
||||
String value = arg.substring( idx+1 );
|
||||
|
||||
expctxWay.addLookupValue( key, value, lookupData );
|
||||
}
|
||||
byte[] description = expctxWay.encode(lookupData);
|
||||
|
||||
// calculate the cost factor from that description
|
||||
expctxWay.evaluate(true, description); // true = "reversedirection=yes" (not encoded in description anymore)
|
||||
expctxWay.evaluate( true, description ); // true = "reversedirection=yes" (not encoded in description anymore)
|
||||
|
||||
System.out.println("description: " + expctxWay.getKeyValueDescription(true, description));
|
||||
System.out.println( "description: " + expctxWay.getKeyValueDescription(true, description) );
|
||||
|
||||
float costfactor = expctxWay.getCostfactor();
|
||||
Assert.assertTrue("costfactor mismatch", Math.abs(costfactor - 5.15) < 0.00001);
|
||||
Assert.assertTrue( "costfactor mismatch", Math.abs( costfactor - 5.15 ) < 0.00001 );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -687,35 +687,6 @@ construction;0000000037 driveway
|
|||
construction;0000000021 mini_roundabout
|
||||
construction;0000000020 turning_loop
|
||||
|
||||
estimated_forest_class;0000000001 1
|
||||
estimated_forest_class;0000000001 2
|
||||
estimated_forest_class;0000000001 3
|
||||
estimated_forest_class;0000000001 4
|
||||
estimated_forest_class;0000000001 5
|
||||
estimated_forest_class;0000000001 6
|
||||
|
||||
estimated_noise_class;0000000001 1
|
||||
estimated_noise_class;0000000001 2
|
||||
estimated_noise_class;0000000001 3
|
||||
estimated_noise_class;0000000001 4
|
||||
estimated_noise_class;0000000001 5
|
||||
estimated_noise_class;0000000001 6
|
||||
|
||||
estimated_river_class;0000000001 1
|
||||
estimated_river_class;0000000001 2
|
||||
estimated_river_class;0000000001 3
|
||||
estimated_river_class;0000000001 4
|
||||
estimated_river_class;0000000001 5
|
||||
estimated_river_class;0000000001 6
|
||||
|
||||
estimated_town_class;0000000001 1
|
||||
estimated_town_class;0000000001 2
|
||||
estimated_town_class;0000000001 3
|
||||
estimated_town_class;0000000001 4
|
||||
estimated_town_class;0000000001 5
|
||||
estimated_town_class;0000000001 6
|
||||
|
||||
|
||||
---context:node
|
||||
|
||||
highway;0001314954 bus_stop
|
||||
|
|
|
|||
|
|
@ -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
1
brouter-map-creator/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build/
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
plugins {
|
||||
id 'brouter.application-conventions'
|
||||
id 'java-library'
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
implementation project(':brouter-codec')
|
||||
implementation project(':brouter-util')
|
||||
implementation project(':brouter-expressions')
|
||||
|
||||
implementation group: 'org.openstreetmap.osmosis', name: 'osmosis-osm-binary', version: '0.48.3'
|
||||
|
||||
testImplementation('junit:junit:4.13.1')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
@ -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 );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -5,67 +5,80 @@
|
|||
*/
|
||||
package btools.mapcreator;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import btools.util.CheapRuler;
|
||||
|
||||
public class DPFilter {
|
||||
public class DPFilter
|
||||
{
|
||||
private static double dp_sql_threshold = 0.4 * 0.4;
|
||||
|
||||
/*
|
||||
* for each node (except first+last), eventually set the DP_SURVIVOR_BIT
|
||||
*/
|
||||
public static void doDPFilter(List<OsmNodeP> nodes) {
|
||||
public static void doDPFilter( ArrayList<OsmNodeP> nodes )
|
||||
{
|
||||
int first = 0;
|
||||
int last = nodes.size() - 1;
|
||||
while (first < last && (nodes.get(first + 1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0) {
|
||||
int last = nodes.size()-1;
|
||||
while( first < last && (nodes.get(first+1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0 )
|
||||
{
|
||||
first++;
|
||||
}
|
||||
while (first < last && (nodes.get(last - 1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0) {
|
||||
while( first < last && (nodes.get(last-1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0 )
|
||||
{
|
||||
last--;
|
||||
}
|
||||
if (last - first > 1) {
|
||||
doDPFilter(nodes, first, last);
|
||||
if ( last - first > 1 )
|
||||
{
|
||||
doDPFilter( nodes, first, last );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void doDPFilter(List<OsmNodeP> nodes, int first, int last) {
|
||||
public static void doDPFilter( ArrayList<OsmNodeP> nodes, int first, int last )
|
||||
{
|
||||
double maxSqDist = -1.;
|
||||
int index = -1;
|
||||
OsmNodeP p1 = nodes.get(first);
|
||||
OsmNodeP p2 = nodes.get(last);
|
||||
OsmNodeP p1 = nodes.get( first );
|
||||
OsmNodeP p2 = nodes.get( last );
|
||||
|
||||
double[] lonlat2m = CheapRuler.getLonLatToMeterScales((p1.ilat + p2.ilat) >> 1);
|
||||
double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (p1.ilat+p2.ilat) >> 1 );
|
||||
double dlon2m = lonlat2m[0];
|
||||
double dlat2m = lonlat2m[1];
|
||||
double dx = (p2.ilon - p1.ilon) * dlon2m;
|
||||
double dy = (p2.ilat - p1.ilat) * dlat2m;
|
||||
double d2 = dx * dx + dy * dy;
|
||||
for (int i = first + 1; i < last; i++) {
|
||||
OsmNodeP p = nodes.get(i);
|
||||
for ( int i = first + 1; i < last; i++ )
|
||||
{
|
||||
OsmNodeP p = nodes.get( i );
|
||||
double t = 0.;
|
||||
if (d2 != 0f) {
|
||||
t = ((p.ilon - p1.ilon) * dlon2m * dx + (p.ilat - p1.ilat) * dlat2m * dy) / d2;
|
||||
t = t > 1. ? 1. : (t < 0. ? 0. : t);
|
||||
if ( d2 != 0f )
|
||||
{
|
||||
t = ( ( p.ilon - p1.ilon ) * dlon2m * dx + ( p.ilat - p1.ilat ) * dlat2m * dy ) / d2;
|
||||
t = t > 1. ? 1. : ( t < 0. ? 0. : t );
|
||||
}
|
||||
double dx2 = (p.ilon - (p1.ilon + t * (p2.ilon - p1.ilon))) * dlon2m;
|
||||
double dy2 = (p.ilat - (p1.ilat + t * (p2.ilat - p1.ilat))) * dlat2m;
|
||||
double dx2 = (p.ilon - ( p1.ilon + t*( p2.ilon - p1.ilon ) ) ) * dlon2m;
|
||||
double dy2 = (p.ilat - ( p1.ilat + t*( p2.ilat - p1.ilat ) ) ) * dlat2m;
|
||||
double sqDist = dx2 * dx2 + dy2 * dy2;
|
||||
if (sqDist > maxSqDist) {
|
||||
if ( sqDist > maxSqDist )
|
||||
{
|
||||
index = i;
|
||||
maxSqDist = sqDist;
|
||||
}
|
||||
}
|
||||
if (index >= 0) {
|
||||
if (index - first > 1) {
|
||||
doDPFilter(nodes, first, index);
|
||||
if ( index >= 0 )
|
||||
{
|
||||
if ( index - first > 1 )
|
||||
{
|
||||
doDPFilter( nodes, first, index );
|
||||
}
|
||||
if (maxSqDist >= dp_sql_threshold) {
|
||||
nodes.get(index).bits |= OsmNodeP.DP_SURVIVOR_BIT;
|
||||
if ( maxSqDist >= dp_sql_threshold )
|
||||
{
|
||||
nodes.get( index ).bits |= OsmNodeP.DP_SURVIVOR_BIT;
|
||||
}
|
||||
if (last - index > 1) {
|
||||
doDPFilter(nodes, index, last);
|
||||
if ( last - index > 1 )
|
||||
{
|
||||
doDPFilter( nodes, index, last );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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 <oliver.wieland@online.de>
|
||||
*/
|
||||
public class HgtReader {
|
||||
final static boolean DEBUG = false;
|
||||
|
||||
private static final int SECONDS_PER_MINUTE = 60;
|
||||
|
||||
public static final String HGT_EXT = ".hgt";
|
||||
public static final String ZIP_EXT = ".zip";
|
||||
|
||||
// alter these values for different SRTM resolutions
|
||||
public static final int HGT3_RES = 3; // resolution in arc seconds
|
||||
public static final int HGT3_ROW_LENGTH = 1201; // number of elevation values per line
|
||||
public static final int HGT_VOID = -32768; // magic number which indicates 'void data' in HGT file
|
||||
public static final int HGT1_RES = 1; // <<- The new SRTM is 1-ARCSEC
|
||||
public static final int HGT1_ROW_LENGTH = 3601; //-- New file resolution is 3601x3601
|
||||
/**
|
||||
* The 'no elevation' data magic.
|
||||
*/
|
||||
public static double NO_ELEVATION = Double.NaN;
|
||||
|
||||
private static String srtmFolder = "";
|
||||
|
||||
private static final Map<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);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,159 +1,171 @@
|
|||
/**
|
||||
* common base class for the map-filters
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
package btools.mapcreator;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import btools.util.DiffCoderDataOutputStream;
|
||||
|
||||
public abstract class MapCreatorBase implements WayListener, NodeListener, RelationListener {
|
||||
private DiffCoderDataOutputStream[] tileOutStreams;
|
||||
protected File outTileDir;
|
||||
|
||||
protected Map<String, String> tags;
|
||||
|
||||
public void putTag(String key, String value) {
|
||||
if (tags == null) tags = new HashMap<>();
|
||||
tags.put(key, value);
|
||||
}
|
||||
|
||||
public String getTag(String key) {
|
||||
return tags == null ? null : tags.get(key);
|
||||
}
|
||||
|
||||
public Map<String, String> getTagsOrNull() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setTags(Map<String, String> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
protected static long readId(DataInputStream is) throws IOException {
|
||||
int offset = is.readByte();
|
||||
if (offset == 32) return -1;
|
||||
long i = is.readInt();
|
||||
i = i << 5;
|
||||
return i | offset;
|
||||
}
|
||||
|
||||
protected static void writeId(DataOutputStream o, long id) throws IOException {
|
||||
if (id == -1) {
|
||||
o.writeByte(32);
|
||||
return;
|
||||
}
|
||||
int offset = (int) (id & 0x1f);
|
||||
int i = (int) (id >> 5);
|
||||
o.writeByte(offset);
|
||||
o.writeInt(i);
|
||||
}
|
||||
|
||||
|
||||
protected static File[] sortBySizeAsc(File[] files) {
|
||||
int n = files.length;
|
||||
long[] sizes = new long[n];
|
||||
File[] sorted = new File[n];
|
||||
for (int i = 0; i < n; i++) sizes[i] = files[i].length();
|
||||
for (int nf = 0; nf < n; nf++) {
|
||||
int idx = -1;
|
||||
long min = -1;
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (sizes[i] != -1 && (idx == -1 || sizes[i] < min)) {
|
||||
min = sizes[i];
|
||||
idx = i;
|
||||
}
|
||||
}
|
||||
sizes[idx] = -1;
|
||||
sorted[nf] = files[idx];
|
||||
}
|
||||
return sorted;
|
||||
}
|
||||
|
||||
protected File fileFromTemplate(File template, File dir, String suffix) {
|
||||
String filename = template.getName();
|
||||
filename = filename.substring(0, filename.length() - 3) + suffix;
|
||||
return new File(dir, filename);
|
||||
}
|
||||
|
||||
protected DataInputStream createInStream(File inFile) throws IOException {
|
||||
return new DataInputStream(new BufferedInputStream(new FileInputStream(inFile)));
|
||||
}
|
||||
|
||||
protected DiffCoderDataOutputStream createOutStream(File outFile) throws IOException {
|
||||
return new DiffCoderDataOutputStream(new BufferedOutputStream(new FileOutputStream(outFile)));
|
||||
}
|
||||
|
||||
protected DiffCoderDataOutputStream getOutStreamForTile(int tileIndex) throws Exception {
|
||||
if (tileOutStreams == null) {
|
||||
tileOutStreams = new DiffCoderDataOutputStream[64];
|
||||
}
|
||||
|
||||
if (tileOutStreams[tileIndex] == null) {
|
||||
tileOutStreams[tileIndex] = createOutStream(new File(outTileDir, getNameForTile(tileIndex)));
|
||||
}
|
||||
return tileOutStreams[tileIndex];
|
||||
}
|
||||
|
||||
protected String getNameForTile(int tileIndex) {
|
||||
throw new IllegalArgumentException("getNameForTile not implemented");
|
||||
}
|
||||
|
||||
protected void closeTileOutStreams() throws Exception {
|
||||
if (tileOutStreams == null) {
|
||||
return;
|
||||
}
|
||||
for (int tileIndex = 0; tileIndex < tileOutStreams.length; tileIndex++) {
|
||||
if (tileOutStreams[tileIndex] != null) tileOutStreams[tileIndex].close();
|
||||
tileOutStreams[tileIndex] = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// interface dummys
|
||||
|
||||
@Override
|
||||
public void nodeFileStart(File nodefile) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nextNode(NodeData n) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nodeFileEnd(File nodefile) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wayFileStart(File wayfile) throws Exception {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nextWay(WayData data) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wayFileEnd(File wayfile) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nextRelation(RelationData data) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nextRestriction(RelationData data, long fromWid, long toWid, long viaNid) throws Exception {
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* common base class for the map-filters
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
package btools.mapcreator;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
import btools.util.DiffCoderDataOutputStream;
|
||||
|
||||
public abstract class MapCreatorBase implements WayListener, NodeListener, RelationListener
|
||||
{
|
||||
private DiffCoderDataOutputStream[] tileOutStreams;
|
||||
protected File outTileDir;
|
||||
|
||||
protected HashMap<String,String> tags;
|
||||
|
||||
public void putTag( String key, String value )
|
||||
{
|
||||
if ( tags == null ) tags = new HashMap<String,String>();
|
||||
tags.put( key, value );
|
||||
}
|
||||
|
||||
public String getTag( String key )
|
||||
{
|
||||
return tags == null ? null : tags.get( key );
|
||||
}
|
||||
|
||||
public HashMap<String,String> getTagsOrNull()
|
||||
{
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setTags( HashMap<String,String> tags )
|
||||
{
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
protected static long readId( DataInputStream is) throws IOException
|
||||
{
|
||||
int offset = is.readByte();
|
||||
if ( offset == 32 ) return -1;
|
||||
long i = is.readInt();
|
||||
i = i << 5;
|
||||
return i | offset;
|
||||
}
|
||||
|
||||
protected static void writeId( DataOutputStream o, long id ) throws IOException
|
||||
{
|
||||
if ( id == -1 )
|
||||
{
|
||||
o.writeByte( 32 );
|
||||
return;
|
||||
}
|
||||
int offset = (int)( id & 0x1f );
|
||||
int i = (int)( id >> 5 );
|
||||
o.writeByte( offset );
|
||||
o.writeInt( i );
|
||||
}
|
||||
|
||||
|
||||
protected static File[] sortBySizeAsc( File[] files )
|
||||
{
|
||||
int n = files.length;
|
||||
long[] sizes = new long[n];
|
||||
File[] sorted = new File[n];
|
||||
for( int i=0; i<n; i++ ) sizes[i] = files[i].length();
|
||||
for(int nf=0; nf<n; nf++)
|
||||
{
|
||||
int idx = -1;
|
||||
long min = -1;
|
||||
for( int i=0; i<n; i++ )
|
||||
{
|
||||
if ( sizes[i] != -1 && ( idx == -1 || sizes[i] < min ) )
|
||||
{
|
||||
min = sizes[i];
|
||||
idx = i;
|
||||
}
|
||||
}
|
||||
sizes[idx] = -1;
|
||||
sorted[nf] = files[idx];
|
||||
}
|
||||
return sorted;
|
||||
}
|
||||
|
||||
protected File fileFromTemplate( File template, File dir, String suffix )
|
||||
{
|
||||
String filename = template.getName();
|
||||
filename = filename.substring( 0, filename.length() - 3 ) + suffix;
|
||||
return new File( dir, filename );
|
||||
}
|
||||
|
||||
protected DataInputStream createInStream( File inFile ) throws IOException
|
||||
{
|
||||
return new DataInputStream( new BufferedInputStream ( new FileInputStream( inFile ) ) );
|
||||
}
|
||||
|
||||
protected DiffCoderDataOutputStream createOutStream( File outFile ) throws IOException
|
||||
{
|
||||
return new DiffCoderDataOutputStream( new BufferedOutputStream( new FileOutputStream( outFile ) ) );
|
||||
}
|
||||
|
||||
protected DiffCoderDataOutputStream getOutStreamForTile( int tileIndex ) throws Exception
|
||||
{
|
||||
if ( tileOutStreams == null )
|
||||
{
|
||||
tileOutStreams = new DiffCoderDataOutputStream[64];
|
||||
}
|
||||
|
||||
if ( tileOutStreams[tileIndex] == null )
|
||||
{
|
||||
tileOutStreams[tileIndex] = createOutStream( new File( outTileDir, getNameForTile( tileIndex ) ) );
|
||||
}
|
||||
return tileOutStreams[tileIndex];
|
||||
}
|
||||
|
||||
protected String getNameForTile( int tileIndex )
|
||||
{
|
||||
throw new IllegalArgumentException( "getNameForTile not implemented" );
|
||||
}
|
||||
|
||||
protected void closeTileOutStreams() throws Exception
|
||||
{
|
||||
if ( tileOutStreams == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
for( int tileIndex=0; tileIndex<tileOutStreams.length; tileIndex++ )
|
||||
{
|
||||
if ( tileOutStreams[tileIndex] != null ) tileOutStreams[tileIndex].close();
|
||||
tileOutStreams[tileIndex] = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// interface dummys
|
||||
|
||||
@Override
|
||||
public void nodeFileStart( File nodefile ) throws Exception {}
|
||||
|
||||
@Override
|
||||
public void nextNode( NodeData n ) throws Exception {}
|
||||
|
||||
@Override
|
||||
public void nodeFileEnd( File nodefile ) throws Exception {}
|
||||
|
||||
@Override
|
||||
public boolean wayFileStart( File wayfile ) throws Exception { return true; }
|
||||
|
||||
@Override
|
||||
public void nextWay( WayData data ) throws Exception {}
|
||||
|
||||
@Override
|
||||
public void wayFileEnd( File wayfile ) throws Exception {}
|
||||
|
||||
@Override
|
||||
public void nextRelation( RelationData data ) throws Exception {}
|
||||
|
||||
@Override
|
||||
public void nextRestriction( RelationData data, long fromWid, long toWid, long viaNid ) throws Exception {}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,75 +1,84 @@
|
|||
package btools.mapcreator;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* NodeCutter does 1 step in map-processing:
|
||||
* <p>
|
||||
* - cuts the 45*30 node tiles into 5*5 pieces
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
public class NodeCutter extends MapCreatorBase {
|
||||
private int lonoffset;
|
||||
private int latoffset;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println("*** NodeCutter: Cut big node-tiles into 5x5 tiles");
|
||||
if (args.length != 2) {
|
||||
System.out.println("usage: java NodeCutter <node-tiles-in> <node-tiles-out>");
|
||||
return;
|
||||
}
|
||||
new NodeCutter().process(new File(args[0]), new File(args[1]));
|
||||
}
|
||||
|
||||
public void init(File nodeTilesOut) {
|
||||
this.outTileDir = nodeTilesOut;
|
||||
}
|
||||
|
||||
public void process(File nodeTilesIn, File nodeTilesOut) throws Exception {
|
||||
init(nodeTilesOut);
|
||||
|
||||
new NodeIterator(this, true).processDir(nodeTilesIn, ".tlf");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nodeFileStart(File nodefile) throws Exception {
|
||||
lonoffset = -1;
|
||||
latoffset = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nextNode(NodeData n) throws Exception {
|
||||
n.writeTo(getOutStreamForTile(getTileIndex(n.ilon, n.ilat)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nodeFileEnd(File nodeFile) throws Exception {
|
||||
closeTileOutStreams();
|
||||
}
|
||||
|
||||
private int getTileIndex(int ilon, int ilat) {
|
||||
int lonoff = (ilon / 45000000) * 45;
|
||||
int latoff = (ilat / 30000000) * 30;
|
||||
if (lonoffset == -1) lonoffset = lonoff;
|
||||
if (latoffset == -1) latoffset = latoff;
|
||||
if (lonoff != lonoffset || latoff != latoffset)
|
||||
throw new IllegalArgumentException("inconsistent node: " + ilon + " " + ilat);
|
||||
|
||||
int lon = (ilon / 5000000) % 9;
|
||||
int lat = (ilat / 5000000) % 6;
|
||||
if (lon < 0 || lon > 8 || lat < 0 || lat > 5)
|
||||
throw new IllegalArgumentException("illegal pos: " + ilon + "," + ilat);
|
||||
return lon * 6 + lat;
|
||||
}
|
||||
|
||||
|
||||
protected String getNameForTile(int tileIndex) {
|
||||
int lon = (tileIndex / 6) * 5 + lonoffset - 180;
|
||||
int lat = (tileIndex % 6) * 5 + latoffset - 90;
|
||||
String slon = lon < 0 ? "W" + (-lon) : "E" + lon;
|
||||
String slat = lat < 0 ? "S" + (-lat) : "N" + lat;
|
||||
return slon + "_" + slat + ".n5d";
|
||||
}
|
||||
|
||||
}
|
||||
package btools.mapcreator;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* NodeCutter does 1 step in map-processing:
|
||||
*
|
||||
* - cuts the 45*30 node tiles into 5*5 pieces
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
public class NodeCutter extends MapCreatorBase
|
||||
{
|
||||
private int lonoffset;
|
||||
private int latoffset;
|
||||
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
System.out.println("*** NodeCutter: Cut big node-tiles into 5x5 tiles");
|
||||
if (args.length != 2)
|
||||
{
|
||||
System.out.println("usage: java NodeCutter <node-tiles-in> <node-tiles-out>" );
|
||||
return;
|
||||
}
|
||||
new NodeCutter().process( new File( args[0] ), new File( args[1] ) );
|
||||
}
|
||||
|
||||
public void init( File nodeTilesOut )
|
||||
{
|
||||
this.outTileDir = nodeTilesOut;
|
||||
}
|
||||
|
||||
public void process( File nodeTilesIn, File nodeTilesOut ) throws Exception
|
||||
{
|
||||
init( nodeTilesOut );
|
||||
|
||||
new NodeIterator( this, true ).processDir( nodeTilesIn, ".tlf" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nodeFileStart( File nodefile ) throws Exception
|
||||
{
|
||||
lonoffset = -1;
|
||||
latoffset = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nextNode( NodeData n ) throws Exception
|
||||
{
|
||||
n.writeTo( getOutStreamForTile( getTileIndex( n.ilon, n.ilat ) ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nodeFileEnd( File nodeFile ) throws Exception
|
||||
{
|
||||
closeTileOutStreams();
|
||||
}
|
||||
|
||||
private int getTileIndex( int ilon, int ilat )
|
||||
{
|
||||
int lonoff = (ilon / 45000000 ) * 45;
|
||||
int latoff = (ilat / 30000000 ) * 30;
|
||||
if ( lonoffset == -1 ) lonoffset = lonoff;
|
||||
if ( latoffset == -1 ) latoffset = latoff;
|
||||
if ( lonoff != lonoffset || latoff != latoffset )
|
||||
throw new IllegalArgumentException( "inconsistent node: " + ilon + " " + ilat );
|
||||
|
||||
int lon = (ilon / 5000000) % 9;
|
||||
int lat = (ilat / 5000000) % 6;
|
||||
if ( lon < 0 || lon > 8 || lat < 0 || lat > 5 ) throw new IllegalArgumentException( "illegal pos: " + ilon + "," + ilat );
|
||||
return lon*6 + lat;
|
||||
}
|
||||
|
||||
|
||||
protected String getNameForTile( int tileIndex )
|
||||
{
|
||||
int lon = (tileIndex / 6 ) * 5 + lonoffset - 180;
|
||||
int lat = (tileIndex % 6 ) * 5 + latoffset - 90;
|
||||
String slon = lon < 0 ? "W" + (-lon) : "E" + lon;
|
||||
String slat = lat < 0 ? "S" + (-lat) : "N" + lat;
|
||||
return slon + "_" + slat + ".n5d";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,49 +1,46 @@
|
|||
package btools.mapcreator;
|
||||
|
||||
import btools.util.DiffCoderDataInputStream;
|
||||
import btools.util.DiffCoderDataOutputStream;
|
||||
|
||||
/**
|
||||
* Container for node data on the preprocessor level
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
public class NodeData extends MapCreatorBase {
|
||||
public long nid;
|
||||
public int ilon;
|
||||
public int ilat;
|
||||
public byte[] description;
|
||||
public short selev = Short.MIN_VALUE;
|
||||
|
||||
public NodeData(long id, double lon, double lat) {
|
||||
nid = id;
|
||||
ilat = (int) ((lat + 90.) * 1000000. + 0.5);
|
||||
ilon = (int) ((lon + 180.) * 1000000. + 0.5);
|
||||
}
|
||||
|
||||
public NodeData(DiffCoderDataInputStream dis) throws Exception {
|
||||
nid = dis.readDiffed(0);
|
||||
ilon = (int) dis.readDiffed(1);
|
||||
ilat = (int) dis.readDiffed(2);
|
||||
int mode = dis.readByte();
|
||||
if ((mode & 1) != 0) {
|
||||
int dlen = dis.readShort();
|
||||
description = new byte[dlen];
|
||||
dis.readFully(description);
|
||||
}
|
||||
if ((mode & 2) != 0) selev = dis.readShort();
|
||||
}
|
||||
|
||||
public void writeTo(DiffCoderDataOutputStream dos) throws Exception {
|
||||
dos.writeDiffed(nid, 0);
|
||||
dos.writeDiffed(ilon, 1);
|
||||
dos.writeDiffed(ilat, 2);
|
||||
int mode = (description == null ? 0 : 1) | (selev == Short.MIN_VALUE ? 0 : 2);
|
||||
dos.writeByte((byte) mode);
|
||||
if ((mode & 1) != 0) {
|
||||
dos.writeShort(description.length);
|
||||
dos.write(description);
|
||||
}
|
||||
if ((mode & 2) != 0) dos.writeShort(selev);
|
||||
}
|
||||
}
|
||||
package btools.mapcreator;
|
||||
|
||||
import btools.util.DiffCoderDataInputStream;
|
||||
import btools.util.DiffCoderDataOutputStream;
|
||||
|
||||
/**
|
||||
* Container for node data on the preprocessor level
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
public class NodeData extends MapCreatorBase
|
||||
{
|
||||
public long nid;
|
||||
public int ilon;
|
||||
public int ilat;
|
||||
public byte[] description;
|
||||
public short selev = Short.MIN_VALUE;
|
||||
|
||||
public NodeData( long id, double lon, double lat )
|
||||
{
|
||||
nid = id;
|
||||
ilat = (int)( ( lat + 90. )*1000000. + 0.5);
|
||||
ilon = (int)( ( lon + 180. )*1000000. + 0.5);
|
||||
}
|
||||
|
||||
public NodeData( DiffCoderDataInputStream dis ) throws Exception
|
||||
{
|
||||
nid = dis.readDiffed( 0 );
|
||||
ilon = (int)dis.readDiffed( 1 );
|
||||
ilat = (int)dis.readDiffed( 2 );
|
||||
int mode = dis.readByte();
|
||||
if ( ( mode & 1 ) != 0 ) { int dlen = dis.readShort(); description = new byte[dlen]; dis.readFully( description ); }
|
||||
if ( ( mode & 2 ) != 0 ) selev = dis.readShort();
|
||||
}
|
||||
|
||||
public void writeTo( DiffCoderDataOutputStream dos ) throws Exception
|
||||
{
|
||||
dos.writeDiffed( nid, 0 );
|
||||
dos.writeDiffed( ilon, 1 );
|
||||
dos.writeDiffed( ilat, 2 );
|
||||
int mode = (description == null ? 0 : 1 ) | ( selev == Short.MIN_VALUE ? 0 : 2 );
|
||||
dos.writeByte( (byte) mode);
|
||||
if ( ( mode & 1 ) != 0 ) { dos.writeShort( description.length ); dos.write( description ); }
|
||||
if ( ( mode & 2 ) != 0 ) dos.writeShort( selev );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,80 +1,92 @@
|
|||
package btools.mapcreator;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
import btools.util.DenseLongMap;
|
||||
import btools.util.DiffCoderDataOutputStream;
|
||||
import btools.util.TinyDenseLongMap;
|
||||
|
||||
/**
|
||||
* NodeFilter does 1 step in map-processing:
|
||||
* <p>
|
||||
* - filters out unused nodes according to the way file
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
public class NodeFilter extends MapCreatorBase {
|
||||
private DiffCoderDataOutputStream nodesOutStream;
|
||||
private File nodeTilesOut;
|
||||
protected DenseLongMap nodebitmap;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println("*** NodeFilter: Filter way related nodes");
|
||||
if (args.length != 3) {
|
||||
System.out.println("usage: java NodeFilter <node-tiles-in> <way-file-in> <node-tiles-out>");
|
||||
return;
|
||||
}
|
||||
|
||||
new NodeFilter().process(new File(args[0]), new File(args[1]), new File(args[2]));
|
||||
}
|
||||
|
||||
public void init() throws Exception {
|
||||
nodebitmap = Boolean.getBoolean("useDenseMaps") ? new DenseLongMap(512) : new TinyDenseLongMap();
|
||||
}
|
||||
|
||||
public void process(File nodeTilesIn, File wayFileIn, File nodeTilesOut) throws Exception {
|
||||
init();
|
||||
|
||||
this.nodeTilesOut = nodeTilesOut;
|
||||
|
||||
// read the wayfile into a bitmap of used nodes
|
||||
new WayIterator(this, false).processFile(wayFileIn);
|
||||
|
||||
// finally filter all node files
|
||||
new NodeIterator(this, true).processDir(nodeTilesIn, ".tls");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nextWay(WayData data) throws Exception {
|
||||
int nnodes = data.nodes.size();
|
||||
for (int i = 0; i < nnodes; i++) {
|
||||
nodebitmap.put(data.nodes.get(i), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nodeFileStart(File nodefile) throws Exception {
|
||||
String filename = nodefile.getName();
|
||||
File outfile = new File(nodeTilesOut, filename);
|
||||
nodesOutStream = new DiffCoderDataOutputStream(new BufferedOutputStream(new FileOutputStream(outfile)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nextNode(NodeData n) throws Exception {
|
||||
if (isRelevant(n)) {
|
||||
n.writeTo(nodesOutStream);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRelevant(NodeData n) {
|
||||
// check if node passes bitmap
|
||||
return nodebitmap.getInt(n.nid) == 0; // 0 -> bit set, -1 -> unset
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nodeFileEnd(File nodeFile) throws Exception {
|
||||
nodesOutStream.close();
|
||||
}
|
||||
}
|
||||
package btools.mapcreator;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
import btools.util.DenseLongMap;
|
||||
import btools.util.DiffCoderDataOutputStream;
|
||||
import btools.util.TinyDenseLongMap;
|
||||
|
||||
/**
|
||||
* NodeFilter does 1 step in map-processing:
|
||||
*
|
||||
* - filters out unused nodes according to the way file
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
public class NodeFilter extends MapCreatorBase
|
||||
{
|
||||
private DiffCoderDataOutputStream nodesOutStream;
|
||||
private File nodeTilesOut;
|
||||
protected DenseLongMap nodebitmap;
|
||||
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
System.out.println("*** NodeFilter: Filter way related nodes");
|
||||
if (args.length != 3)
|
||||
{
|
||||
System.out.println("usage: java NodeFilter <node-tiles-in> <way-file-in> <node-tiles-out>" );
|
||||
return;
|
||||
}
|
||||
|
||||
new NodeFilter().process( new File( args[0] ), new File( args[1] ), new File( args[2] ) );
|
||||
}
|
||||
|
||||
public void init() throws Exception
|
||||
{
|
||||
nodebitmap = Boolean.getBoolean( "useDenseMaps" ) ? new DenseLongMap( 512 ) : new TinyDenseLongMap();
|
||||
}
|
||||
|
||||
public void process( File nodeTilesIn, File wayFileIn, File nodeTilesOut ) throws Exception
|
||||
{
|
||||
init();
|
||||
|
||||
this.nodeTilesOut = nodeTilesOut;
|
||||
|
||||
// read the wayfile into a bitmap of used nodes
|
||||
new WayIterator( this, false ).processFile( wayFileIn );
|
||||
|
||||
// finally filter all node files
|
||||
new NodeIterator( this, true ).processDir( nodeTilesIn, ".tls" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nextWay( WayData data ) throws Exception
|
||||
{
|
||||
int nnodes = data.nodes.size();
|
||||
for (int i=0; i<nnodes; i++ )
|
||||
{
|
||||
nodebitmap.put( data.nodes.get(i), 0 );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nodeFileStart( File nodefile ) throws Exception
|
||||
{
|
||||
String filename = nodefile.getName();
|
||||
File outfile = new File( nodeTilesOut, filename );
|
||||
nodesOutStream = new DiffCoderDataOutputStream( new BufferedOutputStream ( new FileOutputStream( outfile ) ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nextNode( NodeData n ) throws Exception
|
||||
{
|
||||
if ( isRelevant( n ) )
|
||||
{
|
||||
n.writeTo( nodesOutStream );
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRelevant( NodeData n )
|
||||
{
|
||||
// check if node passes bitmap
|
||||
return nodebitmap.getInt( n.nid ) == 0; // 0 -> bit set, -1 -> unset
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nodeFileEnd( File nodeFile ) throws Exception
|
||||
{
|
||||
nodesOutStream.close();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,59 +1,71 @@
|
|||
package btools.mapcreator;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
|
||||
import btools.util.DiffCoderDataInputStream;
|
||||
|
||||
/**
|
||||
* Iterate over a singe nodefile or a directory
|
||||
* of nodetiles and feed the nodes to the callback listener
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
public class NodeIterator extends MapCreatorBase {
|
||||
private NodeListener listener;
|
||||
private boolean delete;
|
||||
|
||||
public NodeIterator(NodeListener nodeListener, boolean deleteAfterReading) {
|
||||
listener = nodeListener;
|
||||
delete = deleteAfterReading;
|
||||
}
|
||||
|
||||
public void processDir(File indir, String inSuffix) throws Exception {
|
||||
if (!indir.isDirectory()) {
|
||||
throw new IllegalArgumentException("not a directory: " + indir);
|
||||
}
|
||||
|
||||
File[] af = sortBySizeAsc(indir.listFiles());
|
||||
for (int i = 0; i < af.length; i++) {
|
||||
File nodefile = af[i];
|
||||
if (nodefile.getName().endsWith(inSuffix)) {
|
||||
processFile(nodefile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void processFile(File nodefile) throws Exception {
|
||||
System.out.println("*** NodeIterator reading: " + nodefile);
|
||||
|
||||
listener.nodeFileStart(nodefile);
|
||||
|
||||
DiffCoderDataInputStream di = new DiffCoderDataInputStream(new BufferedInputStream(new FileInputStream(nodefile)));
|
||||
try {
|
||||
for (; ; ) {
|
||||
NodeData n = new NodeData(di);
|
||||
listener.nextNode(n);
|
||||
}
|
||||
} catch (EOFException eof) {
|
||||
di.close();
|
||||
}
|
||||
listener.nodeFileEnd(nodefile);
|
||||
if (delete && "true".equals(System.getProperty("deletetmpfiles"))) {
|
||||
nodefile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
package btools.mapcreator;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
|
||||
import btools.util.DiffCoderDataInputStream;
|
||||
|
||||
/**
|
||||
* Iterate over a singe nodefile or a directory
|
||||
* of nodetiles and feed the nodes to the callback listener
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
public class NodeIterator extends MapCreatorBase
|
||||
{
|
||||
private NodeListener listener;
|
||||
private boolean delete;
|
||||
|
||||
public NodeIterator( NodeListener nodeListener, boolean deleteAfterReading )
|
||||
{
|
||||
listener = nodeListener;
|
||||
delete = deleteAfterReading;
|
||||
}
|
||||
|
||||
public void processDir( File indir, String inSuffix ) throws Exception
|
||||
{
|
||||
if ( !indir.isDirectory() )
|
||||
{
|
||||
throw new IllegalArgumentException( "not a directory: " + indir );
|
||||
}
|
||||
|
||||
File[] af = sortBySizeAsc( indir.listFiles() );
|
||||
for( int i=0; i<af.length; i++ )
|
||||
{
|
||||
File nodefile = af[i];
|
||||
if ( nodefile.getName().endsWith( inSuffix ) )
|
||||
{
|
||||
processFile( nodefile );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void processFile(File nodefile) throws Exception
|
||||
{
|
||||
System.out.println( "*** NodeIterator reading: " + nodefile );
|
||||
|
||||
listener.nodeFileStart( nodefile );
|
||||
|
||||
DiffCoderDataInputStream di = new DiffCoderDataInputStream( new BufferedInputStream ( new FileInputStream( nodefile ) ) );
|
||||
try
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
NodeData n = new NodeData( di );
|
||||
listener.nextNode( n );
|
||||
}
|
||||
}
|
||||
catch( EOFException eof )
|
||||
{
|
||||
di.close();
|
||||
}
|
||||
listener.nodeFileEnd( nodefile );
|
||||
if ( delete && "true".equals( System.getProperty( "deletetmpfiles" ) ))
|
||||
{
|
||||
nodefile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
package btools.mapcreator;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Callbacklistener for NodeIterator
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
public interface NodeListener {
|
||||
void nodeFileStart(File nodefile) throws Exception;
|
||||
|
||||
void nextNode(NodeData data) throws Exception;
|
||||
|
||||
void nodeFileEnd(File nodefile) throws Exception;
|
||||
}
|
||||
package btools.mapcreator;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Callbacklistener for NodeIterator
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
public interface NodeListener
|
||||
{
|
||||
void nodeFileStart( File nodefile ) throws Exception;
|
||||
|
||||
void nextNode( NodeData data ) throws Exception;
|
||||
|
||||
void nodeFileEnd( File nodefile ) throws Exception;
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue