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
|
name: Gradle Package
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
|
||||||
release:
|
release:
|
||||||
types: [created]
|
types: [created]
|
||||||
|
|
||||||
|
|
@ -12,35 +12,25 @@ jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
environment: BRouter
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 11
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '11'
|
||||||
distribution: 'temurin'
|
distribution: 'adopt'
|
||||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||||
|
|
||||||
- name: Setup keystore
|
|
||||||
env:
|
|
||||||
BROUTER_KEYSTORE_BASE64: ${{ secrets.BROUTER_KEYSTORE_BASE64 }}
|
|
||||||
run: |
|
|
||||||
echo $BROUTER_KEYSTORE_BASE64 | base64 -di > ${{ github.workspace }}/brouter.jks
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
env:
|
|
||||||
ORG_GRADLE_PROJECT_RELEASE_STORE_FILE: ${{ secrets.BROUTER_KEYSTORE_FILE }}
|
|
||||||
ORG_GRADLE_PROJECT_RELEASE_KEY_ALIAS: ${{ secrets.BROUTER_KEY_ALIAS }}
|
|
||||||
ORG_GRADLE_PROJECT_RELEASE_KEY_PASSWORD: ${{ secrets.BROUTER_KEY_PASSWORD }}
|
|
||||||
ORG_GRADLE_PROJECT_RELEASE_STORE_PASSWORD: ${{ secrets.BROUTER_STORE_PASSWORD }}
|
|
||||||
run: gradle build
|
run: gradle build
|
||||||
|
|
||||||
|
|
||||||
# The USERNAME and TOKEN need to correspond to the credentials environment variables used in
|
# The USERNAME and TOKEN need to correspond to the credentials environment variables used in
|
||||||
# the publishing section of your build.gradle
|
# the publishing section of your build.gradle
|
||||||
- name: Publish to GitHub Packages
|
- name: Publish to GitHub Packages
|
||||||
|
|
|
||||||
29
.github/workflows/gradle.yml
vendored
29
.github/workflows/gradle.yml
vendored
|
|
@ -13,31 +13,14 @@ jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
environment: BRouter
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 8
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '8'
|
||||||
distribution: 'temurin'
|
distribution: 'zulu'
|
||||||
cache: gradle
|
cache: gradle
|
||||||
- name: Create local.properties
|
|
||||||
run: touch local.properties
|
|
||||||
- name: Setup keystore
|
|
||||||
env:
|
|
||||||
BROUTER_KEYSTORE_BASE64: ${{ secrets.BROUTER_KEYSTORE_BASE64 }}
|
|
||||||
run: |
|
|
||||||
echo $BROUTER_KEYSTORE_BASE64 | base64 -di > ${{ github.workspace }}/brouter.jks
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
env:
|
|
||||||
ORG_GRADLE_PROJECT_RELEASE_STORE_FILE: ${{ secrets.BROUTER_KEYSTORE_FILE }}
|
|
||||||
ORG_GRADLE_PROJECT_RELEASE_KEY_ALIAS: ${{ secrets.BROUTER_KEY_ALIAS }}
|
|
||||||
ORG_GRADLE_PROJECT_RELEASE_KEY_PASSWORD: ${{ secrets.BROUTER_KEY_PASSWORD }}
|
|
||||||
ORG_GRADLE_PROJECT_RELEASE_STORE_PASSWORD: ${{ secrets.BROUTER_STORE_PASSWORD }}
|
|
||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
- name: Upload ZIP
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: ZIP
|
|
||||||
path: brouter-server/build/distributions/brouter-*.zip
|
|
||||||
|
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,7 +1,6 @@
|
||||||
*.iml
|
*.iml
|
||||||
.gradle
|
.gradle
|
||||||
.idea/
|
.idea/
|
||||||
build
|
|
||||||
/local.properties
|
/local.properties
|
||||||
/.idea/caches
|
/.idea/caches
|
||||||
/.idea/gradle.xml
|
/.idea/gradle.xml
|
||||||
|
|
@ -11,6 +10,7 @@ build
|
||||||
/.idea/navEditor.xml
|
/.idea/navEditor.xml
|
||||||
/.idea/assetWizardSettings.xml
|
/.idea/assetWizardSettings.xml
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
/build
|
||||||
/captures
|
/captures
|
||||||
.externalNativeBuild
|
.externalNativeBuild
|
||||||
.cxx
|
.cxx
|
||||||
|
|
|
||||||
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
|
BRouter
|
||||||
=======
|
=======
|
||||||
|
|
||||||
# Come fare
|
|
||||||
|
|
||||||
To build the Docker image run (in the project's top level directory):
|
|
||||||
|
|
||||||
```
|
|
||||||
docker build -t brouter .
|
|
||||||
```
|
|
||||||
|
|
||||||
Download the segment files
|
|
||||||
```
|
|
||||||
wget -rkpN -np -e robots=off -l1 https://brouter.de/brouter/segments4/
|
|
||||||
```
|
|
||||||
|
|
||||||
Download the profile files from github https://github.com/gpxstudio/brouter/tree/master/misc/profiles2 using
|
|
||||||
```
|
|
||||||
https://downgit.github.io/
|
|
||||||
```
|
|
||||||
or directly copy misc/profiles2 from git clone folder
|
|
||||||
|
|
||||||
si possono creare anche i profili con le varianti usando
|
|
||||||
```
|
|
||||||
generate_profile_variants.sh
|
|
||||||
```
|
|
||||||
si possono scaricare anche i profili di brouter originale usando
|
|
||||||
```
|
|
||||||
wget -rkpN -np -e robots=off -l1 https://brouter.de/brouter/profiles2/
|
|
||||||
```
|
|
||||||
e metterli nei folder che poi verranno caricati con i volumi condivisi nel docker
|
|
||||||
```
|
|
||||||
mv ./brouter.de/brouter/segments4 /home/nvme/dockerdata/brouter/segments
|
|
||||||
mv mv ./brouter.de/brouter/profiles2 /home/nvme/dockerdata/brouter/profiles
|
|
||||||
```
|
|
||||||
oppure usando lo script
|
|
||||||
```
|
|
||||||
download_segments.sh
|
|
||||||
```
|
|
||||||
far partire il docker
|
|
||||||
```
|
|
||||||
services:
|
|
||||||
brouterserver:
|
|
||||||
container_name: brouter
|
|
||||||
image: brouter
|
|
||||||
ports:
|
|
||||||
- 17777:17777
|
|
||||||
volumes:
|
|
||||||
- /home/nvme/dockerdata/brouter/segments:/segments4
|
|
||||||
- /home/nvme/dockerdata/brouter/profiles:/profiles2
|
|
||||||
restart: unless-stopped
|
|
||||||
```
|
|
||||||
## Come si interroga
|
|
||||||
|
|
||||||
```
|
|
||||||
https://brouter.patachina.it/?lonlats=12.08349699,44.25067665|12.09165011,44.24834552&profile=Trekking-dry&format=geojson&alternativeidx=0
|
|
||||||
```
|
|
||||||
|
|
||||||
# Original
|
|
||||||
|
|
||||||
BRouter is a configurable OSM offline router with elevation awareness, Java +
|
BRouter is a configurable OSM offline router with elevation awareness, Java +
|
||||||
Android. Designed to be multi-modal with a particular emphasis on bicycle
|
Android. Designed to be multi-modal with a particular emphasis on bicycle
|
||||||
and energy-based car routing.
|
and energy-based car routing.
|
||||||
|
|
@ -72,7 +15,7 @@ You can install the BRouter app on your Android device from
|
||||||
Store](https://play.google.com/store/apps/details?id=btools.routingapp). You
|
Store](https://play.google.com/store/apps/details?id=btools.routingapp). You
|
||||||
can also [build BRouter](#build-and-install) yourself. You can find detailed
|
can also [build BRouter](#build-and-install) yourself. You can find detailed
|
||||||
documentation of the BRouter Android app in
|
documentation of the BRouter Android app in
|
||||||
[`docs/users/android_quickstart.md`](docs/users/android_quickstart.md).
|
[`misc/readmes/readme.txt`](misc/readmes/readme.txt).
|
||||||
|
|
||||||
<a href="https://f-droid.org/packages/btools.routingapp" target="_blank">
|
<a href="https://f-droid.org/packages/btools.routingapp" target="_blank">
|
||||||
<img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="90"/></a>
|
<img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="90"/></a>
|
||||||
|
|
@ -96,7 +39,7 @@ Alternatively, you can also use BRouter as the offline routing engine for
|
||||||
[OSMAnd](https://osmand.net/) on your Android device.
|
[OSMAnd](https://osmand.net/) on your Android device.
|
||||||
|
|
||||||
A full documentation on how to set this up is available at
|
A full documentation on how to set this up is available at
|
||||||
[`docs/users/osmand.md`](docs/users/osmand.md).
|
[`misc/readmes/osmand/README.md`](misc/readmes/osmand/README.md).
|
||||||
|
|
||||||
|
|
||||||
## BRouter on Windows/Linux/Mac OS
|
## BRouter on Windows/Linux/Mac OS
|
||||||
|
|
@ -155,7 +98,7 @@ Segments files from the whole planet are generated weekly at
|
||||||
[https://brouter.de/brouter/segments4/](http://brouter.de/brouter/segments4/).
|
[https://brouter.de/brouter/segments4/](http://brouter.de/brouter/segments4/).
|
||||||
|
|
||||||
You can download one or more segments files, covering the area of the planet
|
You can download one or more segments files, covering the area of the planet
|
||||||
you want to route, into the `misc/segments4` directory.
|
your want to route, into the `misc/segments4` directory.
|
||||||
|
|
||||||
#### Generate your own segments files
|
#### Generate your own segments files
|
||||||
|
|
||||||
|
|
@ -163,7 +106,7 @@ You can also generate the segments files you need directly from a planet dump
|
||||||
of OpenStreetMap data (or a [GeoFabrik extract](https://download.geofabrik.de/)).
|
of OpenStreetMap data (or a [GeoFabrik extract](https://download.geofabrik.de/)).
|
||||||
|
|
||||||
More documentation of this is available in the
|
More documentation of this is available in the
|
||||||
[`docs/developers/build_segments.md`](docs/developers/build_segments.md) file.
|
[`misc/readmes/mapcreation.md`](misc/readmes/mapcreation.md) file.
|
||||||
|
|
||||||
|
|
||||||
### (Optional) Generate profile variants
|
### (Optional) Generate profile variants
|
||||||
|
|
@ -177,7 +120,7 @@ to help you quickly generate variants based on the default profiles, to create
|
||||||
a default set of profiles covering most of the basic use cases.
|
a default set of profiles covering most of the basic use cases.
|
||||||
|
|
||||||
Have a look at the
|
Have a look at the
|
||||||
[`docs/developers/profile_developers_guide.md`](docs/developers/profile_developers_guide.md)
|
[`misc/readmes/profile_developers_guide.txt`](misc/readmes/profile_developers_guide.txt)
|
||||||
for an in-depth guide on profiles edition and customization.
|
for an in-depth guide on profiles edition and customization.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -194,62 +137,10 @@ The API endpoints exposed by this HTTP server are documented in the
|
||||||
[`brouter-server/src/main/java/btools/server/request/ServerHandler.java`](brouter-server/src/main/java/btools/server/request/ServerHandler.java)
|
[`brouter-server/src/main/java/btools/server/request/ServerHandler.java`](brouter-server/src/main/java/btools/server/request/ServerHandler.java)
|
||||||
file.
|
file.
|
||||||
|
|
||||||
The server emits log data for each routing request on stdout. For each routing
|
|
||||||
request a line with the following eight fields is printed. The fields are
|
|
||||||
separated by whitespace.
|
|
||||||
|
|
||||||
- timestamp, in ISO8601 format, e.g. `2024-05-14T21:11:26.499+02:00`
|
|
||||||
- current server session count (integer number 1-999) or "new" when a new
|
|
||||||
IP address is detected
|
|
||||||
- IP address (IPv4 or IPv6), prefixed by `ip=`
|
|
||||||
- duration of routing request in ms, prefixed by `ms=`
|
|
||||||
- divider `->`
|
|
||||||
- HTTP request method
|
|
||||||
- HTTP request URL
|
|
||||||
- HTTP request version
|
|
||||||
|
|
||||||
Example log output:
|
|
||||||
|
|
||||||
```
|
|
||||||
2024-05-14T21:11:26.499+02:00 new ip=127.0.0.1 ms=189 -> GET /brouter?lonlats=13.377485,52.516247%7C13.351221,52.515004&profile=trekking&alternativeidx=0&format=geojson HTTP/1.1
|
|
||||||
2024-05-14T21:11:33.229+02:00 1 ip=127.0.0.1 ms=65 -> GET /brouter?lonlats=13.377485,52.516247%7C13.351221,52.515004&profile=trekking&alternativeidx=0&format=geojson HTTP/1.1
|
|
||||||
```
|
|
||||||
|
|
||||||
## BRouter with Docker
|
|
||||||
|
|
||||||
To build the Docker image run (in the project's top level directory):
|
|
||||||
|
|
||||||
```
|
|
||||||
docker build -t brouter .
|
|
||||||
```
|
|
||||||
|
|
||||||
Download the segment files as described in the previous chapter. The folder containing the
|
|
||||||
segment files can be mounted into the container. Run BRouter as follows:
|
|
||||||
|
|
||||||
```
|
|
||||||
docker run --rm \
|
|
||||||
-v ./misc/scripts/segments4:/segments4 \
|
|
||||||
-p 17777:17777 \
|
|
||||||
--name brouter \
|
|
||||||
brouter
|
|
||||||
```
|
|
||||||
|
|
||||||
This will start brouter with a set of default routing profiles. It will be accessible on port 17777.
|
|
||||||
|
|
||||||
If you want to provide your own routing profiles, you can also mount the folder containing the custom profiles:
|
|
||||||
|
|
||||||
```
|
|
||||||
docker run --rm \
|
|
||||||
-v ./misc/scripts/segments4:/segments4 \
|
|
||||||
-v /path/to/custom/profiles:/profiles2 \
|
|
||||||
-p 17777:17777 \
|
|
||||||
--name brouter \
|
|
||||||
brouter
|
|
||||||
```
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
More documentation is available in the [`docs`](docs) folder.
|
More documentation is available in the [`misc/readmes`](misc/readmes) folder.
|
||||||
|
|
||||||
|
|
||||||
## Related Projects
|
## Related Projects
|
||||||
|
|
|
||||||
1
brouter-codec/.gitignore
vendored
Normal file
1
brouter-codec/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/build/
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'brouter.library-conventions'
|
id 'java-library'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':brouter-util')
|
implementation project(':brouter-util')
|
||||||
|
testImplementation 'junit:junit:4.13.1'
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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,7 +5,8 @@ import btools.util.BitCoderContext;
|
||||||
/**
|
/**
|
||||||
* Container for some re-usable databuffers for the decoder
|
* Container for some re-usable databuffers for the decoder
|
||||||
*/
|
*/
|
||||||
public final class DataBuffers {
|
public final class DataBuffers
|
||||||
|
{
|
||||||
public byte[] iobuffer;
|
public byte[] iobuffer;
|
||||||
public byte[] tagbuf1 = new byte[256];
|
public byte[] tagbuf1 = new byte[256];
|
||||||
public BitCoderContext bctx1 = new BitCoderContext( tagbuf1 );
|
public BitCoderContext bctx1 = new BitCoderContext( tagbuf1 );
|
||||||
|
|
@ -16,7 +17,8 @@ public final class DataBuffers {
|
||||||
public int[] alon = new int[2048];
|
public int[] alon = new int[2048];
|
||||||
public int[] alat = new int[2048];
|
public int[] alat = new int[2048];
|
||||||
|
|
||||||
public DataBuffers() {
|
public DataBuffers()
|
||||||
|
{
|
||||||
this( new byte[65636] );
|
this( new byte[65636] );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -24,7 +26,8 @@ public final class DataBuffers {
|
||||||
* construct a set of databuffers except
|
* construct a set of databuffers except
|
||||||
* for 'iobuffer', where the given array is used
|
* for 'iobuffer', where the given array is used
|
||||||
*/
|
*/
|
||||||
public DataBuffers(byte[] iobuffer) {
|
public DataBuffers( byte[] iobuffer )
|
||||||
|
{
|
||||||
this.iobuffer = iobuffer;
|
this.iobuffer = iobuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,16 @@ package btools.codec;
|
||||||
/**
|
/**
|
||||||
* Special integer fifo suitable for 3-pass encoding
|
* Special integer fifo suitable for 3-pass encoding
|
||||||
*/
|
*/
|
||||||
public class IntegerFifo3Pass {
|
public class IntegerFifo3Pass
|
||||||
|
{
|
||||||
private int[] a;
|
private int[] a;
|
||||||
private int size;
|
private int size;
|
||||||
private int pos;
|
private int pos;
|
||||||
|
|
||||||
private int pass;
|
private int pass;
|
||||||
|
|
||||||
public IntegerFifo3Pass(int capacity) {
|
public IntegerFifo3Pass( int capacity )
|
||||||
|
{
|
||||||
a = capacity < 4 ? new int[4] : new int[capacity];
|
a = capacity < 4 ? new int[4] : new int[capacity];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -18,7 +20,8 @@ public class IntegerFifo3Pass {
|
||||||
* Starts a new encoding pass and resets the reading pointer
|
* Starts a new encoding pass and resets the reading pointer
|
||||||
* from the stats collected in pass2 and writes that to the given context
|
* from the stats collected in pass2 and writes that to the given context
|
||||||
*/
|
*/
|
||||||
public void init() {
|
public void init()
|
||||||
|
{
|
||||||
pass++;
|
pass++;
|
||||||
pos = 0;
|
pos = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -26,9 +29,12 @@ public class IntegerFifo3Pass {
|
||||||
/**
|
/**
|
||||||
* writes to the fifo in pass2
|
* writes to the fifo in pass2
|
||||||
*/
|
*/
|
||||||
public void add(int value) {
|
public void add( int value )
|
||||||
if (pass == 2) {
|
{
|
||||||
if (size == a.length) {
|
if ( pass == 2 )
|
||||||
|
{
|
||||||
|
if ( size == a.length )
|
||||||
|
{
|
||||||
int[] aa = new int[2 * size];
|
int[] aa = new int[2 * size];
|
||||||
System.arraycopy( a, 0, aa, 0, size );
|
System.arraycopy( a, 0, aa, 0, size );
|
||||||
a = aa;
|
a = aa;
|
||||||
|
|
@ -40,12 +46,15 @@ public class IntegerFifo3Pass {
|
||||||
/**
|
/**
|
||||||
* reads from the fifo in pass3 (in pass1/2 returns just 1)
|
* reads from the fifo in pass3 (in pass1/2 returns just 1)
|
||||||
*/
|
*/
|
||||||
public int getNext() {
|
public int getNext()
|
||||||
|
{
|
||||||
return pass == 3 ? get( pos++ ) : 1;
|
return pass == 3 ? get( pos++ ) : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int get(int idx) {
|
private int get( int idx )
|
||||||
if (idx >= size) {
|
{
|
||||||
|
if ( idx >= size )
|
||||||
|
{
|
||||||
throw new IndexOutOfBoundsException( "list size=" + size + " idx=" + idx );
|
throw new IndexOutOfBoundsException( "list size=" + size + " idx=" + idx );
|
||||||
}
|
}
|
||||||
return a[idx];
|
return a[idx];
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ package btools.codec;
|
||||||
/**
|
/**
|
||||||
* Simple container for a list of lists of integers
|
* Simple container for a list of lists of integers
|
||||||
*/
|
*/
|
||||||
public class LinkedListContainer {
|
public class LinkedListContainer
|
||||||
|
{
|
||||||
private int[] ia; // prev, data, prev, data, ...
|
private int[] ia; // prev, data, prev, data, ...
|
||||||
private int size;
|
private int size;
|
||||||
private int[] startpointer; // 0=void, odd=head-data-cell
|
private int[] startpointer; // 0=void, odd=head-data-cell
|
||||||
|
|
@ -11,14 +12,15 @@ public class LinkedListContainer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a container for the given number of lists
|
* Construct a container for the given number of lists
|
||||||
* <p>
|
*
|
||||||
* If no default-buffer is given, an int[nlists*4] is constructed,
|
* If no default-buffer is given, an int[nlists*4] is constructed,
|
||||||
* able to hold 2 entries per list on average
|
* able to hold 2 entries per list on average
|
||||||
*
|
*
|
||||||
* @param nlists the number of lists
|
* @param nlists the number of lists
|
||||||
* @param defaultbuffer an optional data array for re-use (gets replaced if too small)
|
* @param defaultbuffer an optional data array for re-use (gets replaced if too small)
|
||||||
*/
|
*/
|
||||||
public LinkedListContainer(int nlists, int[] defaultbuffer) {
|
public LinkedListContainer( int nlists, int[] defaultbuffer )
|
||||||
|
{
|
||||||
ia = defaultbuffer == null ? new int[nlists*4] : defaultbuffer;
|
ia = defaultbuffer == null ? new int[nlists*4] : defaultbuffer;
|
||||||
startpointer = new int[nlists];
|
startpointer = new int[nlists];
|
||||||
}
|
}
|
||||||
|
|
@ -29,8 +31,10 @@ public class LinkedListContainer {
|
||||||
* @param listNr the list to add the data to
|
* @param listNr the list to add the data to
|
||||||
* @param data the data value
|
* @param data the data value
|
||||||
*/
|
*/
|
||||||
public void addDataElement(int listNr, int data) {
|
public void addDataElement( int listNr, int data )
|
||||||
if (size + 2 > ia.length) {
|
{
|
||||||
|
if ( size + 2 > ia.length )
|
||||||
|
{
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
ia[size++] = startpointer[ listNr ];
|
ia[size++] = startpointer[ listNr ];
|
||||||
|
|
@ -44,10 +48,12 @@ public class LinkedListContainer {
|
||||||
* @param listNr the list to initialize
|
* @param listNr the list to initialize
|
||||||
* @return the number of entries in that list
|
* @return the number of entries in that list
|
||||||
*/
|
*/
|
||||||
public int initList(int listNr) {
|
public int initList( int listNr )
|
||||||
|
{
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
int lp = listpointer = startpointer[ listNr ];
|
int lp = listpointer = startpointer[ listNr ];
|
||||||
while (lp != 0) {
|
while( lp != 0 )
|
||||||
|
{
|
||||||
lp = ia[ lp-1 ];
|
lp = ia[ lp-1 ];
|
||||||
cnt++;
|
cnt++;
|
||||||
}
|
}
|
||||||
|
|
@ -61,8 +67,10 @@ public class LinkedListContainer {
|
||||||
* @return the data element
|
* @return the data element
|
||||||
* @throws IllegalArgumentException if no more element
|
* @throws IllegalArgumentException if no more element
|
||||||
*/
|
*/
|
||||||
public int getDataElement() {
|
public int getDataElement()
|
||||||
if (listpointer == 0) {
|
{
|
||||||
|
if ( listpointer == 0 )
|
||||||
|
{
|
||||||
throw new IllegalArgumentException( "no more element!" );
|
throw new IllegalArgumentException( "no more element!" );
|
||||||
}
|
}
|
||||||
int data = ia[ listpointer ];
|
int data = ia[ listpointer ];
|
||||||
|
|
@ -70,7 +78,8 @@ public class LinkedListContainer {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resize() {
|
private void resize()
|
||||||
|
{
|
||||||
int[] ia2 = new int[2*ia.length];
|
int[] ia2 = new int[2*ia.length];
|
||||||
System.arraycopy( ia, 0, ia2, 0, ia.length );
|
System.arraycopy( ia, 0, ia2, 0, ia.length );
|
||||||
ia = ia2;
|
ia = ia2;
|
||||||
|
|
|
||||||
|
|
@ -5,20 +5,21 @@ import btools.util.ByteDataWriter;
|
||||||
/**
|
/**
|
||||||
* a micro-cache is a data cache for an area of some square kilometers or some
|
* a micro-cache is a data cache for an area of some square kilometers or some
|
||||||
* hundreds or thousands nodes
|
* hundreds or thousands nodes
|
||||||
* <p>
|
*
|
||||||
* This is the basic io-unit: always a full microcache is loaded from the
|
* This is the basic io-unit: always a full microcache is loaded from the
|
||||||
* data-file if a node is requested at a position not yet covered by the caches
|
* data-file if a node is requested at a position not yet covered by the caches
|
||||||
* already loaded
|
* already loaded
|
||||||
* <p>
|
*
|
||||||
* The nodes are represented in a compact way (typical 20-50 bytes per node),
|
* The nodes are represented in a compact way (typical 20-50 bytes per node),
|
||||||
* but in a way that they do not depend on each other, and garbage collection is
|
* but in a way that they do not depend on each other, and garbage collection is
|
||||||
* supported to remove the nodes already consumed from the cache.
|
* supported to remove the nodes already consumed from the cache.
|
||||||
* <p>
|
*
|
||||||
* The cache-internal data representation is different from that in the
|
* The cache-internal data representation is different from that in the
|
||||||
* data-files, where a cache is encoded as a whole, allowing more
|
* data-files, where a cache is encoded as a whole, allowing more
|
||||||
* redundancy-removal for a more compact encoding
|
* redundancy-removal for a more compact encoding
|
||||||
*/
|
*/
|
||||||
public class MicroCache extends ByteDataWriter {
|
public class MicroCache extends ByteDataWriter
|
||||||
|
{
|
||||||
protected int[] faid;
|
protected int[] faid;
|
||||||
protected int[] fapos;
|
protected int[] fapos;
|
||||||
protected int size = 0;
|
protected int size = 0;
|
||||||
|
|
@ -34,21 +35,25 @@ public class MicroCache extends ByteDataWriter {
|
||||||
|
|
||||||
public static boolean debug = false;
|
public static boolean debug = false;
|
||||||
|
|
||||||
protected MicroCache(byte[] ab) {
|
protected MicroCache( byte[] ab )
|
||||||
|
{
|
||||||
super( ab );
|
super( ab );
|
||||||
}
|
}
|
||||||
|
|
||||||
public final static MicroCache emptyNonVirgin = new MicroCache( null );
|
public final static MicroCache emptyNonVirgin = new MicroCache( null );
|
||||||
|
|
||||||
static {
|
static
|
||||||
|
{
|
||||||
emptyNonVirgin.virgin = false;
|
emptyNonVirgin.virgin = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MicroCache emptyCache() {
|
public static MicroCache emptyCache()
|
||||||
|
{
|
||||||
return new MicroCache( null ); // TODO: singleton?
|
return new MicroCache( null ); // TODO: singleton?
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void init(int size) {
|
protected void init( int size )
|
||||||
|
{
|
||||||
this.size = size;
|
this.size = size;
|
||||||
delcount = 0;
|
delcount = 0;
|
||||||
delbytes = 0;
|
delbytes = 0;
|
||||||
|
|
@ -57,31 +62,35 @@ public class MicroCache extends ByteDataWriter {
|
||||||
p2size >>= 1;
|
p2size >>= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void finishNode(long id) {
|
public final void finishNode( long id )
|
||||||
|
{
|
||||||
fapos[size] = aboffset;
|
fapos[size] = aboffset;
|
||||||
faid[size] = shrinkId( id );
|
faid[size] = shrinkId( id );
|
||||||
size++;
|
size++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void discardNode() {
|
public final void discardNode()
|
||||||
|
{
|
||||||
aboffset = startPos( size );
|
aboffset = startPos( size );
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int getSize() {
|
public final int getSize()
|
||||||
|
{
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int getDataSize() {
|
public final int getDataSize()
|
||||||
|
{
|
||||||
return ab == null ? 0 : ab.length;
|
return ab == null ? 0 : ab.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the internal reader (aboffset, aboffsetEnd) to the body data for the given id
|
* Set the internal reader (aboffset, aboffsetEnd) to the body data for the given id
|
||||||
* <p>
|
*
|
||||||
* If a node is not found in an empty cache, this is usually an edge-effect
|
* If a node is not found in an empty cache, this is usually an edge-effect
|
||||||
* (data-file does not exist or neighboured data-files of differnt age),
|
* (data-file does not exist or neighboured data-files of differnt age),
|
||||||
* but is can as well be a symptom of a node-identity breaking bug.
|
* but is can as well be a symptom of a node-identity breaking bug.
|
||||||
* <p>
|
*
|
||||||
* Current implementation always returns false for not-found, however, for
|
* Current implementation always returns false for not-found, however, for
|
||||||
* regression testing, at least for the case that is most likely a bug
|
* regression testing, at least for the case that is most likely a bug
|
||||||
* (node found but marked as deleted = ready for garbage collection
|
* (node found but marked as deleted = ready for garbage collection
|
||||||
|
|
@ -89,8 +98,10 @@ public class MicroCache extends ByteDataWriter {
|
||||||
*
|
*
|
||||||
* @return true if id was found
|
* @return true if id was found
|
||||||
*/
|
*/
|
||||||
public final boolean getAndClear(long id64) {
|
public final boolean getAndClear( long id64 )
|
||||||
if (size == 0) {
|
{
|
||||||
|
if ( size == 0 )
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int id = shrinkId( id64 );
|
int id = shrinkId( id64 );
|
||||||
|
|
@ -98,22 +109,27 @@ public class MicroCache extends ByteDataWriter {
|
||||||
int offset = p2size;
|
int offset = p2size;
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
|
||||||
while (offset > 0) {
|
while (offset > 0)
|
||||||
|
{
|
||||||
int nn = n + offset;
|
int nn = n + offset;
|
||||||
if (nn < size && a[nn] <= id) {
|
if ( nn < size && a[nn] <= id )
|
||||||
|
{
|
||||||
n = nn;
|
n = nn;
|
||||||
}
|
}
|
||||||
offset >>= 1;
|
offset >>= 1;
|
||||||
}
|
}
|
||||||
if (a[n] == id) {
|
if ( a[n] == id )
|
||||||
if ((fapos[n] & 0x80000000) == 0) {
|
{
|
||||||
|
if ( ( fapos[n] & 0x80000000 ) == 0 )
|
||||||
|
{
|
||||||
aboffset = startPos( n );
|
aboffset = startPos( n );
|
||||||
aboffsetEnd = fapos[n];
|
aboffsetEnd = fapos[n];
|
||||||
fapos[n] |= 0x80000000; // mark deleted
|
fapos[n] |= 0x80000000; // mark deleted
|
||||||
delbytes += aboffsetEnd - aboffset;
|
delbytes += aboffsetEnd - aboffset;
|
||||||
delcount++;
|
delcount++;
|
||||||
return true;
|
return true;
|
||||||
} else // .. marked as deleted
|
}
|
||||||
|
else // .. marked as deleted
|
||||||
{
|
{
|
||||||
// throw new RuntimeException( "MicroCache: node already consumed: id=" + id );
|
// throw new RuntimeException( "MicroCache: node already consumed: id=" + id );
|
||||||
}
|
}
|
||||||
|
|
@ -121,31 +137,39 @@ public class MicroCache extends ByteDataWriter {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final int startPos(int n) {
|
protected final int startPos( int n )
|
||||||
|
{
|
||||||
return n > 0 ? fapos[n - 1] & 0x7fffffff : 0;
|
return n > 0 ? fapos[n - 1] & 0x7fffffff : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int collect(int threshold) {
|
public final int collect( int threshold )
|
||||||
if (delcount <= threshold) {
|
{
|
||||||
|
if ( delcount <= threshold )
|
||||||
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
virgin = false;
|
virgin = false;
|
||||||
|
|
||||||
int nsize = size - delcount;
|
int nsize = size - delcount;
|
||||||
if (nsize == 0) {
|
if ( nsize == 0 )
|
||||||
|
{
|
||||||
faid = null;
|
faid = null;
|
||||||
fapos = null;
|
fapos = null;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
int[] nfaid = new int[nsize];
|
int[] nfaid = new int[nsize];
|
||||||
int[] nfapos = new int[nsize];
|
int[] nfapos = new int[nsize];
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
|
|
||||||
byte[] nab = new byte[ab.length - delbytes];
|
byte[] nab = new byte[ab.length - delbytes];
|
||||||
int nab_off = 0;
|
int nab_off = 0;
|
||||||
for (int i = 0; i < size; i++) {
|
for ( int i = 0; i < size; i++ )
|
||||||
|
{
|
||||||
int pos = fapos[i];
|
int pos = fapos[i];
|
||||||
if ((pos & 0x80000000) == 0) {
|
if ( ( pos & 0x80000000 ) == 0 )
|
||||||
|
{
|
||||||
int start = startPos( i );
|
int start = startPos( i );
|
||||||
int end = fapos[i];
|
int end = fapos[i];
|
||||||
int len = end - start;
|
int len = end - start;
|
||||||
|
|
@ -165,11 +189,13 @@ public class MicroCache extends ByteDataWriter {
|
||||||
return deleted;
|
return deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void unGhost() {
|
public final void unGhost()
|
||||||
|
{
|
||||||
ghost = false;
|
ghost = false;
|
||||||
delcount = 0;
|
delcount = 0;
|
||||||
delbytes = 0;
|
delbytes = 0;
|
||||||
for (int i = 0; i < size; i++) {
|
for ( int i = 0; i < size; i++ )
|
||||||
|
{
|
||||||
fapos[i] &= 0x7fffffff; // clear deleted flags
|
fapos[i] &= 0x7fffffff; // clear deleted flags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -177,7 +203,8 @@ public class MicroCache extends ByteDataWriter {
|
||||||
/**
|
/**
|
||||||
* @return the 64-bit global id for the given cache-position
|
* @return the 64-bit global id for the given cache-position
|
||||||
*/
|
*/
|
||||||
public final long getIdForIndex(int i) {
|
public final long getIdForIndex( int i )
|
||||||
|
{
|
||||||
int id32 = faid[i];
|
int id32 = faid[i];
|
||||||
return expandId( id32 );
|
return expandId( id32 );
|
||||||
}
|
}
|
||||||
|
|
@ -187,7 +214,8 @@ public class MicroCache extends ByteDataWriter {
|
||||||
*
|
*
|
||||||
* @see #shrinkId
|
* @see #shrinkId
|
||||||
*/
|
*/
|
||||||
public long expandId(int id32) {
|
public long expandId( int id32 )
|
||||||
|
{
|
||||||
throw new IllegalArgumentException( "expandId for empty cache" );
|
throw new IllegalArgumentException( "expandId for empty cache" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -196,24 +224,28 @@ public class MicroCache extends ByteDataWriter {
|
||||||
*
|
*
|
||||||
* @see #expandId
|
* @see #expandId
|
||||||
*/
|
*/
|
||||||
public int shrinkId(long id64) {
|
public int shrinkId( long id64 )
|
||||||
|
{
|
||||||
throw new IllegalArgumentException( "shrinkId for empty cache" );
|
throw new IllegalArgumentException( "shrinkId for empty cache" );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if the given lon/lat position is internal for that micro-cache
|
* @return true if the given lon/lat position is internal for that micro-cache
|
||||||
*/
|
*/
|
||||||
public boolean isInternal(int ilon, int ilat) {
|
public boolean isInternal( int ilon, int ilat )
|
||||||
|
{
|
||||||
throw new IllegalArgumentException( "isInternal for empty cache" );
|
throw new IllegalArgumentException( "isInternal for empty cache" );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (stasticially) encode the micro-cache into the format used in the datafiles
|
* (stasticially) encode the micro-cache into the format used in the datafiles
|
||||||
*
|
*
|
||||||
* @param buffer byte array to encode into (considered big enough)
|
* @param buffer
|
||||||
|
* byte array to encode into (considered big enough)
|
||||||
* @return the size of the encoded data
|
* @return the size of the encoded data
|
||||||
*/
|
*/
|
||||||
public int encodeMicroCache(byte[] buffer) {
|
public int encodeMicroCache( byte[] buffer )
|
||||||
|
{
|
||||||
throw new IllegalArgumentException( "encodeMicroCache for empty cache" );
|
throw new IllegalArgumentException( "encodeMicroCache for empty cache" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -222,9 +254,11 @@ public class MicroCache extends ByteDataWriter {
|
||||||
*
|
*
|
||||||
* @return null if equals, else a diff-report
|
* @return null if equals, else a diff-report
|
||||||
*/
|
*/
|
||||||
public String compareWith(MicroCache mc) {
|
public String compareWith( MicroCache mc )
|
||||||
|
{
|
||||||
String msg = _compareWith( mc );
|
String msg = _compareWith( mc );
|
||||||
if (msg != null) {
|
if ( msg != null )
|
||||||
|
{
|
||||||
StringBuilder sb = new StringBuilder( msg );
|
StringBuilder sb = new StringBuilder( msg );
|
||||||
sb.append( "\nencode cache:\n" ).append( summary() );
|
sb.append( "\nencode cache:\n" ).append( summary() );
|
||||||
sb.append( "\ndecode cache:\n" ).append( mc.summary() );
|
sb.append( "\ndecode cache:\n" ).append( mc.summary() );
|
||||||
|
|
@ -233,75 +267,96 @@ public class MicroCache extends ByteDataWriter {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String summary() {
|
private String summary()
|
||||||
|
{
|
||||||
StringBuilder sb = new StringBuilder( "size=" + size + " aboffset=" + aboffset );
|
StringBuilder sb = new StringBuilder( "size=" + size + " aboffset=" + aboffset );
|
||||||
for (int i = 0; i < size; i++) {
|
for ( int i = 0; i < size; i++ )
|
||||||
|
{
|
||||||
sb.append( "\nidx=" + i + " faid=" + faid[i] + " fapos=" + fapos[i] );
|
sb.append( "\nidx=" + i + " faid=" + faid[i] + " fapos=" + fapos[i] );
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String _compareWith(MicroCache mc) {
|
private String _compareWith( MicroCache mc )
|
||||||
if (size != mc.size) {
|
{
|
||||||
return "size mismatch: " + size + "->" + mc.size;
|
if ( size != mc.size )
|
||||||
|
{
|
||||||
|
return "size missmatch: " + size + "->" + mc.size;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < size; i++) {
|
for ( int i = 0; i < size; i++ )
|
||||||
if (faid[i] != mc.faid[i]) {
|
{
|
||||||
return "faid mismatch at index " + i + ":" + faid[i] + "->" + mc.faid[i];
|
if ( faid[i] != mc.faid[i] )
|
||||||
|
{
|
||||||
|
return "faid missmatch at index " + i + ":" + faid[i] + "->" + mc.faid[i];
|
||||||
}
|
}
|
||||||
int start = i > 0 ? fapos[i - 1] : 0;
|
int start = i > 0 ? fapos[i - 1] : 0;
|
||||||
int end = fapos[i] < mc.fapos[i] ? fapos[i] : mc.fapos[i];
|
int end = fapos[i] < mc.fapos[i] ? fapos[i] : mc.fapos[i];
|
||||||
int len = end - start;
|
int len = end - start;
|
||||||
for (int offset = 0; offset < len; offset++) {
|
for ( int offset = 0; offset < len; offset++ )
|
||||||
if (mc.ab.length <= start + offset) {
|
{
|
||||||
|
if ( mc.ab.length <= start + offset )
|
||||||
|
{
|
||||||
return "data buffer too small";
|
return "data buffer too small";
|
||||||
}
|
}
|
||||||
if (ab[start + offset] != mc.ab[start + offset]) {
|
if ( ab[start + offset] != mc.ab[start + offset] )
|
||||||
return "data mismatch at index " + i + " offset=" + offset;
|
{
|
||||||
|
return "data missmatch at index " + i + " offset=" + offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fapos[i] != mc.fapos[i]) {
|
if ( fapos[i] != mc.fapos[i] )
|
||||||
return "fapos mismatch at index " + i + ":" + fapos[i] + "->" + mc.fapos[i];
|
{
|
||||||
|
return "fapos missmatch at index " + i + ":" + fapos[i] + "->" + mc.fapos[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (aboffset != mc.aboffset) {
|
if ( aboffset != mc.aboffset )
|
||||||
return "datasize mismatch: " + aboffset + "->" + mc.aboffset;
|
{
|
||||||
|
return "datasize missmatch: " + aboffset + "->" + mc.aboffset;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void calcDelta(MicroCache mc1, MicroCache mc2) {
|
public void calcDelta( MicroCache mc1, MicroCache mc2 )
|
||||||
|
{
|
||||||
int idx1 = 0;
|
int idx1 = 0;
|
||||||
int idx2 = 0;
|
int idx2 = 0;
|
||||||
|
|
||||||
while (idx1 < mc1.size || idx2 < mc2.size) {
|
while( idx1 < mc1.size || idx2 < mc2.size )
|
||||||
|
{
|
||||||
int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE;
|
int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE;
|
||||||
int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE;
|
int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE;
|
||||||
int id;
|
int id;
|
||||||
if (id1 >= id2) {
|
if ( id1 >= id2 )
|
||||||
|
{
|
||||||
id = id2;
|
id = id2;
|
||||||
int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
|
int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
|
||||||
int len2 = mc2.fapos[idx2++] - start2;
|
int len2 = mc2.fapos[idx2++] - start2;
|
||||||
|
|
||||||
if (id1 == id2) {
|
if ( id1 == id2 )
|
||||||
|
{
|
||||||
// id exists in both caches, compare data
|
// id exists in both caches, compare data
|
||||||
int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
|
int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
|
||||||
int len1 = mc1.fapos[idx1++] - start1;
|
int len1 = mc1.fapos[idx1++] - start1;
|
||||||
if (len1 == len2) {
|
if ( len1 == len2 )
|
||||||
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (i < len1) {
|
while( i<len1 )
|
||||||
if (mc1.ab[start1 + i] != mc2.ab[start2 + i]) {
|
{
|
||||||
|
if ( mc1.ab[start1+i] != mc2.ab[start2+i] )
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
if (i == len1) {
|
if ( i == len1 )
|
||||||
|
{
|
||||||
continue; // same data -> do nothing
|
continue; // same data -> do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
write( mc2.ab, start2, len2 );
|
write( mc2.ab, start2, len2 );
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
idx1++;
|
idx1++;
|
||||||
id = id1; // deleted node
|
id = id1; // deleted node
|
||||||
}
|
}
|
||||||
|
|
@ -311,25 +366,31 @@ public class MicroCache extends ByteDataWriter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDelta(MicroCache mc1, MicroCache mc2, boolean keepEmptyNodes) {
|
public void addDelta( MicroCache mc1, MicroCache mc2, boolean keepEmptyNodes )
|
||||||
|
{
|
||||||
int idx1 = 0;
|
int idx1 = 0;
|
||||||
int idx2 = 0;
|
int idx2 = 0;
|
||||||
|
|
||||||
while (idx1 < mc1.size || idx2 < mc2.size) {
|
while( idx1 < mc1.size || idx2 < mc2.size )
|
||||||
|
{
|
||||||
int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE;
|
int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE;
|
||||||
int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE;
|
int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE;
|
||||||
if (id1 >= id2) { // data from diff file wins
|
if ( id1 >= id2 ) // data from diff file wins
|
||||||
|
{
|
||||||
int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
|
int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
|
||||||
int len2 = mc2.fapos[idx2++] - start2;
|
int len2 = mc2.fapos[idx2++] - start2;
|
||||||
if (keepEmptyNodes || len2 > 0) {
|
if ( keepEmptyNodes || len2 > 0 )
|
||||||
|
{
|
||||||
write( mc2.ab, start2, len2 );
|
write( mc2.ab, start2, len2 );
|
||||||
fapos[size] = aboffset;
|
fapos[size] = aboffset;
|
||||||
faid[size++] = id2;
|
faid[size++] = id2;
|
||||||
}
|
}
|
||||||
if (id1 == id2) { // // id exists in both caches
|
if ( id1 == id2 ) // // id exists in both caches
|
||||||
|
{
|
||||||
idx1++;
|
idx1++;
|
||||||
}
|
}
|
||||||
} else // use data from base file
|
}
|
||||||
|
else // use data from base file
|
||||||
{
|
{
|
||||||
int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
|
int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
|
||||||
int len1 = mc1.fapos[idx1++] - start1;
|
int len1 = mc1.fapos[idx1++] - start1;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package btools.codec;
|
package btools.codec;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import btools.util.ByteDataReader;
|
import btools.util.ByteDataReader;
|
||||||
import btools.util.IByteArrayUnifier;
|
import btools.util.IByteArrayUnifier;
|
||||||
|
|
@ -10,12 +9,14 @@ import btools.util.IByteArrayUnifier;
|
||||||
* MicroCache2 is the new format that uses statistical encoding and
|
* MicroCache2 is the new format that uses statistical encoding and
|
||||||
* is able to do access filtering and waypoint matching during encoding
|
* is able to do access filtering and waypoint matching during encoding
|
||||||
*/
|
*/
|
||||||
public final class MicroCache2 extends MicroCache {
|
public final class MicroCache2 extends MicroCache
|
||||||
|
{
|
||||||
private int lonBase;
|
private int lonBase;
|
||||||
private int latBase;
|
private int latBase;
|
||||||
private int cellsize;
|
private int cellsize;
|
||||||
|
|
||||||
public MicroCache2(int size, byte[] databuffer, int lonIdx, int latIdx, int divisor) {
|
public MicroCache2( int size, byte[] databuffer, int lonIdx, int latIdx, int divisor ) throws Exception
|
||||||
|
{
|
||||||
super( databuffer ); // sets ab=databuffer, aboffset=0
|
super( databuffer ); // sets ab=databuffer, aboffset=0
|
||||||
|
|
||||||
faid = new int[size];
|
faid = new int[size];
|
||||||
|
|
@ -26,13 +27,15 @@ public final class MicroCache2 extends MicroCache {
|
||||||
latBase = latIdx*cellsize;
|
latBase = latIdx*cellsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] readUnified(int len, IByteArrayUnifier u) {
|
public byte[] readUnified( int len, IByteArrayUnifier u )
|
||||||
|
{
|
||||||
byte[] b = u.unify( ab, aboffset, len );
|
byte[] b = u.unify( ab, aboffset, len );
|
||||||
aboffset += len;
|
aboffset += len;
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MicroCache2(StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher) {
|
public MicroCache2( StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher ) throws Exception
|
||||||
|
{
|
||||||
super( null );
|
super( null );
|
||||||
cellsize = 1000000 / divisor;
|
cellsize = 1000000 / divisor;
|
||||||
lonBase = lonIdx*cellsize;
|
lonBase = lonIdx*cellsize;
|
||||||
|
|
@ -51,15 +54,16 @@ public final class MicroCache2 extends MicroCache {
|
||||||
fapos = size > dataBuffers.ibuf3.length ? new int[size] : dataBuffers.ibuf3;
|
fapos = size > dataBuffers.ibuf3.length ? new int[size] : dataBuffers.ibuf3;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int[] alon = size > dataBuffers.alon.length ? new int[size] : dataBuffers.alon;
|
int[] alon = size > dataBuffers.alon.length ? new int[size] : dataBuffers.alon;
|
||||||
int[] alat = size > dataBuffers.alat.length ? new int[size] : dataBuffers.alat;
|
int[] alat = size > dataBuffers.alat.length ? new int[size] : dataBuffers.alat;
|
||||||
|
|
||||||
if (debug)
|
if ( debug ) System.out.println( "*** decoding cache of size=" + size + " for lonIdx=" + lonIdx + " latIdx=" + latIdx );
|
||||||
System.out.println("*** decoding cache of size=" + size + " for lonIdx=" + lonIdx + " latIdx=" + latIdx);
|
|
||||||
|
|
||||||
bc.decodeSortedArray( faid, 0, size, 29, 0 );
|
bc.decodeSortedArray( faid, 0, size, 29, 0 );
|
||||||
|
|
||||||
for (int n = 0; n < size; n++) {
|
for( int n = 0; n<size; n++ )
|
||||||
|
{
|
||||||
long id64 = expandId( faid[n] );
|
long id64 = expandId( faid[n] );
|
||||||
alon[n] = (int)(id64 >> 32);
|
alon[n] = (int)(id64 >> 32);
|
||||||
alat[n] = (int)(id64 & 0xffffffff);
|
alat[n] = (int)(id64 & 0xffffffff);
|
||||||
|
|
@ -76,24 +80,30 @@ public final class MicroCache2 extends MicroCache {
|
||||||
LinkedListContainer reverseLinks = new LinkedListContainer( size, dataBuffers.ibuf1 );
|
LinkedListContainer reverseLinks = new LinkedListContainer( size, dataBuffers.ibuf1 );
|
||||||
|
|
||||||
int selev = 0;
|
int selev = 0;
|
||||||
for (int n = 0; n < size; n++) { // loop over nodes
|
for( int n=0; n<size; n++ ) // loop over nodes
|
||||||
|
{
|
||||||
int ilon = alon[n];
|
int ilon = alon[n];
|
||||||
int ilat = alat[n];
|
int ilat = alat[n];
|
||||||
|
|
||||||
// future escapes (turn restrictions?)
|
// future escapes (turn restrictions?)
|
||||||
short trExceptions = 0;
|
short trExceptions = 0;
|
||||||
int featureId = bc.decodeVarBits();
|
int featureId = bc.decodeVarBits();
|
||||||
if (featureId == 13) {
|
if ( featureId == 13 )
|
||||||
|
{
|
||||||
fapos[n] = aboffset;
|
fapos[n] = aboffset;
|
||||||
validBits[ n >> 5 ] |= 1 << n; // mark dummy-node valid
|
validBits[ n >> 5 ] |= 1 << n; // mark dummy-node valid
|
||||||
continue; // empty node escape (delta files only)
|
continue; // empty node escape (delta files only)
|
||||||
}
|
}
|
||||||
while (featureId != 0) {
|
while( featureId != 0 )
|
||||||
|
{
|
||||||
int bitsize = bc.decodeNoisyNumber( 5 );
|
int bitsize = bc.decodeNoisyNumber( 5 );
|
||||||
|
|
||||||
if (featureId == 2) { // exceptions to turn-restriction
|
if ( featureId == 2 ) // exceptions to turn-restriction
|
||||||
|
{
|
||||||
trExceptions = (short)bc.decodeBounded( 1023 );
|
trExceptions = (short)bc.decodeBounded( 1023 );
|
||||||
} else if (featureId == 1) { // turn-restriction
|
}
|
||||||
|
else if ( featureId == 1 ) // turn-restriction
|
||||||
|
{
|
||||||
writeBoolean( true );
|
writeBoolean( true );
|
||||||
writeShort( trExceptions ); // exceptions from previous feature
|
writeShort( trExceptions ); // exceptions from previous feature
|
||||||
trExceptions = 0;
|
trExceptions = 0;
|
||||||
|
|
@ -103,7 +113,9 @@ public final class MicroCache2 extends MicroCache {
|
||||||
writeInt( ilat + bc.decodeNoisyDiff( 10 ) ); // fromLat
|
writeInt( ilat + bc.decodeNoisyDiff( 10 ) ); // fromLat
|
||||||
writeInt( ilon + bc.decodeNoisyDiff( 10 ) ); // toLon
|
writeInt( ilon + bc.decodeNoisyDiff( 10 ) ); // toLon
|
||||||
writeInt( ilat + bc.decodeNoisyDiff( 10 ) ); // toLat
|
writeInt( ilat + bc.decodeNoisyDiff( 10 ) ); // toLat
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
for( int i=0; i< bitsize; i++ ) bc.decodeBit(); // unknown feature, just skip
|
for( int i=0; i< bitsize; i++ ) bc.decodeBit(); // unknown feature, just skip
|
||||||
}
|
}
|
||||||
featureId = bc.decodeVarBits();
|
featureId = bc.decodeVarBits();
|
||||||
|
|
@ -116,9 +128,9 @@ public final class MicroCache2 extends MicroCache {
|
||||||
writeVarBytes( nodeTags == null ? null : nodeTags.data );
|
writeVarBytes( nodeTags == null ? null : nodeTags.data );
|
||||||
|
|
||||||
int links = bc.decodeNoisyNumber( 1 );
|
int links = bc.decodeNoisyNumber( 1 );
|
||||||
if (debug)
|
if ( debug ) System.out.println( "*** decoding node " + ilon + "/" + ilat + " with links=" + links );
|
||||||
System.out.println("*** decoding node " + ilon + "/" + ilat + " with links=" + links);
|
for( int li=0; li<links; li++ )
|
||||||
for (int li = 0; li < links; li++) {
|
{
|
||||||
int sizeoffset = 0;
|
int sizeoffset = 0;
|
||||||
int nodeIdx = n + nodeIdxDiff.decodeSignedValue();
|
int nodeIdx = n + nodeIdxDiff.decodeSignedValue();
|
||||||
|
|
||||||
|
|
@ -126,21 +138,24 @@ public final class MicroCache2 extends MicroCache {
|
||||||
int dlat_remaining;
|
int dlat_remaining;
|
||||||
|
|
||||||
boolean isReverse = false;
|
boolean isReverse = false;
|
||||||
if (nodeIdx != n) { // internal (forward-) link
|
if ( nodeIdx != n ) // internal (forward-) link
|
||||||
|
{
|
||||||
dlon_remaining = alon[nodeIdx] - ilon;
|
dlon_remaining = alon[nodeIdx] - ilon;
|
||||||
dlat_remaining = alat[nodeIdx] - ilat;
|
dlat_remaining = alat[nodeIdx] - ilat;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
isReverse = bc.decodeBit();
|
isReverse = bc.decodeBit();
|
||||||
dlon_remaining = extLonDiff.decodeSignedValue();
|
dlon_remaining = extLonDiff.decodeSignedValue();
|
||||||
dlat_remaining = extLatDiff.decodeSignedValue();
|
dlat_remaining = extLatDiff.decodeSignedValue();
|
||||||
}
|
}
|
||||||
if (debug)
|
if ( debug ) System.out.println( "*** decoding link to " + (ilon+dlon_remaining) + "/" + (ilat+dlat_remaining) + " extern=" + (nodeIdx == n) );
|
||||||
System.out.println("*** decoding link to " + (ilon + dlon_remaining) + "/" + (ilat + dlat_remaining) + " extern=" + (nodeIdx == n));
|
|
||||||
|
|
||||||
TagValueWrapper wayTags = wayTagCoder.decodeTagValueSet();
|
TagValueWrapper wayTags = wayTagCoder.decodeTagValueSet();
|
||||||
|
|
||||||
boolean linkValid = wayTags != null || wayValidator == null;
|
boolean linkValid = wayTags != null || wayValidator == null;
|
||||||
if (linkValid) {
|
if ( linkValid )
|
||||||
|
{
|
||||||
int startPointer = aboffset;
|
int startPointer = aboffset;
|
||||||
sizeoffset = writeSizePlaceHolder();
|
sizeoffset = writeSizePlaceHolder();
|
||||||
|
|
||||||
|
|
@ -148,7 +163,8 @@ public final class MicroCache2 extends MicroCache {
|
||||||
writeVarLengthSigned( dlat_remaining );
|
writeVarLengthSigned( dlat_remaining );
|
||||||
|
|
||||||
validBits[ n >> 5 ] |= 1 << n; // mark source-node valid
|
validBits[ n >> 5 ] |= 1 << n; // mark source-node valid
|
||||||
if (nodeIdx != n) { // valid internal (forward-) link
|
if ( nodeIdx != n ) // valid internal (forward-) link
|
||||||
|
{
|
||||||
reverseLinks.addDataElement( nodeIdx, n ); // register reverse link
|
reverseLinks.addDataElement( nodeIdx, n ); // register reverse link
|
||||||
finaldatasize += 1 + aboffset-startPointer; // reserve place for reverse
|
finaldatasize += 1 + aboffset-startPointer; // reserve place for reverse
|
||||||
validBits[ nodeIdx >> 5 ] |= 1 << nodeIdx; // mark target-node valid
|
validBits[ nodeIdx >> 5 ] |= 1 << nodeIdx; // mark target-node valid
|
||||||
|
|
@ -156,12 +172,15 @@ public final class MicroCache2 extends MicroCache {
|
||||||
writeModeAndDesc( isReverse, wayTags == null ? null : wayTags.data );
|
writeModeAndDesc( isReverse, wayTags == null ? null : wayTags.data );
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isReverse) { // write geometry for forward links only
|
if ( !isReverse ) // write geometry for forward links only
|
||||||
|
{
|
||||||
WaypointMatcher matcher = wayTags == null || wayTags.accessType < 2 ? null : waypointMatcher;
|
WaypointMatcher matcher = wayTags == null || wayTags.accessType < 2 ? null : waypointMatcher;
|
||||||
int ilontarget = ilon + dlon_remaining;
|
int ilontarget = ilon + dlon_remaining;
|
||||||
int ilattarget = ilat + dlat_remaining;
|
int ilattarget = ilat + dlat_remaining;
|
||||||
if (matcher != null) {
|
if ( matcher != null )
|
||||||
if (!matcher.start(ilon, ilat, ilontarget, ilattarget)) {
|
{
|
||||||
|
if ( !matcher.start( ilon, ilat, ilontarget, ilattarget ) )
|
||||||
|
{
|
||||||
matcher = null;
|
matcher = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -169,25 +188,27 @@ public final class MicroCache2 extends MicroCache {
|
||||||
int transcount = bc.decodeVarBits();
|
int transcount = bc.decodeVarBits();
|
||||||
if ( debug ) System.out.println( "*** decoding geometry with count=" + transcount );
|
if ( debug ) System.out.println( "*** decoding geometry with count=" + transcount );
|
||||||
int count = transcount+1;
|
int count = transcount+1;
|
||||||
for (int i = 0; i < transcount; i++) {
|
for( int i=0; i<transcount; i++ )
|
||||||
|
{
|
||||||
int dlon = bc.decodePredictedValue( dlon_remaining/count );
|
int dlon = bc.decodePredictedValue( dlon_remaining/count );
|
||||||
int dlat = bc.decodePredictedValue( dlat_remaining/count );
|
int dlat = bc.decodePredictedValue( dlat_remaining/count );
|
||||||
dlon_remaining -= dlon;
|
dlon_remaining -= dlon;
|
||||||
dlat_remaining -= dlat;
|
dlat_remaining -= dlat;
|
||||||
count--;
|
count--;
|
||||||
int elediff = transEleDiff.decodeSignedValue();
|
int elediff = transEleDiff.decodeSignedValue();
|
||||||
if (wayTags != null) {
|
if ( wayTags != null )
|
||||||
|
{
|
||||||
writeVarLengthSigned( dlon );
|
writeVarLengthSigned( dlon );
|
||||||
writeVarLengthSigned( dlat );
|
writeVarLengthSigned( dlat );
|
||||||
writeVarLengthSigned( elediff );
|
writeVarLengthSigned( elediff );
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matcher != null)
|
if ( matcher != null ) matcher.transferNode( ilontarget - dlon_remaining, ilattarget - dlat_remaining );
|
||||||
matcher.transferNode(ilontarget - dlon_remaining, ilattarget - dlat_remaining);
|
|
||||||
}
|
}
|
||||||
if ( matcher != null ) matcher.end();
|
if ( matcher != null ) matcher.end();
|
||||||
}
|
}
|
||||||
if (linkValid) {
|
if ( linkValid )
|
||||||
|
{
|
||||||
injectSize( sizeoffset );
|
injectSize( sizeoffset );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -197,9 +218,11 @@ public final class MicroCache2 extends MicroCache {
|
||||||
// calculate final data size
|
// calculate final data size
|
||||||
int finalsize = 0;
|
int finalsize = 0;
|
||||||
int startpos = 0;
|
int startpos = 0;
|
||||||
for (int i = 0; i < size; i++) {
|
for( int i=0; i<size; i++ )
|
||||||
|
{
|
||||||
int endpos = fapos[i];
|
int endpos = fapos[i];
|
||||||
if ((validBits[i >> 5] & (1 << i)) != 0) {
|
if ( ( validBits[ i >> 5 ] & (1 << i ) ) != 0 )
|
||||||
|
{
|
||||||
finaldatasize += endpos-startpos;
|
finaldatasize += endpos-startpos;
|
||||||
finalsize++;
|
finalsize++;
|
||||||
}
|
}
|
||||||
|
|
@ -217,9 +240,11 @@ public final class MicroCache2 extends MicroCache {
|
||||||
size = 0;
|
size = 0;
|
||||||
|
|
||||||
startpos = 0;
|
startpos = 0;
|
||||||
for (int n = 0; n < sizeOld; n++) {
|
for ( int n = 0; n < sizeOld; n++ )
|
||||||
|
{
|
||||||
int endpos = faposOld[n];
|
int endpos = faposOld[n];
|
||||||
if ((validBits[n >> 5] & (1 << n)) != 0) {
|
if ( ( validBits[ n >> 5 ] & (1 << n ) ) != 0 )
|
||||||
|
{
|
||||||
int len = endpos - startpos;
|
int len = endpos - startpos;
|
||||||
System.arraycopy( abOld, startpos, ab, aboffset, len );
|
System.arraycopy( abOld, startpos, ab, aboffset, len );
|
||||||
if ( debug )
|
if ( debug )
|
||||||
|
|
@ -230,7 +255,8 @@ public final class MicroCache2 extends MicroCache {
|
||||||
if ( debug )
|
if ( debug )
|
||||||
System.out.println( "*** appending " + cnt + " reverse links for node " + n );
|
System.out.println( "*** appending " + cnt + " reverse links for node " + n );
|
||||||
|
|
||||||
for (int ri = 0; ri < cnt; ri++) {
|
for ( int ri = 0; ri < cnt; ri++ )
|
||||||
|
{
|
||||||
int nodeIdx = reverseLinks.getDataElement();
|
int nodeIdx = reverseLinks.getDataElement();
|
||||||
int sizeoffset = writeSizePlaceHolder();
|
int sizeoffset = writeSizePlaceHolder();
|
||||||
writeVarLengthSigned( alon[nodeIdx] - alon[n] );
|
writeVarLengthSigned( alon[nodeIdx] - alon[n] );
|
||||||
|
|
@ -248,11 +274,13 @@ public final class MicroCache2 extends MicroCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long expandId(int id32) {
|
public long expandId( int id32 )
|
||||||
|
{
|
||||||
int dlon = 0;
|
int dlon = 0;
|
||||||
int dlat = 0;
|
int dlat = 0;
|
||||||
|
|
||||||
for (int bm = 1; bm < 0x8000; bm <<= 1) {
|
for( int bm = 1; bm < 0x8000; bm <<= 1 )
|
||||||
|
{
|
||||||
if ( (id32 & 1) != 0 ) dlon |= bm;
|
if ( (id32 & 1) != 0 ) dlon |= bm;
|
||||||
if ( (id32 & 2) != 0 ) dlat |= bm;
|
if ( (id32 & 2) != 0 ) dlat |= bm;
|
||||||
id32 >>= 2;
|
id32 >>= 2;
|
||||||
|
|
@ -265,14 +293,16 @@ public final class MicroCache2 extends MicroCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int shrinkId(long id64) {
|
public int shrinkId( long id64 )
|
||||||
|
{
|
||||||
int lon32 = (int)(id64 >> 32);
|
int lon32 = (int)(id64 >> 32);
|
||||||
int lat32 = (int)(id64 & 0xffffffff);
|
int lat32 = (int)(id64 & 0xffffffff);
|
||||||
int dlon = lon32 - lonBase;
|
int dlon = lon32 - lonBase;
|
||||||
int dlat = lat32 - latBase;
|
int dlat = lat32 - latBase;
|
||||||
int id32 = 0;
|
int id32 = 0;
|
||||||
|
|
||||||
for (int bm = 0x4000; bm > 0; bm >>= 1) {
|
for( int bm = 0x4000; bm > 0; bm >>= 1 )
|
||||||
|
{
|
||||||
id32 <<= 2;
|
id32 <<= 2;
|
||||||
if ( ( dlon & bm ) != 0 ) id32 |= 1;
|
if ( ( dlon & bm ) != 0 ) id32 |= 1;
|
||||||
if ( ( dlat & bm ) != 0 ) id32 |= 2;
|
if ( ( dlat & bm ) != 0 ) id32 |= 2;
|
||||||
|
|
@ -281,16 +311,19 @@ public final class MicroCache2 extends MicroCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isInternal(int ilon, int ilat) {
|
public boolean isInternal( int ilon, int ilat )
|
||||||
|
{
|
||||||
return ilon >= lonBase && ilon < lonBase + cellsize
|
return ilon >= lonBase && ilon < lonBase + cellsize
|
||||||
&& ilat >= latBase && ilat < latBase + cellsize;
|
&& ilat >= latBase && ilat < latBase + cellsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int encodeMicroCache(byte[] buffer) {
|
public int encodeMicroCache( byte[] buffer )
|
||||||
Map<Long, Integer> idMap = new HashMap<>();
|
{
|
||||||
for (int n = 0; n < size; n++) { // loop over nodes
|
HashMap<Long,Integer> idMap = new HashMap<Long,Integer>();
|
||||||
idMap.put(expandId(faid[n]), n);
|
for( int n=0; n<size; n++ ) // loop over nodes
|
||||||
|
{
|
||||||
|
idMap.put( Long.valueOf( expandId( faid[n] ) ), Integer.valueOf( n ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
IntegerFifo3Pass linkCounts = new IntegerFifo3Pass( 256 );
|
IntegerFifo3Pass linkCounts = new IntegerFifo3Pass( 256 );
|
||||||
|
|
@ -307,7 +340,8 @@ public final class MicroCache2 extends MicroCache {
|
||||||
|
|
||||||
int netdatasize = 0;
|
int netdatasize = 0;
|
||||||
|
|
||||||
for (int pass = 1; ; pass++) { // 3 passes: counters, stat-collection, encoding
|
for(int pass=1;; pass++) // 3 passes: counters, stat-collection, encoding
|
||||||
|
{
|
||||||
boolean dostats = pass == 3;
|
boolean dostats = pass == 3;
|
||||||
boolean dodebug = debug && pass == 3;
|
boolean dodebug = debug && pass == 3;
|
||||||
|
|
||||||
|
|
@ -338,25 +372,28 @@ public final class MicroCache2 extends MicroCache {
|
||||||
if ( dodebug ) System.out.println( "*** encoding cache of size=" + size );
|
if ( dodebug ) System.out.println( "*** encoding cache of size=" + size );
|
||||||
int lastSelev = 0;
|
int lastSelev = 0;
|
||||||
|
|
||||||
for (int n = 0; n < size; n++) { // loop over nodes
|
for( int n=0; n<size; n++ ) // loop over nodes
|
||||||
|
{
|
||||||
aboffset = startPos( n );
|
aboffset = startPos( n );
|
||||||
aboffsetEnd = fapos[n];
|
aboffsetEnd = fapos[n];
|
||||||
if (dodebug)
|
if ( dodebug ) System.out.println( "*** encoding node " + n + " from " + aboffset + " to " + aboffsetEnd );
|
||||||
System.out.println("*** encoding node " + n + " from " + aboffset + " to " + aboffsetEnd);
|
|
||||||
|
|
||||||
long id64 = expandId( faid[n] );
|
long id64 = expandId( faid[n] );
|
||||||
int ilon = (int)(id64 >> 32);
|
int ilon = (int)(id64 >> 32);
|
||||||
int ilat = (int)(id64 & 0xffffffff);
|
int ilat = (int)(id64 & 0xffffffff);
|
||||||
|
|
||||||
if (aboffset == aboffsetEnd) {
|
if ( aboffset == aboffsetEnd )
|
||||||
|
{
|
||||||
bc.encodeVarBits( 13 ); // empty node escape (delta files only)
|
bc.encodeVarBits( 13 ); // empty node escape (delta files only)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// write turn restrictions
|
// write turn restrictions
|
||||||
while (readBoolean()) {
|
while( readBoolean() )
|
||||||
|
{
|
||||||
short exceptions = readShort(); // except bikes, psv, ...
|
short exceptions = readShort(); // except bikes, psv, ...
|
||||||
if (exceptions != 0) {
|
if ( exceptions != 0 )
|
||||||
|
{
|
||||||
bc.encodeVarBits( 2 ); // 2 = tr exceptions
|
bc.encodeVarBits( 2 ); // 2 = tr exceptions
|
||||||
bc.encodeNoisyNumber( 10 , 5 ); // bit-count
|
bc.encodeNoisyNumber( 10 , 5 ); // bit-count
|
||||||
bc.encodeBounded( 1023 , exceptions & 1023 );
|
bc.encodeBounded( 1023 , exceptions & 1023 );
|
||||||
|
|
@ -387,7 +424,8 @@ public final class MicroCache2 extends MicroCache {
|
||||||
if ( dostats ) bc.assignBits( "link-counts" );
|
if ( dostats ) bc.assignBits( "link-counts" );
|
||||||
|
|
||||||
nlinks = 0;
|
nlinks = 0;
|
||||||
while (hasMoreData()) { // loop over links
|
while( hasMoreData() ) // loop over links
|
||||||
|
{
|
||||||
// read link data
|
// read link data
|
||||||
int startPointer = aboffset;
|
int startPointer = aboffset;
|
||||||
int endPointer = getEndPointer();
|
int endPointer = getEndPointer();
|
||||||
|
|
@ -399,32 +437,35 @@ public final class MicroCache2 extends MicroCache {
|
||||||
boolean isReverse = ( sizecode & 1 ) != 0;
|
boolean isReverse = ( sizecode & 1 ) != 0;
|
||||||
int descSize = sizecode >> 1;
|
int descSize = sizecode >> 1;
|
||||||
byte[] description = null;
|
byte[] description = null;
|
||||||
if (descSize > 0) {
|
if ( descSize > 0 )
|
||||||
|
{
|
||||||
description = new byte[descSize];
|
description = new byte[descSize];
|
||||||
readFully( description );
|
readFully( description );
|
||||||
}
|
}
|
||||||
|
|
||||||
long link64 = ((long)ilonlink)<<32 | ilatlink;
|
long link64 = ((long)ilonlink)<<32 | ilatlink;
|
||||||
Integer idx = idMap.get(link64);
|
Integer idx = idMap.get( Long.valueOf( link64 ) );
|
||||||
boolean isInternal = idx != null;
|
boolean isInternal = idx != null;
|
||||||
|
|
||||||
if (isReverse && isInternal) {
|
if ( isReverse && isInternal )
|
||||||
if (dodebug)
|
{
|
||||||
System.out.println("*** NOT encoding link reverse=" + isReverse + " internal=" + isInternal);
|
if ( dodebug ) System.out.println( "*** NOT encoding link reverse=" + isReverse + " internal=" + isInternal );
|
||||||
netdatasize -= aboffset-startPointer;
|
netdatasize -= aboffset-startPointer;
|
||||||
continue; // do not encode internal reverse links
|
continue; // do not encode internal reverse links
|
||||||
}
|
}
|
||||||
if (dodebug)
|
if ( dodebug ) System.out.println( "*** encoding link reverse=" + isReverse + " internal=" + isInternal );
|
||||||
System.out.println("*** encoding link reverse=" + isReverse + " internal=" + isInternal);
|
|
||||||
nlinks++;
|
nlinks++;
|
||||||
|
|
||||||
if (isInternal) {
|
if ( isInternal )
|
||||||
int nodeIdx = idx;
|
{
|
||||||
|
int nodeIdx = idx.intValue();
|
||||||
if ( dodebug ) System.out.println( "*** target nodeIdx=" + nodeIdx );
|
if ( dodebug ) System.out.println( "*** target nodeIdx=" + nodeIdx );
|
||||||
if ( nodeIdx == n ) throw new RuntimeException( "ups: self ref?" );
|
if ( nodeIdx == n ) throw new RuntimeException( "ups: self ref?" );
|
||||||
nodeIdxDiff.encodeSignedValue( nodeIdx - n );
|
nodeIdxDiff.encodeSignedValue( nodeIdx - n );
|
||||||
if ( dostats ) bc.assignBits( "nodeIdx" );
|
if ( dostats ) bc.assignBits( "nodeIdx" );
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
nodeIdxDiff.encodeSignedValue( 0 );
|
nodeIdxDiff.encodeSignedValue( 0 );
|
||||||
bc.encodeBit( isReverse );
|
bc.encodeBit( isReverse );
|
||||||
extLonDiff.encodeSignedValue( ilonlink - ilon );
|
extLonDiff.encodeSignedValue( ilonlink - ilon );
|
||||||
|
|
@ -434,7 +475,8 @@ public final class MicroCache2 extends MicroCache {
|
||||||
wayTagCoder.encodeTagValueSet( description );
|
wayTagCoder.encodeTagValueSet( description );
|
||||||
if ( dostats ) bc.assignBits( "wayDescIdx" );
|
if ( dostats ) bc.assignBits( "wayDescIdx" );
|
||||||
|
|
||||||
if (!isReverse) {
|
if ( !isReverse )
|
||||||
|
{
|
||||||
byte[] geometry = readDataUntil( endPointer );
|
byte[] geometry = readDataUntil( endPointer );
|
||||||
// write transition nodes
|
// write transition nodes
|
||||||
int count = transCounts.getNext();
|
int count = transCounts.getNext();
|
||||||
|
|
@ -442,12 +484,14 @@ public final class MicroCache2 extends MicroCache {
|
||||||
bc.encodeVarBits( count++ );
|
bc.encodeVarBits( count++ );
|
||||||
if ( dostats ) bc.assignBits( "transcount" );
|
if ( dostats ) bc.assignBits( "transcount" );
|
||||||
int transcount = 0;
|
int transcount = 0;
|
||||||
if (geometry != null) {
|
if ( geometry != null )
|
||||||
|
{
|
||||||
int dlon_remaining = ilonlink - ilon;
|
int dlon_remaining = ilonlink - ilon;
|
||||||
int dlat_remaining = ilatlink - ilat;
|
int dlat_remaining = ilatlink - ilat;
|
||||||
|
|
||||||
ByteDataReader r = new ByteDataReader( geometry );
|
ByteDataReader r = new ByteDataReader( geometry );
|
||||||
while (r.hasMoreData()) {
|
while ( r.hasMoreData() )
|
||||||
|
{
|
||||||
transcount++;
|
transcount++;
|
||||||
|
|
||||||
int dlon = r.readVarLengthSigned();
|
int dlon = r.readVarLengthSigned();
|
||||||
|
|
@ -467,7 +511,8 @@ public final class MicroCache2 extends MicroCache {
|
||||||
}
|
}
|
||||||
linkCounts.add( nlinks );
|
linkCounts.add( nlinks );
|
||||||
}
|
}
|
||||||
if (pass == 3) {
|
if ( pass == 3 )
|
||||||
|
{
|
||||||
return bc.closeAndGetEncodedLength();
|
return bc.closeAndGetEncodedLength();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,12 @@ package btools.codec;
|
||||||
* Encoder/Decoder for signed integers that automatically detects the typical
|
* Encoder/Decoder for signed integers that automatically detects the typical
|
||||||
* range of these numbers to determine a noisy-bit count as a very simple
|
* range of these numbers to determine a noisy-bit count as a very simple
|
||||||
* dictionary
|
* dictionary
|
||||||
* <p>
|
*
|
||||||
* Adapted for 3-pass encoding (counters -> statistics -> encoding )
|
* Adapted for 3-pass encoding (counters -> statistics -> encoding )
|
||||||
* but doesn't do anything at pass1
|
* but doesn't do anything at pass1
|
||||||
*/
|
*/
|
||||||
public final class NoisyDiffCoder {
|
public final class NoisyDiffCoder
|
||||||
|
{
|
||||||
private int tot;
|
private int tot;
|
||||||
private int[] freqs;
|
private int[] freqs;
|
||||||
private int noisybits;
|
private int noisybits;
|
||||||
|
|
@ -18,7 +19,8 @@ public final class NoisyDiffCoder {
|
||||||
/**
|
/**
|
||||||
* Create a decoder and read the noisy-bit count from the gibe context
|
* Create a decoder and read the noisy-bit count from the gibe context
|
||||||
*/
|
*/
|
||||||
public NoisyDiffCoder(StatCoderContext bc) {
|
public NoisyDiffCoder( StatCoderContext bc )
|
||||||
|
{
|
||||||
noisybits = bc.decodeVarBits();
|
noisybits = bc.decodeVarBits();
|
||||||
this.bc = bc;
|
this.bc = bc;
|
||||||
}
|
}
|
||||||
|
|
@ -26,16 +28,21 @@ public final class NoisyDiffCoder {
|
||||||
/**
|
/**
|
||||||
* Create an encoder for 3-pass-encoding
|
* Create an encoder for 3-pass-encoding
|
||||||
*/
|
*/
|
||||||
public NoisyDiffCoder() {
|
public NoisyDiffCoder()
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* encodes a signed int (pass3 only, stats collection in pass2)
|
* encodes a signed int (pass3 only, stats collection in pass2)
|
||||||
*/
|
*/
|
||||||
public void encodeSignedValue(int value) {
|
public void encodeSignedValue( int value )
|
||||||
if (pass == 3) {
|
{
|
||||||
|
if ( pass == 3 )
|
||||||
|
{
|
||||||
bc.encodeNoisyDiff( value, noisybits );
|
bc.encodeNoisyDiff( value, noisybits );
|
||||||
} else if (pass == 2) {
|
}
|
||||||
|
else if ( pass == 2 )
|
||||||
|
{
|
||||||
count( value < 0 ? -value : value );
|
count( value < 0 ? -value : value );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -43,7 +50,8 @@ public final class NoisyDiffCoder {
|
||||||
/**
|
/**
|
||||||
* decodes a signed int
|
* decodes a signed int
|
||||||
*/
|
*/
|
||||||
public int decodeSignedValue() {
|
public int decodeSignedValue()
|
||||||
|
{
|
||||||
return bc.decodeNoisyDiff( noisybits );
|
return bc.decodeNoisyDiff( noisybits );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,10 +59,13 @@ public final class NoisyDiffCoder {
|
||||||
* Starts a new encoding pass and (in pass3) calculates the noisy-bit count
|
* Starts a new encoding pass and (in pass3) calculates the noisy-bit count
|
||||||
* from the stats collected in pass2 and writes that to the given context
|
* from the stats collected in pass2 and writes that to the given context
|
||||||
*/
|
*/
|
||||||
public void encodeDictionary(StatCoderContext bc) {
|
public void encodeDictionary( StatCoderContext bc )
|
||||||
if (++pass == 3) {
|
{
|
||||||
|
if ( ++pass == 3 )
|
||||||
|
{
|
||||||
// how many noisy bits?
|
// how many noisy bits?
|
||||||
for (noisybits = 0; noisybits < 14 && tot > 0; noisybits++) {
|
for ( noisybits = 0; noisybits < 14 && tot > 0; noisybits++ )
|
||||||
|
{
|
||||||
if ( freqs[noisybits] < ( tot >> 1 ) )
|
if ( freqs[noisybits] < ( tot >> 1 ) )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -63,11 +74,13 @@ public final class NoisyDiffCoder {
|
||||||
this.bc = bc;
|
this.bc = bc;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void count(int value) {
|
private void count( int value )
|
||||||
|
{
|
||||||
if ( freqs == null )
|
if ( freqs == null )
|
||||||
freqs = new int[14];
|
freqs = new int[14];
|
||||||
int bm = 1;
|
int bm = 1;
|
||||||
for (int i = 0; i < 14; i++) {
|
for ( int i = 0; i < 14; i++ )
|
||||||
|
{
|
||||||
if ( value < bm )
|
if ( value < bm )
|
||||||
break;
|
break;
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,26 @@
|
||||||
package btools.codec;
|
package btools.codec;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import btools.util.BitCoderContext;
|
import btools.util.BitCoderContext;
|
||||||
|
|
||||||
public final class StatCoderContext extends BitCoderContext {
|
public final class StatCoderContext extends BitCoderContext
|
||||||
private static Map<String, long[]> statsPerName;
|
{
|
||||||
|
private static TreeMap<String, long[]> statsPerName;
|
||||||
private long lastbitpos = 0;
|
private long lastbitpos = 0;
|
||||||
|
|
||||||
|
|
||||||
private static final int[] noisy_bits = new int[1024];
|
private static final int[] noisy_bits = new int[1024];
|
||||||
|
|
||||||
static {
|
static
|
||||||
|
{
|
||||||
// noisybits lookup
|
// noisybits lookup
|
||||||
for (int i = 0; i < 1024; i++) {
|
for( int i=0; i<1024; i++ )
|
||||||
|
{
|
||||||
int p = i;
|
int p = i;
|
||||||
int noisybits = 0;
|
int noisybits = 0;
|
||||||
while (p > 2) {
|
while (p > 2)
|
||||||
|
{
|
||||||
noisybits++;
|
noisybits++;
|
||||||
p >>= 1;
|
p >>= 1;
|
||||||
}
|
}
|
||||||
|
|
@ -26,7 +29,8 @@ public final class StatCoderContext extends BitCoderContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public StatCoderContext(byte[] ab) {
|
public StatCoderContext( byte[] ab )
|
||||||
|
{
|
||||||
super( ab );
|
super( ab );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,13 +40,16 @@ public final class StatCoderContext extends BitCoderContext {
|
||||||
*
|
*
|
||||||
* @see #getBitReport
|
* @see #getBitReport
|
||||||
*/
|
*/
|
||||||
public void assignBits(String name) {
|
public void assignBits( String name )
|
||||||
|
{
|
||||||
long bitpos = getWritingBitPosition();
|
long bitpos = getWritingBitPosition();
|
||||||
if (statsPerName == null) {
|
if ( statsPerName == null )
|
||||||
statsPerName = new TreeMap<>();
|
{
|
||||||
|
statsPerName = new TreeMap<String, long[]>();
|
||||||
}
|
}
|
||||||
long[] stats = statsPerName.get( name );
|
long[] stats = statsPerName.get( name );
|
||||||
if (stats == null) {
|
if ( stats == null )
|
||||||
|
{
|
||||||
stats = new long[2];
|
stats = new long[2];
|
||||||
statsPerName.put( name, stats );
|
statsPerName.put( name, stats );
|
||||||
}
|
}
|
||||||
|
|
@ -56,12 +63,15 @@ public final class StatCoderContext extends BitCoderContext {
|
||||||
*
|
*
|
||||||
* @see #assignBits
|
* @see #assignBits
|
||||||
*/
|
*/
|
||||||
public static String getBitReport() {
|
public static String getBitReport()
|
||||||
if (statsPerName == null) {
|
{
|
||||||
|
if ( statsPerName == null )
|
||||||
|
{
|
||||||
return "<empty bit report>";
|
return "<empty bit report>";
|
||||||
}
|
}
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for (String name : statsPerName.keySet()) {
|
for ( String name : statsPerName.keySet() )
|
||||||
|
{
|
||||||
long[] stats = statsPerName.get( name );
|
long[] stats = statsPerName.get( name );
|
||||||
sb.append( name + " count=" + stats[1] + " bits=" + stats[0] + "\n" );
|
sb.append( name + " count=" + stats[1] + " bits=" + stats[0] + "\n" );
|
||||||
}
|
}
|
||||||
|
|
@ -75,11 +85,14 @@ public final class StatCoderContext extends BitCoderContext {
|
||||||
*
|
*
|
||||||
* @see #decodeNoisyNumber
|
* @see #decodeNoisyNumber
|
||||||
*/
|
*/
|
||||||
public void encodeNoisyNumber(int value, int noisybits) {
|
public void encodeNoisyNumber( int value, int noisybits )
|
||||||
if (value < 0) {
|
{
|
||||||
|
if ( value < 0 )
|
||||||
|
{
|
||||||
throw new IllegalArgumentException( "encodeVarBits expects positive value" );
|
throw new IllegalArgumentException( "encodeVarBits expects positive value" );
|
||||||
}
|
}
|
||||||
if (noisybits > 0) {
|
if ( noisybits > 0 )
|
||||||
|
{
|
||||||
int mask = 0xffffffff >>> ( 32 - noisybits );
|
int mask = 0xffffffff >>> ( 32 - noisybits );
|
||||||
encodeBounded( mask, value & mask );
|
encodeBounded( mask, value & mask );
|
||||||
value >>= noisybits;
|
value >>= noisybits;
|
||||||
|
|
@ -93,7 +106,8 @@ public final class StatCoderContext extends BitCoderContext {
|
||||||
*
|
*
|
||||||
* @see #encodeNoisyNumber
|
* @see #encodeNoisyNumber
|
||||||
*/
|
*/
|
||||||
public int decodeNoisyNumber(int noisybits) {
|
public int decodeNoisyNumber( int noisybits )
|
||||||
|
{
|
||||||
int value = decodeBits( noisybits );
|
int value = decodeBits( noisybits );
|
||||||
return value | ( decodeVarBits() << noisybits );
|
return value | ( decodeVarBits() << noisybits );
|
||||||
}
|
}
|
||||||
|
|
@ -104,15 +118,18 @@ public final class StatCoderContext extends BitCoderContext {
|
||||||
*
|
*
|
||||||
* @see #decodeNoisyDiff
|
* @see #decodeNoisyDiff
|
||||||
*/
|
*/
|
||||||
public void encodeNoisyDiff(int value, int noisybits) {
|
public void encodeNoisyDiff( int value, int noisybits )
|
||||||
if (noisybits > 0) {
|
{
|
||||||
|
if ( noisybits > 0 )
|
||||||
|
{
|
||||||
value += 1 << ( noisybits - 1 );
|
value += 1 << ( noisybits - 1 );
|
||||||
int mask = 0xffffffff >>> ( 32 - noisybits );
|
int mask = 0xffffffff >>> ( 32 - noisybits );
|
||||||
encodeBounded( mask, value & mask );
|
encodeBounded( mask, value & mask );
|
||||||
value >>= noisybits;
|
value >>= noisybits;
|
||||||
}
|
}
|
||||||
encodeVarBits( value < 0 ? -value : value );
|
encodeVarBits( value < 0 ? -value : value );
|
||||||
if (value != 0) {
|
if ( value != 0 )
|
||||||
|
{
|
||||||
encodeBit( value < 0 );
|
encodeBit( value < 0 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -123,14 +140,18 @@ public final class StatCoderContext extends BitCoderContext {
|
||||||
*
|
*
|
||||||
* @see #encodeNoisyDiff
|
* @see #encodeNoisyDiff
|
||||||
*/
|
*/
|
||||||
public int decodeNoisyDiff(int noisybits) {
|
public int decodeNoisyDiff( int noisybits )
|
||||||
|
{
|
||||||
int value = 0;
|
int value = 0;
|
||||||
if (noisybits > 0) {
|
if ( noisybits > 0 )
|
||||||
|
{
|
||||||
value = decodeBits( noisybits ) - ( 1 << ( noisybits - 1 ) );
|
value = decodeBits( noisybits ) - ( 1 << ( noisybits - 1 ) );
|
||||||
}
|
}
|
||||||
int val2 = decodeVarBits() << noisybits;
|
int val2 = decodeVarBits() << noisybits;
|
||||||
if (val2 != 0) {
|
if ( val2 != 0 )
|
||||||
if (decodeBit()) {
|
{
|
||||||
|
if ( decodeBit() )
|
||||||
|
{
|
||||||
val2 = -val2;
|
val2 = -val2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -143,11 +164,13 @@ public final class StatCoderContext extends BitCoderContext {
|
||||||
*
|
*
|
||||||
* @see #decodePredictedValue
|
* @see #decodePredictedValue
|
||||||
*/
|
*/
|
||||||
public void encodePredictedValue(int value, int predictor) {
|
public void encodePredictedValue( int value, int predictor )
|
||||||
|
{
|
||||||
int p = predictor < 0 ? -predictor : predictor;
|
int p = predictor < 0 ? -predictor : predictor;
|
||||||
int noisybits = 0;
|
int noisybits = 0;
|
||||||
|
|
||||||
while (p > 2) {
|
while (p > 2)
|
||||||
|
{
|
||||||
noisybits++;
|
noisybits++;
|
||||||
p >>= 1;
|
p >>= 1;
|
||||||
}
|
}
|
||||||
|
|
@ -160,10 +183,12 @@ public final class StatCoderContext extends BitCoderContext {
|
||||||
*
|
*
|
||||||
* @see #encodePredictedValue
|
* @see #encodePredictedValue
|
||||||
*/
|
*/
|
||||||
public int decodePredictedValue(int predictor) {
|
public int decodePredictedValue( int predictor )
|
||||||
|
{
|
||||||
int p = predictor < 0 ? -predictor : predictor;
|
int p = predictor < 0 ? -predictor : predictor;
|
||||||
int noisybits = 0;
|
int noisybits = 0;
|
||||||
while (p > 1023) {
|
while (p > 1023)
|
||||||
|
{
|
||||||
noisybits++;
|
noisybits++;
|
||||||
p >>= 1;
|
p >>= 1;
|
||||||
}
|
}
|
||||||
|
|
@ -177,20 +202,29 @@ public final class StatCoderContext extends BitCoderContext {
|
||||||
* bits per value that only depends on the typical distance between subsequent
|
* bits per value that only depends on the typical distance between subsequent
|
||||||
* values and also benefits
|
* values and also benefits
|
||||||
*
|
*
|
||||||
* @param values the array to encode
|
* @param values
|
||||||
* @param offset position in this array where to start
|
* the array to encode
|
||||||
* @param subsize number of values to encode
|
* @param offset
|
||||||
* @param nextbit bitmask with the most significant bit set to 1
|
* position in this array where to start
|
||||||
* @param mask should be 0
|
* @param subsize
|
||||||
|
* number of values to encode
|
||||||
|
* @param nextbit
|
||||||
|
* bitmask with the most significant bit set to 1
|
||||||
|
* @param mask
|
||||||
|
* should be 0
|
||||||
*/
|
*/
|
||||||
public void encodeSortedArray(int[] values, int offset, int subsize, int nextbit, int mask) {
|
public void encodeSortedArray( int[] values, int offset, int subsize, int nextbit, int mask )
|
||||||
if (subsize == 1) { // last-choice shortcut
|
{
|
||||||
while (nextbit != 0) {
|
if ( subsize == 1 ) // last-choice shortcut
|
||||||
|
{
|
||||||
|
while (nextbit != 0)
|
||||||
|
{
|
||||||
encodeBit( ( values[offset] & nextbit ) != 0 );
|
encodeBit( ( values[offset] & nextbit ) != 0 );
|
||||||
nextbit >>= 1;
|
nextbit >>= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (nextbit == 0) {
|
if ( nextbit == 0 )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -200,8 +234,10 @@ public final class StatCoderContext extends BitCoderContext {
|
||||||
// count 0-bit-fraction
|
// count 0-bit-fraction
|
||||||
int i = offset;
|
int i = offset;
|
||||||
int end = subsize + offset;
|
int end = subsize + offset;
|
||||||
for (; i < end; i++) {
|
for ( ; i < end; i++ )
|
||||||
if ((values[i] & mask) != data) {
|
{
|
||||||
|
if ( ( values[i] & mask ) != data )
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -209,32 +245,45 @@ public final class StatCoderContext extends BitCoderContext {
|
||||||
int size2 = subsize - size1;
|
int size2 = subsize - size1;
|
||||||
|
|
||||||
encodeBounded( subsize, size1 );
|
encodeBounded( subsize, size1 );
|
||||||
if (size1 > 0) {
|
if ( size1 > 0 )
|
||||||
|
{
|
||||||
encodeSortedArray( values, offset, size1, nextbit >> 1, mask );
|
encodeSortedArray( values, offset, size1, nextbit >> 1, mask );
|
||||||
}
|
}
|
||||||
if (size2 > 0) {
|
if ( size2 > 0 )
|
||||||
|
{
|
||||||
encodeSortedArray( values, i, size2, nextbit >> 1, mask );
|
encodeSortedArray( values, i, size2, nextbit >> 1, mask );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param values the array to encode
|
|
||||||
* @param offset position in this array where to start
|
|
||||||
* @param subsize number of values to encode
|
|
||||||
* @param nextbit bitmask with the most significant bit set to 1
|
|
||||||
* @param value should be 0
|
|
||||||
* @see #encodeSortedArray
|
* @see #encodeSortedArray
|
||||||
|
*
|
||||||
|
* @param values
|
||||||
|
* the array to encode
|
||||||
|
* @param offset
|
||||||
|
* position in this array where to start
|
||||||
|
* @param subsize
|
||||||
|
* number of values to encode
|
||||||
|
* @param nextbit
|
||||||
|
* bitmask with the most significant bit set to 1
|
||||||
|
* @param value
|
||||||
|
* should be 0
|
||||||
*/
|
*/
|
||||||
public void decodeSortedArray(int[] values, int offset, int subsize, int nextbitpos, int value) {
|
public void decodeSortedArray( int[] values, int offset, int subsize, int nextbitpos, int value )
|
||||||
if (subsize == 1) { // last-choice shortcut
|
{
|
||||||
if (nextbitpos >= 0) {
|
if ( subsize == 1 ) // last-choice shortcut
|
||||||
|
{
|
||||||
|
if ( nextbitpos >= 0 )
|
||||||
|
{
|
||||||
value |= decodeBitsReverse( nextbitpos+1 );
|
value |= decodeBitsReverse( nextbitpos+1 );
|
||||||
}
|
}
|
||||||
values[offset] = value;
|
values[offset] = value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (nextbitpos < 0) {
|
if ( nextbitpos < 0 )
|
||||||
while (subsize-- > 0) {
|
{
|
||||||
|
while (subsize-- > 0)
|
||||||
|
{
|
||||||
values[offset++] = value;
|
values[offset++] = value;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
@ -243,10 +292,12 @@ public final class StatCoderContext extends BitCoderContext {
|
||||||
int size1 = decodeBounded( subsize );
|
int size1 = decodeBounded( subsize );
|
||||||
int size2 = subsize - size1;
|
int size2 = subsize - size1;
|
||||||
|
|
||||||
if (size1 > 0) {
|
if ( size1 > 0 )
|
||||||
|
{
|
||||||
decodeSortedArray( values, offset, size1, nextbitpos-1, value );
|
decodeSortedArray( values, offset, size1, nextbitpos-1, value );
|
||||||
}
|
}
|
||||||
if (size2 > 0) {
|
if ( size2 > 0 )
|
||||||
|
{
|
||||||
decodeSortedArray( values, offset + size1, size2, nextbitpos-1, value | (1 << nextbitpos) );
|
decodeSortedArray( values, offset + size1, size2, nextbitpos-1, value | (1 << nextbitpos) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,39 +2,44 @@ package btools.codec;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.PriorityQueue;
|
import java.util.PriorityQueue;
|
||||||
import java.util.Queue;
|
|
||||||
|
|
||||||
import btools.util.BitCoderContext;
|
import btools.util.BitCoderContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encoder/Decoder for way-/node-descriptions
|
* Encoder/Decoder for way-/node-descriptions
|
||||||
* <p>
|
*
|
||||||
* It detects identical descriptions and sorts them
|
* It detects identical descriptions and sorts them
|
||||||
* into a huffman-tree according to their frequencies
|
* into a huffman-tree according to their frequencies
|
||||||
* <p>
|
*
|
||||||
* Adapted for 3-pass encoding (counters -> statistics -> encoding )
|
* Adapted for 3-pass encoding (counters -> statistics -> encoding )
|
||||||
* but doesn't do anything at pass1
|
* but doesn't do anything at pass1
|
||||||
*/
|
*/
|
||||||
public final class TagValueCoder {
|
public final class TagValueCoder
|
||||||
private Map<TagValueSet, TagValueSet> identityMap;
|
{
|
||||||
|
private HashMap<TagValueSet, TagValueSet> identityMap;
|
||||||
private Object tree;
|
private Object tree;
|
||||||
private BitCoderContext bc;
|
private BitCoderContext bc;
|
||||||
private int pass;
|
private int pass;
|
||||||
private int nextTagValueSetId;
|
private int nextTagValueSetId;
|
||||||
|
|
||||||
public void encodeTagValueSet(byte[] data) {
|
public void encodeTagValueSet( byte[] data )
|
||||||
if (pass == 1) {
|
{
|
||||||
|
if ( pass == 1 )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
TagValueSet tvsProbe = new TagValueSet(nextTagValueSetId);
|
TagValueSet tvsProbe = new TagValueSet(nextTagValueSetId);
|
||||||
tvsProbe.data = data;
|
tvsProbe.data = data;
|
||||||
TagValueSet tvs = identityMap.get( tvsProbe );
|
TagValueSet tvs = identityMap.get( tvsProbe );
|
||||||
if (pass == 3) {
|
if ( pass == 3 )
|
||||||
|
{
|
||||||
bc.encodeBounded( tvs.range - 1, tvs.code );
|
bc.encodeBounded( tvs.range - 1, tvs.code );
|
||||||
} else if (pass == 2) {
|
}
|
||||||
if (tvs == null) {
|
else if ( pass == 2 )
|
||||||
|
{
|
||||||
|
if ( tvs == null )
|
||||||
|
{
|
||||||
tvs = tvsProbe;
|
tvs = tvsProbe;
|
||||||
nextTagValueSetId++;
|
nextTagValueSetId++;
|
||||||
identityMap.put( tvs, tvs );
|
identityMap.put( tvs, tvs );
|
||||||
|
|
@ -43,9 +48,11 @@ public final class TagValueCoder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TagValueWrapper decodeTagValueSet() {
|
public TagValueWrapper decodeTagValueSet()
|
||||||
|
{
|
||||||
Object node = tree;
|
Object node = tree;
|
||||||
while (node instanceof TreeNode) {
|
while (node instanceof TreeNode)
|
||||||
|
{
|
||||||
TreeNode tn = (TreeNode) node;
|
TreeNode tn = (TreeNode) node;
|
||||||
boolean nextBit = bc.decodeBit();
|
boolean nextBit = bc.decodeBit();
|
||||||
node = nextBit ? tn.child2 : tn.child1;
|
node = nextBit ? tn.child2 : tn.child1;
|
||||||
|
|
@ -53,15 +60,19 @@ public final class TagValueCoder {
|
||||||
return (TagValueWrapper) node;
|
return (TagValueWrapper) node;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void encodeDictionary(BitCoderContext bc) {
|
public void encodeDictionary( BitCoderContext bc )
|
||||||
if (++pass == 3) {
|
{
|
||||||
if (identityMap.size() == 0) {
|
if ( ++pass == 3 )
|
||||||
|
{
|
||||||
|
if ( identityMap.size() == 0 )
|
||||||
|
{
|
||||||
TagValueSet dummy = new TagValueSet(nextTagValueSetId++);
|
TagValueSet dummy = new TagValueSet(nextTagValueSetId++);
|
||||||
identityMap.put( dummy, dummy );
|
identityMap.put( dummy, dummy );
|
||||||
}
|
}
|
||||||
Queue<TagValueSet> queue = new PriorityQueue<>(2 * identityMap.size(), new TagValueSet.FrequencyComparator());
|
PriorityQueue<TagValueSet> queue = new PriorityQueue<TagValueSet>(2*identityMap.size(), new TagValueSet.FrequencyComparator());
|
||||||
queue.addAll(identityMap.values());
|
queue.addAll(identityMap.values());
|
||||||
while (queue.size() > 1) {
|
while (queue.size() > 1)
|
||||||
|
{
|
||||||
TagValueSet node = new TagValueSet(nextTagValueSetId++);
|
TagValueSet node = new TagValueSet(nextTagValueSetId++);
|
||||||
node.child1 = queue.poll();
|
node.child1 = queue.poll();
|
||||||
node.child2 = queue.poll();
|
node.child2 = queue.poll();
|
||||||
|
|
@ -74,18 +85,22 @@ public final class TagValueCoder {
|
||||||
this.bc = bc;
|
this.bc = bc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TagValueCoder(BitCoderContext bc, DataBuffers buffers, TagValueValidator validator) {
|
public TagValueCoder( BitCoderContext bc, DataBuffers buffers, TagValueValidator validator )
|
||||||
|
{
|
||||||
tree = decodeTree( bc, buffers, validator );
|
tree = decodeTree( bc, buffers, validator );
|
||||||
this.bc = bc;
|
this.bc = bc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TagValueCoder() {
|
public TagValueCoder()
|
||||||
identityMap = new HashMap<>();
|
{
|
||||||
|
identityMap = new HashMap<TagValueSet, TagValueSet>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object decodeTree(BitCoderContext bc, DataBuffers buffers, TagValueValidator validator) {
|
private Object decodeTree( BitCoderContext bc, DataBuffers buffers, TagValueValidator validator )
|
||||||
|
{
|
||||||
boolean isNode = bc.decodeBit();
|
boolean isNode = bc.decodeBit();
|
||||||
if (isNode) {
|
if ( isNode )
|
||||||
|
{
|
||||||
TreeNode node = new TreeNode();
|
TreeNode node = new TreeNode();
|
||||||
node.child1 = decodeTree( bc, buffers, validator );
|
node.child1 = decodeTree( bc, buffers, validator );
|
||||||
node.child2 = decodeTree( bc, buffers, validator );
|
node.child2 = decodeTree( bc, buffers, validator );
|
||||||
|
|
@ -100,14 +115,18 @@ public final class TagValueCoder {
|
||||||
int lastEncodedInum = 0;
|
int lastEncodedInum = 0;
|
||||||
|
|
||||||
boolean hasdata = false;
|
boolean hasdata = false;
|
||||||
for (; ; ) {
|
for ( ;; )
|
||||||
|
{
|
||||||
int delta = bc.decodeVarBits();
|
int delta = bc.decodeVarBits();
|
||||||
if (!hasdata) {
|
if ( !hasdata )
|
||||||
if (delta == 0) {
|
{
|
||||||
|
if ( delta == 0 )
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (delta == 0) {
|
if ( delta == 0 )
|
||||||
|
{
|
||||||
ctx.encodeVarBits( 0 );
|
ctx.encodeVarBits( 0 );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +134,8 @@ public final class TagValueCoder {
|
||||||
|
|
||||||
int data = bc.decodeVarBits();
|
int data = bc.decodeVarBits();
|
||||||
|
|
||||||
if (validator == null || validator.isLookupIdxUsed(inum)) {
|
if ( validator == null || validator.isLookupIdxUsed( inum ) )
|
||||||
|
{
|
||||||
hasdata = true;
|
hasdata = true;
|
||||||
ctx.encodeVarBits( inum - lastEncodedInum );
|
ctx.encodeVarBits( inum - lastEncodedInum );
|
||||||
ctx.encodeVarBits( data );
|
ctx.encodeVarBits( data );
|
||||||
|
|
@ -125,15 +145,19 @@ public final class TagValueCoder {
|
||||||
|
|
||||||
byte[] res;
|
byte[] res;
|
||||||
int len = ctx.closeAndGetEncodedLength();
|
int len = ctx.closeAndGetEncodedLength();
|
||||||
if (validator == null) {
|
if ( validator == null )
|
||||||
|
{
|
||||||
res = new byte[len];
|
res = new byte[len];
|
||||||
System.arraycopy( buffer, 0, res, 0, len );
|
System.arraycopy( buffer, 0, res, 0, len );
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
res = validator.unify( buffer, 0, len );
|
res = validator.unify( buffer, 0, len );
|
||||||
}
|
}
|
||||||
|
|
||||||
int accessType = validator == null ? 2 : validator.accessType( res );
|
int accessType = validator == null ? 2 : validator.accessType( res );
|
||||||
if (accessType > 0) {
|
if ( accessType > 0 )
|
||||||
|
{
|
||||||
TagValueWrapper w = new TagValueWrapper();
|
TagValueWrapper w = new TagValueWrapper();
|
||||||
w.data = res;
|
w.data = res;
|
||||||
w.accessType = accessType;
|
w.accessType = accessType;
|
||||||
|
|
@ -142,12 +166,14 @@ public final class TagValueCoder {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class TreeNode {
|
public static final class TreeNode
|
||||||
|
{
|
||||||
public Object child1;
|
public Object child1;
|
||||||
public Object child2;
|
public Object child2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class TagValueSet {
|
public static final class TagValueSet
|
||||||
|
{
|
||||||
public byte[] data;
|
public byte[] data;
|
||||||
public int frequency;
|
public int frequency;
|
||||||
public int code;
|
public int code;
|
||||||
|
|
@ -156,28 +182,36 @@ public final class TagValueCoder {
|
||||||
public TagValueSet child2;
|
public TagValueSet child2;
|
||||||
private int id; // serial number to make the comparator well defined in case of equal frequencies
|
private int id; // serial number to make the comparator well defined in case of equal frequencies
|
||||||
|
|
||||||
public TagValueSet(int id) {
|
public TagValueSet( int id )
|
||||||
|
{
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void encode(BitCoderContext bc, int range, int code) {
|
public void encode( BitCoderContext bc, int range, int code )
|
||||||
|
{
|
||||||
this.range = range;
|
this.range = range;
|
||||||
this.code = code;
|
this.code = code;
|
||||||
boolean isNode = child1 != null;
|
boolean isNode = child1 != null;
|
||||||
bc.encodeBit( isNode );
|
bc.encodeBit( isNode );
|
||||||
if (isNode) {
|
if ( isNode )
|
||||||
|
{
|
||||||
child1.encode( bc, range << 1, code );
|
child1.encode( bc, range << 1, code );
|
||||||
child2.encode( bc, range << 1, code + range );
|
child2.encode( bc, range << 1, code + range );
|
||||||
} else {
|
}
|
||||||
if (data == null) {
|
else
|
||||||
|
{
|
||||||
|
if ( data == null )
|
||||||
|
{
|
||||||
bc.encodeVarBits( 0 );
|
bc.encodeVarBits( 0 );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
BitCoderContext src = new BitCoderContext( data );
|
BitCoderContext src = new BitCoderContext( data );
|
||||||
for (; ; ) {
|
for ( ;; )
|
||||||
|
{
|
||||||
int delta = src.decodeVarBits();
|
int delta = src.decodeVarBits();
|
||||||
bc.encodeVarBits( delta );
|
bc.encodeVarBits( delta );
|
||||||
if (delta == 0) {
|
if ( delta == 0 )
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int data = src.decodeVarBits();
|
int data = src.decodeVarBits();
|
||||||
|
|
@ -187,20 +221,27 @@ public final class TagValueCoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals( Object o )
|
||||||
if (o instanceof TagValueSet) {
|
{
|
||||||
|
if ( o instanceof TagValueSet )
|
||||||
|
{
|
||||||
TagValueSet tvs = (TagValueSet) o;
|
TagValueSet tvs = (TagValueSet) o;
|
||||||
if (data == null) {
|
if ( data == null )
|
||||||
|
{
|
||||||
return tvs.data == null;
|
return tvs.data == null;
|
||||||
}
|
}
|
||||||
if (tvs.data == null) {
|
if ( tvs.data == null )
|
||||||
|
{
|
||||||
return data == null;
|
return data == null;
|
||||||
}
|
}
|
||||||
if (data.length != tvs.data.length) {
|
if ( data.length != tvs.data.length )
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < data.length; i++) {
|
for ( int i = 0; i < data.length; i++ )
|
||||||
if (data[i] != tvs.data[i]) {
|
{
|
||||||
|
if ( data[i] != tvs.data[i] )
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -210,18 +251,22 @@ public final class TagValueCoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode()
|
||||||
if (data == null) {
|
{
|
||||||
|
if ( data == null )
|
||||||
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int h = 17;
|
int h = 17;
|
||||||
for (int i = 0; i < data.length; i++) {
|
for ( int i = 0; i < data.length; i++ )
|
||||||
|
{
|
||||||
h = ( h << 8 ) + data[i];
|
h = ( h << 8 ) + data[i];
|
||||||
}
|
}
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class FrequencyComparator implements Comparator<TagValueSet> {
|
public static class FrequencyComparator implements Comparator<TagValueSet>
|
||||||
|
{
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compare(TagValueSet tvs1, TagValueSet tvs2) {
|
public int compare(TagValueSet tvs1, TagValueSet tvs2) {
|
||||||
|
|
@ -236,7 +281,8 @@ public final class TagValueCoder {
|
||||||
if ( tvs1.id > tvs2.id )
|
if ( tvs1.id > tvs2.id )
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (tvs1 != tvs2) {
|
if ( tvs1 != tvs2 )
|
||||||
|
{
|
||||||
throw new RuntimeException( "identity corruption!" );
|
throw new RuntimeException( "identity corruption!" );
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
package btools.codec;
|
package btools.codec;
|
||||||
|
|
||||||
|
|
||||||
public interface TagValueValidator {
|
public interface TagValueValidator
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* @param tagValueSet the way description to check
|
* @param tagValueSet the way description to check
|
||||||
* @return 0 = nothing, 1=no matching, 2=normal
|
* @return 0 = nothing, 1=no matching, 2=normal
|
||||||
*/
|
*/
|
||||||
int accessType(byte[] tagValueSet);
|
public int accessType( byte[] tagValueSet );
|
||||||
|
|
||||||
byte[] unify(byte[] tagValueSet, int offset, int len);
|
public byte[] unify( byte[] tagValueSet, int offset, int len );
|
||||||
|
|
||||||
boolean isLookupIdxUsed(int idx);
|
public boolean isLookupIdxUsed( int idx );
|
||||||
|
|
||||||
void setDecodeForbidden(boolean decodeForbidden);
|
public void setDecodeForbidden( boolean decodeForbidden );
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ package btools.codec;
|
||||||
* TagValueWrapper wrapps a description bitmap
|
* TagValueWrapper wrapps a description bitmap
|
||||||
* to add the access-type
|
* to add the access-type
|
||||||
*/
|
*/
|
||||||
public final class TagValueWrapper {
|
public final class TagValueWrapper
|
||||||
|
{
|
||||||
public byte[] data;
|
public byte[] data;
|
||||||
public int accessType;
|
public int accessType;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,9 @@ package btools.codec;
|
||||||
* from the decoder to find the closest
|
* from the decoder to find the closest
|
||||||
* matches to the waypoints
|
* matches to the waypoints
|
||||||
*/
|
*/
|
||||||
public interface WaypointMatcher {
|
public interface WaypointMatcher
|
||||||
|
{
|
||||||
boolean start( int ilonStart, int ilatStart, int ilonTarget, int ilatTarget );
|
boolean start( int ilonStart, int ilatStart, int ilonTarget, int ilatTarget );
|
||||||
|
|
||||||
void transferNode( int ilon, int ilat );
|
void transferNode( int ilon, int ilat );
|
||||||
|
|
||||||
void end();
|
void end();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,35 +3,50 @@ package btools.codec;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class LinkedListContainerTest {
|
public class LinkedListContainerTest
|
||||||
|
{
|
||||||
@Test
|
@Test
|
||||||
public void linkedListTest1() {
|
public void linkedListTest1()
|
||||||
|
{
|
||||||
int nlists = 553;
|
int nlists = 553;
|
||||||
|
|
||||||
LinkedListContainer llc = new LinkedListContainer( nlists, null );
|
LinkedListContainer llc = new LinkedListContainer( nlists, null );
|
||||||
|
|
||||||
for (int ln = 0; ln < nlists; ln++) {
|
for ( int ln = 0; ln < nlists; ln++ )
|
||||||
for (int i = 0; i < 10; i++) {
|
{
|
||||||
|
for ( int i = 0; i < 10; i++ )
|
||||||
|
{
|
||||||
llc.addDataElement( ln, ln * i );
|
llc.addDataElement( ln, ln * i );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++) {
|
for ( int i = 0; i < 10; i++ )
|
||||||
for (int ln = 0; ln < nlists; ln++) {
|
{
|
||||||
|
for ( int ln = 0; ln < nlists; ln++ )
|
||||||
|
{
|
||||||
llc.addDataElement( ln, ln * i );
|
llc.addDataElement( ln, ln * i );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int ln = 0; ln < nlists; ln++) {
|
for ( int ln = 0; ln < nlists; ln++ )
|
||||||
|
{
|
||||||
int cnt = llc.initList( ln );
|
int cnt = llc.initList( ln );
|
||||||
Assert.assertEquals("list size test", 20, cnt);
|
Assert.assertTrue( "list size test", cnt == 20 );
|
||||||
|
|
||||||
for (int i = 19; i >= 0; i--) {
|
for ( int i = 19; i >= 0; i-- )
|
||||||
|
{
|
||||||
int data = llc.getDataElement();
|
int data = llc.getDataElement();
|
||||||
Assert.assertEquals("data value test", ln * (i % 10), data);
|
Assert.assertTrue( "data value test", data == ln * ( i % 10 ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.assertThrows("no more elements expected", IllegalArgumentException.class, () -> llc.getDataElement());
|
try
|
||||||
|
{
|
||||||
|
llc.getDataElement();
|
||||||
|
Assert.fail( "no more elements expected" );
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,23 +6,30 @@ import java.util.Random;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class StatCoderContextTest {
|
public class StatCoderContextTest
|
||||||
|
{
|
||||||
@Test
|
@Test
|
||||||
public void noisyVarBitsEncodeDecodeTest() {
|
public void noisyVarBitsEncodeDecodeTest()
|
||||||
|
{
|
||||||
byte[] ab = new byte[40000];
|
byte[] ab = new byte[40000];
|
||||||
StatCoderContext ctx = new StatCoderContext( ab );
|
StatCoderContext ctx = new StatCoderContext( ab );
|
||||||
for (int noisybits = 1; noisybits < 12; noisybits++) {
|
for ( int noisybits = 1; noisybits < 12; noisybits++ )
|
||||||
for (int i = 0; i < 1000; i++) {
|
{
|
||||||
|
for ( int i = 0; i < 1000; i++ )
|
||||||
|
{
|
||||||
ctx.encodeNoisyNumber( i, noisybits );
|
ctx.encodeNoisyNumber( i, noisybits );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.closeAndGetEncodedLength();
|
ctx.closeAndGetEncodedLength();
|
||||||
ctx = new StatCoderContext( ab );
|
ctx = new StatCoderContext( ab );
|
||||||
|
|
||||||
for (int noisybits = 1; noisybits < 12; noisybits++) {
|
for ( int noisybits = 1; noisybits < 12; noisybits++ )
|
||||||
for (int i = 0; i < 1000; i++) {
|
{
|
||||||
|
for ( int i = 0; i < 1000; i++ )
|
||||||
|
{
|
||||||
int value = ctx.decodeNoisyNumber( noisybits );
|
int value = ctx.decodeNoisyNumber( noisybits );
|
||||||
if (value != i) {
|
if ( value != i )
|
||||||
|
{
|
||||||
Assert.fail( "value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value );
|
Assert.fail( "value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -30,21 +37,27 @@ public class StatCoderContextTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void noisySignedVarBitsEncodeDecodeTest() {
|
public void noisySignedVarBitsEncodeDecodeTest()
|
||||||
|
{
|
||||||
byte[] ab = new byte[80000];
|
byte[] ab = new byte[80000];
|
||||||
StatCoderContext ctx = new StatCoderContext( ab );
|
StatCoderContext ctx = new StatCoderContext( ab );
|
||||||
for (int noisybits = 0; noisybits < 12; noisybits++) {
|
for ( int noisybits = 0; noisybits < 12; noisybits++ )
|
||||||
for (int i = -1000; i < 1000; i++) {
|
{
|
||||||
|
for ( int i = -1000; i < 1000; i++ )
|
||||||
|
{
|
||||||
ctx.encodeNoisyDiff( i, noisybits );
|
ctx.encodeNoisyDiff( i, noisybits );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.closeAndGetEncodedLength();
|
ctx.closeAndGetEncodedLength();
|
||||||
ctx = new StatCoderContext( ab );
|
ctx = new StatCoderContext( ab );
|
||||||
|
|
||||||
for (int noisybits = 0; noisybits < 12; noisybits++) {
|
for ( int noisybits = 0; noisybits < 12; noisybits++ )
|
||||||
for (int i = -1000; i < 1000; i++) {
|
{
|
||||||
|
for ( int i = -1000; i < 1000; i++ )
|
||||||
|
{
|
||||||
int value = ctx.decodeNoisyDiff( noisybits );
|
int value = ctx.decodeNoisyDiff( noisybits );
|
||||||
if (value != i) {
|
if ( value != i )
|
||||||
|
{
|
||||||
Assert.fail( "value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value );
|
Assert.fail( "value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -52,21 +65,27 @@ public class StatCoderContextTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void predictedValueEncodeDecodeTest() {
|
public void predictedValueEncodeDecodeTest()
|
||||||
|
{
|
||||||
byte[] ab = new byte[80000];
|
byte[] ab = new byte[80000];
|
||||||
StatCoderContext ctx = new StatCoderContext( ab );
|
StatCoderContext ctx = new StatCoderContext( ab );
|
||||||
for (int value = -100; value < 100; value += 5) {
|
for ( int value = -100; value < 100; value += 5 )
|
||||||
for (int predictor = -200; predictor < 200; predictor += 7) {
|
{
|
||||||
|
for ( int predictor = -200; predictor < 200; predictor += 7 )
|
||||||
|
{
|
||||||
ctx.encodePredictedValue( value, predictor );
|
ctx.encodePredictedValue( value, predictor );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.closeAndGetEncodedLength();
|
ctx.closeAndGetEncodedLength();
|
||||||
ctx = new StatCoderContext( ab );
|
ctx = new StatCoderContext( ab );
|
||||||
|
|
||||||
for (int value = -100; value < 100; value += 5) {
|
for ( int value = -100; value < 100; value += 5 )
|
||||||
for (int predictor = -200; predictor < 200; predictor += 7) {
|
{
|
||||||
|
for ( int predictor = -200; predictor < 200; predictor += 7 )
|
||||||
|
{
|
||||||
int decodedValue = ctx.decodePredictedValue( predictor );
|
int decodedValue = ctx.decodePredictedValue( predictor );
|
||||||
if (value != decodedValue) {
|
if ( value != decodedValue )
|
||||||
|
{
|
||||||
Assert.fail( "value mismatch: value=" + value + " predictor=" + predictor + " decodedValue=" + decodedValue );
|
Assert.fail( "value mismatch: value=" + value + " predictor=" + predictor + " decodedValue=" + decodedValue );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -74,11 +93,13 @@ public class StatCoderContextTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void sortedArrayEncodeDecodeTest() {
|
public void sortedArrayEncodeDecodeTest()
|
||||||
|
{
|
||||||
Random rand = new Random();
|
Random rand = new Random();
|
||||||
int size = 1000000;
|
int size = 1000000;
|
||||||
int[] values = new int[size];
|
int[] values = new int[size];
|
||||||
for (int i = 0; i < size; i++) {
|
for ( int i = 0; i < size; i++ )
|
||||||
|
{
|
||||||
values[i] = rand.nextInt() & 0x0fffffff;
|
values[i] = rand.nextInt() & 0x0fffffff;
|
||||||
}
|
}
|
||||||
values[5] = 175384; // force collision
|
values[5] = 175384; // force collision
|
||||||
|
|
@ -99,8 +120,10 @@ public class StatCoderContextTest {
|
||||||
int[] decodedValues = new int[size];
|
int[] decodedValues = new int[size];
|
||||||
ctx.decodeSortedArray( decodedValues, 0, size, 27, 0 );
|
ctx.decodeSortedArray( decodedValues, 0, size, 27, 0 );
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) {
|
for ( int i = 0; i < size; i++ )
|
||||||
if (values[i] != decodedValues[i]) {
|
{
|
||||||
|
if ( values[i] != decodedValues[i] )
|
||||||
|
{
|
||||||
Assert.fail( "mismatch at i=" + i + " " + values[i] + "<>" + decodedValues[i] );
|
Assert.fail( "mismatch at i=" + i + " " + values[i] + "<>" + decodedValues[i] );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
brouter-core/.gitignore
vendored
Normal file
1
brouter-core/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/build/
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'brouter.library-conventions'
|
id 'java-library'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation project(':brouter-mapaccess')
|
implementation project(':brouter-mapaccess')
|
||||||
implementation project(':brouter-util')
|
implementation project(':brouter-util')
|
||||||
implementation project(':brouter-expressions')
|
implementation project(':brouter-expressions')
|
||||||
implementation project(':brouter-codec')
|
implementation project(':brouter-codec')
|
||||||
}
|
testImplementation 'junit:junit:4.13.1'
|
||||||
|
|
||||||
// MapcreatorTest generates segments which are used in tests
|
}
|
||||||
test.dependsOn ':brouter-map-creator:test'
|
|
||||||
|
|
|
||||||
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;
|
import btools.expressions.BExpressionContextWay;
|
||||||
|
|
||||||
|
|
||||||
final class KinematicModel extends OsmPathModel {
|
final class KinematicModel extends OsmPathModel
|
||||||
public OsmPrePath createPrePath() {
|
{
|
||||||
|
public OsmPrePath createPrePath()
|
||||||
|
{
|
||||||
return new KinematicPrePath();
|
return new KinematicPrePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmPath createPath() {
|
public OsmPath createPath()
|
||||||
|
{
|
||||||
return new KinematicPath();
|
return new KinematicPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,8 +55,10 @@ final class KinematicModel extends OsmPathModel {
|
||||||
private double lastBreakingSpeed;
|
private double lastBreakingSpeed;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String, String> extraParams) {
|
public void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String,String> extraParams )
|
||||||
if (!initDone) {
|
{
|
||||||
|
if ( !initDone )
|
||||||
|
{
|
||||||
ctxWay = expctxWay;
|
ctxWay = expctxWay;
|
||||||
ctxNode = expctxNode;
|
ctxNode = expctxNode;
|
||||||
wayIdxMaxspeed = ctxWay.getOutputVariableIndex( "maxspeed", false );
|
wayIdxMaxspeed = ctxWay.getOutputVariableIndex( "maxspeed", false );
|
||||||
|
|
@ -81,38 +86,46 @@ final class KinematicModel extends OsmPathModel {
|
||||||
cost0 = (pw+p_standby)/vmax + f_roll + f_air*vmax*vmax;
|
cost0 = (pw+p_standby)/vmax + f_roll + f_air*vmax*vmax;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected float getParam(String name, float defaultValue) {
|
protected float getParam( String name, float defaultValue )
|
||||||
|
{
|
||||||
String sval = params == null ? null : params.get( name );
|
String sval = params == null ? null : params.get( name );
|
||||||
if (sval != null) {
|
if ( sval != null )
|
||||||
|
{
|
||||||
return Float.parseFloat( sval );
|
return Float.parseFloat( sval );
|
||||||
}
|
}
|
||||||
float v = ctxWay.getVariableValue( name, defaultValue );
|
float v = ctxWay.getVariableValue( name, defaultValue );
|
||||||
if (params != null) {
|
if ( params != null )
|
||||||
|
{
|
||||||
params.put( name, "" + v );
|
params.put( name, "" + v );
|
||||||
}
|
}
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getWayMaxspeed() {
|
public float getWayMaxspeed()
|
||||||
|
{
|
||||||
return ctxWay.getBuildInVariable( wayIdxMaxspeed ) / 3.6f;
|
return ctxWay.getBuildInVariable( wayIdxMaxspeed ) / 3.6f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getWayMaxspeedExplicit() {
|
public float getWayMaxspeedExplicit()
|
||||||
|
{
|
||||||
return ctxWay.getBuildInVariable( wayIdxMaxspeedExplicit ) / 3.6f;
|
return ctxWay.getBuildInVariable( wayIdxMaxspeedExplicit ) / 3.6f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getWayMinspeed() {
|
public float getWayMinspeed()
|
||||||
|
{
|
||||||
return ctxWay.getBuildInVariable( wayIdxMinspeed ) / 3.6f;
|
return ctxWay.getBuildInVariable( wayIdxMinspeed ) / 3.6f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getNodeMaxspeed() {
|
public float getNodeMaxspeed()
|
||||||
|
{
|
||||||
return ctxNode.getBuildInVariable( nodeIdxMaxspeed ) / 3.6f;
|
return ctxNode.getBuildInVariable( nodeIdxMaxspeed ) / 3.6f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the effective speed limit from the way-limit and vmax/vmin
|
* get the effective speed limit from the way-limit and vmax/vmin
|
||||||
*/
|
*/
|
||||||
public double getEffectiveSpeedLimit() {
|
public double getEffectiveSpeedLimit( )
|
||||||
|
{
|
||||||
// performance related inline coding
|
// performance related inline coding
|
||||||
double minspeed = getWayMinspeed();
|
double minspeed = getWayMinspeed();
|
||||||
double espeed = minspeed > vmax ? minspeed : vmax;
|
double espeed = minspeed > vmax ? minspeed : vmax;
|
||||||
|
|
@ -123,8 +136,10 @@ final class KinematicModel extends OsmPathModel {
|
||||||
/**
|
/**
|
||||||
* get the breaking speed for current balance-power (pw) and effective speed limit (vl)
|
* get the breaking speed for current balance-power (pw) and effective speed limit (vl)
|
||||||
*/
|
*/
|
||||||
public double getBreakingSpeed(double vl) {
|
public double getBreakingSpeed( double vl )
|
||||||
if (vl == lastEffectiveLimit) {
|
{
|
||||||
|
if ( vl == lastEffectiveLimit )
|
||||||
|
{
|
||||||
return lastBreakingSpeed;
|
return lastBreakingSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,7 +147,8 @@ final class KinematicModel extends OsmPathModel {
|
||||||
double pw2 = pw+p_standby;
|
double pw2 = pw+p_standby;
|
||||||
double e = recup_efficiency;
|
double e = recup_efficiency;
|
||||||
double x0 = pw2/vl+f_air*e*vl*vl+(1.-e)*f_roll;
|
double x0 = pw2/vl+f_air*e*vl*vl+(1.-e)*f_roll;
|
||||||
for (int i = 0; i < 5; i++) {
|
for(int i=0;i<5;i++)
|
||||||
|
{
|
||||||
double v2 = v*v;
|
double v2 = v*v;
|
||||||
double x = pw2/v+f_air*e*v2 - x0;
|
double x = pw2/v+f_air*e*v2 - x0;
|
||||||
double dx = 2.*e*f_air*v - pw2/v2;
|
double dx = 2.*e*f_air*v - pw2/v2;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,11 @@
|
||||||
*/
|
*/
|
||||||
package btools.router;
|
package btools.router;
|
||||||
|
|
||||||
final class KinematicPath extends OsmPath {
|
import btools.util.FastMath;
|
||||||
|
|
||||||
|
|
||||||
|
final class KinematicPath extends OsmPath
|
||||||
|
{
|
||||||
private double ekin; // kinetic energy (Joule)
|
private double ekin; // kinetic energy (Joule)
|
||||||
private double totalTime; // travel time (seconds)
|
private double totalTime; // travel time (seconds)
|
||||||
private double totalEnergy; // total route energy (Joule)
|
private double totalEnergy; // total route energy (Joule)
|
||||||
|
|
@ -13,17 +17,20 @@ final class KinematicPath extends OsmPath {
|
||||||
private float floatingAngleRight; // sliding average right bend (degree)
|
private float floatingAngleRight; // sliding average right bend (degree)
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void init(OsmPath orig) {
|
protected void init( OsmPath orig )
|
||||||
|
{
|
||||||
KinematicPath origin = (KinematicPath)orig;
|
KinematicPath origin = (KinematicPath)orig;
|
||||||
ekin = origin.ekin;
|
ekin = origin.ekin;
|
||||||
totalTime = origin.totalTime;
|
totalTime = origin.totalTime;
|
||||||
totalEnergy = origin.totalEnergy;
|
totalEnergy = origin.totalEnergy;
|
||||||
floatingAngleLeft = origin.floatingAngleLeft;
|
floatingAngleLeft = origin.floatingAngleLeft;
|
||||||
floatingAngleRight = origin.floatingAngleRight;
|
floatingAngleRight = origin.floatingAngleRight;
|
||||||
|
priorityclassifier = origin.priorityclassifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void resetState() {
|
protected void resetState()
|
||||||
|
{
|
||||||
ekin = 0.;
|
ekin = 0.;
|
||||||
totalTime = 0.;
|
totalTime = 0.;
|
||||||
totalEnergy = 0.;
|
totalEnergy = 0.;
|
||||||
|
|
@ -32,37 +39,45 @@ final class KinematicPath extends OsmPath {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected double processWaySection(RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier) {
|
protected double processWaySection( RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier )
|
||||||
|
{
|
||||||
KinematicModel km = (KinematicModel)rc.pm;
|
KinematicModel km = (KinematicModel)rc.pm;
|
||||||
|
|
||||||
double cost = 0.;
|
double cost = 0.;
|
||||||
double extraTime = 0.;
|
double extraTime = 0.;
|
||||||
|
|
||||||
if (isStartpoint) {
|
if ( isStartpoint )
|
||||||
|
{
|
||||||
// for forward direction, we start with target speed
|
// for forward direction, we start with target speed
|
||||||
if (!rc.inverseDirection) {
|
if ( !rc.inverseDirection )
|
||||||
|
{
|
||||||
extraTime = 0.5 * (1. - cosangle ) * 40.; // 40 seconds turn penalty
|
extraTime = 0.5 * (1. - cosangle ) * 40.; // 40 seconds turn penalty
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
double turnspeed = 999.; // just high
|
double turnspeed = 999.; // just high
|
||||||
|
|
||||||
if (km.turnAngleDecayTime != 0.) { // process turn-angle slowdown
|
if ( km.turnAngleDecayTime != 0. ) // process turn-angle slowdown
|
||||||
|
{
|
||||||
if ( angle < 0 ) floatingAngleLeft -= (float)angle;
|
if ( angle < 0 ) floatingAngleLeft -= (float)angle;
|
||||||
else floatingAngleRight += (float)angle;
|
else floatingAngleRight += (float)angle;
|
||||||
float aa = Math.max( floatingAngleLeft, floatingAngleRight );
|
float aa = Math.max( floatingAngleLeft, floatingAngleRight );
|
||||||
|
|
||||||
double curveSpeed = aa > 10. ? 200. / aa : 20.;
|
double curveSpeed = aa > 10. ? 200. / aa : 20.;
|
||||||
double distanceTime = dist / curveSpeed;
|
double distanceTime = dist / curveSpeed;
|
||||||
double decayFactor = Math.exp(-distanceTime / km.turnAngleDecayTime);
|
double decayFactor = FastMath.exp( - distanceTime / km.turnAngleDecayTime );
|
||||||
floatingAngleLeft = (float)( floatingAngleLeft * decayFactor );
|
floatingAngleLeft = (float)( floatingAngleLeft * decayFactor );
|
||||||
floatingAngleRight = (float)( floatingAngleRight * decayFactor );
|
floatingAngleRight = (float)( floatingAngleRight * decayFactor );
|
||||||
|
|
||||||
if (curveSpeed < 20.) {
|
if ( curveSpeed < 20. )
|
||||||
|
{
|
||||||
turnspeed = curveSpeed;
|
turnspeed = curveSpeed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nsection == 0) { // process slowdown by crossing geometry
|
if ( nsection == 0 ) // process slowdown by crossing geometry
|
||||||
|
{
|
||||||
double junctionspeed = 999.; // just high
|
double junctionspeed = 999.; // just high
|
||||||
|
|
||||||
int classifiermask = (int)rc.expctxWay.getClassifierMask();
|
int classifiermask = (int)rc.expctxWay.getClassifierMask();
|
||||||
|
|
@ -71,18 +86,22 @@ final class KinematicPath extends OsmPath {
|
||||||
boolean hasLeftWay = false;
|
boolean hasLeftWay = false;
|
||||||
boolean hasRightWay = false;
|
boolean hasRightWay = false;
|
||||||
boolean hasResidential = false;
|
boolean hasResidential = false;
|
||||||
for (OsmPrePath prePath = rc.firstPrePath; prePath != null; prePath = prePath.next) {
|
for( OsmPrePath prePath = rc.firstPrePath; prePath != null; prePath = prePath.next )
|
||||||
|
{
|
||||||
KinematicPrePath pp = (KinematicPrePath)prePath;
|
KinematicPrePath pp = (KinematicPrePath)prePath;
|
||||||
|
|
||||||
if (((pp.classifiermask ^ classifiermask) & 8) != 0) { // exactly one is linktype
|
if ( ( (pp.classifiermask ^ classifiermask) & 8 ) != 0 ) // exactly one is linktype
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((pp.classifiermask & 32) != 0) { // touching a residential?
|
if ( ( pp.classifiermask & 32 ) != 0 ) // touching a residential?
|
||||||
|
{
|
||||||
hasResidential = true;
|
hasResidential = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pp.priorityclassifier > priorityclassifier || pp.priorityclassifier == priorityclassifier && priorityclassifier < 20) {
|
if ( pp.priorityclassifier > priorityclassifier || pp.priorityclassifier == priorityclassifier && priorityclassifier < 20 )
|
||||||
|
{
|
||||||
double diff = pp.angle - angle;
|
double diff = pp.angle - angle;
|
||||||
if ( diff < -40. && diff > -140.) hasLeftWay = true;
|
if ( diff < -40. && diff > -140.) hasLeftWay = true;
|
||||||
if ( diff > 40. && diff < 140. ) hasRightWay = true;
|
if ( diff > 40. && diff < 140. ) hasRightWay = true;
|
||||||
|
|
@ -94,17 +113,20 @@ final class KinematicPath extends OsmPath {
|
||||||
if ( hasRightWay && junctionspeed > km.rightWaySpeed ) junctionspeed = km.rightWaySpeed;
|
if ( hasRightWay && junctionspeed > km.rightWaySpeed ) junctionspeed = km.rightWaySpeed;
|
||||||
if ( hasResidential && junctionspeed > residentialSpeed ) junctionspeed = residentialSpeed;
|
if ( hasResidential && junctionspeed > residentialSpeed ) junctionspeed = residentialSpeed;
|
||||||
|
|
||||||
if ((lastpriorityclassifier < 20) ^ (priorityclassifier < 20)) {
|
if ( (lastpriorityclassifier < 20) ^ (priorityclassifier < 20) )
|
||||||
|
{
|
||||||
extraTime += 10.;
|
extraTime += 10.;
|
||||||
junctionspeed = 0; // full stop for entering or leaving road network
|
junctionspeed = 0; // full stop for entering or leaving road network
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastpriorityclassifier != priorityclassifier && (classifiermask & 8) != 0) {
|
if ( lastpriorityclassifier != priorityclassifier && (classifiermask & 8) != 0 )
|
||||||
|
{
|
||||||
extraTime += 2.; // two seconds for entering a link-type
|
extraTime += 2.; // two seconds for entering a link-type
|
||||||
}
|
}
|
||||||
turnspeed = turnspeed > junctionspeed ? junctionspeed : turnspeed;
|
turnspeed = turnspeed > junctionspeed ? junctionspeed : turnspeed;
|
||||||
|
|
||||||
if (message != null) {
|
if ( message != null )
|
||||||
|
{
|
||||||
message.vnode0 = (int) ( junctionspeed * 3.6 + 0.5 );
|
message.vnode0 = (int) ( junctionspeed * 3.6 + 0.5 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -121,7 +143,8 @@ final class KinematicPath extends OsmPath {
|
||||||
|
|
||||||
double distanceCost = evolveDistance( km, dist, delta_h, f_air );
|
double distanceCost = evolveDistance( km, dist, delta_h, f_air );
|
||||||
|
|
||||||
if (message != null) {
|
if ( message != null )
|
||||||
|
{
|
||||||
message.costfactor = (float)(distanceCost/dist);
|
message.costfactor = (float)(distanceCost/dist);
|
||||||
message.vmax = (int) ( km.getWayMaxspeed() * 3.6 + 0.5 );
|
message.vmax = (int) ( km.getWayMaxspeed() * 3.6 + 0.5 );
|
||||||
message.vmaxExplicit = (int) ( km.getWayMaxspeedExplicit() * 3.6 + 0.5 );
|
message.vmaxExplicit = (int) ( km.getWayMaxspeedExplicit() * 3.6 + 0.5 );
|
||||||
|
|
@ -136,13 +159,15 @@ final class KinematicPath extends OsmPath {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected double evolveDistance(KinematicModel km, double dist, double delta_h, double f_air) {
|
protected double evolveDistance( KinematicModel km, double dist, double delta_h, double f_air )
|
||||||
|
{
|
||||||
// elevation force
|
// elevation force
|
||||||
double fh = delta_h * km.totalweight * 9.81 / dist;
|
double fh = delta_h * km.totalweight * 9.81 / dist;
|
||||||
|
|
||||||
double effectiveSpeedLimit = km.getEffectiveSpeedLimit();
|
double effectiveSpeedLimit = km.getEffectiveSpeedLimit();
|
||||||
double emax = 0.5*km.totalweight*effectiveSpeedLimit*effectiveSpeedLimit;
|
double emax = 0.5*km.totalweight*effectiveSpeedLimit*effectiveSpeedLimit;
|
||||||
if (emax <= 0.) {
|
if ( emax <= 0. )
|
||||||
|
{
|
||||||
return -1.;
|
return -1.;
|
||||||
}
|
}
|
||||||
double vb = km.getBreakingSpeed( effectiveSpeedLimit );
|
double vb = km.getBreakingSpeed( effectiveSpeedLimit );
|
||||||
|
|
@ -153,7 +178,8 @@ final class KinematicPath extends OsmPath {
|
||||||
|
|
||||||
double v = Math.sqrt( 2. * ekin / km.totalweight );
|
double v = Math.sqrt( 2. * ekin / km.totalweight );
|
||||||
double d = dist;
|
double d = dist;
|
||||||
while (d > 0.) {
|
while( d > 0. )
|
||||||
|
{
|
||||||
boolean slow = ekin < elow;
|
boolean slow = ekin < elow;
|
||||||
boolean fast = ekin >= emax;
|
boolean fast = ekin >= emax;
|
||||||
double etarget = slow ? elow : emax;
|
double etarget = slow ? elow : emax;
|
||||||
|
|
@ -164,24 +190,30 @@ final class KinematicPath extends OsmPath {
|
||||||
double delta_ekin;
|
double delta_ekin;
|
||||||
double timeStep;
|
double timeStep;
|
||||||
double x;
|
double x;
|
||||||
if (fast) {
|
if ( fast )
|
||||||
|
{
|
||||||
x = d;
|
x = d;
|
||||||
delta_ekin = x*f;
|
delta_ekin = x*f;
|
||||||
timeStep = x/v;
|
timeStep = x/v;
|
||||||
ekin = etarget;
|
ekin = etarget;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
delta_ekin = etarget-ekin;
|
delta_ekin = etarget-ekin;
|
||||||
double b = 2.*f_air / km.totalweight;
|
double b = 2.*f_air / km.totalweight;
|
||||||
double x0 = delta_ekin/f;
|
double x0 = delta_ekin/f;
|
||||||
double x0b = x0*b;
|
double x0b = x0*b;
|
||||||
x = x0*(1. - x0b*(0.5 + x0b*(0.333333333-x0b*0.25 ) ) ); // = ln( delta_ekin*b/f + 1.) / b;
|
x = x0*(1. - x0b*(0.5 + x0b*(0.333333333-x0b*0.25 ) ) ); // = ln( delta_ekin*b/f + 1.) / b;
|
||||||
double maxstep = Math.min( 50., d );
|
double maxstep = Math.min( 50., d );
|
||||||
if (x >= maxstep) {
|
if ( x >= maxstep )
|
||||||
|
{
|
||||||
x = maxstep;
|
x = maxstep;
|
||||||
double xb = x*b;
|
double xb = x*b;
|
||||||
delta_ekin = x*f*(1.+xb*(0.5+xb*(0.166666667+xb*0.0416666667 ) ) ); // = f/b* exp(xb-1)
|
delta_ekin = x*f*(1.+xb*(0.5+xb*(0.166666667+xb*0.0416666667 ) ) ); // = f/b* exp(xb-1)
|
||||||
ekin += delta_ekin;
|
ekin += delta_ekin;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
ekin = etarget;
|
ekin = etarget;
|
||||||
}
|
}
|
||||||
double v2 = Math.sqrt( 2. * ekin / km.totalweight );
|
double v2 = Math.sqrt( 2. * ekin / km.totalweight );
|
||||||
|
|
@ -210,19 +242,23 @@ final class KinematicPath extends OsmPath {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected double processTargetNode(RoutingContext rc) {
|
protected double processTargetNode( RoutingContext rc )
|
||||||
|
{
|
||||||
KinematicModel km = (KinematicModel)rc.pm;
|
KinematicModel km = (KinematicModel)rc.pm;
|
||||||
|
|
||||||
// finally add node-costs for target node
|
// finally add node-costs for target node
|
||||||
if (targetNode.nodeDescription != null) {
|
if ( targetNode.nodeDescription != null )
|
||||||
|
{
|
||||||
rc.expctxNode.evaluate( false , targetNode.nodeDescription );
|
rc.expctxNode.evaluate( false , targetNode.nodeDescription );
|
||||||
float initialcost = rc.expctxNode.getInitialcost();
|
float initialcost = rc.expctxNode.getInitialcost();
|
||||||
if (initialcost >= 1000000.) {
|
if ( initialcost >= 1000000. )
|
||||||
|
{
|
||||||
return -1.;
|
return -1.;
|
||||||
}
|
}
|
||||||
cutEkin( km.totalweight, km.getNodeMaxspeed() ); // apply node maxspeed
|
cutEkin( km.totalweight, km.getNodeMaxspeed() ); // apply node maxspeed
|
||||||
|
|
||||||
if (message != null) {
|
if ( message != null )
|
||||||
|
{
|
||||||
message.linknodecost += (int)initialcost;
|
message.linknodecost += (int)initialcost;
|
||||||
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription( false, targetNode.nodeDescription );
|
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription( false, targetNode.nodeDescription );
|
||||||
|
|
||||||
|
|
@ -233,19 +269,22 @@ final class KinematicPath extends OsmPath {
|
||||||
return 0.;
|
return 0.;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cutEkin(double weight, double speed) {
|
private void cutEkin( double weight, double speed )
|
||||||
|
{
|
||||||
double e = 0.5*weight*speed*speed;
|
double e = 0.5*weight*speed*speed;
|
||||||
if ( ekin > e ) ekin = e;
|
if ( ekin > e ) ekin = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int elevationCorrection() {
|
public int elevationCorrection( RoutingContext rc )
|
||||||
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean definitlyWorseThan(OsmPath path) {
|
public boolean definitlyWorseThan( OsmPath path, RoutingContext rc )
|
||||||
|
{
|
||||||
KinematicPath p = (KinematicPath)path;
|
KinematicPath p = (KinematicPath)path;
|
||||||
|
|
||||||
int c = p.cost;
|
int c = p.cost;
|
||||||
|
|
@ -253,12 +292,14 @@ final class KinematicPath extends OsmPath {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getTotalTime() {
|
public double getTotalTime()
|
||||||
|
{
|
||||||
return totalTime;
|
return totalTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getTotalEnergy() {
|
public double getTotalEnergy()
|
||||||
|
{
|
||||||
return totalEnergy;
|
return totalEnergy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,14 @@ package btools.router;
|
||||||
import btools.mapaccess.OsmNode;
|
import btools.mapaccess.OsmNode;
|
||||||
import btools.mapaccess.OsmTransferNode;
|
import btools.mapaccess.OsmTransferNode;
|
||||||
|
|
||||||
final class KinematicPrePath extends OsmPrePath {
|
final class KinematicPrePath extends OsmPrePath
|
||||||
|
{
|
||||||
public double angle;
|
public double angle;
|
||||||
public int priorityclassifier;
|
public int priorityclassifier;
|
||||||
public int classifiermask;
|
public int classifiermask;
|
||||||
|
|
||||||
protected void initPrePath(OsmPath origin, RoutingContext rc) {
|
protected void initPrePath(OsmPath origin, RoutingContext rc )
|
||||||
|
{
|
||||||
byte[] description = link.descriptionBitmap;
|
byte[] description = link.descriptionBitmap;
|
||||||
if ( description == null ) throw new IllegalArgumentException( "null description for: " + link );
|
if ( description == null ) throw new IllegalArgumentException( "null description for: " + link );
|
||||||
|
|
||||||
|
|
@ -36,10 +38,13 @@ final class KinematicPrePath extends OsmPrePath {
|
||||||
int lon2;
|
int lon2;
|
||||||
int lat2;
|
int lat2;
|
||||||
|
|
||||||
if (transferNode == null) {
|
if ( transferNode == null )
|
||||||
|
{
|
||||||
lon2 = targetNode.ilon;
|
lon2 = targetNode.ilon;
|
||||||
lat2 = targetNode.ilat;
|
lat2 = targetNode.ilat;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
lon2 = transferNode.ilon;
|
lon2 = transferNode.ilon;
|
||||||
lat2 = transferNode.ilat;
|
lat2 = transferNode.ilat;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@
|
||||||
package btools.router;
|
package btools.router;
|
||||||
|
|
||||||
|
|
||||||
final class MessageData implements Cloneable {
|
|
||||||
|
final class MessageData implements Cloneable
|
||||||
|
{
|
||||||
int linkdist = 0;
|
int linkdist = 0;
|
||||||
int linkelevationcost = 0;
|
int linkelevationcost = 0;
|
||||||
int linkturncost = 0;
|
int linkturncost = 0;
|
||||||
|
|
@ -35,8 +37,10 @@ final class MessageData implements Cloneable {
|
||||||
int vnode1 = 999;
|
int vnode1 = 999;
|
||||||
int extraTime = 0;
|
int extraTime = 0;
|
||||||
|
|
||||||
String toMessage() {
|
String toMessage()
|
||||||
if (wayKeyValues == null) {
|
{
|
||||||
|
if ( wayKeyValues == null )
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,7 +60,8 @@ final class MessageData implements Cloneable {
|
||||||
+ "\t" + ((int)energy);
|
+ "\t" + ((int)energy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(MessageData d) {
|
void add( MessageData d )
|
||||||
|
{
|
||||||
linkdist += d.linkdist;
|
linkdist += d.linkdist;
|
||||||
linkelevationcost += d.linkelevationcost;
|
linkelevationcost += d.linkelevationcost;
|
||||||
linkturncost += d.linkturncost;
|
linkturncost += d.linkturncost;
|
||||||
|
|
@ -64,40 +69,51 @@ final class MessageData implements Cloneable {
|
||||||
linkinitcost+= d.linkinitcost;
|
linkinitcost+= d.linkinitcost;
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageData copy() {
|
MessageData copy()
|
||||||
try {
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
return (MessageData)clone();
|
return (MessageData)clone();
|
||||||
} catch (CloneNotSupportedException e) {
|
}
|
||||||
|
catch( CloneNotSupportedException e )
|
||||||
|
{
|
||||||
throw new RuntimeException( e );
|
throw new RuntimeException( e );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString()
|
||||||
|
{
|
||||||
return "dist=" + linkdist + " prio=" + priorityclassifier + " turn=" + turnangle;
|
return "dist=" + linkdist + " prio=" + priorityclassifier + " turn=" + turnangle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPrio() {
|
public int getPrio()
|
||||||
|
{
|
||||||
return priorityclassifier;
|
return priorityclassifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isBadOneway() {
|
public boolean isBadOneway()
|
||||||
|
{
|
||||||
return ( classifiermask & 1 ) != 0;
|
return ( classifiermask & 1 ) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isGoodOneway() {
|
public boolean isGoodOneway()
|
||||||
|
{
|
||||||
return ( classifiermask & 2 ) != 0;
|
return ( classifiermask & 2 ) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRoundabout() {
|
public boolean isRoundabout()
|
||||||
|
{
|
||||||
return ( classifiermask & 4 ) != 0;
|
return ( classifiermask & 4 ) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLinktType() {
|
public boolean isLinktType()
|
||||||
|
{
|
||||||
return ( classifiermask & 8 ) != 0;
|
return ( classifiermask & 8 ) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isGoodForCars() {
|
public boolean isGoodForCars()
|
||||||
|
{
|
||||||
return ( classifiermask & 16 ) != 0;
|
return ( classifiermask & 16 ) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,22 +8,25 @@ package btools.router;
|
||||||
import btools.mapaccess.OsmNode;
|
import btools.mapaccess.OsmNode;
|
||||||
import btools.util.CheapRuler;
|
import btools.util.CheapRuler;
|
||||||
|
|
||||||
public class OsmNodeNamed extends OsmNode {
|
public class OsmNodeNamed extends OsmNode
|
||||||
|
{
|
||||||
public String name;
|
public String name;
|
||||||
public double radius; // radius of nogopoint (in meters)
|
public double radius; // radius of nogopoint (in meters)
|
||||||
public double nogoWeight; // weight for nogopoint
|
public double nogoWeight; // weight for nogopoint
|
||||||
public boolean isNogo = false;
|
public boolean isNogo = false;
|
||||||
public boolean direct = false; // mark direct routing
|
|
||||||
|
|
||||||
public OsmNodeNamed() {
|
public OsmNodeNamed()
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmNodeNamed(OsmNode n) {
|
public OsmNodeNamed( OsmNode n)
|
||||||
|
{
|
||||||
super( n.ilon, n.ilat );
|
super( n.ilon, n.ilat );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString()
|
||||||
|
{
|
||||||
if ( Double.isNaN(nogoWeight ) ) {
|
if ( Double.isNaN(nogoWeight ) ) {
|
||||||
return ilon + "," + ilat + "," + name;
|
return ilon + "," + ilat + "," + name;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -79,7 +82,8 @@ public class OsmNodeNamed extends OsmNode {
|
||||||
return 2 * halfDistanceWithin;
|
return 2 * halfDistanceWithin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OsmNodeNamed decodeNogo(String s) {
|
public static OsmNodeNamed decodeNogo( String s )
|
||||||
|
{
|
||||||
OsmNodeNamed n = new OsmNodeNamed();
|
OsmNodeNamed n = new OsmNodeNamed();
|
||||||
int idx1 = s.indexOf( ',' );
|
int idx1 = s.indexOf( ',' );
|
||||||
n.ilon = Integer.parseInt( s.substring( 0, idx1 ) );
|
n.ilon = Integer.parseInt( s.substring( 0, idx1 ) );
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
http://geomalgorithms.com/a03-_inclusion.html
|
http://geomalgorithms.com/a03-_inclusion.html
|
||||||
|
|
||||||
cn_PnPoly, wn_PnPoly, inSegment, intersect2D_2Segments
|
cn_PnPoly, wn_PnPoly, inSegment, intersect2D_2Segments
|
||||||
|
|
||||||
**********************************************************************************************/
|
**********************************************************************************************/
|
||||||
package btools.router;
|
package btools.router;
|
||||||
|
|
||||||
|
|
@ -13,28 +14,33 @@ import java.util.List;
|
||||||
|
|
||||||
import btools.util.CheapRuler;
|
import btools.util.CheapRuler;
|
||||||
|
|
||||||
public class OsmNogoPolygon extends OsmNodeNamed {
|
public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
public final static class Point {
|
{
|
||||||
|
public final static class Point
|
||||||
|
{
|
||||||
public final int y;
|
public final int y;
|
||||||
public final int x;
|
public final int x;
|
||||||
|
|
||||||
Point(final int lon, final int lat) {
|
Point(final int lon, final int lat)
|
||||||
|
{
|
||||||
x = lon;
|
x = lon;
|
||||||
y = lat;
|
y = lat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final List<Point> points = new ArrayList<>();
|
public final List<Point> points = new ArrayList<Point>();
|
||||||
|
|
||||||
public final boolean isClosed;
|
public final boolean isClosed;
|
||||||
|
|
||||||
public OsmNogoPolygon(boolean closed) {
|
public OsmNogoPolygon(boolean closed)
|
||||||
|
{
|
||||||
this.isClosed = closed;
|
this.isClosed = closed;
|
||||||
this.isNogo = true;
|
this.isNogo = true;
|
||||||
this.name = "";
|
this.name = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void addVertex(int lon, int lat) {
|
public final void addVertex(int lon, int lat)
|
||||||
|
{
|
||||||
points.add(new Point(lon, lat));
|
points.add(new Point(lon, lat));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -48,30 +54,36 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
* with each iteration.
|
* with each iteration.
|
||||||
* This is done to ensure the calculated radius being used
|
* This is done to ensure the calculated radius being used
|
||||||
* in RoutingContext.calcDistance will actually contain the whole polygon.
|
* in RoutingContext.calcDistance will actually contain the whole polygon.
|
||||||
* <p>
|
*
|
||||||
* For reasonable distributed vertices the implemented algorithm runs in O(n*ln(n)).
|
* For reasonable distributed vertices the implemented algorithm runs in O(n*ln(n)).
|
||||||
* As this is only run once on initialization of OsmNogoPolygon this methods
|
* As this is only run once on initialization of OsmNogoPolygon this methods
|
||||||
* overall usage of cpu is neglegible in comparism to the cpu-usage of the
|
* overall usage of cpu is neglegible in comparism to the cpu-usage of the
|
||||||
* actual routing algoritm.
|
* actual routing algoritm.
|
||||||
*/
|
*/
|
||||||
public void calcBoundingCircle() {
|
public void calcBoundingCircle()
|
||||||
|
{
|
||||||
int cxmin, cxmax, cymin, cymax;
|
int cxmin, cxmax, cymin, cymax;
|
||||||
cxmin = cymin = Integer.MAX_VALUE;
|
cxmin = cymin = Integer.MAX_VALUE;
|
||||||
cxmax = cymax = Integer.MIN_VALUE;
|
cxmax = cymax = Integer.MIN_VALUE;
|
||||||
|
|
||||||
// first calculate a starting center point as center of boundingbox
|
// first calculate a starting center point as center of boundingbox
|
||||||
for (int i = 0; i < points.size(); i++) {
|
for (int i = 0; i < points.size(); i++)
|
||||||
|
{
|
||||||
final Point p = points.get(i);
|
final Point p = points.get(i);
|
||||||
if (p.x < cxmin) {
|
if (p.x < cxmin)
|
||||||
|
{
|
||||||
cxmin = p.x;
|
cxmin = p.x;
|
||||||
}
|
}
|
||||||
if (p.x > cxmax) {
|
if (p.x > cxmax)
|
||||||
|
{
|
||||||
cxmax = p.x;
|
cxmax = p.x;
|
||||||
}
|
}
|
||||||
if (p.y < cymin) {
|
if (p.y < cymin)
|
||||||
|
{
|
||||||
cymin = p.y;
|
cymin = p.y;
|
||||||
}
|
}
|
||||||
if (p.y > cymax) {
|
if (p.y > cymax)
|
||||||
|
{
|
||||||
cymax = p.y;
|
cymax = p.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -88,9 +100,11 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
double dmax = 0; // length of vector from center to point
|
double dmax = 0; // length of vector from center to point
|
||||||
int i_max = -1;
|
int i_max = -1;
|
||||||
|
|
||||||
do {
|
do
|
||||||
|
{
|
||||||
// now identify the point outside of the circle that has the greatest distance
|
// now identify the point outside of the circle that has the greatest distance
|
||||||
for (int i = 0; i < points.size(); i++) {
|
for (int i = 0; i < points.size(); i++)
|
||||||
|
{
|
||||||
final Point p = points.get(i);
|
final Point p = points.get(i);
|
||||||
|
|
||||||
// to get precisely the same results as in RoutingContext.calcDistance()
|
// to get precisely the same results as in RoutingContext.calcDistance()
|
||||||
|
|
@ -99,16 +113,19 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
final double y1 = (cy - p.y) * dlat2m;
|
final double y1 = (cy - p.y) * dlat2m;
|
||||||
final double dist = Math.sqrt( x1*x1 + y1*y1 );
|
final double dist = Math.sqrt( x1*x1 + y1*y1 );
|
||||||
|
|
||||||
if (dist <= rad) {
|
if (dist <= rad)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (dist > dmax) {
|
if (dist > dmax)
|
||||||
|
{
|
||||||
// new maximum distance found
|
// new maximum distance found
|
||||||
dmax = dist;
|
dmax = dist;
|
||||||
i_max = i;
|
i_max = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i_max < 0) {
|
if (i_max < 0)
|
||||||
|
{
|
||||||
break; // leave loop when no point outside the circle is found any more.
|
break; // leave loop when no point outside the circle is found any more.
|
||||||
}
|
}
|
||||||
final double dd = 0.5 * (1 - rad / dmax);
|
final double dd = 0.5 * (1 - rad / dmax);
|
||||||
|
|
@ -132,6 +149,7 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
ilon = cx;
|
ilon = cx;
|
||||||
ilat = cy;
|
ilat = cy;
|
||||||
radius = rad * 1.001 + 1.0; // ensure the outside-of-enclosing-circle test in RoutingContext.calcDistance() is not passed by segments ending very close to the radius due to limited numerical precision
|
radius = rad * 1.001 + 1.0; // ensure the outside-of-enclosing-circle test in RoutingContext.calcDistance() is not passed by segments ending very close to the radius due to limited numerical precision
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -147,15 +165,18 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
* @param lat1 latitude of start point
|
* @param lat1 latitude of start point
|
||||||
* @return true if segment or any of it's points are 'inside' of polygon
|
* @return true if segment or any of it's points are 'inside' of polygon
|
||||||
*/
|
*/
|
||||||
public boolean intersects(int lon0, int lat0, int lon1, int lat1) {
|
public boolean intersects(int lon0, int lat0, int lon1, int lat1)
|
||||||
|
{
|
||||||
final Point p0 = new Point (lon0,lat0);
|
final Point p0 = new Point (lon0,lat0);
|
||||||
final Point p1 = new Point (lon1,lat1);
|
final Point p1 = new Point (lon1,lat1);
|
||||||
int i_last = points.size()-1;
|
int i_last = points.size()-1;
|
||||||
Point p2 = points.get(isClosed ? i_last : 0 );
|
Point p2 = points.get(isClosed ? i_last : 0 );
|
||||||
for (int i = isClosed ? 0 : 1; i <= i_last; i++) {
|
for (int i = isClosed ? 0 : 1 ; i <= i_last; i++)
|
||||||
|
{
|
||||||
Point p3 = points.get(i);
|
Point p3 = points.get(i);
|
||||||
// does it intersect with at least one of the polygon's segments?
|
// does it intersect with at least one of the polygon's segments?
|
||||||
if (intersect2D_2Segments(p0, p1, p2, p3) > 0) {
|
if (intersect2D_2Segments(p0,p1,p2,p3) > 0)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
p2 = p3;
|
p2 = p3;
|
||||||
|
|
@ -163,12 +184,15 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOnPolyline(long px, long py) {
|
public boolean isOnPolyline( long px, long py )
|
||||||
|
{
|
||||||
int i_last = points.size()-1;
|
int i_last = points.size()-1;
|
||||||
Point p1 = points.get(0);
|
Point p1 = points.get(0);
|
||||||
for (int i = 1; i <= i_last; i++) {
|
for (int i = 1 ; i <= i_last; i++)
|
||||||
|
{
|
||||||
final Point p2 = points.get(i);
|
final Point p2 = points.get(i);
|
||||||
if (isOnLine(px, py, p1.x, p1.y, p2.x, p2.y)) {
|
if (OsmNogoPolygon.isOnLine(px,py,p1.x,p1.y,p2.x,p2.y))
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
p1 = p2;
|
p1 = p2;
|
||||||
|
|
@ -176,30 +200,37 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isOnLine(long px, long py, long p0x, long p0y, long p1x, long p1y) {
|
public static boolean isOnLine( long px, long py, long p0x, long p0y, long p1x, long p1y )
|
||||||
|
{
|
||||||
final double v10x = px-p0x;
|
final double v10x = px-p0x;
|
||||||
final double v10y = py-p0y;
|
final double v10y = py-p0y;
|
||||||
final double v12x = p1x-p0x;
|
final double v12x = p1x-p0x;
|
||||||
final double v12y = p1y-p0y;
|
final double v12y = p1y-p0y;
|
||||||
|
|
||||||
if (v10x == 0) { // P0->P1 vertical?
|
if ( v10x == 0 ) // P0->P1 vertical?
|
||||||
if (v10y == 0) { // P0 == P1?
|
{
|
||||||
|
if ( v10y == 0 ) // P0 == P1?
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (v12x != 0) { // P1->P2 not vertical?
|
if ( v12x != 0 ) // P1->P2 not vertical?
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return ( v12y / v10y ) >= 1; // P1->P2 at least as long as P1->P0?
|
return ( v12y / v10y ) >= 1; // P1->P2 at least as long as P1->P0?
|
||||||
}
|
}
|
||||||
if (v10y == 0) { // P0->P1 horizontal?
|
if ( v10y == 0 ) // P0->P1 horizontal?
|
||||||
if (v12y != 0) { // P1->P2 not horizontal?
|
{
|
||||||
|
if ( v12y != 0 ) // P1->P2 not horizontal?
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// if ( P10x == 0 ) // P0 == P1? already tested
|
// if ( P10x == 0 ) // P0 == P1? already tested
|
||||||
return ( v12x / v10x ) >= 1; // P1->P2 at least as long as P1->P0?
|
return ( v12x / v10x ) >= 1; // P1->P2 at least as long as P1->P0?
|
||||||
}
|
}
|
||||||
final double kx = v12x / v10x;
|
final double kx = v12x / v10x;
|
||||||
if (kx < 1) {
|
if ( kx < 1 )
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return kx == v12y / v10y;
|
return kx == v12y / v10y;
|
||||||
|
|
@ -211,7 +242,6 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
this code, and cannot be held liable for any real or imagined damage
|
this code, and cannot be held liable for any real or imagined damage
|
||||||
resulting from its use. Users of this code must verify correctness for
|
resulting from its use. Users of this code must verify correctness for
|
||||||
their application. */
|
their application. */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* winding number test for a point in a polygon
|
* winding number test for a point in a polygon
|
||||||
*
|
*
|
||||||
|
|
@ -219,7 +249,8 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
* @param py latitude of the point to check
|
* @param py latitude of the point to check
|
||||||
* @return a boolean whether the point is within the polygon or not.
|
* @return a boolean whether the point is within the polygon or not.
|
||||||
*/
|
*/
|
||||||
public boolean isWithin(final long px, final long py) {
|
public boolean isWithin(final long px, final long py)
|
||||||
|
{
|
||||||
int wn = 0; // the winding number counter
|
int wn = 0; // the winding number counter
|
||||||
|
|
||||||
// loop through all edges of the polygon
|
// loop through all edges of the polygon
|
||||||
|
|
@ -228,26 +259,34 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
long p0x = p0.x; // need to use long to avoid overflow in products
|
long p0x = p0.x; // need to use long to avoid overflow in products
|
||||||
long p0y = p0.y;
|
long p0y = p0.y;
|
||||||
|
|
||||||
for (int i = isClosed ? 0 : 1; i <= i_last; i++) { // edge from v[i] to v[i+1]
|
for (int i = isClosed ? 0 : 1; i <= i_last; i++) // edge from v[i] to v[i+1]
|
||||||
|
{
|
||||||
final Point p1 = points.get(i);
|
final Point p1 = points.get(i);
|
||||||
|
|
||||||
final long p1x = p1.x;
|
final long p1x = p1.x;
|
||||||
final long p1y = p1.y;
|
final long p1y = p1.y;
|
||||||
|
|
||||||
if (isOnLine(px, py, p0x, p0y, p1x, p1y)) {
|
if (OsmNogoPolygon.isOnLine(px, py, p0x, p0y, p1x, p1y))
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p0y <= py) // start y <= p.y
|
if (p0y <= py) // start y <= p.y
|
||||||
{
|
{
|
||||||
if (p1y > py) { // an upward crossing, p left of edge
|
if (p1y > py) // an upward crossing
|
||||||
if (((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) > 0) {
|
{ // p left of edge
|
||||||
|
if (((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) > 0)
|
||||||
|
{
|
||||||
++wn; // have a valid up intersect
|
++wn; // have a valid up intersect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // start y > p.y (no test needed)
|
}
|
||||||
if (p1y <= py) { // a downward crossing, p right of edge
|
else // start y > p.y (no test needed)
|
||||||
if (((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) < 0) {
|
{
|
||||||
|
if (p1y <= py) // a downward crossing
|
||||||
|
{ // p right of edge
|
||||||
|
if (((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) < 0)
|
||||||
|
{
|
||||||
--wn; // have a valid down intersect
|
--wn; // have a valid down intersect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -265,6 +304,7 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
* @param lat1 Integer latitude of the first point of the segment.
|
* @param lat1 Integer latitude of the first point of the segment.
|
||||||
* @param lon2 Integer longitude of the last point of the segment.
|
* @param lon2 Integer longitude of the last point of the segment.
|
||||||
* @param lat2 Integer latitude of the last point of the segment.
|
* @param lat2 Integer latitude of the last point of the segment.
|
||||||
|
*
|
||||||
* @return The length, in meters, of the portion of the segment which is
|
* @return The length, in meters, of the portion of the segment which is
|
||||||
* included in the polygon.
|
* included in the polygon.
|
||||||
*/
|
*/
|
||||||
|
|
@ -276,7 +316,8 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
final Point p2 = new Point (lon2, lat2);
|
final Point p2 = new Point (lon2, lat2);
|
||||||
|
|
||||||
Point previousIntersectionOnSegment = null;
|
Point previousIntersectionOnSegment = null;
|
||||||
if (isWithin(lon1, lat1)) {
|
if (isWithin(lon1, lat1))
|
||||||
|
{
|
||||||
// Start point of the segment is within the polygon, this is the first
|
// Start point of the segment is within the polygon, this is the first
|
||||||
// "intersection".
|
// "intersection".
|
||||||
previousIntersectionOnSegment = p1;
|
previousIntersectionOnSegment = p1;
|
||||||
|
|
@ -284,12 +325,14 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
|
|
||||||
// Loop over edges of the polygon to find intersections
|
// Loop over edges of the polygon to find intersections
|
||||||
int i_last = points.size() - 1;
|
int i_last = points.size() - 1;
|
||||||
for (int i = (isClosed ? 0 : 1), j = (isClosed ? i_last : 0); i <= i_last; j = i++) {
|
for (int i = (isClosed ? 0 : 1), j = (isClosed ? i_last : 0); i <= i_last; j = i++)
|
||||||
|
{
|
||||||
Point edgePoint1 = points.get(j);
|
Point edgePoint1 = points.get(j);
|
||||||
Point edgePoint2 = points.get(i);
|
Point edgePoint2 = points.get(i);
|
||||||
int intersectsEdge = intersect2D_2Segments(p1, p2, edgePoint1, edgePoint2);
|
int intersectsEdge = intersect2D_2Segments(p1, p2, edgePoint1, edgePoint2);
|
||||||
|
|
||||||
if (isClosed && intersectsEdge == 1) {
|
if (isClosed && intersectsEdge == 1)
|
||||||
|
{
|
||||||
// Intersects with a (closed) polygon edge on a single point
|
// Intersects with a (closed) polygon edge on a single point
|
||||||
// Distance is zero when crossing a polyline.
|
// Distance is zero when crossing a polyline.
|
||||||
// Let's find this intersection point
|
// Let's find this intersection point
|
||||||
|
|
@ -320,7 +363,8 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
previousIntersectionOnSegment = intersection;
|
previousIntersectionOnSegment = intersection;
|
||||||
} else if (intersectsEdge == 2) {
|
}
|
||||||
|
else if (intersectsEdge == 2) {
|
||||||
// Segment and edge overlaps
|
// Segment and edge overlaps
|
||||||
// FIXME: Could probably be done in a smarter way
|
// FIXME: Could probably be done in a smarter way
|
||||||
distance += Math.min(
|
distance += Math.min(
|
||||||
|
|
@ -357,7 +401,6 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
this code, and cannot be held liable for any real or imagined damage
|
this code, and cannot be held liable for any real or imagined damage
|
||||||
resulting from its use. Users of this code must verify correctness for
|
resulting from its use. Users of this code must verify correctness for
|
||||||
their application. */
|
their application. */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* inSegment(): determine if a point is inside a segment
|
* inSegment(): determine if a point is inside a segment
|
||||||
*
|
*
|
||||||
|
|
@ -367,28 +410,35 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
* @return 1 = P is inside S
|
* @return 1 = P is inside S
|
||||||
* 0 = P is not inside S
|
* 0 = P is not inside S
|
||||||
*/
|
*/
|
||||||
private static boolean inSegment(final Point p, final Point seg_p0, final Point seg_p1) {
|
private static boolean inSegment( final Point p, final Point seg_p0, final Point seg_p1)
|
||||||
|
{
|
||||||
final int sp0x = seg_p0.x;
|
final int sp0x = seg_p0.x;
|
||||||
final int sp1x = seg_p1.x;
|
final int sp1x = seg_p1.x;
|
||||||
|
|
||||||
if (sp0x != sp1x) { // S is not vertical
|
if (sp0x != sp1x) // S is not vertical
|
||||||
|
{
|
||||||
final int px = p.x;
|
final int px = p.x;
|
||||||
if (sp0x <= px && px <= sp1x) {
|
if (sp0x <= px && px <= sp1x)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (sp0x >= px && px >= sp1x) {
|
if (sp0x >= px && px >= sp1x)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else // S is vertical, so test y coordinate
|
}
|
||||||
|
else // S is vertical, so test y coordinate
|
||||||
{
|
{
|
||||||
final int sp0y = seg_p0.y;
|
final int sp0y = seg_p0.y;
|
||||||
final int sp1y = seg_p1.y;
|
final int sp1y = seg_p1.y;
|
||||||
final int py = p.y;
|
final int py = p.y;
|
||||||
|
|
||||||
if (sp0y <= py && py <= sp1y) {
|
if (sp0y <= py && py <= sp1y)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (sp0y >= py && py >= sp1y) {
|
if (sp0y >= py && py >= sp1y)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -401,10 +451,8 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
this code, and cannot be held liable for any real or imagined damage
|
this code, and cannot be held liable for any real or imagined damage
|
||||||
resulting from its use. Users of this code must verify correctness for
|
resulting from its use. Users of this code must verify correctness for
|
||||||
their application. */
|
their application. */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* intersect2D_2Segments(): find the 2D intersection of 2 finite segments
|
* intersect2D_2Segments(): find the 2D intersection of 2 finite segments
|
||||||
*
|
|
||||||
* @param s1p0 start point of segment 1
|
* @param s1p0 start point of segment 1
|
||||||
* @param s1p1 end point of segment 1
|
* @param s1p1 end point of segment 1
|
||||||
* @param s2p0 start point of segment 2
|
* @param s2p0 start point of segment 2
|
||||||
|
|
@ -413,7 +461,8 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
* 1=intersect in unique point I0
|
* 1=intersect in unique point I0
|
||||||
* 2=overlap in segment from I0 to I1
|
* 2=overlap in segment from I0 to I1
|
||||||
*/
|
*/
|
||||||
private static int intersect2D_2Segments(final Point s1p0, final Point s1p1, final Point s2p0, final Point s2p1) {
|
private static int intersect2D_2Segments( final Point s1p0, final Point s1p1, final Point s2p0, final Point s2p1 )
|
||||||
|
{
|
||||||
final long ux = s1p1.x - s1p0.x; // vector u = S1P1-S1P0 (segment 1)
|
final long ux = s1p1.x - s1p0.x; // vector u = S1P1-S1P0 (segment 1)
|
||||||
final long uy = s1p1.y - s1p0.y;
|
final long uy = s1p1.y - s1p0.y;
|
||||||
final long vx = s2p1.x - s2p0.x; // vector v = S2P1-S2P0 (segment 2)
|
final long vx = s2p1.x - s2p0.x; // vector v = S2P1-S2P0 (segment 2)
|
||||||
|
|
@ -426,7 +475,8 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
// test if they are parallel (includes either being a point)
|
// test if they are parallel (includes either being a point)
|
||||||
if (d == 0) // S1 and S2 are parallel
|
if (d == 0) // S1 and S2 are parallel
|
||||||
{
|
{
|
||||||
if ((ux * wy - uy * wx) != 0 || (vx * wy - vy * wx) != 0) {
|
if ((ux * wy - uy * wx) != 0 || (vx * wy - vy * wx) != 0)
|
||||||
|
{
|
||||||
return 0; // they are NOT collinear
|
return 0; // they are NOT collinear
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -450,10 +500,13 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
double t0, t1; // endpoints of S1 in eqn for S2
|
double t0, t1; // endpoints of S1 in eqn for S2
|
||||||
final int w2x = s1p1.x - s2p0.x; // vector w2 = S1P1-S2P0 (from start of segment 2 to end of segment 1)
|
final int w2x = s1p1.x - s2p0.x; // vector w2 = S1P1-S2P0 (from start of segment 2 to end of segment 1)
|
||||||
final int w2y = s1p1.y - s2p0.y;
|
final int w2y = s1p1.y - s2p0.y;
|
||||||
if (vx != 0) {
|
if (vx != 0)
|
||||||
|
{
|
||||||
t0 = wx / vx;
|
t0 = wx / vx;
|
||||||
t1 = w2x / vx;
|
t1 = w2x / vx;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
t0 = wy / vy;
|
t0 = wy / vy;
|
||||||
t1 = w2y / vy;
|
t1 = w2y / vy;
|
||||||
}
|
}
|
||||||
|
|
@ -463,7 +516,8 @@ public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
t0=t1;
|
t0=t1;
|
||||||
t1=t;
|
t1=t;
|
||||||
}
|
}
|
||||||
if (t0 > 1 || t1 < 0) {
|
if (t0 > 1 || t1 < 0)
|
||||||
|
{
|
||||||
return 0; // NO overlap
|
return 0; // NO overlap
|
||||||
}
|
}
|
||||||
t0 = t0<0? 0 : t0; // clip to min 0
|
t0 = t0<0? 0 : t0; // clip to min 0
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@
|
||||||
*/
|
*/
|
||||||
package btools.router;
|
package btools.router;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import btools.mapaccess.OsmLink;
|
import btools.mapaccess.OsmLink;
|
||||||
import btools.mapaccess.OsmLinkHolder;
|
import btools.mapaccess.OsmLinkHolder;
|
||||||
import btools.mapaccess.OsmNode;
|
import btools.mapaccess.OsmNode;
|
||||||
|
|
@ -12,7 +14,8 @@ import btools.mapaccess.OsmTransferNode;
|
||||||
import btools.mapaccess.TurnRestriction;
|
import btools.mapaccess.TurnRestriction;
|
||||||
import btools.util.CheapRuler;
|
import btools.util.CheapRuler;
|
||||||
|
|
||||||
abstract class OsmPath implements OsmLinkHolder {
|
abstract class OsmPath implements OsmLinkHolder
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* The cost of that path (a modified distance)
|
* The cost of that path (a modified distance)
|
||||||
*/
|
*/
|
||||||
|
|
@ -31,6 +34,8 @@ abstract class OsmPath implements OsmLinkHolder {
|
||||||
public OsmPathElement originElement;
|
public OsmPathElement originElement;
|
||||||
public OsmPathElement myElement;
|
public OsmPathElement myElement;
|
||||||
|
|
||||||
|
protected float traffic;
|
||||||
|
|
||||||
private OsmLinkHolder nextForLink = null;
|
private OsmLinkHolder nextForLink = null;
|
||||||
|
|
||||||
public int treedepth = 0;
|
public int treedepth = 0;
|
||||||
|
|
@ -52,23 +57,54 @@ abstract class OsmPath implements OsmLinkHolder {
|
||||||
private static final int HAD_DESTINATION_START_BIT = 8;
|
private static final int HAD_DESTINATION_START_BIT = 8;
|
||||||
protected int bitfield = PATH_START_BIT;
|
protected int bitfield = PATH_START_BIT;
|
||||||
|
|
||||||
private boolean getBit(int mask) {
|
private boolean getBit( int mask )
|
||||||
|
{
|
||||||
return (bitfield & mask ) != 0;
|
return (bitfield & mask ) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setBit(int mask, boolean bit) {
|
private void setBit( int mask, boolean bit )
|
||||||
if (getBit(mask) != bit) {
|
{
|
||||||
|
if ( getBit( mask ) != bit )
|
||||||
|
{
|
||||||
bitfield ^= mask;
|
bitfield ^= mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean didEnterDestinationArea() {
|
public boolean didEnterDestinationArea()
|
||||||
|
{
|
||||||
return !getBit( HAD_DESTINATION_START_BIT ) && getBit( IS_ON_DESTINATION_BIT );
|
return !getBit( HAD_DESTINATION_START_BIT ) && getBit( IS_ON_DESTINATION_BIT );
|
||||||
}
|
}
|
||||||
|
|
||||||
public MessageData message;
|
public MessageData message;
|
||||||
|
|
||||||
public void init(OsmLink link) {
|
public void unregisterUpTree( RoutingContext rc )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OsmPathElement pe = originElement;
|
||||||
|
while( pe instanceof OsmPathElementWithTraffic && ((OsmPathElementWithTraffic)pe).unregister(rc) )
|
||||||
|
{
|
||||||
|
pe = pe.origin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch( IOException ioe )
|
||||||
|
{
|
||||||
|
throw new RuntimeException( ioe );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerUpTree()
|
||||||
|
{
|
||||||
|
if ( originElement instanceof OsmPathElementWithTraffic )
|
||||||
|
{
|
||||||
|
OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic)originElement;
|
||||||
|
ot.register();
|
||||||
|
ot.addTraffic( traffic );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init( OsmLink link )
|
||||||
|
{
|
||||||
this.link = link;
|
this.link = link;
|
||||||
targetNode = link.getTarget( null );
|
targetNode = link.getTarget( null );
|
||||||
selev = targetNode.getSElev();
|
selev = targetNode.getSElev();
|
||||||
|
|
@ -77,9 +113,11 @@ abstract class OsmPath implements OsmLinkHolder {
|
||||||
originLat = -1;
|
originLat = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc) {
|
public void init( OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc )
|
||||||
if (origin.myElement == null) {
|
{
|
||||||
origin.myElement = OsmPathElement.create(origin);
|
if ( origin.myElement == null )
|
||||||
|
{
|
||||||
|
origin.myElement = OsmPathElement.create( origin, rc.countTraffic );
|
||||||
}
|
}
|
||||||
this.originElement = origin.myElement;
|
this.originElement = origin.myElement;
|
||||||
this.link = link;
|
this.link = link;
|
||||||
|
|
@ -89,7 +127,6 @@ abstract class OsmPath implements OsmLinkHolder {
|
||||||
this.lastClassifier = origin.lastClassifier;
|
this.lastClassifier = origin.lastClassifier;
|
||||||
this.lastInitialCost = origin.lastInitialCost;
|
this.lastInitialCost = origin.lastInitialCost;
|
||||||
this.bitfield = origin.bitfield;
|
this.bitfield = origin.bitfield;
|
||||||
this.priorityclassifier = origin.priorityclassifier;
|
|
||||||
init( origin );
|
init( origin );
|
||||||
addAddionalPenalty(refTrack, detailMode, origin, link, rc );
|
addAddionalPenalty(refTrack, detailMode, origin, link, rc );
|
||||||
}
|
}
|
||||||
|
|
@ -98,29 +135,16 @@ abstract class OsmPath implements OsmLinkHolder {
|
||||||
|
|
||||||
protected abstract void resetState();
|
protected abstract void resetState();
|
||||||
|
|
||||||
static int seg = 1;
|
|
||||||
|
|
||||||
protected void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc) {
|
protected void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc )
|
||||||
|
{
|
||||||
byte[] description = link.descriptionBitmap;
|
byte[] description = link.descriptionBitmap;
|
||||||
if (description == null) { // could be a beeline path
|
if ( description == null )
|
||||||
message = new MessageData();
|
{
|
||||||
if (message != null) {
|
return; // could be a beeline path
|
||||||
message.turnangle = 0;
|
|
||||||
message.time = (float) 1;
|
|
||||||
message.energy = (float) 0;
|
|
||||||
message.priorityclassifier = 0;
|
|
||||||
message.classifiermask = 0;
|
|
||||||
message.lon = targetNode.getILon();
|
|
||||||
message.lat = targetNode.getILat();
|
|
||||||
message.ele = Short.MIN_VALUE;
|
|
||||||
message.linkdist = sourceNode.calcDistance(targetNode);
|
|
||||||
message.wayKeyValues = "direct_segment=" + seg;
|
|
||||||
seg++;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean recordTransferNodes = detailMode;
|
boolean recordTransferNodes = detailMode || rc.countTraffic;
|
||||||
|
|
||||||
rc.nogoCost = 0.;
|
rc.nogoCost = 0.;
|
||||||
|
|
||||||
|
|
@ -152,15 +176,18 @@ abstract class OsmPath implements OsmLinkHolder {
|
||||||
float newClassifier = rc.expctxWay.getInitialClassifier();
|
float newClassifier = rc.expctxWay.getInitialClassifier();
|
||||||
float newInitialCost = rc.expctxWay.getInitialcost();
|
float newInitialCost = rc.expctxWay.getInitialcost();
|
||||||
float classifierDiff = newClassifier - lastClassifier;
|
float classifierDiff = newClassifier - lastClassifier;
|
||||||
if (newClassifier != 0. && lastClassifier != 0. && (classifierDiff > 0.0005 || classifierDiff < -0.0005)) {
|
if ( newClassifier != 0. && lastClassifier != 0. && ( classifierDiff > 0.0005 || classifierDiff < -0.0005 ) )
|
||||||
|
{
|
||||||
float initialcost = rc.inverseDirection ? lastInitialCost : newInitialCost;
|
float initialcost = rc.inverseDirection ? lastInitialCost : newInitialCost;
|
||||||
if (initialcost >= 1000000.) {
|
if ( initialcost >= 1000000. )
|
||||||
|
{
|
||||||
cost = -1;
|
cost = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int iicost = (int)initialcost;
|
int iicost = (int)initialcost;
|
||||||
if (message != null) {
|
if ( message != null )
|
||||||
|
{
|
||||||
message.linkinitcost += iicost;
|
message.linkinitcost += iicost;
|
||||||
}
|
}
|
||||||
cost += iicost;
|
cost += iicost;
|
||||||
|
|
@ -172,15 +199,22 @@ abstract class OsmPath implements OsmLinkHolder {
|
||||||
int classifiermask = (int)rc.expctxWay.getClassifierMask();
|
int classifiermask = (int)rc.expctxWay.getClassifierMask();
|
||||||
boolean newDestination = (classifiermask & 64) != 0;
|
boolean newDestination = (classifiermask & 64) != 0;
|
||||||
boolean oldDestination = getBit( IS_ON_DESTINATION_BIT );
|
boolean oldDestination = getBit( IS_ON_DESTINATION_BIT );
|
||||||
if (getBit(PATH_START_BIT)) {
|
if ( getBit( PATH_START_BIT ) )
|
||||||
|
{
|
||||||
setBit( PATH_START_BIT, false );
|
setBit( PATH_START_BIT, false );
|
||||||
setBit( CAN_LEAVE_DESTINATION_BIT, newDestination );
|
setBit( CAN_LEAVE_DESTINATION_BIT, newDestination );
|
||||||
setBit( HAD_DESTINATION_START_BIT, newDestination );
|
setBit( HAD_DESTINATION_START_BIT, newDestination );
|
||||||
} else {
|
}
|
||||||
if (oldDestination && !newDestination) {
|
else
|
||||||
if (getBit(CAN_LEAVE_DESTINATION_BIT)) {
|
{
|
||||||
|
if ( oldDestination && !newDestination )
|
||||||
|
{
|
||||||
|
if ( getBit( CAN_LEAVE_DESTINATION_BIT ) )
|
||||||
|
{
|
||||||
setBit( CAN_LEAVE_DESTINATION_BIT, false );
|
setBit( CAN_LEAVE_DESTINATION_BIT, false );
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
cost = -1;
|
cost = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -192,7 +226,8 @@ abstract class OsmPath implements OsmLinkHolder {
|
||||||
OsmTransferNode transferNode = link.geometry == null ? null
|
OsmTransferNode transferNode = link.geometry == null ? null
|
||||||
: rc.geometryDecoder.decodeGeometry( link.geometry, sourceNode, targetNode, isReverse );
|
: rc.geometryDecoder.decodeGeometry( link.geometry, sourceNode, targetNode, isReverse );
|
||||||
|
|
||||||
for (int nsection = 0; ; nsection++) {
|
for(int nsection=0; ;nsection++)
|
||||||
|
{
|
||||||
|
|
||||||
originLon = lon1;
|
originLon = lon1;
|
||||||
originLat = lat1;
|
originLat = lat1;
|
||||||
|
|
@ -200,33 +235,37 @@ abstract class OsmPath implements OsmLinkHolder {
|
||||||
int lon2;
|
int lon2;
|
||||||
int lat2;
|
int lat2;
|
||||||
short ele2;
|
short ele2;
|
||||||
short originEle2;
|
|
||||||
|
|
||||||
if (transferNode == null) {
|
if ( transferNode == null )
|
||||||
|
{
|
||||||
lon2 = targetNode.ilon;
|
lon2 = targetNode.ilon;
|
||||||
lat2 = targetNode.ilat;
|
lat2 = targetNode.ilat;
|
||||||
originEle2 = targetNode.selev;
|
ele2 = targetNode.selev;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
lon2 = transferNode.ilon;
|
lon2 = transferNode.ilon;
|
||||||
lat2 = transferNode.ilat;
|
lat2 = transferNode.ilat;
|
||||||
originEle2 = transferNode.selev;
|
ele2 = transferNode.selev;
|
||||||
}
|
}
|
||||||
ele2 = originEle2;
|
|
||||||
|
|
||||||
boolean isStartpoint = lon0 == -1 && lat0 == -1;
|
boolean isStartpoint = lon0 == -1 && lat0 == -1;
|
||||||
|
|
||||||
// check turn restrictions (n detail mode (=final pass) no TR to not mess up voice hints)
|
// check turn restrictions (n detail mode (=final pass) no TR to not mess up voice hints)
|
||||||
if (nsection == 0 && rc.considerTurnRestrictions && !detailMode && !isStartpoint) {
|
if ( nsection == 0 && rc.considerTurnRestrictions && !detailMode&& !isStartpoint )
|
||||||
|
{
|
||||||
if ( rc.inverseDirection
|
if ( rc.inverseDirection
|
||||||
? TurnRestriction.isTurnForbidden(sourceNode.firstRestriction, lon2, lat2, lon0, lat0, rc.bikeMode || rc.footMode, rc.carMode)
|
? TurnRestriction.isTurnForbidden( sourceNode.firstRestriction, lon2, lat2, lon0, lat0, rc.bikeMode, rc.carMode )
|
||||||
: TurnRestriction.isTurnForbidden(sourceNode.firstRestriction, lon0, lat0, lon2, lat2, rc.bikeMode || rc.footMode, rc.carMode)) {
|
: TurnRestriction.isTurnForbidden( sourceNode.firstRestriction, lon0, lat0, lon2, lat2, rc.bikeMode, rc.carMode ) )
|
||||||
|
{
|
||||||
cost = -1;
|
cost = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if recording, new MessageData for each section (needed for turn-instructions)
|
// if recording, new MessageData for each section (needed for turn-instructions)
|
||||||
if (message != null && message.wayKeyValues != null) {
|
if ( message != null && message.wayKeyValues != null )
|
||||||
|
{
|
||||||
originElement.message = message;
|
originElement.message = message;
|
||||||
message = new MessageData();
|
message = new MessageData();
|
||||||
}
|
}
|
||||||
|
|
@ -234,11 +273,15 @@ abstract class OsmPath implements OsmLinkHolder {
|
||||||
int dist = rc.calcDistance( lon1, lat1, lon2, lat2 );
|
int dist = rc.calcDistance( lon1, lat1, lon2, lat2 );
|
||||||
|
|
||||||
boolean stopAtEndpoint = false;
|
boolean stopAtEndpoint = false;
|
||||||
if (rc.shortestmatch) {
|
if ( rc.shortestmatch )
|
||||||
if (rc.isEndpoint) {
|
{
|
||||||
|
if ( rc.isEndpoint )
|
||||||
|
{
|
||||||
stopAtEndpoint = true;
|
stopAtEndpoint = true;
|
||||||
ele2 = interpolateEle( ele1, ele2, rc.wayfraction );
|
ele2 = interpolateEle( ele1, ele2, rc.wayfraction );
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// we just start here, reset everything
|
// we just start here, reset everything
|
||||||
cost = 0;
|
cost = 0;
|
||||||
resetState();
|
resetState();
|
||||||
|
|
@ -246,18 +289,24 @@ abstract class OsmPath implements OsmLinkHolder {
|
||||||
lat0 = -1;
|
lat0 = -1;
|
||||||
isStartpoint = true;
|
isStartpoint = true;
|
||||||
|
|
||||||
if (recordTransferNodes) {
|
if ( recordTransferNodes )
|
||||||
if (rc.wayfraction > 0.) {
|
{
|
||||||
|
if ( rc.wayfraction > 0. )
|
||||||
|
{
|
||||||
ele1 = interpolateEle( ele1, ele2, 1. - rc.wayfraction );
|
ele1 = interpolateEle( ele1, ele2, 1. - rc.wayfraction );
|
||||||
originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, ele1, null);
|
originElement = OsmPathElement.create( rc.ilonshortest, rc.ilatshortest, ele1, null, rc.countTraffic );
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
originElement = null; // prevent duplicate point
|
originElement = null; // prevent duplicate point
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rc.checkPendingEndpoint()) {
|
if ( rc.checkPendingEndpoint() )
|
||||||
|
{
|
||||||
dist = rc.calcDistance( rc.ilonshortest, rc.ilatshortest, lon2, lat2 );
|
dist = rc.calcDistance( rc.ilonshortest, rc.ilatshortest, lon2, lat2 );
|
||||||
if (rc.shortestmatch) {
|
if ( rc.shortestmatch )
|
||||||
|
{
|
||||||
stopAtEndpoint = true;
|
stopAtEndpoint = true;
|
||||||
ele2 = interpolateEle( ele1, ele2, rc.wayfraction );
|
ele2 = interpolateEle( ele1, ele2, rc.wayfraction );
|
||||||
}
|
}
|
||||||
|
|
@ -265,19 +314,24 @@ abstract class OsmPath implements OsmLinkHolder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message != null) {
|
if ( message != null )
|
||||||
|
{
|
||||||
message.linkdist += dist;
|
message.linkdist += dist;
|
||||||
}
|
}
|
||||||
linkdisttotal += dist;
|
linkdisttotal += dist;
|
||||||
|
|
||||||
// apply a start-direction if appropriate (by faking the origin position)
|
// apply a start-direction if appropriate (by faking the origin position)
|
||||||
if (isStartpoint) {
|
if ( isStartpoint )
|
||||||
if (rc.startDirectionValid) {
|
{
|
||||||
double dir = rc.startDirection * CheapRuler.DEG_TO_RAD;
|
if ( rc.startDirectionValid )
|
||||||
|
{
|
||||||
|
double dir = rc.startDirection.intValue() * CheapRuler.DEG_TO_RAD;
|
||||||
double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (lon0 + lat1) >> 1 );
|
double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (lon0 + lat1) >> 1 );
|
||||||
lon0 = lon1 - (int) ( 1000. * Math.sin( dir ) / lonlat2m[0] );
|
lon0 = lon1 - (int) ( 1000. * Math.sin( dir ) / lonlat2m[0] );
|
||||||
lat0 = lat1 - (int) ( 1000. * Math.cos( dir ) / lonlat2m[1] );
|
lat0 = lat1 - (int) ( 1000. * Math.cos( dir ) / lonlat2m[1] );
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
lon0 = lon1 - (lon2-lon1);
|
lon0 = lon1 - (lon2-lon1);
|
||||||
lat0 = lat1 - (lat2-lat1);
|
lat0 = lat1 - (lat2-lat1);
|
||||||
}
|
}
|
||||||
|
|
@ -288,32 +342,47 @@ abstract class OsmPath implements OsmLinkHolder {
|
||||||
// *** elevation stuff
|
// *** elevation stuff
|
||||||
double delta_h = 0.;
|
double delta_h = 0.;
|
||||||
if ( ele2 == Short.MIN_VALUE ) ele2 = ele1;
|
if ( ele2 == Short.MIN_VALUE ) ele2 = ele1;
|
||||||
if (ele1 != Short.MIN_VALUE) {
|
if ( ele1 != Short.MIN_VALUE )
|
||||||
|
{
|
||||||
delta_h = (ele2 - ele1)/4.;
|
delta_h = (ele2 - ele1)/4.;
|
||||||
if (rc.inverseDirection) {
|
if ( rc.inverseDirection )
|
||||||
|
{
|
||||||
delta_h = -delta_h;
|
delta_h = -delta_h;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
double elevation = ele2 == Short.MIN_VALUE ? 100. : ele2/4.;
|
double elevation = ele2 == Short.MIN_VALUE ? 100. : ele2/4.;
|
||||||
|
|
||||||
double sectionCost = processWaySection( rc, dist, delta_h, elevation, angle, cosangle, isStartpoint, nsection, lastpriorityclassifier );
|
double sectionCost = processWaySection( rc, dist, delta_h, elevation, angle, cosangle, isStartpoint, nsection, lastpriorityclassifier );
|
||||||
if ((sectionCost < 0. || costfactor > 9998. && !detailMode) || sectionCost + cost >= 2000000000.) {
|
if ( ( sectionCost < 0. || costfactor > 9998. && !detailMode ) || sectionCost + cost >= 2000000000. )
|
||||||
|
{
|
||||||
cost = -1;
|
cost = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isTrafficBackbone) {
|
if ( isTrafficBackbone )
|
||||||
|
{
|
||||||
sectionCost = 0.;
|
sectionCost = 0.;
|
||||||
}
|
}
|
||||||
|
|
||||||
cost += (int)sectionCost;
|
cost += (int)sectionCost;
|
||||||
|
|
||||||
|
// calculate traffic
|
||||||
|
if ( rc.countTraffic )
|
||||||
|
{
|
||||||
|
int minDist = (int)rc.trafficSourceMinDist;
|
||||||
|
int cost2 = cost < minDist ? minDist : cost;
|
||||||
|
traffic += dist*rc.expctxWay.getTrafficSourceDensity()*Math.pow(cost2/10000.f,rc.trafficSourceExponent);
|
||||||
|
}
|
||||||
|
|
||||||
// compute kinematic
|
// compute kinematic
|
||||||
computeKinematic( rc, dist, delta_h, detailMode );
|
computeKinematic( rc, dist, delta_h, detailMode );
|
||||||
|
|
||||||
if (message != null) {
|
if ( message != null )
|
||||||
|
{
|
||||||
message.turnangle = (float)angle;
|
message.turnangle = (float)angle;
|
||||||
message.time = (float)getTotalTime();
|
message.time = (float)getTotalTime();
|
||||||
message.energy = (float)getTotalEnergy();
|
message.energy = (float)getTotalEnergy();
|
||||||
|
|
@ -321,29 +390,37 @@ abstract class OsmPath implements OsmLinkHolder {
|
||||||
message.classifiermask = classifiermask;
|
message.classifiermask = classifiermask;
|
||||||
message.lon = lon2;
|
message.lon = lon2;
|
||||||
message.lat = lat2;
|
message.lat = lat2;
|
||||||
message.ele = originEle2;
|
message.ele = ele2;
|
||||||
message.wayKeyValues = rc.expctxWay.getKeyValueDescription( isReverse, description );
|
message.wayKeyValues = rc.expctxWay.getKeyValueDescription( isReverse, description );
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stopAtEndpoint) {
|
if ( stopAtEndpoint )
|
||||||
if (recordTransferNodes) {
|
{
|
||||||
originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, originEle2, originElement);
|
if ( recordTransferNodes )
|
||||||
|
{
|
||||||
|
originElement = OsmPathElement.create( rc.ilonshortest, rc.ilatshortest, ele2, originElement, rc.countTraffic );
|
||||||
originElement.cost = cost;
|
originElement.cost = cost;
|
||||||
if (message != null) {
|
if ( message != null )
|
||||||
|
{
|
||||||
originElement.message = message;
|
originElement.message = message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rc.nogoCost < 0) {
|
if ( rc.nogoCost < 0)
|
||||||
|
{
|
||||||
cost = -1;
|
cost = -1;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
cost += rc.nogoCost;
|
cost += rc.nogoCost;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transferNode == null) {
|
if ( transferNode == null )
|
||||||
|
{
|
||||||
// *** penalty for being part of the reference track
|
// *** penalty for being part of the reference track
|
||||||
if (refTrack != null && refTrack.containsNode(targetNode) && refTrack.containsNode(sourceNode)) {
|
if ( refTrack != null && refTrack.containsNode( targetNode ) && refTrack.containsNode( sourceNode ) )
|
||||||
|
{
|
||||||
int reftrackcost = linkdisttotal;
|
int reftrackcost = linkdisttotal;
|
||||||
cost += reftrackcost;
|
cost += reftrackcost;
|
||||||
}
|
}
|
||||||
|
|
@ -352,9 +429,12 @@ abstract class OsmPath implements OsmLinkHolder {
|
||||||
}
|
}
|
||||||
transferNode = transferNode.next;
|
transferNode = transferNode.next;
|
||||||
|
|
||||||
if (recordTransferNodes) {
|
if ( recordTransferNodes )
|
||||||
originElement = OsmPathElement.create(lon2, lat2, originEle2, originElement);
|
{
|
||||||
|
originElement = OsmPathElement.create( lon2, lat2, ele2, originElement, rc.countTraffic );
|
||||||
originElement.cost = cost;
|
originElement.cost = cost;
|
||||||
|
originElement.addTraffic( traffic );
|
||||||
|
traffic = 0;
|
||||||
}
|
}
|
||||||
lon0 = lon1;
|
lon0 = lon1;
|
||||||
lat0 = lat1;
|
lat0 = lat1;
|
||||||
|
|
@ -364,16 +444,20 @@ abstract class OsmPath implements OsmLinkHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for nogo-matches (after the *actual* start of segment)
|
// check for nogo-matches (after the *actual* start of segment)
|
||||||
if (rc.nogoCost < 0) {
|
if ( rc.nogoCost < 0)
|
||||||
|
{
|
||||||
cost = -1;
|
cost = -1;
|
||||||
return;
|
return;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
cost += rc.nogoCost;
|
cost += rc.nogoCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add target-node costs
|
// add target-node costs
|
||||||
double targetCost = processTargetNode( rc );
|
double targetCost = processTargetNode( rc );
|
||||||
if (targetCost < 0. || targetCost + cost >= 2000000000.) {
|
if ( targetCost < 0. || targetCost + cost >= 2000000000. )
|
||||||
|
{
|
||||||
cost = -1;
|
cost = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -381,8 +465,10 @@ abstract class OsmPath implements OsmLinkHolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public short interpolateEle(short e1, short e2, double fraction) {
|
public short interpolateEle( short e1, short e2, double fraction )
|
||||||
if (e1 == Short.MIN_VALUE || e2 == Short.MIN_VALUE) {
|
{
|
||||||
|
if ( e1 == Short.MIN_VALUE || e2 == Short.MIN_VALUE )
|
||||||
|
{
|
||||||
return Short.MIN_VALUE;
|
return Short.MIN_VALUE;
|
||||||
}
|
}
|
||||||
return (short)( e1*(1.-fraction) + e2*fraction );
|
return (short)( e1*(1.-fraction) + e2*fraction );
|
||||||
|
|
@ -392,39 +478,47 @@ abstract class OsmPath implements OsmLinkHolder {
|
||||||
|
|
||||||
protected abstract double processTargetNode( RoutingContext rc );
|
protected abstract double processTargetNode( RoutingContext rc );
|
||||||
|
|
||||||
protected void computeKinematic(RoutingContext rc, double dist, double delta_h, boolean detailMode) {
|
protected void computeKinematic( RoutingContext rc, double dist, double delta_h, boolean detailMode )
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract int elevationCorrection();
|
public abstract int elevationCorrection( RoutingContext rc );
|
||||||
|
|
||||||
public abstract boolean definitlyWorseThan(OsmPath p);
|
public abstract boolean definitlyWorseThan( OsmPath p, RoutingContext rc );
|
||||||
|
|
||||||
public OsmNode getSourceNode() {
|
public OsmNode getSourceNode()
|
||||||
|
{
|
||||||
return sourceNode;
|
return sourceNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmNode getTargetNode() {
|
public OsmNode getTargetNode()
|
||||||
|
{
|
||||||
return targetNode;
|
return targetNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmLink getLink() {
|
public OsmLink getLink()
|
||||||
|
{
|
||||||
return link;
|
return link;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setNextForLink(OsmLinkHolder holder) {
|
public void setNextForLink( OsmLinkHolder holder )
|
||||||
|
{
|
||||||
nextForLink = holder;
|
nextForLink = holder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmLinkHolder getNextForLink() {
|
public OsmLinkHolder getNextForLink()
|
||||||
|
{
|
||||||
return nextForLink;
|
return nextForLink;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getTotalTime() {
|
public double getTotalTime()
|
||||||
|
{
|
||||||
return 0.;
|
return 0.;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getTotalEnergy() {
|
public double getTotalEnergy()
|
||||||
|
{
|
||||||
return 0.;
|
return 0.;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ import btools.util.CheapRuler;
|
||||||
* @author ab
|
* @author ab
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class OsmPathElement implements OsmPos {
|
public class OsmPathElement implements OsmPos
|
||||||
|
{
|
||||||
private int ilat; // latitude
|
private int ilat; // latitude
|
||||||
private int ilon; // longitude
|
private int ilon; // longitude
|
||||||
private short selev; // longitude
|
private short selev; // longitude
|
||||||
|
|
@ -24,73 +25,77 @@ public class OsmPathElement implements OsmPos {
|
||||||
public int cost;
|
public int cost;
|
||||||
|
|
||||||
// interface OsmPos
|
// interface OsmPos
|
||||||
public final int getILat() {
|
public final int getILat()
|
||||||
|
{
|
||||||
return ilat;
|
return ilat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int getILon() {
|
public final int getILon()
|
||||||
|
{
|
||||||
return ilon;
|
return ilon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final short getSElev() {
|
public final short getSElev()
|
||||||
|
{
|
||||||
return selev;
|
return selev;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setSElev(short s) {
|
public final double getElev()
|
||||||
selev = s;
|
{
|
||||||
}
|
|
||||||
|
|
||||||
public final double getElev() {
|
|
||||||
return selev / 4.;
|
return selev / 4.;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final float getTime() {
|
public final float getTime()
|
||||||
|
{
|
||||||
return message == null ? 0.f : message.time;
|
return message == null ? 0.f : message.time;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setTime(float t) {
|
public final void setTime( float t )
|
||||||
if (message != null) {
|
{
|
||||||
|
if ( message != null )
|
||||||
|
{
|
||||||
message.time = t;
|
message.time = t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final float getEnergy() {
|
public final float getEnergy()
|
||||||
|
{
|
||||||
return message == null ? 0.f : message.energy;
|
return message == null ? 0.f : message.energy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setEnergy(float e) {
|
public final void setEnergy( float e )
|
||||||
if (message != null) {
|
{
|
||||||
|
if ( message != null )
|
||||||
|
{
|
||||||
message.energy = e;
|
message.energy = e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setAngle(float e) {
|
public final long getIdFromPos()
|
||||||
if (message != null) {
|
{
|
||||||
message.turnangle = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final long getIdFromPos() {
|
|
||||||
return ((long)ilon)<<32 | ilat;
|
return ((long)ilon)<<32 | ilat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int calcDistance(OsmPos p) {
|
public final int calcDistance( OsmPos p )
|
||||||
return (int) Math.max(1.0, Math.round(CheapRuler.distance(ilon, ilat, p.getILon(), p.getILat())));
|
{
|
||||||
|
return (int)(CheapRuler.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmPathElement origin;
|
public OsmPathElement origin;
|
||||||
|
|
||||||
// construct a path element from a path
|
// construct a path element from a path
|
||||||
public static final OsmPathElement create(OsmPath path) {
|
public static final OsmPathElement create( OsmPath path, boolean countTraffic )
|
||||||
|
{
|
||||||
OsmNode n = path.getTargetNode();
|
OsmNode n = path.getTargetNode();
|
||||||
OsmPathElement pe = create(n.getILon(), n.getILat(), n.getSElev(), path.originElement);
|
OsmPathElement pe = create( n.getILon(), n.getILat(), path.selev, path.originElement, countTraffic );
|
||||||
pe.cost = path.cost;
|
pe.cost = path.cost;
|
||||||
pe.message = path.message;
|
pe.message = path.message;
|
||||||
return pe;
|
return pe;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final OsmPathElement create(int ilon, int ilat, short selev, OsmPathElement origin) {
|
public static final OsmPathElement create( int ilon, int ilat, short selev, OsmPathElement origin, boolean countTraffic )
|
||||||
OsmPathElement pe = new OsmPathElement();
|
{
|
||||||
|
OsmPathElement pe = countTraffic ? new OsmPathElementWithTraffic() : new OsmPathElement();
|
||||||
pe.ilon = ilon;
|
pe.ilon = ilon;
|
||||||
pe.ilat = ilat;
|
pe.ilat = ilat;
|
||||||
pe.selev = selev;
|
pe.selev = selev;
|
||||||
|
|
@ -98,25 +103,29 @@ public class OsmPathElement implements OsmPos {
|
||||||
return pe;
|
return pe;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected OsmPathElement() {
|
protected OsmPathElement()
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public void addTraffic( float traffic )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
return ilon + "_" + ilat;
|
return ilon + "_" + ilat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean positionEquals(OsmPathElement e) {
|
public void writeToStream( DataOutput dos ) throws IOException
|
||||||
return this.ilat == e.ilat && this.ilon == e.ilon;
|
{
|
||||||
}
|
|
||||||
|
|
||||||
public void writeToStream(DataOutput dos) throws IOException {
|
|
||||||
dos.writeInt( ilat );
|
dos.writeInt( ilat );
|
||||||
dos.writeInt( ilon );
|
dos.writeInt( ilon );
|
||||||
dos.writeShort( selev );
|
dos.writeShort( selev );
|
||||||
dos.writeInt( cost );
|
dos.writeInt( cost );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OsmPathElement readFromStream(DataInput dis) throws IOException {
|
public static OsmPathElement readFromStream( DataInput dis ) throws IOException
|
||||||
|
{
|
||||||
OsmPathElement pe = new OsmPathElement();
|
OsmPathElement pe = new OsmPathElement();
|
||||||
pe.ilat = dis.readInt();
|
pe.ilat = dis.readInt();
|
||||||
pe.ilon = dis.readInt();
|
pe.ilon = dis.readInt();
|
||||||
|
|
|
||||||
|
|
@ -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,7 +11,8 @@ import btools.expressions.BExpressionContextNode;
|
||||||
import btools.expressions.BExpressionContextWay;
|
import btools.expressions.BExpressionContextWay;
|
||||||
|
|
||||||
|
|
||||||
abstract class OsmPathModel {
|
abstract class OsmPathModel
|
||||||
|
{
|
||||||
public abstract OsmPrePath createPrePath();
|
public abstract OsmPrePath createPrePath();
|
||||||
|
|
||||||
public abstract OsmPath createPath();
|
public abstract OsmPath createPath();
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,18 @@ package btools.router;
|
||||||
|
|
||||||
import btools.mapaccess.OsmLink;
|
import btools.mapaccess.OsmLink;
|
||||||
import btools.mapaccess.OsmNode;
|
import btools.mapaccess.OsmNode;
|
||||||
|
import btools.mapaccess.OsmTransferNode;
|
||||||
|
|
||||||
public abstract class OsmPrePath {
|
public abstract class OsmPrePath
|
||||||
|
{
|
||||||
protected OsmNode sourceNode;
|
protected OsmNode sourceNode;
|
||||||
protected OsmNode targetNode;
|
protected OsmNode targetNode;
|
||||||
protected OsmLink link;
|
protected OsmLink link;
|
||||||
|
|
||||||
public OsmPrePath next;
|
public OsmPrePath next;
|
||||||
|
|
||||||
public void init(OsmPath origin, OsmLink link, RoutingContext rc) {
|
public void init( OsmPath origin, OsmLink link, RoutingContext rc )
|
||||||
|
{
|
||||||
this.link = link;
|
this.link = link;
|
||||||
this.sourceNode = origin.getTargetNode();
|
this.sourceNode = origin.getTargetNode();
|
||||||
this.targetNode = link.getTarget( sourceNode );
|
this.targetNode = link.getTarget( sourceNode );
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -11,7 +11,8 @@ import btools.expressions.BExpressionContextNode;
|
||||||
import btools.expressions.BExpressionContextWay;
|
import btools.expressions.BExpressionContextWay;
|
||||||
import btools.expressions.BExpressionMetaData;
|
import btools.expressions.BExpressionMetaData;
|
||||||
|
|
||||||
public final class ProfileCache {
|
public final class ProfileCache
|
||||||
|
{
|
||||||
|
|
||||||
private static File lastLookupFile;
|
private static File lastLookupFile;
|
||||||
private static long lastLookupTimestamp;
|
private static long lastLookupTimestamp;
|
||||||
|
|
@ -26,18 +27,23 @@ public final class ProfileCache {
|
||||||
private static ProfileCache[] apc = new ProfileCache[1];
|
private static ProfileCache[] apc = new ProfileCache[1];
|
||||||
private static boolean debug = Boolean.getBoolean( "debugProfileCache" );
|
private static boolean debug = Boolean.getBoolean( "debugProfileCache" );
|
||||||
|
|
||||||
public static synchronized void setSize(int size) {
|
public static synchronized void setSize( int size )
|
||||||
|
{
|
||||||
apc = new ProfileCache[size];
|
apc = new ProfileCache[size];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized boolean parseProfile(RoutingContext rc) {
|
public static synchronized boolean parseProfile( RoutingContext rc )
|
||||||
|
{
|
||||||
String profileBaseDir = System.getProperty( "profileBaseDir" );
|
String profileBaseDir = System.getProperty( "profileBaseDir" );
|
||||||
File profileDir;
|
File profileDir;
|
||||||
File profileFile;
|
File profileFile;
|
||||||
if (profileBaseDir == null) {
|
if ( profileBaseDir == null )
|
||||||
|
{
|
||||||
profileDir = new File( rc.localFunction ).getParentFile();
|
profileDir = new File( rc.localFunction ).getParentFile();
|
||||||
profileFile = new File( rc.localFunction ) ;
|
profileFile = new File( rc.localFunction ) ;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
profileDir = new File( profileBaseDir );
|
profileDir = new File( profileBaseDir );
|
||||||
profileFile = new File( profileDir, rc.localFunction + ".brf" ) ;
|
profileFile = new File( profileDir, rc.localFunction + ".brf" ) ;
|
||||||
}
|
}
|
||||||
|
|
@ -46,8 +52,10 @@ public final class ProfileCache {
|
||||||
File lookupFile = new File( profileDir, "lookups.dat" );
|
File lookupFile = new File( profileDir, "lookups.dat" );
|
||||||
|
|
||||||
// invalidate cache at lookup-table update
|
// invalidate cache at lookup-table update
|
||||||
if (!(lookupFile.equals(lastLookupFile) && lookupFile.lastModified() == lastLookupTimestamp)) {
|
if ( !(lookupFile.equals( lastLookupFile ) && lookupFile.lastModified() == lastLookupTimestamp ) )
|
||||||
if (lastLookupFile != null) {
|
{
|
||||||
|
if ( lastLookupFile != null )
|
||||||
|
{
|
||||||
System.out.println( "******** invalidating profile-cache after lookup-file update ******** " );
|
System.out.println( "******** invalidating profile-cache after lookup-file update ******** " );
|
||||||
}
|
}
|
||||||
apc = new ProfileCache[apc.length];
|
apc = new ProfileCache[apc.length];
|
||||||
|
|
@ -59,12 +67,16 @@ public final class ProfileCache {
|
||||||
int unusedSlot =-1;
|
int unusedSlot =-1;
|
||||||
|
|
||||||
// check for re-use
|
// check for re-use
|
||||||
for (int i = 0; i < apc.length; i++) {
|
for( int i=0; i<apc.length; i++)
|
||||||
|
{
|
||||||
ProfileCache pc = apc[i];
|
ProfileCache pc = apc[i];
|
||||||
|
|
||||||
if (pc != null) {
|
if ( pc != null )
|
||||||
if ((!pc.profilesBusy) && profileFile.equals(pc.lastProfileFile)) {
|
{
|
||||||
if (rc.profileTimestamp == pc.lastProfileTimestamp) {
|
if ( (!pc.profilesBusy) && profileFile.equals( pc.lastProfileFile ) )
|
||||||
|
{
|
||||||
|
if ( rc.profileTimestamp == pc.lastProfileTimestamp )
|
||||||
|
{
|
||||||
rc.expctxWay = pc.expctxWay;
|
rc.expctxWay = pc.expctxWay;
|
||||||
rc.expctxNode = pc.expctxNode;
|
rc.expctxNode = pc.expctxNode;
|
||||||
rc.readGlobalConfig();
|
rc.readGlobalConfig();
|
||||||
|
|
@ -75,10 +87,13 @@ public final class ProfileCache {
|
||||||
unusedSlot = -1;
|
unusedSlot = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (lru == null || lru.lastUseTime > pc.lastUseTime) {
|
if ( lru == null || lru.lastUseTime > pc.lastUseTime )
|
||||||
|
{
|
||||||
lru = pc;
|
lru = pc;
|
||||||
}
|
}
|
||||||
} else if (unusedSlot < 0) {
|
}
|
||||||
|
else if ( unusedSlot < 0 )
|
||||||
|
{
|
||||||
unusedSlot = i;
|
unusedSlot = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -91,27 +106,29 @@ public final class ProfileCache {
|
||||||
|
|
||||||
meta.readMetaData( new File( profileDir, "lookups.dat" ) );
|
meta.readMetaData( new File( profileDir, "lookups.dat" ) );
|
||||||
|
|
||||||
rc.expctxWay.parseFile(profileFile, "global", rc.keyValues);
|
rc.expctxWay.parseFile( profileFile, "global" );
|
||||||
rc.expctxNode.parseFile(profileFile, "global", rc.keyValues);
|
rc.expctxNode.parseFile( profileFile, "global" );
|
||||||
|
|
||||||
rc.readGlobalConfig();
|
rc.readGlobalConfig();
|
||||||
|
|
||||||
if (rc.processUnusedTags) {
|
if ( rc.processUnusedTags )
|
||||||
|
{
|
||||||
rc.expctxWay.setAllTagsUsed();
|
rc.expctxWay.setAllTagsUsed();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lru == null || unusedSlot >= 0) {
|
if ( lru == null || unusedSlot >= 0 )
|
||||||
|
{
|
||||||
lru = new ProfileCache();
|
lru = new ProfileCache();
|
||||||
if (unusedSlot >= 0) {
|
if ( unusedSlot >= 0 )
|
||||||
|
{
|
||||||
apc[unusedSlot] = lru;
|
apc[unusedSlot] = lru;
|
||||||
if (debug)
|
if ( debug ) System.out.println( "******* adding new profile at idx=" + unusedSlot + " for " + profileFile );
|
||||||
System.out.println("******* adding new profile at idx=" + unusedSlot + " for " + profileFile);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lru.lastProfileFile != null) {
|
if ( lru.lastProfileFile != null )
|
||||||
if (debug)
|
{
|
||||||
System.out.println("******* replacing profile of age " + ((System.currentTimeMillis() - lru.lastUseTime) / 1000L) + " sec " + lru.lastProfileFile + "->" + profileFile);
|
if ( debug ) System.out.println( "******* replacing profile of age " + ((System.currentTimeMillis()-lru.lastUseTime)/1000L) + " sec " + lru.lastProfileFile + "->" + profileFile );
|
||||||
}
|
}
|
||||||
|
|
||||||
lru.lastProfileTimestamp = rc.profileTimestamp;
|
lru.lastProfileTimestamp = rc.profileTimestamp;
|
||||||
|
|
@ -123,13 +140,17 @@ public final class ProfileCache {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized void releaseProfile(RoutingContext rc) {
|
public static synchronized void releaseProfile( RoutingContext rc )
|
||||||
for (int i = 0; i < apc.length; i++) {
|
{
|
||||||
|
for( int i=0; i<apc.length; i++)
|
||||||
|
{
|
||||||
ProfileCache pc = apc[i];
|
ProfileCache pc = apc[i];
|
||||||
|
|
||||||
if (pc != null) {
|
if ( pc != null )
|
||||||
|
{
|
||||||
// only the thread that holds the cached instance can release it
|
// only the thread that holds the cached instance can release it
|
||||||
if (rc.expctxWay == pc.expctxWay && rc.expctxNode == pc.expctxNode) {
|
if ( rc.expctxWay == pc.expctxWay && rc.expctxNode == pc.expctxNode )
|
||||||
|
{
|
||||||
pc.profilesBusy = false;
|
pc.profilesBusy = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
package btools.router;
|
package btools.router;
|
||||||
|
|
||||||
|
import java.io.DataOutput;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -14,21 +15,21 @@ import btools.expressions.BExpressionContext;
|
||||||
import btools.expressions.BExpressionContextNode;
|
import btools.expressions.BExpressionContextNode;
|
||||||
import btools.expressions.BExpressionContextWay;
|
import btools.expressions.BExpressionContextWay;
|
||||||
import btools.mapaccess.GeometryDecoder;
|
import btools.mapaccess.GeometryDecoder;
|
||||||
import btools.mapaccess.MatchedWaypoint;
|
|
||||||
import btools.mapaccess.OsmLink;
|
import btools.mapaccess.OsmLink;
|
||||||
import btools.mapaccess.OsmNode;
|
import btools.mapaccess.OsmNode;
|
||||||
import btools.util.CheapAngleMeter;
|
import btools.util.CheapAngleMeter;
|
||||||
import btools.util.CheapRuler;
|
import btools.util.CheapRuler;
|
||||||
|
|
||||||
public final class RoutingContext {
|
public final class RoutingContext
|
||||||
public void setAlternativeIdx(int idx) {
|
{
|
||||||
|
public void setAlternativeIdx(int idx )
|
||||||
|
{
|
||||||
alternativeIdx = idx;
|
alternativeIdx = idx;
|
||||||
}
|
}
|
||||||
|
public int getAlternativeIdx(int min, int max)
|
||||||
public int getAlternativeIdx(int min, int max) {
|
{
|
||||||
return alternativeIdx < min ? min : (alternativeIdx > max ? max : alternativeIdx);
|
return alternativeIdx < min ? min : (alternativeIdx > max ? max : alternativeIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int alternativeIdx = 0;
|
public int alternativeIdx = 0;
|
||||||
public String localFunction;
|
public String localFunction;
|
||||||
public long profileTimestamp;
|
public long profileTimestamp;
|
||||||
|
|
@ -37,7 +38,8 @@ public final class RoutingContext {
|
||||||
|
|
||||||
public String rawTrackPath;
|
public String rawTrackPath;
|
||||||
|
|
||||||
public String getProfileName() {
|
public String getProfileName()
|
||||||
|
{
|
||||||
String name = localFunction == null ? "unknown" : localFunction;
|
String name = localFunction == null ? "unknown" : localFunction;
|
||||||
if ( name.endsWith( ".brf" ) ) name = name.substring( 0, localFunction.length() - 4 );
|
if ( name.endsWith( ".brf" ) ) name = name.substring( 0, localFunction.length() - 4 );
|
||||||
int idx = name.lastIndexOf( File.separatorChar );
|
int idx = name.lastIndexOf( File.separatorChar );
|
||||||
|
|
@ -52,6 +54,10 @@ public final class RoutingContext {
|
||||||
|
|
||||||
public int memoryclass = 64;
|
public int memoryclass = 64;
|
||||||
|
|
||||||
|
public int downhillcostdiv;
|
||||||
|
public int downhillcutoff;
|
||||||
|
public int uphillcostdiv;
|
||||||
|
public int uphillcutoff;
|
||||||
public boolean carMode;
|
public boolean carMode;
|
||||||
public boolean bikeMode;
|
public boolean bikeMode;
|
||||||
public boolean footMode;
|
public boolean footMode;
|
||||||
|
|
@ -74,52 +80,75 @@ public final class RoutingContext {
|
||||||
public boolean transitonly;
|
public boolean transitonly;
|
||||||
|
|
||||||
public double waypointCatchingRange;
|
public double waypointCatchingRange;
|
||||||
public boolean correctMisplacedViaPoints;
|
|
||||||
public double correctMisplacedViaPointsDistance;
|
|
||||||
|
|
||||||
private void setModel(String className) {
|
private void setModel( String className )
|
||||||
if (className == null) {
|
{
|
||||||
|
if ( className == null )
|
||||||
|
{
|
||||||
pm = new StdModel();
|
pm = new StdModel();
|
||||||
} else {
|
}
|
||||||
try {
|
else
|
||||||
Class<?> clazz = Class.forName(className);
|
{
|
||||||
pm = (OsmPathModel) clazz.getDeclaredConstructor().newInstance();
|
try
|
||||||
} catch (Exception e) {
|
{
|
||||||
|
Class clazz = Class.forName( className );
|
||||||
|
pm = (OsmPathModel) clazz.newInstance();
|
||||||
|
}
|
||||||
|
catch( Exception e )
|
||||||
|
{
|
||||||
throw new RuntimeException( "Cannot create path-model: " + e );
|
throw new RuntimeException( "Cannot create path-model: " + e );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
initModel();
|
initModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initModel() {
|
public void initModel()
|
||||||
|
{
|
||||||
pm.init( expctxWay, expctxNode, keyValues );
|
pm.init( expctxWay, expctxNode, keyValues );
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getKeyValueChecksum() {
|
public long getKeyValueChecksum()
|
||||||
|
{
|
||||||
long s = 0L;
|
long s = 0L;
|
||||||
if (keyValues != null) {
|
if ( keyValues != null )
|
||||||
for (Map.Entry<String, String> e : keyValues.entrySet()) {
|
{
|
||||||
|
for( Map.Entry<String,String> e : keyValues.entrySet() )
|
||||||
|
{
|
||||||
s += e.getKey().hashCode() + e.getValue().hashCode();
|
s += e.getKey().hashCode() + e.getValue().hashCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readGlobalConfig() {
|
public void readGlobalConfig()
|
||||||
|
{
|
||||||
BExpressionContext expctxGlobal = expctxWay; // just one of them...
|
BExpressionContext expctxGlobal = expctxWay; // just one of them...
|
||||||
|
|
||||||
|
if (keyValues != null) {
|
||||||
|
// add parameter to context
|
||||||
|
for (Map.Entry<String, String> e : keyValues.entrySet()) {
|
||||||
|
float f = Float.parseFloat(e.getValue());
|
||||||
|
expctxWay.setVariableValue(e.getKey(), f, false );
|
||||||
|
expctxNode.setVariableValue(e.getKey(), f, false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setModel( expctxGlobal._modelClass );
|
setModel( expctxGlobal._modelClass );
|
||||||
|
|
||||||
|
downhillcostdiv = (int)expctxGlobal.getVariableValue( "downhillcost", 0.f );
|
||||||
|
downhillcutoff = (int)(expctxGlobal.getVariableValue( "downhillcutoff", 0.f )*10000);
|
||||||
|
uphillcostdiv = (int)expctxGlobal.getVariableValue( "uphillcost", 0.f );
|
||||||
|
uphillcutoff = (int)(expctxGlobal.getVariableValue( "uphillcutoff", 0.f )*10000);
|
||||||
|
if ( downhillcostdiv != 0 ) downhillcostdiv = 1000000/downhillcostdiv;
|
||||||
|
if ( uphillcostdiv != 0 ) uphillcostdiv = 1000000/uphillcostdiv;
|
||||||
carMode = 0.f != expctxGlobal.getVariableValue( "validForCars", 0.f );
|
carMode = 0.f != expctxGlobal.getVariableValue( "validForCars", 0.f );
|
||||||
bikeMode = 0.f != expctxGlobal.getVariableValue( "validForBikes", 0.f );
|
bikeMode = 0.f != expctxGlobal.getVariableValue( "validForBikes", 0.f );
|
||||||
footMode = 0.f != expctxGlobal.getVariableValue( "validForFoot", 0.f );
|
footMode = 0.f != expctxGlobal.getVariableValue( "validForFoot", 0.f );
|
||||||
|
|
||||||
waypointCatchingRange = expctxGlobal.getVariableValue( "waypointCatchingRange", 250.f );
|
waypointCatchingRange = expctxGlobal.getVariableValue( "waypointCatchingRange", 250.f );
|
||||||
|
|
||||||
// turn-restrictions not used per default for foot profiles
|
// turn-restrictions used per default for car profiles
|
||||||
considerTurnRestrictions = 0.f != expctxGlobal.getVariableValue("considerTurnRestrictions", footMode ? 0.f : 1.f);
|
considerTurnRestrictions = 0.f != expctxGlobal.getVariableValue( "considerTurnRestrictions", 1.f );
|
||||||
|
|
||||||
correctMisplacedViaPoints = 0.f != expctxGlobal.getVariableValue("correctMisplacedViaPoints", 1.f);
|
|
||||||
correctMisplacedViaPointsDistance = expctxGlobal.getVariableValue("correctMisplacedViaPointsDistance", 40.f);
|
|
||||||
|
|
||||||
// process tags not used in the profile (to have them in the data-tab)
|
// process tags not used in the profile (to have them in the data-tab)
|
||||||
processUnusedTags = 0.f != expctxGlobal.getVariableValue( "processUnusedTags", 0.f );
|
processUnusedTags = 0.f != expctxGlobal.getVariableValue( "processUnusedTags", 0.f );
|
||||||
|
|
@ -140,13 +169,21 @@ public final class RoutingContext {
|
||||||
starttimeoffset = expctxGlobal.getVariableValue( "starttimeoffset", 0.f );
|
starttimeoffset = expctxGlobal.getVariableValue( "starttimeoffset", 0.f );
|
||||||
transitonly = expctxGlobal.getVariableValue( "transitonly", 0.f ) != 0.f;
|
transitonly = expctxGlobal.getVariableValue( "transitonly", 0.f ) != 0.f;
|
||||||
|
|
||||||
|
farTrafficWeight = expctxGlobal.getVariableValue( "farTrafficWeight", 2.f );
|
||||||
|
nearTrafficWeight = expctxGlobal.getVariableValue( "nearTrafficWeight", 2.f );
|
||||||
|
farTrafficDecayLength = expctxGlobal.getVariableValue( "farTrafficDecayLength", 30000.f );
|
||||||
|
nearTrafficDecayLength = expctxGlobal.getVariableValue( "nearTrafficDecayLength", 3000.f );
|
||||||
|
trafficDirectionFactor = expctxGlobal.getVariableValue( "trafficDirectionFactor", 0.9f );
|
||||||
|
trafficSourceExponent = expctxGlobal.getVariableValue( "trafficSourceExponent", -0.7f );
|
||||||
|
trafficSourceMinDist = expctxGlobal.getVariableValue( "trafficSourceMinDist", 3000.f );
|
||||||
|
|
||||||
showspeed = 0.f != expctxGlobal.getVariableValue( "showspeed", 0.f );
|
showspeed = 0.f != expctxGlobal.getVariableValue( "showspeed", 0.f );
|
||||||
showSpeedProfile = 0.f != expctxGlobal.getVariableValue( "showSpeedProfile", 0.f );
|
showSpeedProfile = 0.f != expctxGlobal.getVariableValue( "showSpeedProfile", 0.f );
|
||||||
inverseRouting = 0.f != expctxGlobal.getVariableValue( "inverseRouting", 0.f );
|
inverseRouting = 0.f != expctxGlobal.getVariableValue( "inverseRouting", 0.f );
|
||||||
showTime = 0.f != expctxGlobal.getVariableValue("showtime", 0.f);
|
|
||||||
|
|
||||||
int tiMode = (int)expctxGlobal.getVariableValue( "turnInstructionMode", 0.f );
|
int tiMode = (int)expctxGlobal.getVariableValue( "turnInstructionMode", 0.f );
|
||||||
if (tiMode != 1) { // automatic selection from coordinate source
|
if ( tiMode != 1 ) // automatic selection from coordinate source
|
||||||
|
{
|
||||||
turnInstructionMode = tiMode;
|
turnInstructionMode = tiMode;
|
||||||
}
|
}
|
||||||
turnInstructionCatchingRange = expctxGlobal.getVariableValue( "turnInstructionCatchingRange", 40.f );
|
turnInstructionCatchingRange = expctxGlobal.getVariableValue( "turnInstructionCatchingRange", 40.f );
|
||||||
|
|
@ -190,15 +227,21 @@ public final class RoutingContext {
|
||||||
public int ilatshortest;
|
public int ilatshortest;
|
||||||
public int ilonshortest;
|
public int ilonshortest;
|
||||||
|
|
||||||
|
public boolean countTraffic;
|
||||||
public boolean inverseDirection;
|
public boolean inverseDirection;
|
||||||
|
public DataOutput trafficOutputStream;
|
||||||
|
|
||||||
|
public double farTrafficWeight;
|
||||||
|
public double nearTrafficWeight;
|
||||||
|
public double farTrafficDecayLength;
|
||||||
|
public double nearTrafficDecayLength;
|
||||||
|
public double trafficDirectionFactor;
|
||||||
|
public double trafficSourceExponent;
|
||||||
|
public double trafficSourceMinDist;
|
||||||
|
|
||||||
public boolean showspeed;
|
public boolean showspeed;
|
||||||
public boolean showSpeedProfile;
|
public boolean showSpeedProfile;
|
||||||
public boolean inverseRouting;
|
public boolean inverseRouting;
|
||||||
public boolean showTime;
|
|
||||||
|
|
||||||
public String outputFormat = "gpx";
|
|
||||||
public boolean exportWaypoints = false;
|
|
||||||
|
|
||||||
public OsmPrePath firstPrePath;
|
public OsmPrePath firstPrePath;
|
||||||
|
|
||||||
|
|
@ -213,19 +256,22 @@ public final class RoutingContext {
|
||||||
public double defaultC_r;
|
public double defaultC_r;
|
||||||
public double bikerPower;
|
public double bikerPower;
|
||||||
|
|
||||||
public static void prepareNogoPoints(List<OsmNodeNamed> nogos) {
|
public static void prepareNogoPoints( List<OsmNodeNamed> nogos )
|
||||||
for (OsmNodeNamed nogo : nogos) {
|
{
|
||||||
if (nogo instanceof OsmNogoPolygon) {
|
for( OsmNodeNamed nogo : nogos )
|
||||||
|
{
|
||||||
|
if (nogo instanceof OsmNogoPolygon)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String s = nogo.name;
|
String s = nogo.name;
|
||||||
int idx = s.indexOf( ' ' );
|
int idx = s.indexOf( ' ' );
|
||||||
if ( idx > 0 ) s = s.substring( 0 , idx );
|
if ( idx > 0 ) s = s.substring( 0 , idx );
|
||||||
int ir = 20; // default radius
|
int ir = 20; // default radius
|
||||||
if (s.length() > 4) {
|
if ( s.length() > 4 )
|
||||||
try {
|
{
|
||||||
ir = Integer.parseInt(s.substring(4));
|
try { ir = Integer.parseInt( s.substring( 4 ) ); }
|
||||||
} catch (Exception e) { /* ignore */ }
|
catch( Exception e ) { /* ignore */ }
|
||||||
}
|
}
|
||||||
// Radius of the nogo point in meters
|
// Radius of the nogo point in meters
|
||||||
nogo.radius = ir;
|
nogo.radius = ir;
|
||||||
|
|
@ -235,7 +281,8 @@ public final class RoutingContext {
|
||||||
/**
|
/**
|
||||||
* restore the full nogolist previously saved by cleanNogoList
|
* restore the full nogolist previously saved by cleanNogoList
|
||||||
*/
|
*/
|
||||||
public void restoreNogoList() {
|
public void restoreNogoList()
|
||||||
|
{
|
||||||
nogopoints = nogopoints_all;
|
nogopoints = nogopoints_all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -245,18 +292,22 @@ public final class RoutingContext {
|
||||||
*
|
*
|
||||||
* @return true if all wayoints are all in the same (full-weigth) nogo area (triggering bee-line-mode)
|
* @return true if all wayoints are all in the same (full-weigth) nogo area (triggering bee-line-mode)
|
||||||
*/
|
*/
|
||||||
public void cleanNogoList(List<OsmNode> waypoints) {
|
public void cleanNogoList( List<OsmNode> waypoints )
|
||||||
|
{
|
||||||
nogopoints_all = nogopoints;
|
nogopoints_all = nogopoints;
|
||||||
if ( nogopoints == null ) return;
|
if ( nogopoints == null ) return;
|
||||||
List<OsmNodeNamed> nogos = new ArrayList<>();
|
List<OsmNodeNamed> nogos = new ArrayList<OsmNodeNamed>();
|
||||||
for (OsmNodeNamed nogo : nogopoints) {
|
for( OsmNodeNamed nogo : nogopoints )
|
||||||
|
{
|
||||||
boolean goodGuy = true;
|
boolean goodGuy = true;
|
||||||
for (OsmNode wp : waypoints) {
|
for( OsmNode wp : waypoints )
|
||||||
|
{
|
||||||
if ( wp.calcDistance( nogo ) < nogo.radius
|
if ( wp.calcDistance( nogo ) < nogo.radius
|
||||||
&& (!(nogo instanceof OsmNogoPolygon)
|
&& (!(nogo instanceof OsmNogoPolygon)
|
||||||
|| (((OsmNogoPolygon)nogo).isClosed
|
|| (((OsmNogoPolygon)nogo).isClosed
|
||||||
? ((OsmNogoPolygon)nogo).isWithin(wp.ilon, wp.ilat)
|
? ((OsmNogoPolygon)nogo).isWithin(wp.ilon, wp.ilat)
|
||||||
: ((OsmNogoPolygon) nogo).isOnPolyline(wp.ilon, wp.ilat)))) {
|
: ((OsmNogoPolygon)nogo).isOnPolyline(wp.ilon, wp.ilat))))
|
||||||
|
{
|
||||||
goodGuy = false;
|
goodGuy = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -265,73 +316,22 @@ public final class RoutingContext {
|
||||||
nogopoints = nogos.isEmpty() ? null : nogos;
|
nogopoints = nogos.isEmpty() ? null : nogos;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkMatchedWaypointAgainstNogos(List<MatchedWaypoint> matchedWaypoints) {
|
public boolean allInOneNogo( List<OsmNode> waypoints )
|
||||||
if (nogopoints == null) return;
|
{
|
||||||
int theSize = matchedWaypoints.size();
|
|
||||||
if (theSize<2) return;
|
|
||||||
int removed = 0;
|
|
||||||
List<MatchedWaypoint> newMatchedWaypoints = new ArrayList<>();
|
|
||||||
MatchedWaypoint prevMwp = null;
|
|
||||||
boolean prevMwpIsInside = false;
|
|
||||||
for (int i = 0; i < theSize; i++) {
|
|
||||||
MatchedWaypoint mwp = matchedWaypoints.get(i);
|
|
||||||
boolean isInsideNogo = false;
|
|
||||||
OsmNode wp = mwp.crosspoint;
|
|
||||||
for (OsmNodeNamed nogo : nogopoints) {
|
|
||||||
if (Double.isNaN(nogo.nogoWeight)
|
|
||||||
&& wp.calcDistance(nogo) < nogo.radius
|
|
||||||
&& (!(nogo instanceof OsmNogoPolygon)
|
|
||||||
|| (((OsmNogoPolygon) nogo).isClosed
|
|
||||||
? ((OsmNogoPolygon) nogo).isWithin(wp.ilon, wp.ilat)
|
|
||||||
: ((OsmNogoPolygon) nogo).isOnPolyline(wp.ilon, wp.ilat)))) {
|
|
||||||
isInsideNogo = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isInsideNogo) {
|
|
||||||
boolean useAnyway = false;
|
|
||||||
if (prevMwp == null) useAnyway = true;
|
|
||||||
else if (mwp.direct) useAnyway = true;
|
|
||||||
else if (prevMwp.direct) useAnyway = true;
|
|
||||||
else if (prevMwpIsInside) useAnyway = true;
|
|
||||||
else if (i == theSize-1) {
|
|
||||||
throw new IllegalArgumentException("last wpt in restricted area ");
|
|
||||||
}
|
|
||||||
if (useAnyway) {
|
|
||||||
prevMwpIsInside = true;
|
|
||||||
newMatchedWaypoints.add(mwp);
|
|
||||||
} else {
|
|
||||||
removed++;
|
|
||||||
prevMwpIsInside = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
prevMwpIsInside = false;
|
|
||||||
newMatchedWaypoints.add(mwp);
|
|
||||||
}
|
|
||||||
prevMwp = mwp;
|
|
||||||
}
|
|
||||||
if (newMatchedWaypoints.size() < 2) {
|
|
||||||
throw new IllegalArgumentException("a wpt in restricted area ");
|
|
||||||
}
|
|
||||||
if (removed > 0) {
|
|
||||||
matchedWaypoints.clear();
|
|
||||||
matchedWaypoints.addAll(newMatchedWaypoints);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean allInOneNogo(List<OsmNode> waypoints) {
|
|
||||||
if ( nogopoints == null ) return false;
|
if ( nogopoints == null ) return false;
|
||||||
boolean allInTotal = false;
|
boolean allInTotal = false;
|
||||||
for (OsmNodeNamed nogo : nogopoints) {
|
for( OsmNodeNamed nogo : nogopoints )
|
||||||
|
{
|
||||||
boolean allIn = Double.isNaN( nogo.nogoWeight );
|
boolean allIn = Double.isNaN( nogo.nogoWeight );
|
||||||
for (OsmNode wp : waypoints) {
|
for( OsmNode wp : waypoints )
|
||||||
|
{
|
||||||
int dist = wp.calcDistance( nogo );
|
int dist = wp.calcDistance( nogo );
|
||||||
if ( dist < nogo.radius
|
if ( dist < nogo.radius
|
||||||
&& (!(nogo instanceof OsmNogoPolygon)
|
&& (!(nogo instanceof OsmNogoPolygon)
|
||||||
|| (((OsmNogoPolygon)nogo).isClosed
|
|| (((OsmNogoPolygon)nogo).isClosed
|
||||||
? ((OsmNogoPolygon)nogo).isWithin(wp.ilon, wp.ilat)
|
? ((OsmNogoPolygon)nogo).isWithin(wp.ilon, wp.ilat)
|
||||||
: ((OsmNogoPolygon) nogo).isOnPolyline(wp.ilon, wp.ilat)))) {
|
: ((OsmNogoPolygon)nogo).isOnPolyline(wp.ilon, wp.ilat))))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
allIn = false;
|
allIn = false;
|
||||||
|
|
@ -341,10 +341,12 @@ public final class RoutingContext {
|
||||||
return allInTotal;
|
return allInTotal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long[] getNogoChecksums() {
|
public long[] getNogoChecksums()
|
||||||
|
{
|
||||||
long[] cs = new long[3];
|
long[] cs = new long[3];
|
||||||
int n = nogopoints == null ? 0 : nogopoints.size();
|
int n = nogopoints == null ? 0 : nogopoints.size();
|
||||||
for (int i = 0; i < n; i++) {
|
for( int i=0; i<n; i++ )
|
||||||
|
{
|
||||||
OsmNodeNamed nogo = nogopoints.get(i);
|
OsmNodeNamed nogo = nogopoints.get(i);
|
||||||
cs[0] += nogo.ilon;
|
cs[0] += nogo.ilon;
|
||||||
cs[1] += nogo.ilat;
|
cs[1] += nogo.ilat;
|
||||||
|
|
@ -354,21 +356,25 @@ public final class RoutingContext {
|
||||||
return cs;
|
return cs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setWaypoint(OsmNodeNamed wp, boolean endpoint) {
|
public void setWaypoint( OsmNodeNamed wp, boolean endpoint )
|
||||||
|
{
|
||||||
setWaypoint( wp, null, endpoint );
|
setWaypoint( wp, null, endpoint );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setWaypoint(OsmNodeNamed wp, OsmNodeNamed pendingEndpoint, boolean endpoint) {
|
public void setWaypoint( OsmNodeNamed wp, OsmNodeNamed pendingEndpoint, boolean endpoint )
|
||||||
|
{
|
||||||
keepnogopoints = nogopoints;
|
keepnogopoints = nogopoints;
|
||||||
nogopoints = new ArrayList<>();
|
nogopoints = new ArrayList<OsmNodeNamed>();
|
||||||
nogopoints.add( wp );
|
nogopoints.add( wp );
|
||||||
if ( keepnogopoints != null ) nogopoints.addAll( keepnogopoints );
|
if ( keepnogopoints != null ) nogopoints.addAll( keepnogopoints );
|
||||||
isEndpoint = endpoint;
|
isEndpoint = endpoint;
|
||||||
this.pendingEndpoint = pendingEndpoint;
|
this.pendingEndpoint = pendingEndpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean checkPendingEndpoint() {
|
public boolean checkPendingEndpoint()
|
||||||
if (pendingEndpoint != null) {
|
{
|
||||||
|
if ( pendingEndpoint != null )
|
||||||
|
{
|
||||||
isEndpoint = true;
|
isEndpoint = true;
|
||||||
nogopoints.set( 0, pendingEndpoint );
|
nogopoints.set( 0, pendingEndpoint );
|
||||||
pendingEndpoint = null;
|
pendingEndpoint = null;
|
||||||
|
|
@ -377,13 +383,15 @@ public final class RoutingContext {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unsetWaypoint() {
|
public void unsetWaypoint()
|
||||||
|
{
|
||||||
nogopoints = keepnogopoints;
|
nogopoints = keepnogopoints;
|
||||||
pendingEndpoint = null;
|
pendingEndpoint = null;
|
||||||
isEndpoint = false;
|
isEndpoint = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int calcDistance(int lon1, int lat1, int lon2, int lat2) {
|
public int calcDistance( int lon1, int lat1, int lon2, int lat2 )
|
||||||
|
{
|
||||||
double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (lat1+lat2) >> 1 );
|
double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (lat1+lat2) >> 1 );
|
||||||
double dlon2m = lonlat2m[0];
|
double dlon2m = lonlat2m[0];
|
||||||
double dlat2m = lonlat2m[1];
|
double dlat2m = lonlat2m[1];
|
||||||
|
|
@ -393,8 +401,10 @@ public final class RoutingContext {
|
||||||
|
|
||||||
shortestmatch = false;
|
shortestmatch = false;
|
||||||
|
|
||||||
if (nogopoints != null && !nogopoints.isEmpty() && d > 0.) {
|
if ( nogopoints != null && !nogopoints.isEmpty() && d > 0. )
|
||||||
for (int ngidx = 0; ngidx < nogopoints.size(); ngidx++) {
|
{
|
||||||
|
for( int ngidx = 0; ngidx < nogopoints.size(); ngidx++ )
|
||||||
|
{
|
||||||
OsmNodeNamed nogo = nogopoints.get(ngidx);
|
OsmNodeNamed nogo = nogopoints.get(ngidx);
|
||||||
double x1 = (lon1 - nogo.ilon) * dlon2m;
|
double x1 = (lon1 - nogo.ilon) * dlon2m;
|
||||||
double y1 = (lat1 - nogo.ilat) * dlat2m;
|
double y1 = (lat1 - nogo.ilat) * dlat2m;
|
||||||
|
|
@ -404,20 +414,20 @@ public final class RoutingContext {
|
||||||
double r22 = x2*x2 + y2*y2;
|
double r22 = x2*x2 + y2*y2;
|
||||||
double radius = Math.abs( r12 < r22 ? y1*dx - x1*dy : y2*dx - x2*dy ) / d;
|
double radius = Math.abs( r12 < r22 ? y1*dx - x1*dy : y2*dx - x2*dy ) / d;
|
||||||
|
|
||||||
if (radius < nogo.radius) { // 20m
|
if ( radius < nogo.radius ) // 20m
|
||||||
|
{
|
||||||
double s1 = x1*dx + y1*dy;
|
double s1 = x1*dx + y1*dy;
|
||||||
double s2 = x2*dx + y2*dy;
|
double s2 = x2*dx + y2*dy;
|
||||||
|
|
||||||
|
|
||||||
if (s1 < 0.) {
|
if ( s1 < 0. ) { s1 = -s1; s2 = -s2; }
|
||||||
s1 = -s1;
|
if ( s2 > 0. )
|
||||||
s2 = -s2;
|
{
|
||||||
}
|
|
||||||
if (s2 > 0.) {
|
|
||||||
radius = Math.sqrt( s1 < s2 ? r12 : r22 );
|
radius = Math.sqrt( s1 < s2 ? r12 : r22 );
|
||||||
if ( radius > nogo.radius ) continue;
|
if ( radius > nogo.radius ) continue;
|
||||||
}
|
}
|
||||||
if (nogo.isNogo) {
|
if ( nogo.isNogo )
|
||||||
|
{
|
||||||
if (!(nogo instanceof OsmNogoPolygon)) { // nogo is a circle
|
if (!(nogo instanceof OsmNogoPolygon)) { // nogo is a circle
|
||||||
if (Double.isNaN(nogo.nogoWeight)) {
|
if (Double.isNaN(nogo.nogoWeight)) {
|
||||||
// default nogo behaviour (ignore completely)
|
// default nogo behaviour (ignore completely)
|
||||||
|
|
@ -426,7 +436,9 @@ public final class RoutingContext {
|
||||||
// nogo weight, compute distance within the circle
|
// nogo weight, compute distance within the circle
|
||||||
nogoCost = nogo.distanceWithinRadius(lon1, lat1, lon2, lat2, d) * nogo.nogoWeight;
|
nogoCost = nogo.distanceWithinRadius(lon1, lat1, lon2, lat2, d) * nogo.nogoWeight;
|
||||||
}
|
}
|
||||||
} else if (((OsmNogoPolygon) nogo).intersects(lon1, lat1, lon2, lat2)) {
|
}
|
||||||
|
else if (((OsmNogoPolygon)nogo).intersects(lon1, lat1, lon2, lat2))
|
||||||
|
{
|
||||||
// nogo is a polyline/polygon, we have to check there is indeed
|
// nogo is a polyline/polygon, we have to check there is indeed
|
||||||
// an intersection in this case (radius check is not enough).
|
// an intersection in this case (radius check is not enough).
|
||||||
if (Double.isNaN(nogo.nogoWeight)) {
|
if (Double.isNaN(nogo.nogoWeight)) {
|
||||||
|
|
@ -442,21 +454,28 @@ public final class RoutingContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
shortestmatch = true;
|
shortestmatch = true;
|
||||||
nogo.radius = radius; // shortest distance to way
|
nogo.radius = radius; // shortest distance to way
|
||||||
// calculate remaining distance
|
// calculate remaining distance
|
||||||
if (s2 < 0.) {
|
if ( s2 < 0. )
|
||||||
|
{
|
||||||
wayfraction = -s2 / (d*d);
|
wayfraction = -s2 / (d*d);
|
||||||
double xm = x2 - wayfraction*dx;
|
double xm = x2 - wayfraction*dx;
|
||||||
double ym = y2 - wayfraction*dy;
|
double ym = y2 - wayfraction*dy;
|
||||||
ilonshortest = (int)(xm / dlon2m + nogo.ilon);
|
ilonshortest = (int)(xm / dlon2m + nogo.ilon);
|
||||||
ilatshortest = (int)(ym / dlat2m + nogo.ilat);
|
ilatshortest = (int)(ym / dlat2m + nogo.ilat);
|
||||||
} else if (s1 > s2) {
|
}
|
||||||
|
else if ( s1 > s2 )
|
||||||
|
{
|
||||||
wayfraction = 0.;
|
wayfraction = 0.;
|
||||||
ilonshortest = lon2;
|
ilonshortest = lon2;
|
||||||
ilatshortest = lat2;
|
ilatshortest = lat2;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
wayfraction = 1.;
|
wayfraction = 1.;
|
||||||
ilonshortest = lon1;
|
ilonshortest = lon1;
|
||||||
ilatshortest = lat1;
|
ilatshortest = lat1;
|
||||||
|
|
@ -466,11 +485,14 @@ public final class RoutingContext {
|
||||||
// *after* the shortest distance point. In case of a shortest-match
|
// *after* the shortest distance point. In case of a shortest-match
|
||||||
// we use the reduced way segment for nogo-matching, in order not
|
// we use the reduced way segment for nogo-matching, in order not
|
||||||
// to cut our escape-way if we placed a nogo just in front of where we are
|
// to cut our escape-way if we placed a nogo just in front of where we are
|
||||||
if (isEndpoint) {
|
if ( isEndpoint )
|
||||||
|
{
|
||||||
wayfraction = 1. - wayfraction;
|
wayfraction = 1. - wayfraction;
|
||||||
lon2 = ilonshortest;
|
lon2 = ilonshortest;
|
||||||
lat2 = ilatshortest;
|
lat2 = ilatshortest;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
nogoCost = 0.;
|
nogoCost = 0.;
|
||||||
lon1 = ilonshortest;
|
lon1 = ilonshortest;
|
||||||
lat1 = ilatshortest;
|
lat1 = ilatshortest;
|
||||||
|
|
@ -482,26 +504,30 @@ public final class RoutingContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (int) Math.max(1.0, Math.round(d));
|
return (int)(d + 1.0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmPathModel pm;
|
public OsmPathModel pm;
|
||||||
|
|
||||||
public OsmPrePath createPrePath(OsmPath origin, OsmLink link) {
|
public OsmPrePath createPrePath( OsmPath origin, OsmLink link )
|
||||||
|
{
|
||||||
OsmPrePath p = pm.createPrePath();
|
OsmPrePath p = pm.createPrePath();
|
||||||
if (p != null) {
|
if ( p != null )
|
||||||
|
{
|
||||||
p.init( origin, link, this );
|
p.init( origin, link, this );
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmPath createPath(OsmLink link) {
|
public OsmPath createPath( OsmLink link )
|
||||||
|
{
|
||||||
OsmPath p = pm.createPath();
|
OsmPath p = pm.createPath();
|
||||||
p.init( link );
|
p.init( link );
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmPath createPath(OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode) {
|
public OsmPath createPath( OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode )
|
||||||
|
{
|
||||||
OsmPath p = pm.createPath();
|
OsmPath p = pm.createPath();
|
||||||
p.init( origin, link, refTrack, detailMode, this );
|
p.init( origin, link, refTrack, detailMode, this );
|
||||||
return p;
|
return p;
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -9,31 +9,39 @@ import java.io.File;
|
||||||
|
|
||||||
import btools.mapaccess.StorageConfigHelper;
|
import btools.mapaccess.StorageConfigHelper;
|
||||||
|
|
||||||
public final class RoutingHelper {
|
public final class RoutingHelper
|
||||||
public static File getAdditionalMaptoolDir(File segmentDir) {
|
{
|
||||||
|
public static File getAdditionalMaptoolDir( File segmentDir )
|
||||||
|
{
|
||||||
return StorageConfigHelper.getAdditionalMaptoolDir(segmentDir);
|
return StorageConfigHelper.getAdditionalMaptoolDir(segmentDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File getSecondarySegmentDir(File segmentDir) {
|
public static File getSecondarySegmentDir( File segmentDir )
|
||||||
|
{
|
||||||
return StorageConfigHelper.getSecondarySegmentDir(segmentDir);
|
return StorageConfigHelper.getSecondarySegmentDir(segmentDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static boolean hasDirectoryAnyDatafiles(File segmentDir) {
|
public static boolean hasDirectoryAnyDatafiles( File segmentDir )
|
||||||
if (hasAnyDatafiles(segmentDir)) {
|
{
|
||||||
|
if ( hasAnyDatafiles( segmentDir ) )
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// check secondary, too
|
// check secondary, too
|
||||||
File secondary = StorageConfigHelper.getSecondarySegmentDir( segmentDir );
|
File secondary = StorageConfigHelper.getSecondarySegmentDir( segmentDir );
|
||||||
if (secondary != null) {
|
if ( secondary != null )
|
||||||
|
{
|
||||||
return hasAnyDatafiles( secondary );
|
return hasAnyDatafiles( secondary );
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasAnyDatafiles(File dir) {
|
private static boolean hasAnyDatafiles( File dir )
|
||||||
|
{
|
||||||
String[] fileNames = dir.list();
|
String[] fileNames = dir.list();
|
||||||
for (String fileName : fileNames) {
|
for( String fileName : fileNames )
|
||||||
|
{
|
||||||
if ( fileName.endsWith( ".rd5" ) ) return true;
|
if ( fileName.endsWith( ".rd5" ) ) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
package btools.router;
|
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,7 +8,8 @@ package btools.router;
|
||||||
import btools.mapaccess.OsmNode;
|
import btools.mapaccess.OsmNode;
|
||||||
|
|
||||||
|
|
||||||
public final class SearchBoundary {
|
public final class SearchBoundary
|
||||||
|
{
|
||||||
|
|
||||||
private int minlon0;
|
private int minlon0;
|
||||||
private int minlat0;
|
private int minlat0;
|
||||||
|
|
@ -27,7 +28,8 @@ public final class SearchBoundary {
|
||||||
/**
|
/**
|
||||||
* @param radius Search radius in meters.
|
* @param radius Search radius in meters.
|
||||||
*/
|
*/
|
||||||
public SearchBoundary(OsmNode n, int radius, int direction) {
|
public SearchBoundary( OsmNode n, int radius, int direction )
|
||||||
|
{
|
||||||
this.radius = radius;
|
this.radius = radius;
|
||||||
this.direction = direction;
|
this.direction = direction;
|
||||||
|
|
||||||
|
|
@ -47,7 +49,8 @@ public final class SearchBoundary {
|
||||||
maxlat = lat + 6000000;
|
maxlat = lat + 6000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getFileName(OsmNode n) {
|
public static String getFileName( OsmNode n )
|
||||||
|
{
|
||||||
int lon = (n.ilon / 5000000 ) * 5000000;
|
int lon = (n.ilon / 5000000 ) * 5000000;
|
||||||
int lat = (n.ilat / 5000000 ) * 5000000;
|
int lat = (n.ilat / 5000000 ) * 5000000;
|
||||||
|
|
||||||
|
|
@ -59,28 +62,28 @@ public final class SearchBoundary {
|
||||||
return slon + "_" + slat + ".trf";
|
return slon + "_" + slat + ".trf";
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInBoundary(OsmNode n, int cost) {
|
public boolean isInBoundary( OsmNode n, int cost )
|
||||||
if (radius > 0) {
|
{
|
||||||
|
if ( radius > 0 )
|
||||||
|
{
|
||||||
return n.calcDistance( p ) < radius;
|
return n.calcDistance( p ) < radius;
|
||||||
}
|
}
|
||||||
if (cost == 0) {
|
if ( cost == 0 )
|
||||||
|
{
|
||||||
return n.ilon > minlon0 && n.ilon < maxlon0 && n.ilat > minlat0 && n.ilat < maxlat0;
|
return n.ilon > minlon0 && n.ilon < maxlon0 && n.ilat > minlat0 && n.ilat < maxlat0;
|
||||||
}
|
}
|
||||||
return n.ilon > minlon && n.ilon < maxlon && n.ilat > minlat && n.ilat < maxlat;
|
return n.ilon > minlon && n.ilon < maxlon && n.ilat > minlat && n.ilat < maxlat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getBoundaryDistance(OsmNode n) {
|
public int getBoundaryDistance( OsmNode n )
|
||||||
switch (direction) {
|
{
|
||||||
case 0:
|
switch( direction )
|
||||||
return n.calcDistance(new OsmNode(n.ilon, minlat));
|
{
|
||||||
case 1:
|
case 0: return n.calcDistance( new OsmNode( n.ilon, minlat ) );
|
||||||
return n.calcDistance(new OsmNode(minlon, n.ilat));
|
case 1: return n.calcDistance( new OsmNode( minlon, n.ilat ) );
|
||||||
case 2:
|
case 2: return n.calcDistance( new OsmNode( n.ilon, maxlat ) );
|
||||||
return n.calcDistance(new OsmNode(n.ilon, maxlat));
|
case 3: return n.calcDistance( new OsmNode( maxlon, n.ilat ) );
|
||||||
case 3:
|
default: throw new IllegalArgumentException( "undefined direction: "+ direction );
|
||||||
return n.calcDistance(new OsmNode(maxlon, n.ilat));
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("undefined direction: " + direction);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,15 @@ import btools.expressions.BExpressionContextNode;
|
||||||
import btools.expressions.BExpressionContextWay;
|
import btools.expressions.BExpressionContextWay;
|
||||||
|
|
||||||
|
|
||||||
final class StdModel extends OsmPathModel {
|
final class StdModel extends OsmPathModel
|
||||||
public OsmPrePath createPrePath() {
|
{
|
||||||
|
public OsmPrePath createPrePath()
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmPath createPath() {
|
public OsmPath createPath()
|
||||||
|
{
|
||||||
return new StdPath();
|
return new StdPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -26,7 +29,8 @@ final class StdModel extends OsmPathModel {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String, String> keyValues) {
|
public void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String,String> keyValues )
|
||||||
|
{
|
||||||
ctxWay = expctxWay;
|
ctxWay = expctxWay;
|
||||||
ctxNode = expctxNode;
|
ctxNode = expctxNode;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@
|
||||||
*/
|
*/
|
||||||
package btools.router;
|
package btools.router;
|
||||||
|
|
||||||
final class StdPath extends OsmPath {
|
import btools.util.FastMath;
|
||||||
|
|
||||||
|
final class StdPath extends OsmPath
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* The elevation-hysteresis-buffer (0-10 m)
|
* The elevation-hysteresis-buffer (0-10 m)
|
||||||
*/
|
*/
|
||||||
|
|
@ -16,14 +19,12 @@ final class StdPath extends OsmPath {
|
||||||
private float totalEnergy; // total route energy (Joule)
|
private float totalEnergy; // total route energy (Joule)
|
||||||
private float elevation_buffer; // just another elevation buffer (for travel time)
|
private float elevation_buffer; // just another elevation buffer (for travel time)
|
||||||
|
|
||||||
private int uphillcostdiv;
|
|
||||||
private int downhillcostdiv;
|
|
||||||
|
|
||||||
// Gravitational constant, g
|
// Gravitational constant, g
|
||||||
private static final double GRAVITY = 9.81; // in meters per second^(-2)
|
private static final double GRAVITY = 9.81; // in meters per second^(-2)
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(OsmPath orig) {
|
public void init( OsmPath orig )
|
||||||
|
{
|
||||||
StdPath origin = (StdPath)orig;
|
StdPath origin = (StdPath)orig;
|
||||||
this.ehbd = origin.ehbd;
|
this.ehbd = origin.ehbd;
|
||||||
this.ehbu = origin.ehbu;
|
this.ehbu = origin.ehbu;
|
||||||
|
|
@ -33,61 +34,32 @@ final class StdPath extends OsmPath {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void resetState() {
|
protected void resetState()
|
||||||
|
{
|
||||||
ehbd = 0;
|
ehbd = 0;
|
||||||
ehbu = 0;
|
ehbu = 0;
|
||||||
totalTime = 0.f;
|
totalTime = 0.f;
|
||||||
totalEnergy = 0.f;
|
totalEnergy = 0.f;
|
||||||
uphillcostdiv = 0;
|
|
||||||
downhillcostdiv = 0;
|
|
||||||
elevation_buffer = 0.f;
|
elevation_buffer = 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected double processWaySection(RoutingContext rc, double distance, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier) {
|
protected double processWaySection( RoutingContext rc, double distance, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier )
|
||||||
|
{
|
||||||
// calculate the costfactor inputs
|
// calculate the costfactor inputs
|
||||||
float turncostbase = rc.expctxWay.getTurncost();
|
float turncostbase = rc.expctxWay.getTurncost();
|
||||||
float uphillcutoff = rc.expctxWay.getUphillcutoff() * 10000;
|
|
||||||
float downhillcutoff = rc.expctxWay.getDownhillcutoff() * 10000;
|
|
||||||
float uphillmaxslope = rc.expctxWay.getUphillmaxslope() * 10000;
|
|
||||||
float downhillmaxslope = rc.expctxWay.getDownhillmaxslope() * 10000;
|
|
||||||
float cfup = rc.expctxWay.getUphillCostfactor();
|
float cfup = rc.expctxWay.getUphillCostfactor();
|
||||||
float cfdown = rc.expctxWay.getDownhillCostfactor();
|
float cfdown = rc.expctxWay.getDownhillCostfactor();
|
||||||
float cf = rc.expctxWay.getCostfactor();
|
float cf = rc.expctxWay.getCostfactor();
|
||||||
cfup = cfup == 0.f ? cf : cfup;
|
cfup = cfup == 0.f ? cf : cfup;
|
||||||
cfdown = cfdown == 0.f ? cf : cfdown;
|
cfdown = cfdown == 0.f ? cf : cfdown;
|
||||||
|
|
||||||
downhillcostdiv = (int) rc.expctxWay.getDownhillcost();
|
|
||||||
if (downhillcostdiv > 0) {
|
|
||||||
downhillcostdiv = 1000000 / downhillcostdiv;
|
|
||||||
}
|
|
||||||
|
|
||||||
int downhillmaxslopecostdiv = (int) rc.expctxWay.getDownhillmaxslopecost();
|
|
||||||
if (downhillmaxslopecostdiv > 0) {
|
|
||||||
downhillmaxslopecostdiv = 1000000 / downhillmaxslopecostdiv;
|
|
||||||
} else {
|
|
||||||
// if not given, use legacy behavior
|
|
||||||
downhillmaxslopecostdiv = downhillcostdiv;
|
|
||||||
}
|
|
||||||
|
|
||||||
uphillcostdiv = (int) rc.expctxWay.getUphillcost();
|
|
||||||
if (uphillcostdiv > 0) {
|
|
||||||
uphillcostdiv = 1000000 / uphillcostdiv;
|
|
||||||
}
|
|
||||||
|
|
||||||
int uphillmaxslopecostdiv = (int) rc.expctxWay.getUphillmaxslopecost();
|
|
||||||
if (uphillmaxslopecostdiv > 0) {
|
|
||||||
uphillmaxslopecostdiv = 1000000 / uphillmaxslopecostdiv;
|
|
||||||
} else {
|
|
||||||
// if not given, use legacy behavior
|
|
||||||
uphillmaxslopecostdiv = uphillcostdiv;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dist = (int)distance; // legacy arithmetics needs int
|
int dist = (int)distance; // legacy arithmetics needs int
|
||||||
|
|
||||||
// penalty for turning angle
|
// penalty for turning angle
|
||||||
int turncost = (int)((1.-cosangle) * turncostbase + 0.2 ); // e.g. turncost=90 -> 90 degree = 90m penalty
|
int turncost = (int)((1.-cosangle) * turncostbase + 0.2 ); // e.g. turncost=90 -> 90 degree = 90m penalty
|
||||||
if (message != null) {
|
if ( message != null )
|
||||||
|
{
|
||||||
message.linkturncost += turncost;
|
message.linkturncost += turncost;
|
||||||
message.turnangle = (float)angle;
|
message.turnangle = (float)angle;
|
||||||
}
|
}
|
||||||
|
|
@ -99,77 +71,80 @@ final class StdPath extends OsmPath {
|
||||||
// leads to an immediate penalty
|
// leads to an immediate penalty
|
||||||
|
|
||||||
int delta_h_micros = (int)(1000000. * delta_h);
|
int delta_h_micros = (int)(1000000. * delta_h);
|
||||||
ehbd += -delta_h_micros - dist * downhillcutoff;
|
ehbd += -delta_h_micros - dist * rc.downhillcutoff;
|
||||||
ehbu += delta_h_micros - dist * uphillcutoff;
|
ehbu += delta_h_micros - dist * rc.uphillcutoff;
|
||||||
|
|
||||||
float downweight = 0.f;
|
float downweight = 0.f;
|
||||||
if (ehbd > rc.elevationpenaltybuffer) {
|
if ( ehbd > rc.elevationpenaltybuffer )
|
||||||
|
{
|
||||||
downweight = 1.f;
|
downweight = 1.f;
|
||||||
|
|
||||||
int excess = ehbd - rc.elevationpenaltybuffer;
|
int excess = ehbd - rc.elevationpenaltybuffer;
|
||||||
int reduce = dist * rc.elevationbufferreduce;
|
int reduce = dist * rc.elevationbufferreduce;
|
||||||
if (reduce > excess) {
|
if ( reduce > excess )
|
||||||
|
{
|
||||||
downweight = ((float)excess)/reduce;
|
downweight = ((float)excess)/reduce;
|
||||||
reduce = excess;
|
reduce = excess;
|
||||||
}
|
}
|
||||||
excess = ehbd - rc.elevationmaxbuffer;
|
excess = ehbd - rc.elevationmaxbuffer;
|
||||||
if (reduce < excess) {
|
if ( reduce < excess )
|
||||||
|
{
|
||||||
reduce = excess;
|
reduce = excess;
|
||||||
}
|
}
|
||||||
ehbd -= reduce;
|
ehbd -= reduce;
|
||||||
float elevationCost = 0.f;
|
if ( rc.downhillcostdiv > 0 )
|
||||||
if (downhillcostdiv > 0) {
|
{
|
||||||
elevationCost += Math.min(reduce, dist * downhillmaxslope) / downhillcostdiv;
|
int elevationCost = reduce/rc.downhillcostdiv;
|
||||||
}
|
|
||||||
if (downhillmaxslopecostdiv > 0) {
|
|
||||||
elevationCost += Math.max(0, reduce - dist * downhillmaxslope) / downhillmaxslopecostdiv;
|
|
||||||
}
|
|
||||||
if (elevationCost > 0) {
|
|
||||||
sectionCost += elevationCost;
|
sectionCost += elevationCost;
|
||||||
if (message != null) {
|
if ( message != null )
|
||||||
|
{
|
||||||
message.linkelevationcost += elevationCost;
|
message.linkelevationcost += elevationCost;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (ehbd < 0) {
|
}
|
||||||
|
else if ( ehbd < 0 )
|
||||||
|
{
|
||||||
ehbd = 0;
|
ehbd = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
float upweight = 0.f;
|
float upweight = 0.f;
|
||||||
if (ehbu > rc.elevationpenaltybuffer) {
|
if ( ehbu > rc.elevationpenaltybuffer )
|
||||||
|
{
|
||||||
upweight = 1.f;
|
upweight = 1.f;
|
||||||
|
|
||||||
int excess = ehbu - rc.elevationpenaltybuffer;
|
int excess = ehbu - rc.elevationpenaltybuffer;
|
||||||
int reduce = dist * rc.elevationbufferreduce;
|
int reduce = dist * rc.elevationbufferreduce;
|
||||||
if (reduce > excess) {
|
if ( reduce > excess )
|
||||||
|
{
|
||||||
upweight = ((float)excess)/reduce;
|
upweight = ((float)excess)/reduce;
|
||||||
reduce = excess;
|
reduce = excess;
|
||||||
}
|
}
|
||||||
excess = ehbu - rc.elevationmaxbuffer;
|
excess = ehbu - rc.elevationmaxbuffer;
|
||||||
if (reduce < excess) {
|
if ( reduce < excess )
|
||||||
|
{
|
||||||
reduce = excess;
|
reduce = excess;
|
||||||
}
|
}
|
||||||
ehbu -= reduce;
|
ehbu -= reduce;
|
||||||
float elevationCost = 0.f;
|
if ( rc.uphillcostdiv > 0 )
|
||||||
if (uphillcostdiv > 0) {
|
{
|
||||||
elevationCost += Math.min(reduce, dist * uphillmaxslope) / uphillcostdiv;
|
int elevationCost = reduce/rc.uphillcostdiv;
|
||||||
}
|
|
||||||
if (uphillmaxslopecostdiv > 0) {
|
|
||||||
elevationCost += Math.max(0, reduce - dist * uphillmaxslope) / uphillmaxslopecostdiv;
|
|
||||||
}
|
|
||||||
if (elevationCost > 0) {
|
|
||||||
sectionCost += elevationCost;
|
sectionCost += elevationCost;
|
||||||
if (message != null) {
|
if ( message != null )
|
||||||
|
{
|
||||||
message.linkelevationcost += elevationCost;
|
message.linkelevationcost += elevationCost;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (ehbu < 0) {
|
}
|
||||||
|
else if ( ehbu < 0 )
|
||||||
|
{
|
||||||
ehbu = 0;
|
ehbu = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the effective costfactor (slope dependent)
|
// get the effective costfactor (slope dependent)
|
||||||
float costfactor = cfup*upweight + cf*(1.f - upweight - downweight) + cfdown*downweight;
|
float costfactor = cfup*upweight + cf*(1.f - upweight - downweight) + cfdown*downweight;
|
||||||
|
|
||||||
if (message != null) {
|
if ( message != null )
|
||||||
|
{
|
||||||
message.costfactor = costfactor;
|
message.costfactor = costfactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -179,16 +154,20 @@ final class StdPath extends OsmPath {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected double processTargetNode(RoutingContext rc) {
|
protected double processTargetNode( RoutingContext rc )
|
||||||
|
{
|
||||||
// finally add node-costs for target node
|
// finally add node-costs for target node
|
||||||
if (targetNode.nodeDescription != null) {
|
if ( targetNode.nodeDescription != null )
|
||||||
|
{
|
||||||
boolean nodeAccessGranted = rc.expctxWay.getNodeAccessGranted() != 0.;
|
boolean nodeAccessGranted = rc.expctxWay.getNodeAccessGranted() != 0.;
|
||||||
rc.expctxNode.evaluate( nodeAccessGranted , targetNode.nodeDescription );
|
rc.expctxNode.evaluate( nodeAccessGranted , targetNode.nodeDescription );
|
||||||
float initialcost = rc.expctxNode.getInitialcost();
|
float initialcost = rc.expctxNode.getInitialcost();
|
||||||
if (initialcost >= 1000000.) {
|
if ( initialcost >= 1000000. )
|
||||||
|
{
|
||||||
return -1.;
|
return -1.;
|
||||||
}
|
}
|
||||||
if (message != null) {
|
if ( message != null )
|
||||||
|
{
|
||||||
message.linknodecost += (int)initialcost;
|
message.linknodecost += (int)initialcost;
|
||||||
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription( nodeAccessGranted, targetNode.nodeDescription );
|
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription( nodeAccessGranted, targetNode.nodeDescription );
|
||||||
}
|
}
|
||||||
|
|
@ -198,37 +177,49 @@ final class StdPath extends OsmPath {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int elevationCorrection() {
|
public int elevationCorrection( RoutingContext rc )
|
||||||
return (downhillcostdiv > 0 ? ehbd / downhillcostdiv : 0)
|
{
|
||||||
+ (uphillcostdiv > 0 ? ehbu / uphillcostdiv : 0);
|
return ( rc.downhillcostdiv > 0 ? ehbd/rc.downhillcostdiv : 0 )
|
||||||
|
+ ( rc.uphillcostdiv > 0 ? ehbu/rc.uphillcostdiv : 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean definitlyWorseThan(OsmPath path) {
|
public boolean definitlyWorseThan( OsmPath path, RoutingContext rc )
|
||||||
|
{
|
||||||
StdPath p = (StdPath)path;
|
StdPath p = (StdPath)path;
|
||||||
|
|
||||||
int c = p.cost;
|
int c = p.cost;
|
||||||
if (p.downhillcostdiv > 0) {
|
if ( rc.downhillcostdiv > 0 )
|
||||||
int delta = p.ehbd / p.downhillcostdiv - (downhillcostdiv > 0 ? ehbd / downhillcostdiv : 0);
|
{
|
||||||
if (delta > 0) c += delta;
|
int delta = p.ehbd - ehbd;
|
||||||
|
if ( delta > 0 ) c += delta/rc.downhillcostdiv;
|
||||||
}
|
}
|
||||||
if (p.uphillcostdiv > 0) {
|
if ( rc.uphillcostdiv > 0 )
|
||||||
int delta = p.ehbu / p.uphillcostdiv - (uphillcostdiv > 0 ? ehbu / uphillcostdiv : 0);
|
{
|
||||||
if (delta > 0) c += delta;
|
int delta = p.ehbu - ehbu;
|
||||||
|
if ( delta > 0 ) c += delta/rc.uphillcostdiv;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cost > c;
|
return cost > c;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double calcIncline(double dist) {
|
private double calcIncline( double dist )
|
||||||
|
{
|
||||||
double min_delta = 3.;
|
double min_delta = 3.;
|
||||||
double shift = 0.;
|
double shift;
|
||||||
if (elevation_buffer > min_delta) {
|
if ( elevation_buffer > min_delta )
|
||||||
|
{
|
||||||
shift = -min_delta;
|
shift = -min_delta;
|
||||||
} else if (elevation_buffer < -min_delta) {
|
|
||||||
shift = min_delta;
|
|
||||||
}
|
}
|
||||||
double decayFactor = Math.exp(-dist / 100.);
|
else if ( elevation_buffer < min_delta )
|
||||||
|
{
|
||||||
|
shift = -min_delta;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0.;
|
||||||
|
}
|
||||||
|
double decayFactor = FastMath.exp( - dist / 100. );
|
||||||
float new_elevation_buffer = (float)( (elevation_buffer+shift) * decayFactor - shift);
|
float new_elevation_buffer = (float)( (elevation_buffer+shift) * decayFactor - shift);
|
||||||
double incline = ( elevation_buffer - new_elevation_buffer ) / dist;
|
double incline = ( elevation_buffer - new_elevation_buffer ) / dist;
|
||||||
elevation_buffer = new_elevation_buffer;
|
elevation_buffer = new_elevation_buffer;
|
||||||
|
|
@ -236,8 +227,10 @@ final class StdPath extends OsmPath {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void computeKinematic(RoutingContext rc, double dist, double delta_h, boolean detailMode) {
|
protected void computeKinematic( RoutingContext rc, double dist, double delta_h, boolean detailMode )
|
||||||
if (!detailMode) {
|
{
|
||||||
|
if ( !detailMode )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -245,41 +238,57 @@ final class StdPath extends OsmPath {
|
||||||
elevation_buffer += delta_h;
|
elevation_buffer += delta_h;
|
||||||
double incline = calcIncline( dist );
|
double incline = calcIncline( dist );
|
||||||
|
|
||||||
double maxSpeed = rc.maxSpeed;
|
double wayMaxspeed;
|
||||||
double speedLimit = rc.expctxWay.getMaxspeed() / 3.6f;
|
|
||||||
if (speedLimit > 0) {
|
|
||||||
maxSpeed = Math.min(maxSpeed, speedLimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
double speed = maxSpeed; // Travel speed
|
wayMaxspeed = rc.expctxWay.getMaxspeed() / 3.6f;
|
||||||
|
if (wayMaxspeed == 0)
|
||||||
|
{
|
||||||
|
wayMaxspeed = rc.maxSpeed;
|
||||||
|
}
|
||||||
|
wayMaxspeed = Math.min(wayMaxspeed,rc.maxSpeed);
|
||||||
|
|
||||||
|
double speed; // Travel speed
|
||||||
double f_roll = rc.totalMass * GRAVITY * ( rc.defaultC_r + incline );
|
double f_roll = rc.totalMass * GRAVITY * ( rc.defaultC_r + incline );
|
||||||
if (rc.footMode) {
|
if (rc.footMode || rc.expctxWay.getCostfactor() > 4.9 )
|
||||||
|
{
|
||||||
// Use Tobler's hiking function for walking sections
|
// Use Tobler's hiking function for walking sections
|
||||||
speed = rc.maxSpeed * Math.exp(-3.5 * Math.abs(incline + 0.05));
|
speed = rc.maxSpeed * 3.6;
|
||||||
} else if (rc.bikeMode) {
|
speed = (speed * FastMath.exp(-3.5 * Math.abs( incline + 0.05))) / 3.6;
|
||||||
|
}
|
||||||
|
else if (rc.bikeMode)
|
||||||
|
{
|
||||||
speed = solveCubic( rc.S_C_x, f_roll, rc.bikerPower );
|
speed = solveCubic( rc.S_C_x, f_roll, rc.bikerPower );
|
||||||
speed = Math.min(speed, maxSpeed);
|
speed = Math.min(speed, wayMaxspeed);
|
||||||
|
}
|
||||||
|
else // all other
|
||||||
|
{
|
||||||
|
speed = wayMaxspeed;
|
||||||
}
|
}
|
||||||
float dt = (float) ( dist / speed );
|
float dt = (float) ( dist / speed );
|
||||||
totalTime += dt;
|
totalTime += dt;
|
||||||
// Calc energy assuming biking (no good model yet for hiking)
|
// Calc energy assuming biking (no good model yet for hiking)
|
||||||
// (Count only positive, negative would mean breaking to enforce maxspeed)
|
// (Count only positive, negative would mean breaking to enforce maxspeed)
|
||||||
double energy = dist*(rc.S_C_x*speed*speed + f_roll);
|
double energy = dist*(rc.S_C_x*speed*speed + f_roll);
|
||||||
if (energy > 0.) {
|
if ( energy > 0. )
|
||||||
|
{
|
||||||
totalEnergy += energy;
|
totalEnergy += energy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double solveCubic(double a, double c, double d) {
|
private static double solveCubic( double a, double c, double d )
|
||||||
|
{
|
||||||
// Solves a * v^3 + c * v = d with a Newton method
|
// Solves a * v^3 + c * v = d with a Newton method
|
||||||
// to get the speed v for the section.
|
// to get the speed v for the section.
|
||||||
|
|
||||||
double v = 8.;
|
double v = 8.;
|
||||||
boolean findingStartvalue = true;
|
boolean findingStartvalue = true;
|
||||||
for (int i = 0; i < 10; i++) {
|
for ( int i = 0; i < 10; i++ )
|
||||||
|
{
|
||||||
double y = ( a * v * v + c ) * v - d;
|
double y = ( a * v * v + c ) * v - d;
|
||||||
if (y < .1) {
|
if ( y < .1 )
|
||||||
if (findingStartvalue) {
|
{
|
||||||
|
if ( findingStartvalue )
|
||||||
|
{
|
||||||
v *= 2.;
|
v *= 2.;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -293,12 +302,14 @@ final class StdPath extends OsmPath {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getTotalTime() {
|
public double getTotalTime()
|
||||||
|
{
|
||||||
return totalTime;
|
return totalTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getTotalEnergy() {
|
public double getTotalEnergy()
|
||||||
|
{
|
||||||
return totalEnergy;
|
return totalEnergy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class VoiceHint {
|
public class VoiceHint
|
||||||
|
{
|
||||||
static final int C = 1; // continue (go straight)
|
static final int C = 1; // continue (go straight)
|
||||||
static final int TL = 2; // turn left
|
static final int TL = 2; // turn left
|
||||||
static final int TSLL = 3; // turn slightly left
|
static final int TSLL = 3; // turn slightly left
|
||||||
|
|
@ -19,13 +20,11 @@ public class VoiceHint {
|
||||||
static final int TSHR = 7; // turn sharply right
|
static final int TSHR = 7; // turn sharply right
|
||||||
static final int KL = 8; // keep left
|
static final int KL = 8; // keep left
|
||||||
static final int KR = 9; // keep right
|
static final int KR = 9; // keep right
|
||||||
static final int TLU = 10; // U-turn
|
static final int TU = 10; // U-turn
|
||||||
static final int TRU = 11; // Right U-turn
|
static final int TRU = 11; // Right U-turn
|
||||||
static final int OFFR = 12; // Off route
|
static final int OFFR = 12; // Off route
|
||||||
static final int RNDB = 13; // Roundabout
|
static final int RNDB = 13; // Roundabout
|
||||||
static final int RNLB = 14; // Roundabout left
|
static final int RNLB = 14; // Roundabout left
|
||||||
static final int TU = 15; // 180 degree u-turn
|
|
||||||
static final int BL = 16; // Beeline routing
|
|
||||||
|
|
||||||
int ilon;
|
int ilon;
|
||||||
int ilat;
|
int ilat;
|
||||||
|
|
@ -37,450 +36,168 @@ public class VoiceHint {
|
||||||
double distanceToNext;
|
double distanceToNext;
|
||||||
int indexInTrack;
|
int indexInTrack;
|
||||||
|
|
||||||
public float getTime() {
|
public float getTime()
|
||||||
|
{
|
||||||
return oldWay == null ? 0.f : oldWay.time;
|
return oldWay == null ? 0.f : oldWay.time;
|
||||||
}
|
}
|
||||||
|
|
||||||
float angle = Float.MAX_VALUE;
|
float angle;
|
||||||
boolean turnAngleConsumed;
|
boolean turnAngleConsumed;
|
||||||
boolean needsRealTurn;
|
boolean needsRealTurn;
|
||||||
int maxBadPrio = -1;
|
|
||||||
|
|
||||||
int roundaboutExit;
|
int roundaboutExit;
|
||||||
|
|
||||||
boolean isRoundabout() {
|
boolean isRoundabout()
|
||||||
|
{
|
||||||
return roundaboutExit != 0;
|
return roundaboutExit != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addBadWay(MessageData badWay) {
|
public void addBadWay( MessageData badWay )
|
||||||
if (badWay == null) {
|
{
|
||||||
|
if ( badWay == null )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (badWays == null) {
|
if ( badWays == null )
|
||||||
badWays = new ArrayList<>();
|
{
|
||||||
|
badWays = new ArrayList<MessageData>();
|
||||||
}
|
}
|
||||||
badWays.add( badWay );
|
badWays.add( badWay );
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getJsonCommandIndex() {
|
public int getCommand()
|
||||||
switch (cmd) {
|
{
|
||||||
case TLU:
|
return cmd;
|
||||||
return 10;
|
|
||||||
case TU:
|
|
||||||
return 15;
|
|
||||||
case TSHL:
|
|
||||||
return 4;
|
|
||||||
case TL:
|
|
||||||
return 2;
|
|
||||||
case TSLL:
|
|
||||||
return 3;
|
|
||||||
case KL:
|
|
||||||
return 8;
|
|
||||||
case C:
|
|
||||||
return 1;
|
|
||||||
case KR:
|
|
||||||
return 9;
|
|
||||||
case TSLR:
|
|
||||||
return 6;
|
|
||||||
case TR:
|
|
||||||
return 5;
|
|
||||||
case TSHR:
|
|
||||||
return 7;
|
|
||||||
case TRU:
|
|
||||||
return 11;
|
|
||||||
case RNDB:
|
|
||||||
return 13;
|
|
||||||
case RNLB:
|
|
||||||
return 14;
|
|
||||||
case BL:
|
|
||||||
return 16;
|
|
||||||
case OFFR:
|
|
||||||
return 12;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("unknown command: " + cmd);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getExitNumber() {
|
public int getExitNumber()
|
||||||
|
{
|
||||||
return roundaboutExit;
|
return roundaboutExit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
public String getCommandString()
|
||||||
* used by comment style, osmand style
|
{
|
||||||
*/
|
switch ( cmd )
|
||||||
public String getCommandString() {
|
{
|
||||||
switch (cmd) {
|
case TU : return "TU";
|
||||||
case TLU:
|
case TSHL : return "TSHL";
|
||||||
return "TU"; // should be changed to TLU when osmand uses new voice hint constants
|
case TL : return "TL";
|
||||||
case TU:
|
case TSLL : return "TSLL";
|
||||||
return "TU";
|
case KL : return "KL";
|
||||||
case TSHL:
|
case C : return "C";
|
||||||
return "TSHL";
|
case KR : return "KR";
|
||||||
case TL:
|
case TSLR : return "TSLR";
|
||||||
return "TL";
|
case TR : return "TR";
|
||||||
case TSLL:
|
case TSHR : return "TSHR";
|
||||||
return "TSLL";
|
case TRU : return "TRU";
|
||||||
case KL:
|
case RNDB : return "RNDB" + roundaboutExit;
|
||||||
return "KL";
|
case RNLB : return "RNLB" + (-roundaboutExit);
|
||||||
case C:
|
default : throw new IllegalArgumentException( "unknown command: " + cmd );
|
||||||
return "C";
|
|
||||||
case KR:
|
|
||||||
return "KR";
|
|
||||||
case TSLR:
|
|
||||||
return "TSLR";
|
|
||||||
case TR:
|
|
||||||
return "TR";
|
|
||||||
case TSHR:
|
|
||||||
return "TSHR";
|
|
||||||
case TRU:
|
|
||||||
return "TRU";
|
|
||||||
case RNDB:
|
|
||||||
return "RNDB" + roundaboutExit;
|
|
||||||
case RNLB:
|
|
||||||
return "RNLB" + (-roundaboutExit);
|
|
||||||
case BL:
|
|
||||||
return "BL";
|
|
||||||
case OFFR:
|
|
||||||
return "OFFR";
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("unknown command: " + cmd);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
public String getSymbolString()
|
||||||
* used by trkpt/sym style
|
{
|
||||||
*/
|
switch ( cmd )
|
||||||
public String getCommandString(int c) {
|
{
|
||||||
switch (c) {
|
case TU : return "TU";
|
||||||
case TLU:
|
case TSHL : return "TSHL";
|
||||||
return "TLU";
|
case TL : return "Left";
|
||||||
case TU:
|
case TSLL : return "TSLL";
|
||||||
return "TU";
|
case KL : return "TSLL"; // ?
|
||||||
case TSHL:
|
case C : return "Straight";
|
||||||
return "TSHL";
|
case KR : return "TSLR"; // ?
|
||||||
case TL:
|
case TSLR : return "TSLR";
|
||||||
return "TL";
|
case TR : return "Right";
|
||||||
case TSLL:
|
case TSHR : return "TSHR";
|
||||||
return "TSLL";
|
case TRU : return "TU";
|
||||||
case KL:
|
case RNDB : return "RNDB" + roundaboutExit;
|
||||||
return "KL";
|
case RNLB : return "RNLB" + (-roundaboutExit);
|
||||||
case C:
|
default : throw new IllegalArgumentException( "unknown command: " + cmd );
|
||||||
return "C";
|
|
||||||
case KR:
|
|
||||||
return "KR";
|
|
||||||
case TSLR:
|
|
||||||
return "TSLR";
|
|
||||||
case TR:
|
|
||||||
return "TR";
|
|
||||||
case TSHR:
|
|
||||||
return "TSHR";
|
|
||||||
case TRU:
|
|
||||||
return "TRU";
|
|
||||||
case RNDB:
|
|
||||||
return "RNDB" + roundaboutExit;
|
|
||||||
case RNLB:
|
|
||||||
return "RNLB" + (-roundaboutExit);
|
|
||||||
case BL:
|
|
||||||
return "BL";
|
|
||||||
case OFFR:
|
|
||||||
return "OFFR";
|
|
||||||
default:
|
|
||||||
return "unknown command: " + c;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
public String getMessageString()
|
||||||
* used by gpsies style
|
{
|
||||||
*/
|
switch ( cmd )
|
||||||
public String getSymbolString() {
|
{
|
||||||
switch (cmd) {
|
case TU : return "u-turn";
|
||||||
case TLU:
|
case TSHL : return "sharp left";
|
||||||
return "TU";
|
case TL : return "left";
|
||||||
case TU:
|
case TSLL : return "slight left";
|
||||||
return "TU";
|
case KL : return "keep left";
|
||||||
case TSHL:
|
case C : return "straight";
|
||||||
return "TSHL";
|
case KR : return "keep right";
|
||||||
case TL:
|
case TSLR : return "slight right";
|
||||||
return "Left";
|
case TR : return "right";
|
||||||
case TSLL:
|
case TSHR : return "sharp right";
|
||||||
return "TSLL";
|
case TRU : return "u-turn";
|
||||||
case KL:
|
case RNDB : return "Take exit " + roundaboutExit;
|
||||||
return "TSLL"; // ?
|
case RNLB : return "Take exit " + (-roundaboutExit);
|
||||||
case C:
|
default : throw new IllegalArgumentException( "unknown command: " + cmd );
|
||||||
return "Straight";
|
|
||||||
case KR:
|
|
||||||
return "TSLR"; // ?
|
|
||||||
case TSLR:
|
|
||||||
return "TSLR";
|
|
||||||
case TR:
|
|
||||||
return "Right";
|
|
||||||
case TSHR:
|
|
||||||
return "TSHR";
|
|
||||||
case TRU:
|
|
||||||
return "TU";
|
|
||||||
case RNDB:
|
|
||||||
return "RNDB" + roundaboutExit;
|
|
||||||
case RNLB:
|
|
||||||
return "RNLB" + (-roundaboutExit);
|
|
||||||
case BL:
|
|
||||||
return "BL";
|
|
||||||
case OFFR:
|
|
||||||
return "OFFR";
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("unknown command: " + cmd);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
public int getLocusAction()
|
||||||
* used by new locus trkpt style
|
{
|
||||||
*/
|
switch ( cmd )
|
||||||
public String getLocusSymbolString() {
|
{
|
||||||
switch (cmd) {
|
case TU : return 13;
|
||||||
case TLU:
|
case TSHL : return 5;
|
||||||
return "u-turn_left";
|
case TL : return 4;
|
||||||
case TU:
|
case TSLL : return 3;
|
||||||
return "u-turn";
|
case KL : return 9; // ?
|
||||||
case TSHL:
|
case C : return 1;
|
||||||
return "left_sharp";
|
case KR : return 10; // ?
|
||||||
case TL:
|
case TSLR : return 6;
|
||||||
return "left";
|
case TR : return 7;
|
||||||
case TSLL:
|
case TSHR : return 8;
|
||||||
return "left_slight";
|
case TRU : return 14;
|
||||||
case KL:
|
case RNDB : return 26 + roundaboutExit;
|
||||||
return "stay_left"; // ?
|
case RNLB : return 26 - roundaboutExit;
|
||||||
case C:
|
default : throw new IllegalArgumentException( "unknown command: " + cmd );
|
||||||
return "straight";
|
|
||||||
case KR:
|
|
||||||
return "stay_right"; // ?
|
|
||||||
case TSLR:
|
|
||||||
return "right_slight";
|
|
||||||
case TR:
|
|
||||||
return "right";
|
|
||||||
case TSHR:
|
|
||||||
return "right_sharp";
|
|
||||||
case TRU:
|
|
||||||
return "u-turn_right";
|
|
||||||
case RNDB:
|
|
||||||
return "roundabout_e" + roundaboutExit;
|
|
||||||
case RNLB:
|
|
||||||
return "roundabout_e" + (-roundaboutExit);
|
|
||||||
case BL:
|
|
||||||
return "beeline";
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("unknown command: " + cmd);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
public int getOruxAction()
|
||||||
* used by osmand style
|
{
|
||||||
*/
|
switch ( cmd )
|
||||||
public String getMessageString() {
|
{
|
||||||
switch (cmd) {
|
case TU : return 1003;
|
||||||
case TLU:
|
case TSHL : return 1019;
|
||||||
return "u-turn"; // should be changed to u-turn-left when osmand uses new voice hint constants
|
case TL : return 1000;
|
||||||
case TU:
|
case TSLL : return 1017;
|
||||||
return "u-turn";
|
case KL : return 1015; // ?
|
||||||
case TSHL:
|
case C : return 1002;
|
||||||
return "sharp left";
|
case KR : return 1014; // ?
|
||||||
case TL:
|
case TSLR : return 1016;
|
||||||
return "left";
|
case TR : return 1001;
|
||||||
case TSLL:
|
case TSHR : return 1018;
|
||||||
return "slight left";
|
case TRU : return 1003;
|
||||||
case KL:
|
case RNDB : return 1008 + roundaboutExit;
|
||||||
return "keep left";
|
case RNLB : return 1008 + roundaboutExit;
|
||||||
case C:
|
default : throw new IllegalArgumentException( "unknown command: " + cmd );
|
||||||
return "straight";
|
|
||||||
case KR:
|
|
||||||
return "keep right";
|
|
||||||
case TSLR:
|
|
||||||
return "slight right";
|
|
||||||
case TR:
|
|
||||||
return "right";
|
|
||||||
case TSHR:
|
|
||||||
return "sharp right";
|
|
||||||
case TRU:
|
|
||||||
return "u-turn"; // should be changed to u-turn-right when osmand uses new voice hint constants
|
|
||||||
case RNDB:
|
|
||||||
return "Take exit " + roundaboutExit;
|
|
||||||
case RNLB:
|
|
||||||
return "Take exit " + (-roundaboutExit);
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("unknown command: " + cmd);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
public void calcCommand()
|
||||||
* used by old locus style
|
{
|
||||||
*/
|
|
||||||
public int getLocusAction() {
|
|
||||||
switch (cmd) {
|
|
||||||
case TLU:
|
|
||||||
return 13;
|
|
||||||
case TU:
|
|
||||||
return 12;
|
|
||||||
case TSHL:
|
|
||||||
return 5;
|
|
||||||
case TL:
|
|
||||||
return 4;
|
|
||||||
case TSLL:
|
|
||||||
return 3;
|
|
||||||
case KL:
|
|
||||||
return 9; // ?
|
|
||||||
case C:
|
|
||||||
return 1;
|
|
||||||
case KR:
|
|
||||||
return 10; // ?
|
|
||||||
case TSLR:
|
|
||||||
return 6;
|
|
||||||
case TR:
|
|
||||||
return 7;
|
|
||||||
case TSHR:
|
|
||||||
return 8;
|
|
||||||
case TRU:
|
|
||||||
return 14;
|
|
||||||
case RNDB:
|
|
||||||
return 26 + roundaboutExit;
|
|
||||||
case RNLB:
|
|
||||||
return 26 - roundaboutExit;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("unknown command: " + cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* used by orux style
|
|
||||||
*/
|
|
||||||
public int getOruxAction() {
|
|
||||||
switch (cmd) {
|
|
||||||
case TLU:
|
|
||||||
return 1003;
|
|
||||||
case TU:
|
|
||||||
return 1003;
|
|
||||||
case TSHL:
|
|
||||||
return 1019;
|
|
||||||
case TL:
|
|
||||||
return 1000;
|
|
||||||
case TSLL:
|
|
||||||
return 1017;
|
|
||||||
case KL:
|
|
||||||
return 1015; // ?
|
|
||||||
case C:
|
|
||||||
return 1002;
|
|
||||||
case KR:
|
|
||||||
return 1014; // ?
|
|
||||||
case TSLR:
|
|
||||||
return 1016;
|
|
||||||
case TR:
|
|
||||||
return 1001;
|
|
||||||
case TSHR:
|
|
||||||
return 1018;
|
|
||||||
case TRU:
|
|
||||||
return 1003;
|
|
||||||
case RNDB:
|
|
||||||
return 1008 + roundaboutExit;
|
|
||||||
case RNLB:
|
|
||||||
return 1008 + roundaboutExit;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("unknown command: " + cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* used by cruiser, equivalent to getCommandString() - osmand style - when osmand changes the voice hint constants
|
|
||||||
*/
|
|
||||||
public String getCruiserCommandString() {
|
|
||||||
switch (cmd) {
|
|
||||||
case TLU:
|
|
||||||
return "TLU";
|
|
||||||
case TU:
|
|
||||||
return "TU";
|
|
||||||
case TSHL:
|
|
||||||
return "TSHL";
|
|
||||||
case TL:
|
|
||||||
return "TL";
|
|
||||||
case TSLL:
|
|
||||||
return "TSLL";
|
|
||||||
case KL:
|
|
||||||
return "KL";
|
|
||||||
case C:
|
|
||||||
return "C";
|
|
||||||
case KR:
|
|
||||||
return "KR";
|
|
||||||
case TSLR:
|
|
||||||
return "TSLR";
|
|
||||||
case TR:
|
|
||||||
return "TR";
|
|
||||||
case TSHR:
|
|
||||||
return "TSHR";
|
|
||||||
case TRU:
|
|
||||||
return "TRU";
|
|
||||||
case RNDB:
|
|
||||||
return "RNDB" + roundaboutExit;
|
|
||||||
case RNLB:
|
|
||||||
return "RNLB" + (-roundaboutExit);
|
|
||||||
case BL:
|
|
||||||
return "BL";
|
|
||||||
case OFFR:
|
|
||||||
return "OFFR";
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("unknown command: " + cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* used by cruiser, equivalent to getMessageString() - osmand style - when osmand changes the voice hint constants
|
|
||||||
*/
|
|
||||||
public String getCruiserMessageString() {
|
|
||||||
switch (cmd) {
|
|
||||||
case TLU:
|
|
||||||
return "u-turn left";
|
|
||||||
case TU:
|
|
||||||
return "u-turn";
|
|
||||||
case TSHL:
|
|
||||||
return "sharp left";
|
|
||||||
case TL:
|
|
||||||
return "left";
|
|
||||||
case TSLL:
|
|
||||||
return "slight left";
|
|
||||||
case KL:
|
|
||||||
return "keep left";
|
|
||||||
case C:
|
|
||||||
return "straight";
|
|
||||||
case KR:
|
|
||||||
return "keep right";
|
|
||||||
case TSLR:
|
|
||||||
return "slight right";
|
|
||||||
case TR:
|
|
||||||
return "right";
|
|
||||||
case TSHR:
|
|
||||||
return "sharp right";
|
|
||||||
case TRU:
|
|
||||||
return "u-turn right";
|
|
||||||
case RNDB:
|
|
||||||
return "take exit " + roundaboutExit;
|
|
||||||
case RNLB:
|
|
||||||
return "take exit " + (-roundaboutExit);
|
|
||||||
case BL:
|
|
||||||
return "beeline";
|
|
||||||
case OFFR:
|
|
||||||
return "offroad";
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("unknown command: " + cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void calcCommand() {
|
|
||||||
float lowerBadWayAngle = -181;
|
float lowerBadWayAngle = -181;
|
||||||
float higherBadWayAngle = 181;
|
float higherBadWayAngle = 181;
|
||||||
if (badWays != null) {
|
if ( badWays != null )
|
||||||
for (MessageData badWay : badWays) {
|
{
|
||||||
if (badWay.isBadOneway()) {
|
for ( MessageData badWay : badWays )
|
||||||
|
{
|
||||||
|
if ( badWay.isBadOneway() )
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (lowerBadWayAngle < badWay.turnangle && badWay.turnangle < goodWay.turnangle) {
|
if ( lowerBadWayAngle < badWay.turnangle && badWay.turnangle < goodWay.turnangle )
|
||||||
|
{
|
||||||
lowerBadWayAngle = badWay.turnangle;
|
lowerBadWayAngle = badWay.turnangle;
|
||||||
}
|
}
|
||||||
if (higherBadWayAngle > badWay.turnangle && badWay.turnangle > goodWay.turnangle) {
|
if ( higherBadWayAngle > badWay.turnangle && badWay.turnangle > goodWay.turnangle )
|
||||||
|
{
|
||||||
higherBadWayAngle = badWay.turnangle;
|
higherBadWayAngle = badWay.turnangle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -489,105 +206,100 @@ public class VoiceHint {
|
||||||
float cmdAngle= angle;
|
float cmdAngle= angle;
|
||||||
|
|
||||||
// fall back to local angle if otherwise inconsistent
|
// fall back to local angle if otherwise inconsistent
|
||||||
//if ( lowerBadWayAngle > angle || higherBadWayAngle < angle )
|
if ( lowerBadWayAngle > angle || higherBadWayAngle < angle )
|
||||||
//{
|
{
|
||||||
//cmdAngle = goodWay.turnangle;
|
|
||||||
//}
|
|
||||||
if (angle == Float.MAX_VALUE) {
|
|
||||||
cmdAngle = goodWay.turnangle;
|
cmdAngle = goodWay.turnangle;
|
||||||
}
|
}
|
||||||
if (cmd == BL) return;
|
|
||||||
|
|
||||||
if (roundaboutExit > 0) {
|
if (roundaboutExit > 0)
|
||||||
|
{
|
||||||
cmd = RNDB;
|
cmd = RNDB;
|
||||||
} else if (roundaboutExit < 0) {
|
}
|
||||||
|
else if (roundaboutExit < 0)
|
||||||
|
{
|
||||||
cmd = RNLB;
|
cmd = RNLB;
|
||||||
} else if (is180DegAngle(cmdAngle) && cmdAngle <= -179.f && higherBadWayAngle == 181.f && lowerBadWayAngle == -181.f) {
|
}
|
||||||
|
else if ( cmdAngle < -159. )
|
||||||
|
{
|
||||||
cmd = TU;
|
cmd = TU;
|
||||||
} else if (cmdAngle < -159.f) {
|
}
|
||||||
cmd = TLU;
|
else if ( cmdAngle < -135. )
|
||||||
} else if (cmdAngle < -135.f) {
|
{
|
||||||
cmd = TSHL;
|
cmd = TSHL;
|
||||||
} else if (cmdAngle < -45.f) {
|
}
|
||||||
|
else if ( cmdAngle < -45. )
|
||||||
|
{
|
||||||
// a TL can be pushed in either direction by a close-by alternative
|
// a TL can be pushed in either direction by a close-by alternative
|
||||||
if (cmdAngle < -95.f && higherBadWayAngle < -30.f && lowerBadWayAngle < -180.f) {
|
if ( higherBadWayAngle > -90. && higherBadWayAngle < -15. && lowerBadWayAngle < -180. )
|
||||||
|
{
|
||||||
cmd = TSHL;
|
cmd = TSHL;
|
||||||
} else if (cmdAngle > -85.f && lowerBadWayAngle > -180.f && higherBadWayAngle > -10.f) {
|
}
|
||||||
|
else if ( lowerBadWayAngle > -180. && lowerBadWayAngle < -90. && higherBadWayAngle > 0. )
|
||||||
|
{
|
||||||
cmd = TSLL;
|
cmd = TSLL;
|
||||||
} else {
|
}
|
||||||
if (cmdAngle < -110.f) {
|
else
|
||||||
cmd = TSHL;
|
{
|
||||||
} else if (cmdAngle > -60.f) {
|
|
||||||
cmd = TSLL;
|
|
||||||
} else {
|
|
||||||
cmd = TL;
|
cmd = TL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (cmdAngle < -21.f) {
|
else if ( cmdAngle < -21. )
|
||||||
if (cmd != KR) { // don't overwrite KR with TSLL
|
{
|
||||||
|
if ( cmd != KR ) // don't overwrite KR with TSLL
|
||||||
|
{
|
||||||
cmd = TSLL;
|
cmd = TSLL;
|
||||||
}
|
}
|
||||||
} else if (cmdAngle < -5.f) {
|
}
|
||||||
if (lowerBadWayAngle < -100.f && higherBadWayAngle < 45.f) {
|
else if ( cmdAngle < 21. )
|
||||||
cmd = TSLL;
|
{
|
||||||
} else if (lowerBadWayAngle >= -100.f && higherBadWayAngle < 45.f) {
|
if ( cmd != KR && cmd != KL ) // don't overwrite KL/KR hints!
|
||||||
cmd = KL;
|
{
|
||||||
} else {
|
|
||||||
cmd = C;
|
cmd = C;
|
||||||
}
|
}
|
||||||
} else if (cmdAngle < 5.f) {
|
|
||||||
if (lowerBadWayAngle > -30.f) {
|
|
||||||
cmd = KR;
|
|
||||||
} else if (higherBadWayAngle < 30.f) {
|
|
||||||
cmd = KL;
|
|
||||||
} else {
|
|
||||||
cmd = C;
|
|
||||||
}
|
}
|
||||||
} else if (cmdAngle < 21.f) {
|
else if ( cmdAngle < 45. )
|
||||||
|
{
|
||||||
|
if ( cmd != KL ) // don't overwrite KL with TSLR
|
||||||
|
{
|
||||||
|
cmd = TSLR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( cmdAngle < 135. )
|
||||||
|
{
|
||||||
// a TR can be pushed in either direction by a close-by alternative
|
// a TR can be pushed in either direction by a close-by alternative
|
||||||
if (lowerBadWayAngle > -45.f && higherBadWayAngle > 100.f) {
|
if ( higherBadWayAngle > 90. && higherBadWayAngle < 180. && lowerBadWayAngle < 0. )
|
||||||
|
{
|
||||||
cmd = TSLR;
|
cmd = TSLR;
|
||||||
} else if (lowerBadWayAngle > -45.f && higherBadWayAngle <= 100.f) {
|
|
||||||
cmd = KR;
|
|
||||||
} else {
|
|
||||||
cmd = C;
|
|
||||||
}
|
}
|
||||||
} else if (cmdAngle < 45.f) {
|
else if ( lowerBadWayAngle > 15. && lowerBadWayAngle < 90. && higherBadWayAngle > 180. )
|
||||||
cmd = TSLR;
|
{
|
||||||
} else if (cmdAngle < 135.f) {
|
|
||||||
if (cmdAngle < 85.f && higherBadWayAngle < 180.f && lowerBadWayAngle < 10.f) {
|
|
||||||
cmd = TSLR;
|
|
||||||
} else if (cmdAngle > 95.f && lowerBadWayAngle > 30.f && higherBadWayAngle > 180.f) {
|
|
||||||
cmd = TSHR;
|
cmd = TSHR;
|
||||||
} else {
|
}
|
||||||
if (cmdAngle > 110.) {
|
else
|
||||||
cmd = TSHR;
|
{
|
||||||
} else if (cmdAngle < 60.) {
|
|
||||||
cmd = TSLR;
|
|
||||||
} else {
|
|
||||||
cmd = TR;
|
cmd = TR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (cmdAngle < 159.f) {
|
else if ( cmdAngle < 159. )
|
||||||
|
{
|
||||||
cmd = TSHR;
|
cmd = TSHR;
|
||||||
} else if (is180DegAngle(cmdAngle) && cmdAngle >= 179.f && higherBadWayAngle == 181.f && lowerBadWayAngle == -181.f) {
|
}
|
||||||
cmd = TU;
|
else
|
||||||
} else {
|
{
|
||||||
cmd = TRU;
|
cmd = TRU;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean is180DegAngle(float angle) {
|
public String formatGeometry()
|
||||||
return (Math.abs(angle) <= 180.f && Math.abs(angle) >= 179.f);
|
{
|
||||||
}
|
|
||||||
|
|
||||||
public String formatGeometry() {
|
|
||||||
float oldPrio = oldWay == null ? 0.f : oldWay.priorityclassifier;
|
float oldPrio = oldWay == null ? 0.f : oldWay.priorityclassifier;
|
||||||
StringBuilder sb = new StringBuilder(30);
|
StringBuilder sb = new StringBuilder(30);
|
||||||
sb.append( ' ' ).append( (int)oldPrio );
|
sb.append( ' ' ).append( (int)oldPrio );
|
||||||
appendTurnGeometry(sb,goodWay);
|
appendTurnGeometry(sb,goodWay);
|
||||||
if (badWays != null) {
|
if ( badWays != null )
|
||||||
for (MessageData badWay : badWays) {
|
{
|
||||||
|
for ( MessageData badWay : badWays )
|
||||||
|
{
|
||||||
sb.append( " " );
|
sb.append( " " );
|
||||||
appendTurnGeometry( sb, badWay );
|
appendTurnGeometry( sb, badWay );
|
||||||
}
|
}
|
||||||
|
|
@ -595,7 +307,8 @@ public class VoiceHint {
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendTurnGeometry(StringBuilder sb, MessageData msg) {
|
private void appendTurnGeometry( StringBuilder sb, MessageData msg )
|
||||||
|
{
|
||||||
sb.append( "(" ).append( (int)(msg.turnangle+0.5) ).append( ")" ).append( (int)(msg.priorityclassifier) );
|
sb.append( "(" ).append( (int)(msg.turnangle+0.5) ).append( ")" ).append( (int)(msg.priorityclassifier) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,51 +9,30 @@ package btools.router;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class VoiceHintList {
|
public class VoiceHintList
|
||||||
|
{
|
||||||
static final int TRANS_MODE_NONE = 0;
|
private String transportMode;
|
||||||
static final int TRANS_MODE_FOOT = 1;
|
|
||||||
static final int TRANS_MODE_BIKE = 2;
|
|
||||||
static final int TRANS_MODE_CAR = 3;
|
|
||||||
|
|
||||||
private int transportMode = TRANS_MODE_BIKE;
|
|
||||||
int turnInstructionMode;
|
int turnInstructionMode;
|
||||||
List<VoiceHint> list = new ArrayList<>();
|
ArrayList<VoiceHint> list = new ArrayList<VoiceHint>();
|
||||||
|
|
||||||
public void setTransportMode(boolean isCar, boolean isBike) {
|
public void setTransportMode( boolean isCar, boolean isBike )
|
||||||
transportMode = isCar ? TRANS_MODE_CAR : (isBike ? TRANS_MODE_BIKE : TRANS_MODE_FOOT);
|
{
|
||||||
|
transportMode = isCar ? "car" : ( isBike ? "bike" : "foot" );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTransportMode(int mode) {
|
public String getTransportMode()
|
||||||
transportMode = mode;
|
{
|
||||||
}
|
|
||||||
|
|
||||||
public String getTransportMode() {
|
|
||||||
String ret;
|
|
||||||
switch (transportMode) {
|
|
||||||
case TRANS_MODE_FOOT:
|
|
||||||
ret = "foot";
|
|
||||||
break;
|
|
||||||
case TRANS_MODE_CAR:
|
|
||||||
ret = "car";
|
|
||||||
break;
|
|
||||||
case TRANS_MODE_BIKE:
|
|
||||||
default:
|
|
||||||
ret = "bike";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int transportMode() {
|
|
||||||
return transportMode;
|
return transportMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLocusRouteType() {
|
public int getLocusRouteType()
|
||||||
if (transportMode == TRANS_MODE_CAR) {
|
{
|
||||||
|
if ( "car".equals( transportMode ) )
|
||||||
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (transportMode == TRANS_MODE_BIKE) {
|
if ( "bike".equals( transportMode ) )
|
||||||
|
{
|
||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
return 3; // foot
|
return 3; // foot
|
||||||
|
|
|
||||||
|
|
@ -8,27 +8,26 @@ package btools.router;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public final class VoiceHintProcessor {
|
public final class VoiceHintProcessor
|
||||||
|
{
|
||||||
double SIGNIFICANT_ANGLE = 22.5;
|
private double catchingRange; // range to catch angles and merge turns
|
||||||
double INTERNAL_CATCHING_RANGE = 2.;
|
|
||||||
|
|
||||||
// private double catchingRange; // range to catch angles and merge turns
|
|
||||||
private boolean explicitRoundabouts;
|
private boolean explicitRoundabouts;
|
||||||
private int transportMode;
|
|
||||||
|
|
||||||
public VoiceHintProcessor(double catchingRange, boolean explicitRoundabouts, int transportMode) {
|
public VoiceHintProcessor( double catchingRange, boolean explicitRoundabouts )
|
||||||
// this.catchingRange = catchingRange;
|
{
|
||||||
|
this.catchingRange = catchingRange;
|
||||||
this.explicitRoundabouts = explicitRoundabouts;
|
this.explicitRoundabouts = explicitRoundabouts;
|
||||||
this.transportMode = transportMode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private float sumNonConsumedWithinCatchingRange(List<VoiceHint> inputs, int offset) {
|
private float sumNonConsumedWithinCatchingRange( List<VoiceHint> inputs, int offset )
|
||||||
|
{
|
||||||
double distance = 0.;
|
double distance = 0.;
|
||||||
float angle = 0.f;
|
float angle = 0.f;
|
||||||
while (offset >= 0 && distance < INTERNAL_CATCHING_RANGE) {
|
while( offset >= 0 && distance < catchingRange )
|
||||||
|
{
|
||||||
VoiceHint input = inputs.get( offset-- );
|
VoiceHint input = inputs.get( offset-- );
|
||||||
if (input.turnAngleConsumed) {
|
if ( input.turnAngleConsumed )
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
angle += input.goodWay.turnangle;
|
angle += input.goodWay.turnangle;
|
||||||
|
|
@ -45,10 +44,10 @@ public final class VoiceHintProcessor {
|
||||||
* order (from target to start), but output is
|
* order (from target to start), but output is
|
||||||
* returned in travel-direction and only for
|
* returned in travel-direction and only for
|
||||||
* those nodes that trigger a voice hint.
|
* those nodes that trigger a voice hint.
|
||||||
* <p>
|
*
|
||||||
* Input objects are expected for every segment
|
* Input objects are expected for every segment
|
||||||
* of the track, also for those without a junction
|
* of the track, also for those without a junction
|
||||||
* <p>
|
*
|
||||||
* VoiceHint objects in the output list are enriched
|
* VoiceHint objects in the output list are enriched
|
||||||
* by the voice-command, the total angle and the distance
|
* by the voice-command, the total angle and the distance
|
||||||
* to the next hint
|
* to the next hint
|
||||||
|
|
@ -56,21 +55,18 @@ public final class VoiceHintProcessor {
|
||||||
* @param inputs tracknodes, un reverse order
|
* @param inputs tracknodes, un reverse order
|
||||||
* @return voice hints, in forward order
|
* @return voice hints, in forward order
|
||||||
*/
|
*/
|
||||||
public List<VoiceHint> process(List<VoiceHint> inputs) {
|
public List<VoiceHint> process( List<VoiceHint> inputs )
|
||||||
List<VoiceHint> results = new ArrayList<>();
|
{
|
||||||
|
List<VoiceHint> results = new ArrayList<VoiceHint>();
|
||||||
double distance = 0.;
|
double distance = 0.;
|
||||||
float roundAboutTurnAngle = 0.f; // sums up angles in roundabout
|
float roundAboutTurnAngle = 0.f; // sums up angles in roundabout
|
||||||
|
|
||||||
int roundaboutExit = 0;
|
int roundaboutExit = 0;
|
||||||
int roundaboudStartIdx = -1;
|
|
||||||
|
|
||||||
for (int hintIdx = 0; hintIdx < inputs.size(); hintIdx++) {
|
for ( int hintIdx = 0; hintIdx < inputs.size(); hintIdx++ )
|
||||||
|
{
|
||||||
VoiceHint input = inputs.get( hintIdx );
|
VoiceHint input = inputs.get( hintIdx );
|
||||||
|
|
||||||
if (input.cmd == VoiceHint.BL) {
|
|
||||||
results.add(input);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
float turnAngle = input.goodWay.turnangle;
|
float turnAngle = input.goodWay.turnangle;
|
||||||
distance += input.goodWay.linkdist;
|
distance += input.goodWay.linkdist;
|
||||||
int currentPrio = input.goodWay.getPrio();
|
int currentPrio = input.goodWay.getPrio();
|
||||||
|
|
@ -78,69 +74,37 @@ public final class VoiceHintProcessor {
|
||||||
int minPrio = Math.min( oldPrio, currentPrio );
|
int minPrio = Math.min( oldPrio, currentPrio );
|
||||||
|
|
||||||
boolean isLink2Highway = input.oldWay.isLinktType() && !input.goodWay.isLinktType();
|
boolean isLink2Highway = input.oldWay.isLinktType() && !input.goodWay.isLinktType();
|
||||||
boolean isHighway2Link = !input.oldWay.isLinktType() && input.goodWay.isLinktType();
|
|
||||||
|
|
||||||
if (explicitRoundabouts && input.oldWay.isRoundabout()) {
|
if ( input.oldWay.isRoundabout() )
|
||||||
if (roundaboudStartIdx == -1) roundaboudStartIdx = hintIdx;
|
{
|
||||||
roundAboutTurnAngle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
|
roundAboutTurnAngle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
|
||||||
if (roundaboudStartIdx == hintIdx) {
|
|
||||||
if (input.badWays != null) {
|
|
||||||
// remove goodWay
|
|
||||||
roundAboutTurnAngle -= input.goodWay.turnangle;
|
|
||||||
// add a badWay
|
|
||||||
for (MessageData badWay : input.badWays) {
|
|
||||||
if (!badWay.isBadOneway()) roundAboutTurnAngle += badWay.turnangle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
boolean isExit = roundaboutExit == 0; // exit point is always exit
|
boolean isExit = roundaboutExit == 0; // exit point is always exit
|
||||||
if (input.badWays != null) {
|
if ( input.badWays != null )
|
||||||
for (MessageData badWay : input.badWays) {
|
{
|
||||||
if (!badWay.isBadOneway() &&
|
for ( MessageData badWay : input.badWays )
|
||||||
badWay.isGoodForCars()) {
|
{
|
||||||
|
if ( !badWay.isBadOneway() && badWay.isGoodForCars() && Math.abs( badWay.turnangle ) < 120. )
|
||||||
|
{
|
||||||
isExit = true;
|
isExit = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isExit) {
|
if ( isExit )
|
||||||
|
{
|
||||||
roundaboutExit++;
|
roundaboutExit++;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (roundaboutExit > 0) {
|
if ( roundaboutExit > 0 )
|
||||||
//roundAboutTurnAngle += sumNonConsumedWithinCatchingRange(inputs, hintIdx);
|
{
|
||||||
//double startTurn = (roundaboudStartIdx != -1 ? inputs.get(roundaboudStartIdx + 1).goodWay.turnangle : turnAngle);
|
roundAboutTurnAngle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
|
||||||
input.angle = roundAboutTurnAngle;
|
input.angle = roundAboutTurnAngle;
|
||||||
input.goodWay.turnangle = roundAboutTurnAngle;
|
|
||||||
input.distanceToNext = distance;
|
input.distanceToNext = distance;
|
||||||
//input.roundaboutExit = startTurn < 0 ? roundaboutExit : -roundaboutExit;
|
input.roundaboutExit = turnAngle < 0 ? -roundaboutExit : roundaboutExit;
|
||||||
input.roundaboutExit = roundAboutTurnAngle < 0 ? roundaboutExit : -roundaboutExit;
|
|
||||||
float tmpangle = 0;
|
|
||||||
VoiceHint tmpRndAbt = new VoiceHint();
|
|
||||||
tmpRndAbt.badWays = new ArrayList<>();
|
|
||||||
for (int i = hintIdx-1; i > roundaboudStartIdx; i--) {
|
|
||||||
VoiceHint vh = inputs.get(i);
|
|
||||||
tmpangle += inputs.get(i).goodWay.turnangle;
|
|
||||||
if (vh.badWays != null) {
|
|
||||||
for (MessageData badWay : vh.badWays) {
|
|
||||||
if (!badWay.isBadOneway()) {
|
|
||||||
MessageData md = new MessageData();
|
|
||||||
md.linkdist = vh.goodWay.linkdist;
|
|
||||||
md.priorityclassifier = vh.goodWay.priorityclassifier;
|
|
||||||
md.turnangle = tmpangle;
|
|
||||||
tmpRndAbt.badWays.add(md);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
distance = 0.;
|
distance = 0.;
|
||||||
|
|
||||||
input.badWays = tmpRndAbt.badWays;
|
|
||||||
|
|
||||||
results.add( input );
|
results.add( input );
|
||||||
roundAboutTurnAngle = 0.f;
|
roundAboutTurnAngle = 0.f;
|
||||||
roundaboutExit = 0;
|
roundaboutExit = 0;
|
||||||
roundaboudStartIdx = -1;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int maxPrioAll = -1; // max prio of all detours
|
int maxPrioAll = -1; // max prio of all detours
|
||||||
|
|
@ -150,265 +114,134 @@ public final class VoiceHintProcessor {
|
||||||
float minAngle = 180.f;
|
float minAngle = 180.f;
|
||||||
float minAbsAngeRaw = 180.f;
|
float minAbsAngeRaw = 180.f;
|
||||||
|
|
||||||
boolean isBadwayLink = false;
|
if ( input.badWays != null )
|
||||||
|
{
|
||||||
if (input.badWays != null) {
|
for ( MessageData badWay : input.badWays )
|
||||||
for (MessageData badWay : input.badWays) {
|
{
|
||||||
int badPrio = badWay.getPrio();
|
int badPrio = badWay.getPrio();
|
||||||
float badTurn = badWay.turnangle;
|
float badTurn = badWay.turnangle;
|
||||||
if (badWay.isLinktType()) {
|
|
||||||
isBadwayLink = true;
|
|
||||||
}
|
|
||||||
boolean isBadHighway2Link = !input.oldWay.isLinktType() && badWay.isLinktType();
|
|
||||||
|
|
||||||
if (badPrio > maxPrioAll && !isBadHighway2Link) {
|
boolean isHighway2Link = !input.oldWay.isLinktType() && badWay.isLinktType();
|
||||||
|
|
||||||
|
if ( badPrio > maxPrioAll && !isHighway2Link )
|
||||||
|
{
|
||||||
maxPrioAll = badPrio;
|
maxPrioAll = badPrio;
|
||||||
input.maxBadPrio = Math.max(input.maxBadPrio, badPrio);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (badPrio < minPrio) {
|
if ( badWay.costfactor < 20.f && Math.abs( badTurn ) < minAbsAngeRaw )
|
||||||
continue; // ignore low prio ways
|
{
|
||||||
}
|
|
||||||
|
|
||||||
if (badWay.isBadOneway()) {
|
|
||||||
continue; // ignore wrong oneways
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Math.abs(badTurn) - Math.abs(turnAngle) > 80.f) {
|
|
||||||
continue; // ways from the back should not trigger a slight turn
|
|
||||||
}
|
|
||||||
|
|
||||||
if (badWay.costfactor < 20.f && Math.abs(badTurn) < minAbsAngeRaw) {
|
|
||||||
minAbsAngeRaw = Math.abs( badTurn );
|
minAbsAngeRaw = Math.abs( badTurn );
|
||||||
}
|
}
|
||||||
|
|
||||||
if (badPrio > maxPrioCandidates) {
|
if ( badPrio < minPrio )
|
||||||
maxPrioCandidates = badPrio;
|
{
|
||||||
input.maxBadPrio = Math.max(input.maxBadPrio, badPrio);
|
continue; // ignore low prio ways
|
||||||
}
|
}
|
||||||
if (badTurn > maxAngle) {
|
|
||||||
|
if ( badWay.isBadOneway() )
|
||||||
|
{
|
||||||
|
continue; // ignore wrong oneways
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( Math.abs( badTurn ) - Math.abs( turnAngle ) > 80.f )
|
||||||
|
{
|
||||||
|
continue; // ways from the back should not trigger a slight turn
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( badPrio > maxPrioCandidates )
|
||||||
|
{
|
||||||
|
maxPrioCandidates = badPrio;
|
||||||
|
}
|
||||||
|
if ( badTurn > maxAngle )
|
||||||
|
{
|
||||||
maxAngle = badTurn;
|
maxAngle = badTurn;
|
||||||
}
|
}
|
||||||
if (badTurn < minAngle) {
|
if ( badTurn < minAngle )
|
||||||
|
{
|
||||||
minAngle = badTurn;
|
minAngle = badTurn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// boolean hasSomethingMoreStraight = (Math.abs(turnAngle) - minAbsAngeRaw) > 20.;
|
boolean hasSomethingMoreStraight = Math.abs( turnAngle ) - minAbsAngeRaw > 20.;
|
||||||
boolean hasSomethingMoreStraight = (Math.abs(turnAngle - minAbsAngeRaw)) > 20. && input.badWays != null; // && !ignoreBadway;
|
|
||||||
|
|
||||||
// unconditional triggers are all junctions with
|
// unconditional triggers are all junctions with
|
||||||
// - higher detour prios than the minimum route prio (except link->highway junctions)
|
// - higher detour prios than the minimum route prio (except link->highway junctions)
|
||||||
// - or candidate detours with higher prio then the route exit leg
|
// - or candidate detours with higher prio then the route exit leg
|
||||||
boolean unconditionalTrigger = hasSomethingMoreStraight ||
|
boolean unconditionalTrigger = hasSomethingMoreStraight || ( maxPrioAll > minPrio && !isLink2Highway ) || ( maxPrioCandidates > currentPrio );
|
||||||
(maxPrioAll > minPrio && !isLink2Highway) ||
|
|
||||||
(maxPrioCandidates > currentPrio) ||
|
|
||||||
VoiceHint.is180DegAngle(turnAngle) ||
|
|
||||||
(!isHighway2Link && isBadwayLink && Math.abs(turnAngle) > 5.f) ||
|
|
||||||
(isHighway2Link && !isBadwayLink && Math.abs(turnAngle) < 5.f);
|
|
||||||
|
|
||||||
// conditional triggers (=real turning angle required) are junctions
|
// conditional triggers (=real turning angle required) are junctions
|
||||||
// with candidate detours equal in priority than the route exit leg
|
// with candidate detours equal in priority than the route exit leg
|
||||||
boolean conditionalTrigger = maxPrioCandidates >= minPrio;
|
boolean conditionalTrigger = maxPrioCandidates >= minPrio;
|
||||||
|
|
||||||
if (unconditionalTrigger || conditionalTrigger) {
|
if ( unconditionalTrigger || conditionalTrigger )
|
||||||
|
{
|
||||||
input.angle = turnAngle;
|
input.angle = turnAngle;
|
||||||
input.calcCommand();
|
input.calcCommand();
|
||||||
boolean isStraight = input.cmd == VoiceHint.C;
|
boolean isStraight = input.cmd == VoiceHint.C;
|
||||||
input.needsRealTurn = (!unconditionalTrigger) && isStraight;
|
input.needsRealTurn = (!unconditionalTrigger) && isStraight;
|
||||||
|
|
||||||
// check for KR/KL
|
// check for KR/KL
|
||||||
if (Math.abs(turnAngle) > 5.) { // don't use too small angles
|
if ( maxAngle < turnAngle && maxAngle > turnAngle - 45.f - (turnAngle > 0.f ? turnAngle : 0.f ) )
|
||||||
if (maxAngle < turnAngle && maxAngle > turnAngle - 45.f - (Math.max(turnAngle, 0.f))) {
|
{
|
||||||
input.cmd = VoiceHint.KR;
|
input.cmd = VoiceHint.KR;
|
||||||
}
|
}
|
||||||
if (minAngle > turnAngle && minAngle < turnAngle + 45.f - (Math.min(turnAngle, 0.f))) {
|
if ( minAngle > turnAngle && minAngle < turnAngle + 45.f - (turnAngle < 0.f ? turnAngle : 0.f ) )
|
||||||
|
{
|
||||||
input.cmd = VoiceHint.KL;
|
input.cmd = VoiceHint.KL;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
input.angle = sumNonConsumedWithinCatchingRange( inputs, hintIdx );
|
input.angle = sumNonConsumedWithinCatchingRange( inputs, hintIdx );
|
||||||
input.distanceToNext = distance;
|
input.distanceToNext = distance;
|
||||||
distance = 0.;
|
distance = 0.;
|
||||||
results.add( input );
|
results.add( input );
|
||||||
}
|
}
|
||||||
if (results.size() > 0 && distance < INTERNAL_CATCHING_RANGE) { //catchingRange
|
if ( results.size() > 0 && distance < catchingRange )
|
||||||
|
{
|
||||||
results.get( results.size()-1 ).angle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
|
results.get( results.size()-1 ).angle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// go through the hint list again in reverse order (=travel direction)
|
// go through the hint list again in reverse order (=travel direction)
|
||||||
// and filter out non-significant hints and hints too close to its predecessor
|
// and filter out non-signficant hints and hints too close to it's predecessor
|
||||||
|
|
||||||
List<VoiceHint> results2 = new ArrayList<>();
|
List<VoiceHint> results2 = new ArrayList<VoiceHint>();
|
||||||
int i = results.size();
|
int i = results.size();
|
||||||
while (i > 0) {
|
while( i > 0 )
|
||||||
|
{
|
||||||
VoiceHint hint = results.get(--i);
|
VoiceHint hint = results.get(--i);
|
||||||
if (hint.cmd == 0) {
|
if ( hint.cmd == 0 )
|
||||||
|
{
|
||||||
hint.calcCommand();
|
hint.calcCommand();
|
||||||
}
|
}
|
||||||
if (!(hint.needsRealTurn && (hint.cmd == VoiceHint.C || hint.cmd == VoiceHint.BL))) {
|
if ( ! ( hint.needsRealTurn && hint.cmd == VoiceHint.C ) )
|
||||||
|
{
|
||||||
double dist = hint.distanceToNext;
|
double dist = hint.distanceToNext;
|
||||||
// sum up other hints within the catching range (e.g. 40m)
|
// sum up other hints within the catching range (e.g. 40m)
|
||||||
while (dist < INTERNAL_CATCHING_RANGE && i > 0) {
|
while( dist < catchingRange && i > 0 )
|
||||||
|
{
|
||||||
VoiceHint h2 = results.get(i-1);
|
VoiceHint h2 = results.get(i-1);
|
||||||
dist = h2.distanceToNext;
|
dist = h2.distanceToNext;
|
||||||
hint.distanceToNext+= dist;
|
hint.distanceToNext+= dist;
|
||||||
hint.angle += h2.angle;
|
hint.angle += h2.angle;
|
||||||
i--;
|
i--;
|
||||||
if (h2.isRoundabout()) { // if we hit a roundabout, use that as the trigger
|
if ( h2.isRoundabout() ) // if we hit a roundabout, use that as the trigger
|
||||||
|
{
|
||||||
h2.angle = hint.angle;
|
h2.angle = hint.angle;
|
||||||
hint = h2;
|
hint = h2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!explicitRoundabouts) {
|
if ( !explicitRoundabouts )
|
||||||
|
{
|
||||||
hint.roundaboutExit = 0; // use an angular hint instead
|
hint.roundaboutExit = 0; // use an angular hint instead
|
||||||
}
|
}
|
||||||
hint.calcCommand();
|
hint.calcCommand();
|
||||||
results2.add( hint );
|
results2.add( hint );
|
||||||
} else if (hint.cmd == VoiceHint.BL) {
|
|
||||||
results2.add(hint);
|
|
||||||
} else {
|
|
||||||
if (results2.size() > 0)
|
|
||||||
results2.get(results2.size() - 1).distanceToNext += hint.distanceToNext;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return results2;
|
return results2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<VoiceHint> postProcess(List<VoiceHint> inputs, double catchingRange, double minRange) {
|
|
||||||
List<VoiceHint> results = new ArrayList<>();
|
|
||||||
double distance = 0;
|
|
||||||
VoiceHint inputLast = null;
|
|
||||||
VoiceHint inputLastSaved = null;
|
|
||||||
for (int hintIdx = 0; hintIdx < inputs.size(); hintIdx++) {
|
|
||||||
VoiceHint input = inputs.get(hintIdx);
|
|
||||||
VoiceHint nextInput = null;
|
|
||||||
if (hintIdx + 1 < inputs.size()) {
|
|
||||||
nextInput = inputs.get(hintIdx + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextInput == null) {
|
|
||||||
if (input.cmd == VoiceHint.C && !input.goodWay.isLinktType()) {
|
|
||||||
if (input.goodWay.getPrio() < input.maxBadPrio && (inputLastSaved != null && inputLastSaved.distanceToNext > catchingRange)) {
|
|
||||||
results.add(input);
|
|
||||||
} else {
|
|
||||||
if (inputLast != null) { // when drop add distance to last
|
|
||||||
inputLast.distanceToNext += input.distanceToNext;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
results.add(input);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ((inputLastSaved != null && inputLastSaved.distanceToNext > catchingRange) || input.distanceToNext > catchingRange) {
|
|
||||||
if (input.cmd == VoiceHint.C && !input.goodWay.isLinktType()) {
|
|
||||||
if (input.goodWay.getPrio() < input.maxBadPrio
|
|
||||||
&& (inputLastSaved != null && inputLastSaved.distanceToNext > minRange)
|
|
||||||
&& (input.distanceToNext > minRange)) {
|
|
||||||
// add only on prio
|
|
||||||
results.add(input);
|
|
||||||
inputLastSaved = input;
|
|
||||||
} else {
|
|
||||||
if (inputLastSaved != null) { // when drop add distance to last
|
|
||||||
inputLastSaved.distanceToNext += input.distanceToNext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// add all others
|
|
||||||
// ignore motorway / primary continue
|
|
||||||
if (((input.goodWay.getPrio() != 28) &&
|
|
||||||
(input.goodWay.getPrio() != 30) &&
|
|
||||||
(input.goodWay.getPrio() != 26))
|
|
||||||
|| input.isRoundabout()
|
|
||||||
|| Math.abs(input.angle) > 21.f) {
|
|
||||||
results.add(input);
|
|
||||||
inputLastSaved = input;
|
|
||||||
} else {
|
|
||||||
if (inputLastSaved != null) { // when drop add distance to last
|
|
||||||
inputLastSaved.distanceToNext += input.distanceToNext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (input.distanceToNext < catchingRange) {
|
|
||||||
double dist = input.distanceToNext;
|
|
||||||
float angles = input.angle;
|
|
||||||
int i = 1;
|
|
||||||
boolean save = false;
|
|
||||||
|
|
||||||
dist += nextInput.distanceToNext;
|
|
||||||
angles += nextInput.angle;
|
|
||||||
|
|
||||||
if (input.cmd == VoiceHint.C && !input.goodWay.isLinktType()) {
|
|
||||||
if (input.goodWay.getPrio() < input.maxBadPrio) {
|
|
||||||
if (inputLastSaved != null && inputLastSaved.cmd != VoiceHint.C
|
|
||||||
&& (inputLastSaved != null && inputLastSaved.distanceToNext > minRange)
|
|
||||||
&& transportMode != VoiceHintList.TRANS_MODE_CAR) {
|
|
||||||
// add when straight and not linktype
|
|
||||||
// and last vh not straight
|
|
||||||
save = true;
|
|
||||||
// remove when next straight and not linktype
|
|
||||||
if (nextInput != null &&
|
|
||||||
nextInput.cmd == VoiceHint.C &&
|
|
||||||
!nextInput.goodWay.isLinktType()) {
|
|
||||||
input.distanceToNext += nextInput.distanceToNext;
|
|
||||||
hintIdx++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (inputLastSaved != null) { // when drop add distance to last
|
|
||||||
inputLastSaved.distanceToNext += input.distanceToNext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (VoiceHint.is180DegAngle(input.angle)) {
|
|
||||||
// add u-turn, 180 degree
|
|
||||||
save = true;
|
|
||||||
} else if (transportMode == VoiceHintList.TRANS_MODE_CAR && Math.abs(angles) > 180 - SIGNIFICANT_ANGLE) {
|
|
||||||
// add when inc car mode and u-turn, collects e.g. two left turns in range
|
|
||||||
input.angle = angles;
|
|
||||||
input.calcCommand();
|
|
||||||
input.distanceToNext += nextInput.distanceToNext;
|
|
||||||
save = true;
|
|
||||||
hintIdx++;
|
|
||||||
} else if (Math.abs(angles) < SIGNIFICANT_ANGLE && input.distanceToNext < minRange) {
|
|
||||||
input.angle = angles;
|
|
||||||
input.calcCommand();
|
|
||||||
input.distanceToNext += nextInput.distanceToNext;
|
|
||||||
save = true;
|
|
||||||
hintIdx++;
|
|
||||||
} else if (Math.abs(input.angle) > SIGNIFICANT_ANGLE) {
|
|
||||||
// add when angle above 22.5 deg
|
|
||||||
save = true;
|
|
||||||
} else if (Math.abs(input.angle) < SIGNIFICANT_ANGLE) {
|
|
||||||
// add when angle below 22.5 deg ???
|
|
||||||
// save = true;
|
|
||||||
} else {
|
|
||||||
// otherwise ignore but add distance to next
|
|
||||||
if (nextInput != null) { // when drop add distance to last
|
|
||||||
nextInput.distanceToNext += input.distanceToNext;
|
|
||||||
}
|
|
||||||
save = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (save) {
|
|
||||||
results.add(input); // add when last
|
|
||||||
inputLastSaved = input;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
results.add(input);
|
|
||||||
inputLastSaved = input;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inputLast = input;
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
package btools.router;
|
package btools.router;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import btools.util.CheapRuler;
|
import btools.util.CheapRuler;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import btools.router.OsmNogoPolygon.Point;
|
||||||
import btools.util.CheapRuler;
|
import btools.util.CheapRuler;
|
||||||
|
|
||||||
public class OsmNogoPolygonTest {
|
public class OsmNogoPolygonTest {
|
||||||
|
|
@ -76,9 +77,9 @@ public class OsmNogoPolygonTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsWithin() {
|
public void testIsWithin() {
|
||||||
double[] plons = {0.0, 0.5, 1.0, -1.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5};
|
double[] plons = { 0.0, 0.5, 1.0, -1.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5, };
|
||||||
double[] plats = {0.0, 1.5, 0.0, 0.5, -1.5, -1.0, -0.1, -0.1, 0.0, 0.1};
|
double[] plats = { 0.0, 1.5, 0.0, 0.5, -1.5, -1.0, -0.1, -0.1, 0.0, 0.1, };
|
||||||
boolean[] within = {true, false, false, false, false, true, true, true, true, true};
|
boolean[] within = { true, false, false, false, false, true, true, true, true, true, };
|
||||||
|
|
||||||
for (int i=0; i<plons.length; i++) {
|
for (int i=0; i<plons.length; i++) {
|
||||||
assertEquals("("+plons[i]+","+plats[i]+")",within[i],polygon.isWithin(toOsmLon(plons[i], OFFSET_X), toOsmLat(plats[i], OFFSET_Y)));
|
assertEquals("("+plons[i]+","+plats[i]+")",within[i],polygon.isWithin(toOsmLon(plons[i], OFFSET_X), toOsmLat(plats[i], OFFSET_Y)));
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
plugins {
|
||||||
id 'brouter.library-conventions'
|
id 'java-library'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':brouter-util')
|
implementation project(':brouter-util')
|
||||||
implementation project(':brouter-codec')
|
implementation project(':brouter-codec')
|
||||||
|
testImplementation 'junit:junit:4.13.1'
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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" />
|
||||||
|
|
@ -2,22 +2,22 @@ package btools.expressions;
|
||||||
|
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
final class BExpression {
|
final class BExpression
|
||||||
|
{
|
||||||
private static final int OR_EXP = 10;
|
private static final int OR_EXP = 10;
|
||||||
private static final int AND_EXP = 11;
|
private static final int AND_EXP = 11;
|
||||||
private static final int NOT_EXP = 12;
|
private static final int NOT_EXP = 12;
|
||||||
|
|
||||||
private static final int ADD_EXP = 20;
|
private static final int ADD_EXP = 20;
|
||||||
private static final int MULTIPLY_EXP = 21;
|
private static final int MULTIPLY_EXP = 21;
|
||||||
private static final int DIVIDE_EXP = 22;
|
private static final int MAX_EXP = 22;
|
||||||
private static final int MAX_EXP = 23;
|
private static final int EQUAL_EXP = 23;
|
||||||
private static final int EQUAL_EXP = 24;
|
private static final int GREATER_EXP = 24;
|
||||||
private static final int GREATER_EXP = 25;
|
private static final int MIN_EXP = 25;
|
||||||
private static final int MIN_EXP = 26;
|
|
||||||
|
|
||||||
private static final int SUB_EXP = 27;
|
private static final int SUB_EXP = 26;
|
||||||
private static final int LESSER_EXP = 28;
|
private static final int LESSER_EXP = 27;
|
||||||
private static final int XOR_EXP = 29;
|
private static final int XOR_EXP = 28;
|
||||||
|
|
||||||
private static final int SWITCH_EXP = 30;
|
private static final int SWITCH_EXP = 30;
|
||||||
private static final int ASSIGN_EXP = 31;
|
private static final int ASSIGN_EXP = 31;
|
||||||
|
|
@ -33,176 +33,154 @@ final class BExpression {
|
||||||
private BExpression op3;
|
private BExpression op3;
|
||||||
private float numberValue;
|
private float numberValue;
|
||||||
private int variableIdx;
|
private int variableIdx;
|
||||||
private int lookupNameIdx = -1;
|
private int lookupNameIdx;
|
||||||
private int[] lookupValueIdxArray;
|
private int[] lookupValueIdxArray;
|
||||||
private boolean doNotChange;
|
|
||||||
|
|
||||||
// Parse the expression and all subexpression
|
// Parse the expression and all subexpression
|
||||||
public static BExpression parse(BExpressionContext ctx, int level) throws Exception {
|
public static BExpression parse( BExpressionContext ctx, int level ) throws Exception
|
||||||
|
{
|
||||||
return parse( ctx, level, null );
|
return parse( ctx, level, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BExpression parse(BExpressionContext ctx, int level, String optionalToken) throws Exception {
|
private static BExpression parse( BExpressionContext ctx, int level, String optionalToken ) throws Exception
|
||||||
BExpression e = parseRaw(ctx, level, optionalToken);
|
{
|
||||||
if (e == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ASSIGN_EXP == e.typ) {
|
|
||||||
// manage assined an injected values
|
|
||||||
BExpression assignedBefore = ctx.lastAssignedExpression.get(e.variableIdx);
|
|
||||||
if (assignedBefore != null && assignedBefore.doNotChange) {
|
|
||||||
e.op1 = assignedBefore; // was injected as key-value
|
|
||||||
e.op1.doNotChange = false; // protect just once, can be changed in second assignement
|
|
||||||
}
|
|
||||||
ctx.lastAssignedExpression.set(e.variableIdx, e.op1);
|
|
||||||
}
|
|
||||||
else if (!ctx.skipConstantExpressionOptimizations) {
|
|
||||||
// try to simplify the expression
|
|
||||||
if (VARIABLE_EXP == e.typ) {
|
|
||||||
BExpression ae = ctx.lastAssignedExpression.get(e.variableIdx);
|
|
||||||
if (ae != null && ae.typ == NUMBER_EXP) {
|
|
||||||
e = ae;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
BExpression eCollapsed = e.tryCollapse();
|
|
||||||
if (e != eCollapsed) {
|
|
||||||
e = eCollapsed; // allow breakpoint..
|
|
||||||
}
|
|
||||||
BExpression eEvaluated = e.tryEvaluateConstant();
|
|
||||||
if (e != eEvaluated) {
|
|
||||||
e = eEvaluated; // allow breakpoint..
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (level == 0) {
|
|
||||||
// mark the used lookups after the
|
|
||||||
// expression is collapsed to not mark
|
|
||||||
// lookups as used that appear in the profile
|
|
||||||
// but are de-activated by constant expressions
|
|
||||||
int nodeCount = e.markLookupIdxUsed(ctx);
|
|
||||||
ctx.expressionNodeCount += nodeCount;
|
|
||||||
}
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int markLookupIdxUsed(BExpressionContext ctx) {
|
|
||||||
int nodeCount = 1;
|
|
||||||
if (lookupNameIdx >= 0) {
|
|
||||||
ctx.markLookupIdxUsed(lookupNameIdx);
|
|
||||||
}
|
|
||||||
if (op1 != null) {
|
|
||||||
nodeCount += op1.markLookupIdxUsed(ctx);
|
|
||||||
}
|
|
||||||
if (op2 != null) {
|
|
||||||
nodeCount += op2.markLookupIdxUsed(ctx);
|
|
||||||
}
|
|
||||||
if (op3 != null) {
|
|
||||||
nodeCount += op3.markLookupIdxUsed(ctx);
|
|
||||||
}
|
|
||||||
return nodeCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BExpression parseRaw(BExpressionContext ctx, int level, String optionalToken) throws Exception {
|
|
||||||
|
|
||||||
boolean brackets = false;
|
boolean brackets = false;
|
||||||
String operator = ctx.parseToken();
|
String operator = ctx.parseToken();
|
||||||
if (optionalToken != null && optionalToken.equals(operator)) {
|
if ( optionalToken != null && optionalToken.equals( operator ) )
|
||||||
|
{
|
||||||
operator = ctx.parseToken();
|
operator = ctx.parseToken();
|
||||||
}
|
}
|
||||||
if ("(".equals(operator)) {
|
if ( "(".equals( operator ) )
|
||||||
|
{
|
||||||
brackets = true;
|
brackets = true;
|
||||||
operator = ctx.parseToken();
|
operator = ctx.parseToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (operator == null) {
|
if ( operator == null )
|
||||||
|
{
|
||||||
if ( level == 0 ) return null;
|
if ( level == 0 ) return null;
|
||||||
else throw new IllegalArgumentException( "unexpected end of file" );
|
else throw new IllegalArgumentException( "unexpected end of file" );
|
||||||
}
|
}
|
||||||
|
|
||||||
if (level == 0) {
|
if ( level == 0 )
|
||||||
if (!"assign".equals(operator)) {
|
{
|
||||||
|
if ( !"assign".equals( operator ) )
|
||||||
|
{
|
||||||
throw new IllegalArgumentException( "operator " + operator + " is invalid on toplevel (only 'assign' allowed)" );
|
throw new IllegalArgumentException( "operator " + operator + " is invalid on toplevel (only 'assign' allowed)" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BExpression exp = new BExpression();
|
BExpression exp = new BExpression();
|
||||||
int nops = 3;
|
int nops = 3;
|
||||||
|
|
||||||
boolean ifThenElse = false;
|
boolean ifThenElse = false;
|
||||||
|
|
||||||
if ("switch".equals(operator)) {
|
if ( "switch".equals( operator ) )
|
||||||
|
{
|
||||||
exp.typ = SWITCH_EXP;
|
exp.typ = SWITCH_EXP;
|
||||||
} else if ("if".equals(operator)) {
|
}
|
||||||
|
else if ( "if".equals( operator ) )
|
||||||
|
{
|
||||||
exp.typ = SWITCH_EXP;
|
exp.typ = SWITCH_EXP;
|
||||||
ifThenElse = true;
|
ifThenElse = true;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
nops = 2; // check binary expressions
|
nops = 2; // check binary expressions
|
||||||
|
|
||||||
if ("or".equals(operator)) {
|
if ( "or".equals( operator ) )
|
||||||
|
{
|
||||||
exp.typ = OR_EXP;
|
exp.typ = OR_EXP;
|
||||||
} else if ("and".equals(operator)) {
|
}
|
||||||
|
else if ( "and".equals( operator ) )
|
||||||
|
{
|
||||||
exp.typ = AND_EXP;
|
exp.typ = AND_EXP;
|
||||||
} else if ("multiply".equals(operator)) {
|
}
|
||||||
|
else if ( "multiply".equals( operator ) )
|
||||||
|
{
|
||||||
exp.typ = MULTIPLY_EXP;
|
exp.typ = MULTIPLY_EXP;
|
||||||
} else if ("divide".equals(operator)) {
|
}
|
||||||
exp.typ = DIVIDE_EXP;
|
else if ( "add".equals( operator ) )
|
||||||
} else if ("add".equals(operator)) {
|
{
|
||||||
exp.typ = ADD_EXP;
|
exp.typ = ADD_EXP;
|
||||||
} else if ("max".equals(operator)) {
|
}
|
||||||
|
else if ( "max".equals( operator ) )
|
||||||
|
{
|
||||||
exp.typ = MAX_EXP;
|
exp.typ = MAX_EXP;
|
||||||
} else if ("min".equals(operator)) {
|
}
|
||||||
|
else if ( "min".equals( operator ) )
|
||||||
|
{
|
||||||
exp.typ = MIN_EXP;
|
exp.typ = MIN_EXP;
|
||||||
} else if ("equal".equals(operator)) {
|
}
|
||||||
|
else if ( "equal".equals( operator ) )
|
||||||
|
{
|
||||||
exp.typ = EQUAL_EXP;
|
exp.typ = EQUAL_EXP;
|
||||||
} else if ("greater".equals(operator)) {
|
}
|
||||||
|
else if ( "greater".equals( operator ) )
|
||||||
|
{
|
||||||
exp.typ = GREATER_EXP;
|
exp.typ = GREATER_EXP;
|
||||||
} else if ("sub".equals(operator)) {
|
}
|
||||||
|
else if ( "sub".equals( operator ) )
|
||||||
|
{
|
||||||
exp.typ = SUB_EXP;
|
exp.typ = SUB_EXP;
|
||||||
} else if ("lesser".equals(operator)) {
|
}
|
||||||
|
else if ( "lesser".equals( operator ) )
|
||||||
|
{
|
||||||
exp.typ = LESSER_EXP;
|
exp.typ = LESSER_EXP;
|
||||||
} else if ("xor".equals(operator)) {
|
}
|
||||||
|
else if ( "xor".equals( operator ) )
|
||||||
|
{
|
||||||
exp.typ = XOR_EXP;
|
exp.typ = XOR_EXP;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
nops = 1; // check unary expressions
|
nops = 1; // check unary expressions
|
||||||
if ("assign".equals(operator)) {
|
if ( "assign".equals( operator ) )
|
||||||
|
{
|
||||||
if ( level > 0 ) throw new IllegalArgumentException( "assign operator within expression" );
|
if ( level > 0 ) throw new IllegalArgumentException( "assign operator within expression" );
|
||||||
exp.typ = ASSIGN_EXP;
|
exp.typ = ASSIGN_EXP;
|
||||||
String variable = ctx.parseToken();
|
String variable = ctx.parseToken();
|
||||||
if ( variable == null ) throw new IllegalArgumentException( "unexpected end of file" );
|
if ( variable == null ) throw new IllegalArgumentException( "unexpected end of file" );
|
||||||
if (variable.indexOf('=') >= 0)
|
if ( variable.indexOf( '=' ) >= 0 ) throw new IllegalArgumentException( "variable name cannot contain '=': " + variable );
|
||||||
throw new IllegalArgumentException("variable name cannot contain '=': " + variable);
|
if ( variable.indexOf( ':' ) >= 0 ) throw new IllegalArgumentException( "cannot assign context-prefixed variable: " + variable );
|
||||||
if (variable.indexOf(':') >= 0)
|
|
||||||
throw new IllegalArgumentException("cannot assign context-prefixed variable: " + variable);
|
|
||||||
exp.variableIdx = ctx.getVariableIdx( variable, true );
|
exp.variableIdx = ctx.getVariableIdx( variable, true );
|
||||||
if (exp.variableIdx < ctx.getMinWriteIdx())
|
if ( exp.variableIdx < ctx.getMinWriteIdx() ) throw new IllegalArgumentException( "cannot assign to readonly variable " + variable );
|
||||||
throw new IllegalArgumentException("cannot assign to readonly variable " + variable);
|
}
|
||||||
} else if ("not".equals(operator)) {
|
else if ( "not".equals( operator ) )
|
||||||
|
{
|
||||||
exp.typ = NOT_EXP;
|
exp.typ = NOT_EXP;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
nops = 0; // check elemantary expressions
|
nops = 0; // check elemantary expressions
|
||||||
int idx = operator.indexOf( '=' );
|
int idx = operator.indexOf( '=' );
|
||||||
if (idx >= 0) {
|
if ( idx >= 0 )
|
||||||
|
{
|
||||||
exp.typ = LOOKUP_EXP;
|
exp.typ = LOOKUP_EXP;
|
||||||
String name = operator.substring( 0, idx );
|
String name = operator.substring( 0, idx );
|
||||||
String values = operator.substring( idx+1 );
|
String values = operator.substring( idx+1 );
|
||||||
|
|
||||||
exp.lookupNameIdx = ctx.getLookupNameIdx( name );
|
exp.lookupNameIdx = ctx.getLookupNameIdx( name );
|
||||||
if (exp.lookupNameIdx < 0) {
|
if ( exp.lookupNameIdx < 0 )
|
||||||
|
{
|
||||||
throw new IllegalArgumentException( "unknown lookup name: " + name );
|
throw new IllegalArgumentException( "unknown lookup name: " + name );
|
||||||
}
|
}
|
||||||
|
ctx.markLookupIdxUsed( exp.lookupNameIdx );
|
||||||
StringTokenizer tk = new StringTokenizer( values, "|" );
|
StringTokenizer tk = new StringTokenizer( values, "|" );
|
||||||
int nt = tk.countTokens();
|
int nt = tk.countTokens();
|
||||||
int nt2 = nt == 0 ? 1 : nt;
|
int nt2 = nt == 0 ? 1 : nt;
|
||||||
exp.lookupValueIdxArray = new int[nt2];
|
exp.lookupValueIdxArray = new int[nt2];
|
||||||
for (int ti = 0; ti < nt2; ti++) {
|
for( int ti=0; ti<nt2; ti++ )
|
||||||
|
{
|
||||||
String value = ti < nt ? tk.nextToken() : "";
|
String value = ti < nt ? tk.nextToken() : "";
|
||||||
exp.lookupValueIdxArray[ti] = ctx.getLookupValueIdx( exp.lookupNameIdx, value );
|
exp.lookupValueIdxArray[ti] = ctx.getLookupValueIdx( exp.lookupNameIdx, value );
|
||||||
if (exp.lookupValueIdxArray[ti] < 0) {
|
if ( exp.lookupValueIdxArray[ti] < 0 )
|
||||||
|
{
|
||||||
throw new IllegalArgumentException( "unknown lookup value: " + value );
|
throw new IllegalArgumentException( "unknown lookup value: " + value );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ((idx = operator.indexOf(':')) >= 0) {
|
}
|
||||||
|
else if ( ( idx = operator.indexOf( ':' ) ) >= 0 )
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
use of variable values
|
use of variable values
|
||||||
assign no_height
|
assign no_height
|
||||||
|
|
@ -220,20 +198,31 @@ final class BExpression {
|
||||||
exp.typ = FOREIGN_VARIABLE_EXP;
|
exp.typ = FOREIGN_VARIABLE_EXP;
|
||||||
exp.variableIdx = ctx.getForeignVariableIdx( context, varname );
|
exp.variableIdx = ctx.getForeignVariableIdx( context, varname );
|
||||||
}
|
}
|
||||||
} else if ((idx = ctx.getVariableIdx(operator, false)) >= 0) {
|
}
|
||||||
|
else if ( (idx = ctx.getVariableIdx( operator, false )) >= 0 )
|
||||||
|
{
|
||||||
exp.typ = VARIABLE_EXP;
|
exp.typ = VARIABLE_EXP;
|
||||||
exp.variableIdx = idx;
|
exp.variableIdx = idx;
|
||||||
} else if ("true".equals(operator)) {
|
}
|
||||||
|
else if ( "true".equals( operator ) )
|
||||||
|
{
|
||||||
exp.numberValue = 1.f;
|
exp.numberValue = 1.f;
|
||||||
exp.typ = NUMBER_EXP;
|
exp.typ = NUMBER_EXP;
|
||||||
} else if ("false".equals(operator)) {
|
}
|
||||||
|
else if ( "false".equals( operator ) )
|
||||||
|
{
|
||||||
exp.numberValue = 0.f;
|
exp.numberValue = 0.f;
|
||||||
exp.typ = NUMBER_EXP;
|
exp.typ = NUMBER_EXP;
|
||||||
} else {
|
}
|
||||||
try {
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
exp.numberValue = Float.parseFloat( operator );
|
exp.numberValue = Float.parseFloat( operator );
|
||||||
exp.typ = NUMBER_EXP;
|
exp.typ = NUMBER_EXP;
|
||||||
} catch (NumberFormatException nfe) {
|
}
|
||||||
|
catch( NumberFormatException nfe )
|
||||||
|
{
|
||||||
throw new IllegalArgumentException( "unknown expression: " + operator );
|
throw new IllegalArgumentException( "unknown expression: " + operator );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -241,166 +230,71 @@ final class BExpression {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// parse operands
|
// parse operands
|
||||||
if (nops > 0) {
|
if ( nops > 0 )
|
||||||
exp.op1 = parse(ctx, level + 1, exp.typ == ASSIGN_EXP ? "=" : null);
|
{
|
||||||
|
exp.op1 = BExpression.parse( ctx, level+1, exp.typ == ASSIGN_EXP ? "=" : null );
|
||||||
}
|
}
|
||||||
if (nops > 1) {
|
if ( nops > 1 )
|
||||||
|
{
|
||||||
if ( ifThenElse ) checkExpectedToken( ctx, "then" );
|
if ( ifThenElse ) checkExpectedToken( ctx, "then" );
|
||||||
exp.op2 = parse(ctx, level + 1, null);
|
exp.op2 = BExpression.parse( ctx, level+1, null );
|
||||||
}
|
}
|
||||||
if (nops > 2) {
|
if ( nops > 2 )
|
||||||
|
{
|
||||||
if ( ifThenElse ) checkExpectedToken( ctx, "else" );
|
if ( ifThenElse ) checkExpectedToken( ctx, "else" );
|
||||||
exp.op3 = parse(ctx, level + 1, null);
|
exp.op3 = BExpression.parse( ctx, level+1, null );
|
||||||
}
|
}
|
||||||
if (brackets) {
|
if ( brackets )
|
||||||
|
{
|
||||||
checkExpectedToken( ctx, ")" );
|
checkExpectedToken( ctx, ")" );
|
||||||
}
|
}
|
||||||
return exp;
|
return exp;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkExpectedToken(BExpressionContext ctx, String expected) throws Exception {
|
private static void checkExpectedToken( BExpressionContext ctx, String expected ) throws Exception
|
||||||
|
{
|
||||||
String token = ctx.parseToken();
|
String token = ctx.parseToken();
|
||||||
if (!expected.equals(token)) {
|
if ( ! expected.equals( token ) )
|
||||||
|
{
|
||||||
throw new IllegalArgumentException( "unexpected token: " + token + ", expected: " + expected );
|
throw new IllegalArgumentException( "unexpected token: " + token + ", expected: " + expected );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the expression
|
// Evaluate the expression
|
||||||
public float evaluate(BExpressionContext ctx) {
|
public float evaluate( BExpressionContext ctx )
|
||||||
switch (typ) {
|
{
|
||||||
case OR_EXP:
|
switch( typ )
|
||||||
return op1.evaluate(ctx) != 0.f ? 1.f : (op2.evaluate(ctx) != 0.f ? 1.f : 0.f);
|
{
|
||||||
case XOR_EXP:
|
case OR_EXP: return op1.evaluate(ctx) != 0.f ? 1.f : ( op2.evaluate(ctx) != 0.f ? 1.f : 0.f );
|
||||||
return ((op1.evaluate(ctx) != 0.f) ^ (op2.evaluate(ctx) != 0.f) ? 1.f : 0.f);
|
case XOR_EXP: return ( (op1.evaluate(ctx) != 0.f) ^ ( op2.evaluate(ctx) != 0.f ) ? 1.f : 0.f );
|
||||||
case AND_EXP:
|
case AND_EXP: return op1.evaluate(ctx) != 0.f ? ( op2.evaluate(ctx) != 0.f ? 1.f : 0.f ) : 0.f;
|
||||||
return op1.evaluate(ctx) != 0.f ? (op2.evaluate(ctx) != 0.f ? 1.f : 0.f) : 0.f;
|
case ADD_EXP: return op1.evaluate(ctx) + op2.evaluate(ctx);
|
||||||
case ADD_EXP:
|
case SUB_EXP: return op1.evaluate(ctx) - op2.evaluate(ctx);
|
||||||
return op1.evaluate(ctx) + op2.evaluate(ctx);
|
case MULTIPLY_EXP: return op1.evaluate(ctx) * op2.evaluate(ctx);
|
||||||
case SUB_EXP:
|
case MAX_EXP: return max( op1.evaluate(ctx), op2.evaluate(ctx) );
|
||||||
return op1.evaluate(ctx) - op2.evaluate(ctx);
|
case MIN_EXP: return min( op1.evaluate(ctx), op2.evaluate(ctx) );
|
||||||
case MULTIPLY_EXP:
|
case EQUAL_EXP: return op1.evaluate(ctx) == op2.evaluate(ctx) ? 1.f : 0.f;
|
||||||
return op1.evaluate(ctx) * op2.evaluate(ctx);
|
case GREATER_EXP: return op1.evaluate(ctx) > op2.evaluate(ctx) ? 1.f : 0.f;
|
||||||
case DIVIDE_EXP:
|
case LESSER_EXP: return op1.evaluate(ctx) < op2.evaluate(ctx) ? 1.f : 0.f;
|
||||||
return divide(op1.evaluate(ctx), op2.evaluate(ctx));
|
case SWITCH_EXP: return op1.evaluate(ctx) != 0.f ? op2.evaluate(ctx) : op3.evaluate(ctx);
|
||||||
case MAX_EXP:
|
case ASSIGN_EXP: return ctx.assign( variableIdx, op1.evaluate(ctx) );
|
||||||
return max(op1.evaluate(ctx), op2.evaluate(ctx));
|
case LOOKUP_EXP: return ctx.getLookupMatch( lookupNameIdx, lookupValueIdxArray );
|
||||||
case MIN_EXP:
|
case NUMBER_EXP: return numberValue;
|
||||||
return min(op1.evaluate(ctx), op2.evaluate(ctx));
|
case VARIABLE_EXP: return ctx.getVariableValue( variableIdx );
|
||||||
case EQUAL_EXP:
|
case FOREIGN_VARIABLE_EXP: return ctx.getForeignVariableValue( variableIdx );
|
||||||
return op1.evaluate(ctx) == op2.evaluate(ctx) ? 1.f : 0.f;
|
case VARIABLE_GET_EXP: return ctx.getLookupValue(lookupNameIdx);
|
||||||
case GREATER_EXP:
|
case NOT_EXP: return op1.evaluate(ctx) == 0.f ? 1.f : 0.f;
|
||||||
return op1.evaluate(ctx) > op2.evaluate(ctx) ? 1.f : 0.f;
|
default: throw new IllegalArgumentException( "unknown op-code: " + typ );
|
||||||
case LESSER_EXP:
|
|
||||||
return op1.evaluate(ctx) < op2.evaluate(ctx) ? 1.f : 0.f;
|
|
||||||
case SWITCH_EXP:
|
|
||||||
return op1.evaluate(ctx) != 0.f ? op2.evaluate(ctx) : op3.evaluate(ctx);
|
|
||||||
case ASSIGN_EXP:
|
|
||||||
return ctx.assign(variableIdx, op1.evaluate(ctx));
|
|
||||||
case LOOKUP_EXP:
|
|
||||||
return ctx.getLookupMatch(lookupNameIdx, lookupValueIdxArray);
|
|
||||||
case NUMBER_EXP:
|
|
||||||
return numberValue;
|
|
||||||
case VARIABLE_EXP:
|
|
||||||
return ctx.getVariableValue(variableIdx);
|
|
||||||
case FOREIGN_VARIABLE_EXP:
|
|
||||||
return ctx.getForeignVariableValue(variableIdx);
|
|
||||||
case VARIABLE_GET_EXP:
|
|
||||||
return ctx.getLookupValue(lookupNameIdx);
|
|
||||||
case NOT_EXP:
|
|
||||||
return op1.evaluate(ctx) == 0.f ? 1.f : 0.f;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("unknown op-code: " + typ);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to collapse the expression
|
private float max( float v1, float v2 )
|
||||||
// if logically possible
|
{
|
||||||
private BExpression tryCollapse() {
|
|
||||||
switch (typ) {
|
|
||||||
case OR_EXP:
|
|
||||||
return NUMBER_EXP == op1.typ ?
|
|
||||||
(op1.numberValue != 0.f ? op1 : op2)
|
|
||||||
: (NUMBER_EXP == op2.typ ?
|
|
||||||
(op2.numberValue != 0.f ? op2 : op1)
|
|
||||||
: this);
|
|
||||||
case AND_EXP:
|
|
||||||
return NUMBER_EXP == op1.typ ?
|
|
||||||
(op1.numberValue == 0.f ? op1 : op2)
|
|
||||||
: (NUMBER_EXP == op2.typ ?
|
|
||||||
(op2.numberValue == 0.f ? op2 : op1)
|
|
||||||
: this);
|
|
||||||
case ADD_EXP:
|
|
||||||
return NUMBER_EXP == op1.typ ?
|
|
||||||
(op1.numberValue == 0.f ? op2 : this)
|
|
||||||
: (NUMBER_EXP == op2.typ ?
|
|
||||||
(op2.numberValue == 0.f ? op1 : this)
|
|
||||||
: this);
|
|
||||||
case SWITCH_EXP:
|
|
||||||
return NUMBER_EXP == op1.typ ?
|
|
||||||
(op1.numberValue == 0.f ? op3 : op2) : this;
|
|
||||||
default:
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to evaluate the expression
|
|
||||||
// if all operands are constant
|
|
||||||
private BExpression tryEvaluateConstant() {
|
|
||||||
if (op1 != null && NUMBER_EXP == op1.typ
|
|
||||||
&& (op2 == null || NUMBER_EXP == op2.typ)
|
|
||||||
&& (op3 == null || NUMBER_EXP == op3.typ)) {
|
|
||||||
BExpression exp = new BExpression();
|
|
||||||
exp.typ = NUMBER_EXP;
|
|
||||||
exp.numberValue = evaluate(null);
|
|
||||||
return exp;
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private float max(float v1, float v2) {
|
|
||||||
return v1 > v2 ? v1 : v2;
|
return v1 > v2 ? v1 : v2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float min(float v1, float v2) {
|
private float min( float v1, float v2 )
|
||||||
|
{
|
||||||
return v1 < v2 ? v1 : v2;
|
return v1 < v2 ? v1 : v2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float divide(float v1, float v2) {
|
|
||||||
if (v2 == 0f) throw new IllegalArgumentException("div by zero");
|
|
||||||
return v1 / v2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
if (typ == NUMBER_EXP) {
|
|
||||||
return "" + numberValue;
|
|
||||||
}
|
|
||||||
if (typ == VARIABLE_EXP) {
|
|
||||||
return "vidx=" + variableIdx;
|
|
||||||
}
|
|
||||||
StringBuilder sb = new StringBuilder("typ=" + typ + " ops=(");
|
|
||||||
addOp(sb, op1);
|
|
||||||
addOp(sb, op2);
|
|
||||||
addOp(sb, op3);
|
|
||||||
sb.append(')');
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addOp(StringBuilder sb, BExpression e) {
|
|
||||||
if (e != null) {
|
|
||||||
sb.append('[').append(e.toString()).append(']');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static BExpression createAssignExpressionFromKeyValue(BExpressionContext ctx, String key, String value) {
|
|
||||||
BExpression e = new BExpression();
|
|
||||||
e.typ = ASSIGN_EXP;
|
|
||||||
e.variableIdx = ctx.getVariableIdx(key, true);
|
|
||||||
e.op1 = new BExpression();
|
|
||||||
e.op1.typ = NUMBER_EXP;
|
|
||||||
e.op1.numberValue = Float.parseFloat(value);
|
|
||||||
e.op1.doNotChange = true;
|
|
||||||
ctx.lastAssignedExpression.set(e.variableIdx, e.op1);
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -7,20 +7,22 @@
|
||||||
package btools.expressions;
|
package btools.expressions;
|
||||||
|
|
||||||
|
|
||||||
public final class BExpressionContextNode extends BExpressionContext {
|
|
||||||
|
public final class BExpressionContextNode extends BExpressionContext
|
||||||
|
{
|
||||||
private static String[] buildInVariables =
|
private static String[] buildInVariables =
|
||||||
{ "initialcost" };
|
{ "initialcost" };
|
||||||
|
|
||||||
protected String[] getBuildInVariableNames() {
|
protected String[] getBuildInVariableNames()
|
||||||
|
{
|
||||||
return buildInVariables;
|
return buildInVariables;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getInitialcost() {
|
public float getInitialcost() { return getBuildInVariable(0); }
|
||||||
return getBuildInVariable(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public BExpressionContextNode(BExpressionMetaData meta) {
|
public BExpressionContextNode( BExpressionMetaData meta )
|
||||||
|
{
|
||||||
super( "node", meta );
|
super( "node", meta );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,7 +31,8 @@ public final class BExpressionContextNode extends BExpressionContext {
|
||||||
*
|
*
|
||||||
* @param hashSize size of hashmap for result caching
|
* @param hashSize size of hashmap for result caching
|
||||||
*/
|
*/
|
||||||
public BExpressionContextNode(int hashSize, BExpressionMetaData meta) {
|
public BExpressionContextNode( int hashSize, BExpressionMetaData meta )
|
||||||
|
{
|
||||||
super( "node", hashSize, meta );
|
super( "node", hashSize, meta );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,97 +8,33 @@ package btools.expressions;
|
||||||
|
|
||||||
import btools.codec.TagValueValidator;
|
import btools.codec.TagValueValidator;
|
||||||
|
|
||||||
public final class BExpressionContextWay extends BExpressionContext implements TagValueValidator {
|
public final class BExpressionContextWay extends BExpressionContext implements TagValueValidator
|
||||||
|
{
|
||||||
private boolean decodeForbidden = true;
|
private boolean decodeForbidden = true;
|
||||||
|
|
||||||
private static String[] buildInVariables =
|
private static String[] buildInVariables =
|
||||||
{"costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "classifiermask", "maxspeed", "uphillcost", "downhillcost", "uphillcutoff", "downhillcutoff", "uphillmaxslope", "downhillmaxslope", "uphillmaxslopecost", "downhillmaxslopecost"};
|
{ "costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "classifiermask", "maxspeed" };
|
||||||
|
|
||||||
protected String[] getBuildInVariableNames() {
|
protected String[] getBuildInVariableNames()
|
||||||
|
{
|
||||||
return buildInVariables;
|
return buildInVariables;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getCostfactor() {
|
public float getCostfactor() { return getBuildInVariable(0); }
|
||||||
return getBuildInVariable(0);
|
public float getTurncost() { return getBuildInVariable(1); }
|
||||||
}
|
public float getUphillCostfactor() { return getBuildInVariable(2); }
|
||||||
|
public float getDownhillCostfactor() { return getBuildInVariable(3); }
|
||||||
|
public float getInitialcost() { return getBuildInVariable(4); }
|
||||||
|
public float getNodeAccessGranted() { return getBuildInVariable(5); }
|
||||||
|
public float getInitialClassifier() { return getBuildInVariable(6); }
|
||||||
|
public float getTrafficSourceDensity() { return getBuildInVariable(7); }
|
||||||
|
public float getIsTrafficBackbone() { return getBuildInVariable(8); }
|
||||||
|
public float getPriorityClassifier() { return getBuildInVariable(9); }
|
||||||
|
public float getClassifierMask() { return getBuildInVariable(10); }
|
||||||
|
public float getMaxspeed() { return getBuildInVariable(11); }
|
||||||
|
|
||||||
public float getTurncost() {
|
public BExpressionContextWay( BExpressionMetaData meta )
|
||||||
return getBuildInVariable(1);
|
{
|
||||||
}
|
|
||||||
|
|
||||||
public float getUphillCostfactor() {
|
|
||||||
return getBuildInVariable(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getDownhillCostfactor() {
|
|
||||||
return getBuildInVariable(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getInitialcost() {
|
|
||||||
return getBuildInVariable(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getNodeAccessGranted() {
|
|
||||||
return getBuildInVariable(5);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getInitialClassifier() {
|
|
||||||
return getBuildInVariable(6);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getTrafficSourceDensity() {
|
|
||||||
return getBuildInVariable(7);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getIsTrafficBackbone() {
|
|
||||||
return getBuildInVariable(8);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getPriorityClassifier() {
|
|
||||||
return getBuildInVariable(9);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getClassifierMask() {
|
|
||||||
return getBuildInVariable(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getMaxspeed() {
|
|
||||||
return getBuildInVariable(11);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getUphillcost() {
|
|
||||||
return getBuildInVariable(12);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getDownhillcost() {
|
|
||||||
return getBuildInVariable(13);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getUphillcutoff() {
|
|
||||||
return getBuildInVariable(14);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getDownhillcutoff() {
|
|
||||||
return getBuildInVariable(15);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getUphillmaxslope() {
|
|
||||||
return getBuildInVariable(16);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getDownhillmaxslope() {
|
|
||||||
return getBuildInVariable(17);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getUphillmaxslopecost() {
|
|
||||||
return getBuildInVariable(18);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getDownhillmaxslopecost() {
|
|
||||||
return getBuildInVariable(19);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BExpressionContextWay(BExpressionMetaData meta) {
|
|
||||||
super( "way", meta );
|
super( "way", meta );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,18 +43,22 @@ public final class BExpressionContextWay extends BExpressionContext implements T
|
||||||
*
|
*
|
||||||
* @param hashSize size of hashmap for result caching
|
* @param hashSize size of hashmap for result caching
|
||||||
*/
|
*/
|
||||||
public BExpressionContextWay(int hashSize, BExpressionMetaData meta) {
|
public BExpressionContextWay( int hashSize, BExpressionMetaData meta )
|
||||||
|
{
|
||||||
super( "way", hashSize, meta );
|
super( "way", hashSize, meta );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int accessType(byte[] description) {
|
public int accessType( byte[] description )
|
||||||
|
{
|
||||||
evaluate( false, description );
|
evaluate( false, description );
|
||||||
float minCostFactor = getCostfactor();
|
float minCostFactor = getCostfactor();
|
||||||
if (minCostFactor >= 9999.f) {
|
if ( minCostFactor >= 9999.f )
|
||||||
|
{
|
||||||
setInverseVars();
|
setInverseVars();
|
||||||
float reverseCostFactor = getCostfactor();
|
float reverseCostFactor = getCostfactor();
|
||||||
if (reverseCostFactor < minCostFactor) {
|
if ( reverseCostFactor < minCostFactor )
|
||||||
|
{
|
||||||
minCostFactor = reverseCostFactor;
|
minCostFactor = reverseCostFactor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -126,7 +66,8 @@ public final class BExpressionContextWay extends BExpressionContext implements T
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDecodeForbidden(boolean decodeForbidden) {
|
public void setDecodeForbidden( boolean decodeForbidden )
|
||||||
|
{
|
||||||
this.decodeForbidden= decodeForbidden;
|
this.decodeForbidden= decodeForbidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* A lookup value with optional aliases
|
* A lookup value with optional aliases
|
||||||
* <p>
|
*
|
||||||
* toString just gives the primary value,
|
* toString just gives the primary value,
|
||||||
* equals just compares against primary value
|
* equals just compares against primary value
|
||||||
* matches() also compares aliases
|
* matches() also compares aliases
|
||||||
|
|
@ -10,33 +10,39 @@
|
||||||
package btools.expressions;
|
package btools.expressions;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
final class BExpressionLookupValue {
|
final class BExpressionLookupValue
|
||||||
|
{
|
||||||
String value;
|
String value;
|
||||||
List<String> aliases;
|
ArrayList<String> aliases;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString()
|
||||||
|
{
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BExpressionLookupValue(String value) {
|
public BExpressionLookupValue( String value )
|
||||||
|
{
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addAlias(String alias) {
|
public void addAlias( String alias )
|
||||||
if (aliases == null) aliases = new ArrayList<>();
|
{
|
||||||
|
if ( aliases == null ) aliases = new ArrayList<String>();
|
||||||
aliases.add( alias );
|
aliases.add( alias );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals( Object o )
|
||||||
if (o instanceof String) {
|
{
|
||||||
|
if ( o instanceof String )
|
||||||
|
{
|
||||||
String v = (String)o;
|
String v = (String)o;
|
||||||
return value.equals( v );
|
return value.equals( v );
|
||||||
}
|
}
|
||||||
if (o instanceof BExpressionLookupValue) {
|
if ( o instanceof BExpressionLookupValue )
|
||||||
|
{
|
||||||
BExpressionLookupValue v = (BExpressionLookupValue)o;
|
BExpressionLookupValue v = (BExpressionLookupValue)o;
|
||||||
|
|
||||||
return value.equals( v.value );
|
return value.equals( v.value );
|
||||||
|
|
@ -44,10 +50,13 @@ final class BExpressionLookupValue {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean matches(String s) {
|
public boolean matches( String s )
|
||||||
|
{
|
||||||
if ( value.equals( s ) ) return true;
|
if ( value.equals( s ) ) return true;
|
||||||
if (aliases != null) {
|
if ( aliases != null )
|
||||||
for (String alias : aliases) {
|
{
|
||||||
|
for( String alias : aliases )
|
||||||
|
{
|
||||||
if ( alias.equals( s ) ) return true;
|
if ( alias.equals( s ) ) return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,66 +9,80 @@ package btools.expressions;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import btools.util.BitCoderContext;
|
||||||
|
import btools.util.Crc32;
|
||||||
|
|
||||||
|
|
||||||
public final class BExpressionMetaData {
|
public final class BExpressionMetaData
|
||||||
|
{
|
||||||
private static final String CONTEXT_TAG = "---context:";
|
private static final String CONTEXT_TAG = "---context:";
|
||||||
private static final String VERSION_TAG = "---lookupversion:";
|
private static final String VERSION_TAG = "---lookupversion:";
|
||||||
private static final String MINOR_VERSION_TAG = "---minorversion:";
|
private static final String MINOR_VERSION_TAG = "---minorversion:";
|
||||||
private static final String VARLENGTH_TAG = "---readvarlength";
|
private static final String VARLENGTH_TAG = "---readvarlength";
|
||||||
private static final String MIN_APP_VERSION_TAG = "---minappversion:";
|
|
||||||
|
|
||||||
public short lookupVersion = -1;
|
public short lookupVersion = -1;
|
||||||
public short lookupMinorVersion = -1;
|
public short lookupMinorVersion = -1;
|
||||||
public short minAppVersion = -1;
|
|
||||||
|
|
||||||
private Map<String, BExpressionContext> listeners = new HashMap<>();
|
private HashMap<String,BExpressionContext> listeners = new HashMap<String,BExpressionContext>();
|
||||||
|
|
||||||
public void registerListener(String context, BExpressionContext ctx) {
|
public void registerListener( String context, BExpressionContext ctx )
|
||||||
|
{
|
||||||
listeners.put( context, ctx );
|
listeners.put( context, ctx );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readMetaData(File lookupsFile) {
|
public void readMetaData( File lookupsFile )
|
||||||
try {
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
BufferedReader br = new BufferedReader( new FileReader( lookupsFile ) );
|
BufferedReader br = new BufferedReader( new FileReader( lookupsFile ) );
|
||||||
|
|
||||||
BExpressionContext ctx = null;
|
BExpressionContext ctx = null;
|
||||||
|
|
||||||
for (; ; ) {
|
for(;;)
|
||||||
|
{
|
||||||
String line = br.readLine();
|
String line = br.readLine();
|
||||||
if ( line == null ) break;
|
if ( line == null ) break;
|
||||||
line = line.trim();
|
line = line.trim();
|
||||||
if ( line.length() == 0 || line.startsWith( "#" ) ) continue;
|
if ( line.length() == 0 || line.startsWith( "#" ) ) continue;
|
||||||
if (line.startsWith(CONTEXT_TAG)) {
|
if ( line.startsWith( CONTEXT_TAG ) )
|
||||||
|
{
|
||||||
ctx = listeners.get( line.substring( CONTEXT_TAG.length() ) );
|
ctx = listeners.get( line.substring( CONTEXT_TAG.length() ) );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (line.startsWith(VERSION_TAG)) {
|
if ( line.startsWith( VERSION_TAG ) )
|
||||||
|
{
|
||||||
lookupVersion = Short.parseShort( line.substring( VERSION_TAG.length() ) );
|
lookupVersion = Short.parseShort( line.substring( VERSION_TAG.length() ) );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (line.startsWith(MINOR_VERSION_TAG)) {
|
if ( line.startsWith( MINOR_VERSION_TAG ) )
|
||||||
|
{
|
||||||
lookupMinorVersion = Short.parseShort( line.substring( MINOR_VERSION_TAG.length() ) );
|
lookupMinorVersion = Short.parseShort( line.substring( MINOR_VERSION_TAG.length() ) );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (line.startsWith(MIN_APP_VERSION_TAG)) {
|
if ( line.startsWith( VARLENGTH_TAG ) ) // tag removed...
|
||||||
minAppVersion = Short.parseShort(line.substring(MIN_APP_VERSION_TAG.length()));
|
{
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (line.startsWith(VARLENGTH_TAG)) { // tag removed...
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ( ctx != null ) ctx.parseMetaLine( line );
|
if ( ctx != null ) ctx.parseMetaLine( line );
|
||||||
}
|
}
|
||||||
br.close();
|
br.close();
|
||||||
|
|
||||||
for (BExpressionContext c : listeners.values()) {
|
for( BExpressionContext c : listeners.values() )
|
||||||
|
{
|
||||||
c.finishMetaParsing();
|
c.finishMetaParsing();
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
}
|
||||||
|
catch( Exception e )
|
||||||
|
{
|
||||||
throw new RuntimeException( e );
|
throw new RuntimeException( e );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,22 +4,27 @@ import java.util.Arrays;
|
||||||
|
|
||||||
import btools.util.LruMapNode;
|
import btools.util.LruMapNode;
|
||||||
|
|
||||||
public final class CacheNode extends LruMapNode {
|
public final class CacheNode extends LruMapNode
|
||||||
|
{
|
||||||
byte[] ab;
|
byte[] ab;
|
||||||
float[] vars;
|
float[] vars;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode()
|
||||||
|
{
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals( Object o )
|
||||||
|
{
|
||||||
CacheNode n = (CacheNode) o;
|
CacheNode n = (CacheNode) o;
|
||||||
if (hash != n.hash) {
|
if ( hash != n.hash )
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ab == null) {
|
if ( ab == null )
|
||||||
|
{
|
||||||
return true; // hack: null = crc match only
|
return true; // hack: null = crc match only
|
||||||
}
|
}
|
||||||
return Arrays.equals( ab, n.ab );
|
return Arrays.equals( ab, n.ab );
|
||||||
|
|
|
||||||
|
|
@ -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,9 +3,12 @@ package btools.expressions;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public final class ProfileComparator {
|
public final class ProfileComparator
|
||||||
public static void main(String[] args) {
|
{
|
||||||
if (args.length != 4) {
|
public static void main( String[] args )
|
||||||
|
{
|
||||||
|
if ( args.length != 4 )
|
||||||
|
{
|
||||||
System.out.println( "usage: java ProfileComparator <lookup-file> <profile1> <profile2> <nsamples>" );
|
System.out.println( "usage: java ProfileComparator <lookup-file> <profile1> <profile2> <nsamples>" );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -19,29 +22,21 @@ public final class ProfileComparator {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void testContext(File lookupFile, File profile1File, File profile2File, int nsamples, boolean nodeContext) {
|
private static void testContext( File lookupFile, File profile1File, File profile2File, int nsamples, boolean nodeContext )
|
||||||
|
{
|
||||||
// read lookup.dat + profiles
|
// read lookup.dat + profiles
|
||||||
BExpressionMetaData meta1 = new BExpressionMetaData();
|
BExpressionMetaData meta1 = new BExpressionMetaData();
|
||||||
BExpressionMetaData meta2 = new BExpressionMetaData();
|
BExpressionMetaData meta2 = new BExpressionMetaData();
|
||||||
BExpressionContext expctx1 = nodeContext ? new BExpressionContextNode( meta1 ) : new BExpressionContextWay( meta1 );
|
BExpressionContext expctx1 = nodeContext ? new BExpressionContextNode( meta1 ) : new BExpressionContextWay( meta1 );
|
||||||
BExpressionContext expctx2 = nodeContext ? new BExpressionContextNode( meta2 ) : new BExpressionContextWay( meta2 );
|
BExpressionContext expctx2 = nodeContext ? new BExpressionContextNode( meta2 ) : new BExpressionContextWay( meta2 );
|
||||||
|
|
||||||
// if same profiles, compare different optimization levels
|
|
||||||
if (profile1File.getName().equals(profile2File.getName())) {
|
|
||||||
expctx2.skipConstantExpressionOptimizations = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
meta1.readMetaData( lookupFile );
|
meta1.readMetaData( lookupFile );
|
||||||
meta2.readMetaData( lookupFile );
|
meta2.readMetaData( lookupFile );
|
||||||
expctx1.parseFile( profile1File, "global" );
|
expctx1.parseFile( profile1File, "global" );
|
||||||
System.out.println("usedTags1=" + expctx1.usedTagList());
|
|
||||||
expctx2.parseFile( profile2File, "global" );
|
expctx2.parseFile( profile2File, "global" );
|
||||||
System.out.println("usedTags2=" + expctx2.usedTagList());
|
|
||||||
|
|
||||||
System.out.println("nodeContext=" + nodeContext + " nodeCount1=" + expctx1.expressionNodeCount + " nodeCount2=" + expctx2.expressionNodeCount);
|
|
||||||
|
|
||||||
Random rnd = new Random();
|
Random rnd = new Random();
|
||||||
for (int i = 0; i < nsamples; i++) {
|
for( int i=0; i<nsamples; i++ )
|
||||||
|
{
|
||||||
int[] data = expctx1.generateRandomValues( rnd );
|
int[] data = expctx1.generateRandomValues( rnd );
|
||||||
expctx1.evaluate( data );
|
expctx1.evaluate( data );
|
||||||
expctx2.evaluate( data );
|
expctx2.evaluate( data );
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,22 @@ import java.util.Arrays;
|
||||||
|
|
||||||
import btools.util.LruMapNode;
|
import btools.util.LruMapNode;
|
||||||
|
|
||||||
public final class VarWrapper extends LruMapNode {
|
public final class VarWrapper extends LruMapNode
|
||||||
|
{
|
||||||
float[] vars;
|
float[] vars;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode()
|
||||||
|
{
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals( Object o )
|
||||||
|
{
|
||||||
VarWrapper n = (VarWrapper) o;
|
VarWrapper n = (VarWrapper) o;
|
||||||
if (hash != n.hash) {
|
if ( hash != n.hash )
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return Arrays.equals( vars, n.vars );
|
return Arrays.equals( vars, n.vars );
|
||||||
|
|
|
||||||
|
|
@ -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,14 +1,17 @@
|
||||||
package btools.expressions;
|
package btools.expressions;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.File;
|
public class EncodeDecodeTest
|
||||||
import java.net.URL;
|
{
|
||||||
|
|
||||||
public class EncodeDecodeTest {
|
|
||||||
@Test
|
@Test
|
||||||
public void encodeDecodeTest() {
|
public void encodeDecodeTest()
|
||||||
|
{
|
||||||
URL testpurl = this.getClass().getResource( "/dummy.txt" );
|
URL testpurl = this.getClass().getResource( "/dummy.txt" );
|
||||||
File workingDir = new File(testpurl.getFile()).getParentFile();
|
File workingDir = new File(testpurl.getFile()).getParentFile();
|
||||||
File profileDir = new File( workingDir, "/../../../../misc/profiles2" );
|
File profileDir = new File( workingDir, "/../../../../misc/profiles2" );
|
||||||
|
|
@ -29,16 +32,16 @@ public class EncodeDecodeTest {
|
||||||
"depth=1'6\"",
|
"depth=1'6\"",
|
||||||
// "depth=6 feet",
|
// "depth=6 feet",
|
||||||
"maxheight=5.1m",
|
"maxheight=5.1m",
|
||||||
"maxdraft=~3 m - 4 m",
|
"maxdraft=~3 mt",
|
||||||
"reversedirection=yes"
|
"reversedirection=yes"
|
||||||
};
|
};
|
||||||
|
|
||||||
// encode the tags into 64 bit description word
|
// encode the tags into 64 bit description word
|
||||||
int[] lookupData = expctxWay.createNewLookupData();
|
int[] lookupData = expctxWay.createNewLookupData();
|
||||||
for (String arg : tags) {
|
for( String arg: tags )
|
||||||
|
{
|
||||||
int idx = arg.indexOf( '=' );
|
int idx = arg.indexOf( '=' );
|
||||||
if (idx < 0)
|
if ( idx < 0 ) throw new IllegalArgumentException( "bad argument (should be <tag>=<value>): " + arg );
|
||||||
throw new IllegalArgumentException("bad argument (should be <tag>=<value>): " + arg);
|
|
||||||
String key = arg.substring( 0, idx );
|
String key = arg.substring( 0, idx );
|
||||||
String value = arg.substring( idx+1 );
|
String value = arg.substring( idx+1 );
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;0000000021 mini_roundabout
|
||||||
construction;0000000020 turning_loop
|
construction;0000000020 turning_loop
|
||||||
|
|
||||||
estimated_forest_class;0000000001 1
|
|
||||||
estimated_forest_class;0000000001 2
|
|
||||||
estimated_forest_class;0000000001 3
|
|
||||||
estimated_forest_class;0000000001 4
|
|
||||||
estimated_forest_class;0000000001 5
|
|
||||||
estimated_forest_class;0000000001 6
|
|
||||||
|
|
||||||
estimated_noise_class;0000000001 1
|
|
||||||
estimated_noise_class;0000000001 2
|
|
||||||
estimated_noise_class;0000000001 3
|
|
||||||
estimated_noise_class;0000000001 4
|
|
||||||
estimated_noise_class;0000000001 5
|
|
||||||
estimated_noise_class;0000000001 6
|
|
||||||
|
|
||||||
estimated_river_class;0000000001 1
|
|
||||||
estimated_river_class;0000000001 2
|
|
||||||
estimated_river_class;0000000001 3
|
|
||||||
estimated_river_class;0000000001 4
|
|
||||||
estimated_river_class;0000000001 5
|
|
||||||
estimated_river_class;0000000001 6
|
|
||||||
|
|
||||||
estimated_town_class;0000000001 1
|
|
||||||
estimated_town_class;0000000001 2
|
|
||||||
estimated_town_class;0000000001 3
|
|
||||||
estimated_town_class;0000000001 4
|
|
||||||
estimated_town_class;0000000001 5
|
|
||||||
estimated_town_class;0000000001 6
|
|
||||||
|
|
||||||
|
|
||||||
---context:node
|
---context:node
|
||||||
|
|
||||||
highway;0001314954 bus_stop
|
highway;0001314954 bus_stop
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
plugins {
|
||||||
id 'brouter.application-conventions'
|
id 'java-library'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':brouter-codec')
|
implementation project(':brouter-codec')
|
||||||
implementation project(':brouter-util')
|
implementation project(':brouter-util')
|
||||||
implementation project(':brouter-expressions')
|
implementation project(':brouter-expressions')
|
||||||
|
|
||||||
implementation group: 'org.openstreetmap.osmosis', name: 'osmosis-osm-binary', version: '0.48.3'
|
testImplementation('junit:junit:4.13.1')
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,32 +5,38 @@
|
||||||
*/
|
*/
|
||||||
package btools.mapcreator;
|
package btools.mapcreator;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import btools.util.CheapRuler;
|
import btools.util.CheapRuler;
|
||||||
|
|
||||||
public class DPFilter {
|
public class DPFilter
|
||||||
|
{
|
||||||
private static double dp_sql_threshold = 0.4 * 0.4;
|
private static double dp_sql_threshold = 0.4 * 0.4;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* for each node (except first+last), eventually set the DP_SURVIVOR_BIT
|
* for each node (except first+last), eventually set the DP_SURVIVOR_BIT
|
||||||
*/
|
*/
|
||||||
public static void doDPFilter(List<OsmNodeP> nodes) {
|
public static void doDPFilter( ArrayList<OsmNodeP> nodes )
|
||||||
|
{
|
||||||
int first = 0;
|
int first = 0;
|
||||||
int last = nodes.size()-1;
|
int last = nodes.size()-1;
|
||||||
while (first < last && (nodes.get(first + 1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0) {
|
while( first < last && (nodes.get(first+1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0 )
|
||||||
|
{
|
||||||
first++;
|
first++;
|
||||||
}
|
}
|
||||||
while (first < last && (nodes.get(last - 1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0) {
|
while( first < last && (nodes.get(last-1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0 )
|
||||||
|
{
|
||||||
last--;
|
last--;
|
||||||
}
|
}
|
||||||
if (last - first > 1) {
|
if ( last - first > 1 )
|
||||||
|
{
|
||||||
doDPFilter( nodes, first, last );
|
doDPFilter( nodes, first, last );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void doDPFilter(List<OsmNodeP> nodes, int first, int last) {
|
public static void doDPFilter( ArrayList<OsmNodeP> nodes, int first, int last )
|
||||||
|
{
|
||||||
double maxSqDist = -1.;
|
double maxSqDist = -1.;
|
||||||
int index = -1;
|
int index = -1;
|
||||||
OsmNodeP p1 = nodes.get( first );
|
OsmNodeP p1 = nodes.get( first );
|
||||||
|
|
@ -42,29 +48,36 @@ public class DPFilter {
|
||||||
double dx = (p2.ilon - p1.ilon) * dlon2m;
|
double dx = (p2.ilon - p1.ilon) * dlon2m;
|
||||||
double dy = (p2.ilat - p1.ilat) * dlat2m;
|
double dy = (p2.ilat - p1.ilat) * dlat2m;
|
||||||
double d2 = dx * dx + dy * dy;
|
double d2 = dx * dx + dy * dy;
|
||||||
for (int i = first + 1; i < last; i++) {
|
for ( int i = first + 1; i < last; i++ )
|
||||||
|
{
|
||||||
OsmNodeP p = nodes.get( i );
|
OsmNodeP p = nodes.get( i );
|
||||||
double t = 0.;
|
double t = 0.;
|
||||||
if (d2 != 0f) {
|
if ( d2 != 0f )
|
||||||
|
{
|
||||||
t = ( ( p.ilon - p1.ilon ) * dlon2m * dx + ( p.ilat - p1.ilat ) * dlat2m * dy ) / d2;
|
t = ( ( p.ilon - p1.ilon ) * dlon2m * dx + ( p.ilat - p1.ilat ) * dlat2m * dy ) / d2;
|
||||||
t = t > 1. ? 1. : ( t < 0. ? 0. : t );
|
t = t > 1. ? 1. : ( t < 0. ? 0. : t );
|
||||||
}
|
}
|
||||||
double dx2 = (p.ilon - ( p1.ilon + t*( p2.ilon - p1.ilon ) ) ) * dlon2m;
|
double dx2 = (p.ilon - ( p1.ilon + t*( p2.ilon - p1.ilon ) ) ) * dlon2m;
|
||||||
double dy2 = (p.ilat - ( p1.ilat + t*( p2.ilat - p1.ilat ) ) ) * dlat2m;
|
double dy2 = (p.ilat - ( p1.ilat + t*( p2.ilat - p1.ilat ) ) ) * dlat2m;
|
||||||
double sqDist = dx2 * dx2 + dy2 * dy2;
|
double sqDist = dx2 * dx2 + dy2 * dy2;
|
||||||
if (sqDist > maxSqDist) {
|
if ( sqDist > maxSqDist )
|
||||||
|
{
|
||||||
index = i;
|
index = i;
|
||||||
maxSqDist = sqDist;
|
maxSqDist = sqDist;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (index >= 0) {
|
if ( index >= 0 )
|
||||||
if (index - first > 1) {
|
{
|
||||||
|
if ( index - first > 1 )
|
||||||
|
{
|
||||||
doDPFilter( nodes, first, index );
|
doDPFilter( nodes, first, index );
|
||||||
}
|
}
|
||||||
if (maxSqDist >= dp_sql_threshold) {
|
if ( maxSqDist >= dp_sql_threshold )
|
||||||
|
{
|
||||||
nodes.get( index ).bits |= OsmNodeP.DP_SURVIVOR_BIT;
|
nodes.get( index ).bits |= OsmNodeP.DP_SURVIVOR_BIT;
|
||||||
}
|
}
|
||||||
if (last - index > 1) {
|
if ( last - index > 1 )
|
||||||
|
{
|
||||||
doDPFilter( nodes, index, last );
|
doDPFilter( nodes, index, last );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -14,34 +14,39 @@ import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import btools.util.DiffCoderDataOutputStream;
|
import btools.util.DiffCoderDataOutputStream;
|
||||||
|
|
||||||
public abstract class MapCreatorBase implements WayListener, NodeListener, RelationListener {
|
public abstract class MapCreatorBase implements WayListener, NodeListener, RelationListener
|
||||||
|
{
|
||||||
private DiffCoderDataOutputStream[] tileOutStreams;
|
private DiffCoderDataOutputStream[] tileOutStreams;
|
||||||
protected File outTileDir;
|
protected File outTileDir;
|
||||||
|
|
||||||
protected Map<String, String> tags;
|
protected HashMap<String,String> tags;
|
||||||
|
|
||||||
public void putTag(String key, String value) {
|
public void putTag( String key, String value )
|
||||||
if (tags == null) tags = new HashMap<>();
|
{
|
||||||
|
if ( tags == null ) tags = new HashMap<String,String>();
|
||||||
tags.put( key, value );
|
tags.put( key, value );
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTag(String key) {
|
public String getTag( String key )
|
||||||
|
{
|
||||||
return tags == null ? null : tags.get( key );
|
return tags == null ? null : tags.get( key );
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, String> getTagsOrNull() {
|
public HashMap<String,String> getTagsOrNull()
|
||||||
|
{
|
||||||
return tags;
|
return tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTags(Map<String, String> tags) {
|
public void setTags( HashMap<String,String> tags )
|
||||||
|
{
|
||||||
this.tags = tags;
|
this.tags = tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static long readId(DataInputStream is) throws IOException {
|
protected static long readId( DataInputStream is) throws IOException
|
||||||
|
{
|
||||||
int offset = is.readByte();
|
int offset = is.readByte();
|
||||||
if ( offset == 32 ) return -1;
|
if ( offset == 32 ) return -1;
|
||||||
long i = is.readInt();
|
long i = is.readInt();
|
||||||
|
|
@ -49,8 +54,10 @@ public abstract class MapCreatorBase implements WayListener, NodeListener, Relat
|
||||||
return i | offset;
|
return i | offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void writeId(DataOutputStream o, long id) throws IOException {
|
protected static void writeId( DataOutputStream o, long id ) throws IOException
|
||||||
if (id == -1) {
|
{
|
||||||
|
if ( id == -1 )
|
||||||
|
{
|
||||||
o.writeByte( 32 );
|
o.writeByte( 32 );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -61,16 +68,20 @@ public abstract class MapCreatorBase implements WayListener, NodeListener, Relat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected static File[] sortBySizeAsc(File[] files) {
|
protected static File[] sortBySizeAsc( File[] files )
|
||||||
|
{
|
||||||
int n = files.length;
|
int n = files.length;
|
||||||
long[] sizes = new long[n];
|
long[] sizes = new long[n];
|
||||||
File[] sorted = new File[n];
|
File[] sorted = new File[n];
|
||||||
for( int i=0; i<n; i++ ) sizes[i] = files[i].length();
|
for( int i=0; i<n; i++ ) sizes[i] = files[i].length();
|
||||||
for (int nf = 0; nf < n; nf++) {
|
for(int nf=0; nf<n; nf++)
|
||||||
|
{
|
||||||
int idx = -1;
|
int idx = -1;
|
||||||
long min = -1;
|
long min = -1;
|
||||||
for (int i = 0; i < n; i++) {
|
for( int i=0; i<n; i++ )
|
||||||
if (sizes[i] != -1 && (idx == -1 || sizes[i] < min)) {
|
{
|
||||||
|
if ( sizes[i] != -1 && ( idx == -1 || sizes[i] < min ) )
|
||||||
|
{
|
||||||
min = sizes[i];
|
min = sizes[i];
|
||||||
idx = i;
|
idx = i;
|
||||||
}
|
}
|
||||||
|
|
@ -81,40 +92,50 @@ public abstract class MapCreatorBase implements WayListener, NodeListener, Relat
|
||||||
return sorted;
|
return sorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected File fileFromTemplate(File template, File dir, String suffix) {
|
protected File fileFromTemplate( File template, File dir, String suffix )
|
||||||
|
{
|
||||||
String filename = template.getName();
|
String filename = template.getName();
|
||||||
filename = filename.substring( 0, filename.length() - 3 ) + suffix;
|
filename = filename.substring( 0, filename.length() - 3 ) + suffix;
|
||||||
return new File( dir, filename );
|
return new File( dir, filename );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DataInputStream createInStream(File inFile) throws IOException {
|
protected DataInputStream createInStream( File inFile ) throws IOException
|
||||||
|
{
|
||||||
return new DataInputStream( new BufferedInputStream ( new FileInputStream( inFile ) ) );
|
return new DataInputStream( new BufferedInputStream ( new FileInputStream( inFile ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DiffCoderDataOutputStream createOutStream(File outFile) throws IOException {
|
protected DiffCoderDataOutputStream createOutStream( File outFile ) throws IOException
|
||||||
|
{
|
||||||
return new DiffCoderDataOutputStream( new BufferedOutputStream( new FileOutputStream( outFile ) ) );
|
return new DiffCoderDataOutputStream( new BufferedOutputStream( new FileOutputStream( outFile ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DiffCoderDataOutputStream getOutStreamForTile(int tileIndex) throws Exception {
|
protected DiffCoderDataOutputStream getOutStreamForTile( int tileIndex ) throws Exception
|
||||||
if (tileOutStreams == null) {
|
{
|
||||||
|
if ( tileOutStreams == null )
|
||||||
|
{
|
||||||
tileOutStreams = new DiffCoderDataOutputStream[64];
|
tileOutStreams = new DiffCoderDataOutputStream[64];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tileOutStreams[tileIndex] == null) {
|
if ( tileOutStreams[tileIndex] == null )
|
||||||
|
{
|
||||||
tileOutStreams[tileIndex] = createOutStream( new File( outTileDir, getNameForTile( tileIndex ) ) );
|
tileOutStreams[tileIndex] = createOutStream( new File( outTileDir, getNameForTile( tileIndex ) ) );
|
||||||
}
|
}
|
||||||
return tileOutStreams[tileIndex];
|
return tileOutStreams[tileIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getNameForTile(int tileIndex) {
|
protected String getNameForTile( int tileIndex )
|
||||||
|
{
|
||||||
throw new IllegalArgumentException( "getNameForTile not implemented" );
|
throw new IllegalArgumentException( "getNameForTile not implemented" );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void closeTileOutStreams() throws Exception {
|
protected void closeTileOutStreams() throws Exception
|
||||||
if (tileOutStreams == null) {
|
{
|
||||||
|
if ( tileOutStreams == null )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (int tileIndex = 0; tileIndex < tileOutStreams.length; tileIndex++) {
|
for( int tileIndex=0; tileIndex<tileOutStreams.length; tileIndex++ )
|
||||||
|
{
|
||||||
if ( tileOutStreams[tileIndex] != null ) tileOutStreams[tileIndex].close();
|
if ( tileOutStreams[tileIndex] != null ) tileOutStreams[tileIndex].close();
|
||||||
tileOutStreams[tileIndex] = null;
|
tileOutStreams[tileIndex] = null;
|
||||||
}
|
}
|
||||||
|
|
@ -124,36 +145,27 @@ public abstract class MapCreatorBase implements WayListener, NodeListener, Relat
|
||||||
// interface dummys
|
// interface dummys
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nodeFileStart(File nodefile) throws Exception {
|
public void nodeFileStart( File nodefile ) throws Exception {}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nextNode(NodeData n) throws Exception {
|
public void nextNode( NodeData n ) throws Exception {}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nodeFileEnd(File nodefile) throws Exception {
|
public void nodeFileEnd( File nodefile ) throws Exception {}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean wayFileStart(File wayfile) throws Exception {
|
public boolean wayFileStart( File wayfile ) throws Exception { return true; }
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nextWay(WayData data) throws Exception {
|
public void nextWay( WayData data ) throws Exception {}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void wayFileEnd(File wayfile) throws Exception {
|
public void wayFileEnd( File wayfile ) throws Exception {}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nextRelation(RelationData data) throws Exception {
|
public void nextRelation( RelationData data ) throws Exception {}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nextRestriction(RelationData data, long fromWid, long toWid, long viaNid) throws Exception {
|
public void nextRestriction( RelationData data, long fromWid, long toWid, long viaNid ) throws Exception {}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,51 +4,60 @@ import java.io.File;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NodeCutter does 1 step in map-processing:
|
* NodeCutter does 1 step in map-processing:
|
||||||
* <p>
|
*
|
||||||
* - cuts the 45*30 node tiles into 5*5 pieces
|
* - cuts the 45*30 node tiles into 5*5 pieces
|
||||||
*
|
*
|
||||||
* @author ab
|
* @author ab
|
||||||
*/
|
*/
|
||||||
public class NodeCutter extends MapCreatorBase {
|
public class NodeCutter extends MapCreatorBase
|
||||||
|
{
|
||||||
private int lonoffset;
|
private int lonoffset;
|
||||||
private int latoffset;
|
private int latoffset;
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception
|
||||||
|
{
|
||||||
System.out.println("*** NodeCutter: Cut big node-tiles into 5x5 tiles");
|
System.out.println("*** NodeCutter: Cut big node-tiles into 5x5 tiles");
|
||||||
if (args.length != 2) {
|
if (args.length != 2)
|
||||||
|
{
|
||||||
System.out.println("usage: java NodeCutter <node-tiles-in> <node-tiles-out>" );
|
System.out.println("usage: java NodeCutter <node-tiles-in> <node-tiles-out>" );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
new NodeCutter().process( new File( args[0] ), new File( args[1] ) );
|
new NodeCutter().process( new File( args[0] ), new File( args[1] ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(File nodeTilesOut) {
|
public void init( File nodeTilesOut )
|
||||||
|
{
|
||||||
this.outTileDir = nodeTilesOut;
|
this.outTileDir = nodeTilesOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process(File nodeTilesIn, File nodeTilesOut) throws Exception {
|
public void process( File nodeTilesIn, File nodeTilesOut ) throws Exception
|
||||||
|
{
|
||||||
init( nodeTilesOut );
|
init( nodeTilesOut );
|
||||||
|
|
||||||
new NodeIterator( this, true ).processDir( nodeTilesIn, ".tlf" );
|
new NodeIterator( this, true ).processDir( nodeTilesIn, ".tlf" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nodeFileStart(File nodefile) throws Exception {
|
public void nodeFileStart( File nodefile ) throws Exception
|
||||||
|
{
|
||||||
lonoffset = -1;
|
lonoffset = -1;
|
||||||
latoffset = -1;
|
latoffset = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nextNode(NodeData n) throws Exception {
|
public void nextNode( NodeData n ) throws Exception
|
||||||
|
{
|
||||||
n.writeTo( getOutStreamForTile( getTileIndex( n.ilon, n.ilat ) ) );
|
n.writeTo( getOutStreamForTile( getTileIndex( n.ilon, n.ilat ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nodeFileEnd(File nodeFile) throws Exception {
|
public void nodeFileEnd( File nodeFile ) throws Exception
|
||||||
|
{
|
||||||
closeTileOutStreams();
|
closeTileOutStreams();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getTileIndex(int ilon, int ilat) {
|
private int getTileIndex( int ilon, int ilat )
|
||||||
|
{
|
||||||
int lonoff = (ilon / 45000000 ) * 45;
|
int lonoff = (ilon / 45000000 ) * 45;
|
||||||
int latoff = (ilat / 30000000 ) * 30;
|
int latoff = (ilat / 30000000 ) * 30;
|
||||||
if ( lonoffset == -1 ) lonoffset = lonoff;
|
if ( lonoffset == -1 ) lonoffset = lonoff;
|
||||||
|
|
@ -58,13 +67,13 @@ public class NodeCutter extends MapCreatorBase {
|
||||||
|
|
||||||
int lon = (ilon / 5000000) % 9;
|
int lon = (ilon / 5000000) % 9;
|
||||||
int lat = (ilat / 5000000) % 6;
|
int lat = (ilat / 5000000) % 6;
|
||||||
if (lon < 0 || lon > 8 || lat < 0 || lat > 5)
|
if ( lon < 0 || lon > 8 || lat < 0 || lat > 5 ) throw new IllegalArgumentException( "illegal pos: " + ilon + "," + ilat );
|
||||||
throw new IllegalArgumentException("illegal pos: " + ilon + "," + ilat);
|
|
||||||
return lon*6 + lat;
|
return lon*6 + lat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected String getNameForTile(int tileIndex) {
|
protected String getNameForTile( int tileIndex )
|
||||||
|
{
|
||||||
int lon = (tileIndex / 6 ) * 5 + lonoffset - 180;
|
int lon = (tileIndex / 6 ) * 5 + lonoffset - 180;
|
||||||
int lat = (tileIndex % 6 ) * 5 + latoffset - 90;
|
int lat = (tileIndex % 6 ) * 5 + latoffset - 90;
|
||||||
String slon = lon < 0 ? "W" + (-lon) : "E" + lon;
|
String slon = lon < 0 ? "W" + (-lon) : "E" + lon;
|
||||||
|
|
|
||||||
|
|
@ -8,42 +8,39 @@ import btools.util.DiffCoderDataOutputStream;
|
||||||
*
|
*
|
||||||
* @author ab
|
* @author ab
|
||||||
*/
|
*/
|
||||||
public class NodeData extends MapCreatorBase {
|
public class NodeData extends MapCreatorBase
|
||||||
|
{
|
||||||
public long nid;
|
public long nid;
|
||||||
public int ilon;
|
public int ilon;
|
||||||
public int ilat;
|
public int ilat;
|
||||||
public byte[] description;
|
public byte[] description;
|
||||||
public short selev = Short.MIN_VALUE;
|
public short selev = Short.MIN_VALUE;
|
||||||
|
|
||||||
public NodeData(long id, double lon, double lat) {
|
public NodeData( long id, double lon, double lat )
|
||||||
|
{
|
||||||
nid = id;
|
nid = id;
|
||||||
ilat = (int)( ( lat + 90. )*1000000. + 0.5);
|
ilat = (int)( ( lat + 90. )*1000000. + 0.5);
|
||||||
ilon = (int)( ( lon + 180. )*1000000. + 0.5);
|
ilon = (int)( ( lon + 180. )*1000000. + 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NodeData(DiffCoderDataInputStream dis) throws Exception {
|
public NodeData( DiffCoderDataInputStream dis ) throws Exception
|
||||||
|
{
|
||||||
nid = dis.readDiffed( 0 );
|
nid = dis.readDiffed( 0 );
|
||||||
ilon = (int)dis.readDiffed( 1 );
|
ilon = (int)dis.readDiffed( 1 );
|
||||||
ilat = (int)dis.readDiffed( 2 );
|
ilat = (int)dis.readDiffed( 2 );
|
||||||
int mode = dis.readByte();
|
int mode = dis.readByte();
|
||||||
if ((mode & 1) != 0) {
|
if ( ( mode & 1 ) != 0 ) { int dlen = dis.readShort(); description = new byte[dlen]; dis.readFully( description ); }
|
||||||
int dlen = dis.readShort();
|
|
||||||
description = new byte[dlen];
|
|
||||||
dis.readFully(description);
|
|
||||||
}
|
|
||||||
if ( ( mode & 2 ) != 0 ) selev = dis.readShort();
|
if ( ( mode & 2 ) != 0 ) selev = dis.readShort();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeTo(DiffCoderDataOutputStream dos) throws Exception {
|
public void writeTo( DiffCoderDataOutputStream dos ) throws Exception
|
||||||
|
{
|
||||||
dos.writeDiffed( nid, 0 );
|
dos.writeDiffed( nid, 0 );
|
||||||
dos.writeDiffed( ilon, 1 );
|
dos.writeDiffed( ilon, 1 );
|
||||||
dos.writeDiffed( ilat, 2 );
|
dos.writeDiffed( ilat, 2 );
|
||||||
int mode = (description == null ? 0 : 1 ) | ( selev == Short.MIN_VALUE ? 0 : 2 );
|
int mode = (description == null ? 0 : 1 ) | ( selev == Short.MIN_VALUE ? 0 : 2 );
|
||||||
dos.writeByte( (byte) mode);
|
dos.writeByte( (byte) mode);
|
||||||
if ((mode & 1) != 0) {
|
if ( ( mode & 1 ) != 0 ) { dos.writeShort( description.length ); dos.write( description ); }
|
||||||
dos.writeShort(description.length);
|
|
||||||
dos.write(description);
|
|
||||||
}
|
|
||||||
if ( ( mode & 2 ) != 0 ) dos.writeShort( selev );
|
if ( ( mode & 2 ) != 0 ) dos.writeShort( selev );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,19 +10,22 @@ import btools.util.TinyDenseLongMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NodeFilter does 1 step in map-processing:
|
* NodeFilter does 1 step in map-processing:
|
||||||
* <p>
|
*
|
||||||
* - filters out unused nodes according to the way file
|
* - filters out unused nodes according to the way file
|
||||||
*
|
*
|
||||||
* @author ab
|
* @author ab
|
||||||
*/
|
*/
|
||||||
public class NodeFilter extends MapCreatorBase {
|
public class NodeFilter extends MapCreatorBase
|
||||||
|
{
|
||||||
private DiffCoderDataOutputStream nodesOutStream;
|
private DiffCoderDataOutputStream nodesOutStream;
|
||||||
private File nodeTilesOut;
|
private File nodeTilesOut;
|
||||||
protected DenseLongMap nodebitmap;
|
protected DenseLongMap nodebitmap;
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception
|
||||||
|
{
|
||||||
System.out.println("*** NodeFilter: Filter way related nodes");
|
System.out.println("*** NodeFilter: Filter way related nodes");
|
||||||
if (args.length != 3) {
|
if (args.length != 3)
|
||||||
|
{
|
||||||
System.out.println("usage: java NodeFilter <node-tiles-in> <way-file-in> <node-tiles-out>" );
|
System.out.println("usage: java NodeFilter <node-tiles-in> <way-file-in> <node-tiles-out>" );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -30,11 +33,13 @@ public class NodeFilter extends MapCreatorBase {
|
||||||
new NodeFilter().process( new File( args[0] ), new File( args[1] ), new File( args[2] ) );
|
new NodeFilter().process( new File( args[0] ), new File( args[1] ), new File( args[2] ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init() throws Exception {
|
public void init() throws Exception
|
||||||
|
{
|
||||||
nodebitmap = Boolean.getBoolean( "useDenseMaps" ) ? new DenseLongMap( 512 ) : new TinyDenseLongMap();
|
nodebitmap = Boolean.getBoolean( "useDenseMaps" ) ? new DenseLongMap( 512 ) : new TinyDenseLongMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process(File nodeTilesIn, File wayFileIn, File nodeTilesOut) throws Exception {
|
public void process( File nodeTilesIn, File wayFileIn, File nodeTilesOut ) throws Exception
|
||||||
|
{
|
||||||
init();
|
init();
|
||||||
|
|
||||||
this.nodeTilesOut = nodeTilesOut;
|
this.nodeTilesOut = nodeTilesOut;
|
||||||
|
|
@ -47,34 +52,41 @@ public class NodeFilter extends MapCreatorBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nextWay(WayData data) throws Exception {
|
public void nextWay( WayData data ) throws Exception
|
||||||
|
{
|
||||||
int nnodes = data.nodes.size();
|
int nnodes = data.nodes.size();
|
||||||
for (int i = 0; i < nnodes; i++) {
|
for (int i=0; i<nnodes; i++ )
|
||||||
|
{
|
||||||
nodebitmap.put( data.nodes.get(i), 0 );
|
nodebitmap.put( data.nodes.get(i), 0 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nodeFileStart(File nodefile) throws Exception {
|
public void nodeFileStart( File nodefile ) throws Exception
|
||||||
|
{
|
||||||
String filename = nodefile.getName();
|
String filename = nodefile.getName();
|
||||||
File outfile = new File( nodeTilesOut, filename );
|
File outfile = new File( nodeTilesOut, filename );
|
||||||
nodesOutStream = new DiffCoderDataOutputStream( new BufferedOutputStream ( new FileOutputStream( outfile ) ) );
|
nodesOutStream = new DiffCoderDataOutputStream( new BufferedOutputStream ( new FileOutputStream( outfile ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nextNode(NodeData n) throws Exception {
|
public void nextNode( NodeData n ) throws Exception
|
||||||
if (isRelevant(n)) {
|
{
|
||||||
|
if ( isRelevant( n ) )
|
||||||
|
{
|
||||||
n.writeTo( nodesOutStream );
|
n.writeTo( nodesOutStream );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRelevant(NodeData n) {
|
public boolean isRelevant( NodeData n )
|
||||||
|
{
|
||||||
// check if node passes bitmap
|
// check if node passes bitmap
|
||||||
return nodebitmap.getInt( n.nid ) == 0; // 0 -> bit set, -1 -> unset
|
return nodebitmap.getInt( n.nid ) == 0; // 0 -> bit set, -1 -> unset
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nodeFileEnd(File nodeFile) throws Exception {
|
public void nodeFileEnd( File nodeFile ) throws Exception
|
||||||
|
{
|
||||||
nodesOutStream.close();
|
nodesOutStream.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,46 +13,58 @@ import btools.util.DiffCoderDataInputStream;
|
||||||
*
|
*
|
||||||
* @author ab
|
* @author ab
|
||||||
*/
|
*/
|
||||||
public class NodeIterator extends MapCreatorBase {
|
public class NodeIterator extends MapCreatorBase
|
||||||
|
{
|
||||||
private NodeListener listener;
|
private NodeListener listener;
|
||||||
private boolean delete;
|
private boolean delete;
|
||||||
|
|
||||||
public NodeIterator(NodeListener nodeListener, boolean deleteAfterReading) {
|
public NodeIterator( NodeListener nodeListener, boolean deleteAfterReading )
|
||||||
|
{
|
||||||
listener = nodeListener;
|
listener = nodeListener;
|
||||||
delete = deleteAfterReading;
|
delete = deleteAfterReading;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void processDir(File indir, String inSuffix) throws Exception {
|
public void processDir( File indir, String inSuffix ) throws Exception
|
||||||
if (!indir.isDirectory()) {
|
{
|
||||||
|
if ( !indir.isDirectory() )
|
||||||
|
{
|
||||||
throw new IllegalArgumentException( "not a directory: " + indir );
|
throw new IllegalArgumentException( "not a directory: " + indir );
|
||||||
}
|
}
|
||||||
|
|
||||||
File[] af = sortBySizeAsc( indir.listFiles() );
|
File[] af = sortBySizeAsc( indir.listFiles() );
|
||||||
for (int i = 0; i < af.length; i++) {
|
for( int i=0; i<af.length; i++ )
|
||||||
|
{
|
||||||
File nodefile = af[i];
|
File nodefile = af[i];
|
||||||
if (nodefile.getName().endsWith(inSuffix)) {
|
if ( nodefile.getName().endsWith( inSuffix ) )
|
||||||
|
{
|
||||||
processFile( nodefile );
|
processFile( nodefile );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void processFile(File nodefile) throws Exception {
|
public void processFile(File nodefile) throws Exception
|
||||||
|
{
|
||||||
System.out.println( "*** NodeIterator reading: " + nodefile );
|
System.out.println( "*** NodeIterator reading: " + nodefile );
|
||||||
|
|
||||||
listener.nodeFileStart( nodefile );
|
listener.nodeFileStart( nodefile );
|
||||||
|
|
||||||
DiffCoderDataInputStream di = new DiffCoderDataInputStream( new BufferedInputStream ( new FileInputStream( nodefile ) ) );
|
DiffCoderDataInputStream di = new DiffCoderDataInputStream( new BufferedInputStream ( new FileInputStream( nodefile ) ) );
|
||||||
try {
|
try
|
||||||
for (; ; ) {
|
{
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
NodeData n = new NodeData( di );
|
NodeData n = new NodeData( di );
|
||||||
listener.nextNode( n );
|
listener.nextNode( n );
|
||||||
}
|
}
|
||||||
} catch (EOFException eof) {
|
}
|
||||||
|
catch( EOFException eof )
|
||||||
|
{
|
||||||
di.close();
|
di.close();
|
||||||
}
|
}
|
||||||
listener.nodeFileEnd( nodefile );
|
listener.nodeFileEnd( nodefile );
|
||||||
if (delete && "true".equals(System.getProperty("deletetmpfiles"))) {
|
if ( delete && "true".equals( System.getProperty( "deletetmpfiles" ) ))
|
||||||
|
{
|
||||||
nodefile.delete();
|
nodefile.delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ import java.io.File;
|
||||||
*
|
*
|
||||||
* @author ab
|
* @author ab
|
||||||
*/
|
*/
|
||||||
public interface NodeListener {
|
public interface NodeListener
|
||||||
|
{
|
||||||
void nodeFileStart( File nodefile ) throws Exception;
|
void nodeFileStart( File nodefile ) throws Exception;
|
||||||
|
|
||||||
void nextNode( NodeData data ) throws Exception;
|
void nextNode( NodeData data ) throws Exception;
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue